i18n: 全部用户可见字符串统一为英文(46个文件)

- 所有Command/Tool/Core/MCP/Plugin中的中文提示改为英文
- Javadoc注释和行内注释保留中文不变
- AI提示词(compact/commit/review等)改为英文
- 编译验证通过

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
liuzh
2026-04-02 00:30:47 +08:00
co-authored by Copilot
parent 1172ab6b08
commit ff734d6b0d
46 changed files with 469 additions and 469 deletions
@@ -20,7 +20,7 @@ public class CommandRegistry {
for (String alias : command.aliases()) {
commands.put(alias.toLowerCase(), command);
}
log.debug("注册命令: /{}", command.name());
log.debug("Registered command: /{}", command.name());
}
/** 批量注册 */
@@ -44,7 +44,7 @@ public class BranchCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用。");
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
String trimmedArgs = args != null ? args.trim() : "";
@@ -63,7 +63,7 @@ public class BranchCommand implements SlashCommand {
case "load" -> loadBranch(branchName, context);
case "list" -> listBranches(context);
case "delete" -> deleteBranch(branchName);
default -> AnsiStyle.red("未知子命令: " + subCommand) + "\n" + showUsage();
default -> AnsiStyle.red("Unknown subcommand: " + subCommand) + "\n" + showUsage();
};
}
@@ -76,8 +76,8 @@ public class BranchCommand implements SlashCommand {
*/
private String saveBranch(String branchName, CommandContext context) {
if (branchName.isEmpty()) {
return AnsiStyle.red("请指定分支名称。") + "\n"
+ AnsiStyle.dim(" 用法: /branch save <name>");
return AnsiStyle.red("Please specify branch name.") + "\n"
+ AnsiStyle.dim(" Usage: /branch save <name>");
}
List<Message> currentHistory = context.agentLoop().getMessageHistory();
@@ -87,8 +87,8 @@ public class BranchCommand implements SlashCommand {
String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
branches.put(branchName, new BranchSnapshot(snapshot, timestamp));
return AnsiStyle.green("分支已保存: ") + AnsiStyle.bold(branchName) + "\n"
+ AnsiStyle.dim(" 消息数: " + snapshot.size() + " 时间: " + timestamp);
return AnsiStyle.green("Branch saved: ") + AnsiStyle.bold(branchName) + "\n"
+ AnsiStyle.dim(" Messages: " + snapshot.size() + " Time: " + timestamp);
}
/**
@@ -100,21 +100,21 @@ public class BranchCommand implements SlashCommand {
*/
private String loadBranch(String branchName, CommandContext context) {
if (branchName.isEmpty()) {
return AnsiStyle.red("请指定分支名称。") + "\n"
+ AnsiStyle.dim(" 用法: /branch load <name>");
return AnsiStyle.red("Please specify branch name.") + "\n"
+ AnsiStyle.dim(" Usage: /branch load <name>");
}
BranchSnapshot snapshot = branches.get(branchName);
if (snapshot == null) {
return AnsiStyle.red("分支不存在: " + branchName) + "\n"
+ AnsiStyle.dim(" 使用 /branch list 查看所有可用分支。");
return AnsiStyle.red("Branch not found: " + branchName) + "\n"
+ AnsiStyle.dim(" Use /branch list to see all available branches.");
}
// 恢复对话历史
context.agentLoop().replaceHistory(new ArrayList<>(snapshot.messages()));
return AnsiStyle.green("已恢复到分支: ") + AnsiStyle.bold(branchName) + "\n"
+ AnsiStyle.dim(" 已加载 " + snapshot.messages().size() + " 条消息 (保存于 " + snapshot.timestamp() + ")");
return AnsiStyle.green("Restored to branch: ") + AnsiStyle.bold(branchName) + "\n"
+ AnsiStyle.dim(" Loaded " + snapshot.messages().size() + " messages (saved at " + snapshot.timestamp() + ")");
}
/**
@@ -125,8 +125,8 @@ public class BranchCommand implements SlashCommand {
*/
private String listBranches(CommandContext context) {
if (branches.isEmpty()) {
return AnsiStyle.dim(" 没有保存的分支。") + "\n"
+ AnsiStyle.dim(" 使用 /branch save <name> 保存当前对话。");
return AnsiStyle.dim(" No saved branches.") + "\n"
+ AnsiStyle.dim(" Use /branch save <name> to save current conversation.");
}
int currentSize = context.agentLoop().getMessageHistory().size();
@@ -149,7 +149,7 @@ public class BranchCommand implements SlashCommand {
.append("\n");
}
sb.append("\n").append(AnsiStyle.dim(" " + branches.size() + " 个分支。")).append("\n");
sb.append("\n").append(AnsiStyle.dim(" Total " + branches.size() + " branches.")).append("\n");
return sb.toString();
}
@@ -161,16 +161,16 @@ public class BranchCommand implements SlashCommand {
*/
private String deleteBranch(String branchName) {
if (branchName.isEmpty()) {
return AnsiStyle.red("请指定分支名称。") + "\n"
+ AnsiStyle.dim(" 用法: /branch delete <name>");
return AnsiStyle.red("Please specify branch name.") + "\n"
+ AnsiStyle.dim(" Usage: /branch delete <name>");
}
BranchSnapshot removed = branches.remove(branchName);
if (removed == null) {
return AnsiStyle.red("分支不存在: " + branchName);
return AnsiStyle.red("Branch not found: " + branchName);
}
return AnsiStyle.green("分支已删除: ") + AnsiStyle.bold(branchName);
return AnsiStyle.green("Branch deleted: ") + AnsiStyle.bold(branchName);
}
/**
@@ -180,11 +180,11 @@ public class BranchCommand implements SlashCommand {
*/
private String showUsage() {
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.bold("\n 🌿 Branch — 对话分支管理\n\n"));
sb.append(" ").append(AnsiStyle.cyan("/branch save <name>")).append(" 保存当前对话为分支\n");
sb.append(" ").append(AnsiStyle.cyan("/branch load <name>")).append(" 恢复到指定分支\n");
sb.append(" ").append(AnsiStyle.cyan("/branch list")).append(" 列出所有分支\n");
sb.append(" ").append(AnsiStyle.cyan("/branch delete <name>")).append(" 删除指定分支\n");
sb.append(AnsiStyle.bold("\n 🌿 Branch — Conversation branch management\n\n"));
sb.append(" ").append(AnsiStyle.cyan("/branch save <name>")).append(" Save current conversation as branch\n");
sb.append(" ").append(AnsiStyle.cyan("/branch load <name>")).append(" Restore to specified branch\n");
sb.append(" ").append(AnsiStyle.cyan("/branch list")).append(" List all branches\n");
sb.append(" ").append(AnsiStyle.cyan("/branch delete <name>")).append(" Delete specified branch\n");
return sb.toString();
}
@@ -36,7 +36,7 @@ public class CommitCommand implements SlashCommand {
public String execute(String args, CommandContext context) {
Path projectDir = Path.of(System.getProperty("user.dir"));
if (!Files.isDirectory(projectDir.resolve(".git"))) {
return AnsiStyle.yellow("当前目录不是 Git 仓库");
return AnsiStyle.yellow("Current directory is not a Git repository");
}
args = args == null ? "" : args.strip();
@@ -49,7 +49,7 @@ public class CommitCommand implements SlashCommand {
if (addAll) {
String addResult = runGit(projectDir, "add", "-A");
if (addResult == null) {
return AnsiStyle.red(" ✗ git add 失败");
return AnsiStyle.red(" ✗ git add failed");
}
}
@@ -58,29 +58,29 @@ public class CommitCommand implements SlashCommand {
if (staged == null || staged.isBlank()) {
String status = runGit(projectDir, "status", "--short");
if (status != null && !status.isBlank()) {
return AnsiStyle.yellow("没有已暂存的变更\n")
+ AnsiStyle.dim(" 使用 /commit --all 自动添加所有文件\n")
+ AnsiStyle.dim(" 或先手动执行 git add");
return AnsiStyle.yellow("No staged changes\n")
+ AnsiStyle.dim(" Use /commit --all to add all files\n")
+ AnsiStyle.dim(" Or run git add manually first");
}
return AnsiStyle.green("工作区干净,无需提交");
return AnsiStyle.green("Working directory clean, nothing to commit");
}
// 如果没有指定 message,使用 AI 生成
if (message.isEmpty()) {
message = generateCommitMessage(projectDir, context);
if (message == null || message.isBlank()) {
return AnsiStyle.red("无法生成 commit message");
return AnsiStyle.red("Failed to generate commit message");
}
}
// 执行 git commit
String commitResult = runGit(projectDir, "commit", "-m", message);
if (commitResult == null) {
return AnsiStyle.red(" ✗ git commit 失败");
return AnsiStyle.red(" ✗ git commit failed");
}
StringBuilder sb = new StringBuilder();
sb.append("\n").append(AnsiStyle.green(" ✓ Commit 成功\n"));
sb.append("\n").append(AnsiStyle.green(" ✓ Commit successful\n"));
sb.append(" ").append("".repeat(50)).append("\n");
sb.append(" ").append(AnsiStyle.bold("Message: ")).append(message).append("\n");
@@ -90,7 +90,7 @@ public class CommitCommand implements SlashCommand {
return sb.toString();
} catch (Exception e) {
return AnsiStyle.red("提交失败: " + e.getMessage());
return AnsiStyle.red("Commit failed: " + e.getMessage());
}
}
@@ -108,12 +108,12 @@ public class CommitCommand implements SlashCommand {
// 使用 ChatModel 生成 commit message
String prompt = """
分析以下 git diff,生成一个简洁的 commit message
要求:
1. 使用 conventional commits 格式(feat/fix/docs/refactor/chore等前缀)
2. 第一行不超过 72 个字符
3. 如果有多个变更,可以在第一行后空一行添加详细说明
4. 只返回 commit message 文本,不要添加其他说明
Analyze the following git diff and generate a concise commit message.
Requirements:
1. Use conventional commits format (feat/fix/docs/refactor/chore prefix)
2. First line should not exceed 72 characters
3. For multiple changes, add details after a blank line
4. Return only the commit message text, no additional explanation
Git diff:
```
@@ -22,14 +22,14 @@ import java.util.List;
public class CompactCommand implements SlashCommand {
private static final String COMPACT_PROMPT = """
请将以下对话历史压缩为一段简洁的摘要。要求:
1. 保留所有关键决策、代码变更和技术细节
2. 保留文件路径、函数名等具体信息
3. 保留用户的偏好和要求
4. 省略重复的讨论和无关的细节
5. 用中文输出,控制在500字以内
Please compress the following conversation history into a concise summary. Requirements:
1. Preserve all key decisions, code changes, and technical details
2. Keep file paths, function names, and specific information
3. Preserve user preferences and requirements
4. Omit repeated discussions and irrelevant details
5. Output within 500 words
对话历史:
Conversation history:
""";
@Override
@@ -45,14 +45,14 @@ public class CompactCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.yellow("没有活跃的对话可压缩。");
return AnsiStyle.yellow("No active conversation to compact.");
}
List<Message> history = context.agentLoop().getMessageHistory();
int before = history.size();
if (before <= 3) {
return AnsiStyle.dim(" 上下文已经很小(" + before + " 条消息),无需压缩。");
return AnsiStyle.dim(" Context is already small (" + before + " messages), no compaction needed.");
}
TokenTracker tracker = context.agentLoop().getTokenTracker();
@@ -66,7 +66,7 @@ public class CompactCommand implements SlashCommand {
compacted.add(history.getFirst()); // 原始系统提示词
if (summary != null && !summary.isBlank()) {
compacted.add(new SystemMessage("[对话历史摘要] " + summary));
compacted.add(new SystemMessage("[Conversation Summary] " + summary));
}
// 保留最后一轮用户消息和助手回复(如果有)
@@ -78,15 +78,15 @@ public class CompactCommand implements SlashCommand {
int after = compacted.size();
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.green("上下文已压缩")).append("\n");
sb.append(" 消息数: ").append(before).append("").append(after).append("\n");
sb.append(AnsiStyle.green("Context compacted")).append("\n");
sb.append(" Messages: ").append(before).append("").append(after).append("\n");
if (tokensBefore > 0) {
sb.append(" 压缩前累计 Token: ").append(TokenTracker.formatTokens(tokensBefore)).append("\n");
sb.append(" Tokens before compaction: ").append(TokenTracker.formatTokens(tokensBefore)).append("\n");
}
if (summary != null) {
sb.append(AnsiStyle.dim(" 📝 AI 摘要已生成并注入上下文"));
sb.append(AnsiStyle.dim(" 📝 AI summary generated and injected into context"));
} else {
sb.append(AnsiStyle.dim(" ⚠ AI 摘要生成失败,仅保留最近对话"));
sb.append(AnsiStyle.dim(" ⚠ AI summary generation failed, keeping recent conversation only"));
}
return sb.toString();
@@ -101,17 +101,17 @@ public class CompactCommand implements SlashCommand {
StringBuilder dialogText = new StringBuilder();
for (Message msg : history) {
switch (msg) {
case UserMessage um -> dialogText.append("[用户] ").append(um.getText()).append("\n");
case UserMessage um -> dialogText.append("[User] ").append(um.getText()).append("\n");
case AssistantMessage am -> {
if (am.getText() != null && !am.getText().isBlank()) {
// 截断过长的助手回复
String text = am.getText();
if (text.length() > 500) text = text.substring(0, 500) + "...";
dialogText.append("[助手] ").append(text).append("\n");
dialogText.append("[Assistant] ").append(text).append("\n");
}
if (am.hasToolCalls()) {
for (var tc : am.getToolCalls()) {
dialogText.append("[工具调用] ").append(tc.name()).append("\n");
dialogText.append("[Tool Call] ").append(tc.name()).append("\n");
}
}
}
@@ -46,7 +46,7 @@ public class CopyCommand implements SlashCommand {
}
if (lastResponse == null) {
return AnsiStyle.yellow("暂无 AI 回复可复制");
return AnsiStyle.yellow("No AI response to copy");
}
try {
@@ -56,14 +56,14 @@ public class CopyCommand implements SlashCommand {
int charCount = lastResponse.length();
int lineCount = (int) lastResponse.lines().count();
return AnsiStyle.green("已复制到剪贴板")
+ AnsiStyle.dim(" (" + charCount + " 字符, " + lineCount + " )");
return AnsiStyle.green("Copied to clipboard")
+ AnsiStyle.dim(" (" + charCount + " chars, " + lineCount + " lines)");
} catch (java.awt.HeadlessException e) {
// 无头环境(如 SSH)无法使用 AWT 剪贴板
return AnsiStyle.yellow("当前环境不支持剪贴板(Headless 模式)\n")
+ AnsiStyle.dim(" 提示:在有图形界面的终端中运行可使用此功能");
return AnsiStyle.yellow("Clipboard not supported (headless mode)\n")
+ AnsiStyle.dim(" Tip: Run in a graphical terminal to use this feature");
} catch (Exception e) {
return AnsiStyle.red("复制失败: " + e.getMessage());
return AnsiStyle.red("Copy failed: " + e.getMessage());
}
}
}
@@ -36,7 +36,7 @@ public class DiffCommand implements SlashCommand {
public String execute(String args, CommandContext context) {
Path projectDir = Path.of(System.getProperty("user.dir"));
if (!Files.isDirectory(projectDir.resolve(".git"))) {
return AnsiStyle.yellow("当前目录不是 Git 仓库");
return AnsiStyle.yellow("Current directory is not a Git repository");
}
args = args == null ? "" : args.strip();
@@ -72,7 +72,7 @@ public class DiffCommand implements SlashCommand {
long lineCount = unstaged.lines().count();
if (lineCount > 100) {
unstaged.lines().limit(100).forEach(l -> sb.append(" ").append(l).append("\n"));
sb.append(AnsiStyle.dim(" ... (" + lineCount + " 行,截断显示前100)\n"));
sb.append(AnsiStyle.dim(" ... (" + lineCount + " lines total, showing first 100)\n"));
} else {
unstaged.lines().forEach(l -> sb.append(" ").append(l).append("\n"));
}
@@ -84,7 +84,7 @@ public class DiffCommand implements SlashCommand {
}
if (staged.isBlank() && unstaged.isBlank() && untracked.isBlank()) {
sb.append("\n").append(AnsiStyle.green("工作区干净,无变更\n"));
sb.append("\n").append(AnsiStyle.green("Working directory clean, no changes\n"));
}
return sb.toString();
@@ -96,12 +96,12 @@ public class DiffCommand implements SlashCommand {
sb.append(" ").append("".repeat(50)).append("\n\n");
if (diffOutput.isBlank()) {
sb.append(AnsiStyle.green("无变更\n"));
sb.append(AnsiStyle.green("No changes\n"));
} else {
long lineCount = diffOutput.lines().count();
if (lineCount > 100) {
diffOutput.lines().limit(100).forEach(l -> sb.append(" ").append(l).append("\n"));
sb.append(AnsiStyle.dim(" ... (" + lineCount + " )\n"));
sb.append(AnsiStyle.dim(" ... (" + lineCount + " lines)\n"));
} else {
diffOutput.lines().forEach(l -> sb.append(" ").append(l).append("\n"));
}
@@ -110,7 +110,7 @@ public class DiffCommand implements SlashCommand {
return sb.toString();
} catch (Exception e) {
return AnsiStyle.red(" ✗ Git diff 执行失败: " + e.getMessage());
return AnsiStyle.red(" ✗ Git diff failed: " + e.getMessage());
}
}
@@ -42,7 +42,7 @@ public class ExportCommand implements SlashCommand {
// 至少需要系统提示 + 用户消息 + 助手回复
if (history.size() < 3) {
return AnsiStyle.yellow("暂无足够的对话内容可导出");
return AnsiStyle.yellow("Not enough conversation content to export");
}
// 确定输出路径
@@ -67,10 +67,10 @@ public class ExportCommand implements SlashCommand {
int msgCount = history.size();
int lineCount = (int) markdown.lines().count();
return AnsiStyle.green("对话已导出: " + outputPath)
return AnsiStyle.green("Conversation exported: " + outputPath)
+ AnsiStyle.dim(" (" + msgCount + " messages, " + lineCount + " lines)");
} catch (IOException e) {
return AnsiStyle.red("导出失败: " + e.getMessage());
return AnsiStyle.red("Export failed: " + e.getMessage());
}
}
@@ -28,12 +28,12 @@ public class HistoryCommand implements SlashCommand {
var conversations = persistence.listConversations();
if (conversations.isEmpty()) {
return AnsiStyle.dim(" 📂 暂无保存的对话历史");
return AnsiStyle.dim(" 📂 No saved conversation history");
}
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.bold(" 📂 对话历史") + AnsiStyle.dim(" (")
+ conversations.size() + AnsiStyle.dim(" 条记录)\n"));
sb.append(AnsiStyle.bold(" 📂 Conversation History") + AnsiStyle.dim(" (")
+ conversations.size() + AnsiStyle.dim(" records)\n"));
sb.append(AnsiStyle.dim(" " + "".repeat(50)) + "\n");
int shown = Math.min(conversations.size(), 10);
@@ -46,10 +46,10 @@ public class HistoryCommand implements SlashCommand {
}
if (conversations.size() > 10) {
sb.append(AnsiStyle.dim(" ... 还有 " + (conversations.size() - 10) + " 条更早的记录\n"));
sb.append(AnsiStyle.dim(" ... and " + (conversations.size() - 10) + " older records\n"));
}
sb.append(AnsiStyle.dim("\n 对话存储位置: " + persistence.getConversationsDir()));
sb.append(AnsiStyle.dim("\n Storage location: " + persistence.getConversationsDir()));
return sb.toString();
}
@@ -32,7 +32,7 @@ public class HooksCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null || context.agentLoop().getHookManager() == null) {
return AnsiStyle.yellow(" ⚠ Hook 管理器不可用。");
return AnsiStyle.yellow(" ⚠ Hook manager unavailable.");
}
HookManager hookManager = context.agentLoop().getHookManager();
@@ -86,10 +86,10 @@ public class HooksCommand implements SlashCommand {
*/
private String formatTypeName(HookType type) {
return switch (type) {
case PRE_TOOL_USE -> "PRE_TOOL_USE (工具执行前)";
case POST_TOOL_USE -> "POST_TOOL_USE (工具执行后)";
case PRE_PROMPT -> "PRE_PROMPT (发送提示前)";
case POST_RESPONSE -> "POST_RESPONSE (收到响应后)";
case PRE_TOOL_USE -> "PRE_TOOL_USE (before tool execution)";
case POST_TOOL_USE -> "POST_TOOL_USE (after tool execution)";
case PRE_PROMPT -> "PRE_PROMPT (before sending prompt)";
case POST_RESPONSE -> "POST_RESPONSE (after receiving response)";
};
}
}
@@ -44,7 +44,7 @@ public class McpCommand implements SlashCommand {
// 暂时通过静态持有者或类似机制获取。
McpManager manager = McpManagerHolder.getInstance();
if (manager == null) {
return AnsiStyle.red(" ❌ MCP 管理器未初始化");
return AnsiStyle.red(" ❌ MCP manager not initialized");
}
String trimmed = args.strip();
@@ -63,7 +63,7 @@ public class McpCommand implements SlashCommand {
case "resources" -> handleResources(manager, subArgs);
case "reload" -> handleReload(manager, context);
case "help" -> showHelp();
default -> AnsiStyle.red(" 未知子命令: " + subCommand) + "\n" + showHelp();
default -> AnsiStyle.red(" Unknown subcommand: " + subCommand) + "\n" + showHelp();
};
}
@@ -73,14 +73,14 @@ public class McpCommand implements SlashCommand {
private String showStatus(McpManager manager) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.bold(" 🔌 MCP 服务器状态\n"));
sb.append(AnsiStyle.bold(" 🔌 MCP Server Status\n"));
sb.append(" ").append("".repeat(50)).append("\n\n");
Map<String, McpClient> clients = manager.getClients();
if (clients.isEmpty()) {
sb.append(" 无已连接的 MCP 服务器\n\n");
sb.append(AnsiStyle.dim(" 提示: 使用 /mcp connect <name> <command> [args] 连接服务器\n"));
sb.append(AnsiStyle.dim(" 或在 .mcp.json 配置文件中定义服务器\n"));
sb.append(" No connected MCP servers\n\n");
sb.append(AnsiStyle.dim(" Tip: Use /mcp connect <name> <command> [args] to connect\n"));
sb.append(AnsiStyle.dim(" Or define servers in .mcp.json config file\n"));
return sb.toString();
}
@@ -92,13 +92,13 @@ public class McpCommand implements SlashCommand {
String statusText;
if (client.isConnected() && client.isInitialized()) {
statusIcon = "";
statusText = AnsiStyle.green("已连接");
statusText = AnsiStyle.green("Connected");
} else if (client.isConnected()) {
statusIcon = "🔄";
statusText = AnsiStyle.yellow("连接中");
statusText = AnsiStyle.yellow("Connecting");
} else {
statusIcon = "";
statusText = AnsiStyle.red("已断开");
statusText = AnsiStyle.red("Disconnected");
}
sb.append(String.format(" %s %-18s %s%n", statusIcon, AnsiStyle.bold(name), statusText));
@@ -107,7 +107,7 @@ public class McpCommand implements SlashCommand {
int toolCount = client.getTools().size();
int resCount = client.getResources().size();
sb.append(String.format(" %s%n",
AnsiStyle.dim(toolCount + " 工具, " + resCount + " 资源")));
AnsiStyle.dim(toolCount + " tools, " + resCount + " resources")));
// 显示服务器信息
if (client.getServerInfo() != null) {
@@ -117,8 +117,8 @@ public class McpCommand implements SlashCommand {
}
sb.append("\n");
sb.append(AnsiStyle.dim(" " + clients.size() + " 个服务器, "
+ manager.getAllTools().size() + " 个工具\n"));
sb.append(AnsiStyle.dim(" Total " + clients.size() + " servers, "
+ manager.getAllTools().size() + " tools\n"));
return sb.toString();
}
@@ -128,12 +128,12 @@ public class McpCommand implements SlashCommand {
*/
private String handleConnect(McpManager manager, String args, CommandContext context) {
if (args.isEmpty()) {
return AnsiStyle.red(" 用法: /mcp connect <name> <command> [args...]");
return AnsiStyle.red(" Usage: /mcp connect <name> <command> [args...]");
}
String[] parts = args.split("\\s+");
if (parts.length < 2) {
return AnsiStyle.red(" 用法: /mcp connect <name> <command> [args...]");
return AnsiStyle.red(" Usage: /mcp connect <name> <command> [args...]");
}
String name = parts[0];
@@ -149,13 +149,13 @@ public class McpCommand implements SlashCommand {
registerBridgedTools(client, name, context);
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.green("已连接 MCP 服务器: " + name)).append("\n");
sb.append(AnsiStyle.dim(" " + client.getTools().size() + " 个工具, "
+ client.getResources().size() + " 个资源")).append("\n");
sb.append(AnsiStyle.green("Connected to MCP server: " + name)).append("\n");
sb.append(AnsiStyle.dim(" " + client.getTools().size() + " tools, "
+ client.getResources().size() + " resources")).append("\n");
// 列出发现的工具
if (!client.getTools().isEmpty()) {
sb.append("\n 工具:\n");
sb.append("\n Tools:\n");
for (McpClient.McpTool tool : client.getTools()) {
sb.append("").append(tool.name());
if (!tool.description().isEmpty()) {
@@ -167,7 +167,7 @@ public class McpCommand implements SlashCommand {
return sb.toString();
} catch (McpException e) {
return AnsiStyle.red("连接失败: " + e.getMessage());
return AnsiStyle.red("Connection failed: " + e.getMessage());
}
}
@@ -176,15 +176,15 @@ public class McpCommand implements SlashCommand {
*/
private String handleDisconnect(McpManager manager, String args) {
if (args.isEmpty()) {
return AnsiStyle.red(" 用法: /mcp disconnect <name>");
return AnsiStyle.red(" Usage: /mcp disconnect <name>");
}
String name = args.split("\\s+")[0];
try {
manager.disconnect(name);
return AnsiStyle.green("已断开 MCP 服务器: " + name);
return AnsiStyle.green("Disconnected MCP server: " + name);
} catch (McpException e) {
return AnsiStyle.red("断开失败: " + e.getMessage());
return AnsiStyle.red("Disconnect failed: " + e.getMessage());
}
}
@@ -194,7 +194,7 @@ public class McpCommand implements SlashCommand {
private String handleTools(McpManager manager, String args) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.bold(" 🛠️ MCP 工具列表\n"));
sb.append(AnsiStyle.bold(" 🛠️ MCP Tools\n"));
sb.append(" ").append("".repeat(50)).append("\n\n");
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
@@ -203,13 +203,13 @@ public class McpCommand implements SlashCommand {
if (serverFilter != null) {
tools = manager.getServerTools(serverFilter);
if (tools.isEmpty()) {
return sb + " 服务器 '" + serverFilter + "' 无工具或不存在\n";
return sb + " Server '" + serverFilter + "' has no tools or does not exist\n";
}
sb.append(AnsiStyle.dim(" 服务器: " + serverFilter)).append("\n\n");
sb.append(AnsiStyle.dim(" Server: " + serverFilter)).append("\n\n");
} else {
tools = manager.getAllTools();
if (tools.isEmpty()) {
return sb + " 无可用的 MCP 工具\n";
return sb + " No available MCP tools\n";
}
}
@@ -225,7 +225,7 @@ public class McpCommand implements SlashCommand {
sb.append("\n");
}
sb.append(AnsiStyle.dim(" " + tools.size() + " 个工具")).append("\n");
sb.append(AnsiStyle.dim(" Total " + tools.size() + " tools")).append("\n");
return sb.toString();
}
@@ -235,7 +235,7 @@ public class McpCommand implements SlashCommand {
private String handleResources(McpManager manager, String args) {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.bold(" 📦 MCP 资源列表\n"));
sb.append(AnsiStyle.bold(" 📦 MCP Resources\n"));
sb.append(" ").append("".repeat(50)).append("\n\n");
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
@@ -244,13 +244,13 @@ public class McpCommand implements SlashCommand {
if (serverFilter != null) {
resourceList = manager.getServerResources(serverFilter);
if (resourceList.isEmpty()) {
return sb + " 服务器 '" + serverFilter + "' 无资源或不存在\n";
return sb + " Server '" + serverFilter + "' has no resources or does not exist\n";
}
sb.append(AnsiStyle.dim(" 服务器: " + serverFilter)).append("\n\n");
sb.append(AnsiStyle.dim(" Server: " + serverFilter)).append("\n\n");
} else {
resourceList = manager.getAllResources();
if (resourceList.isEmpty()) {
return sb + " 无可用的 MCP 资源\n";
return sb + " No available MCP resources\n";
}
}
@@ -263,7 +263,7 @@ public class McpCommand implements SlashCommand {
sb.append(" MIME: ").append(AnsiStyle.dim(resource.mimeType())).append("\n\n");
}
sb.append(AnsiStyle.dim(" " + resourceList.size() + " 个资源")).append("\n");
sb.append(AnsiStyle.dim(" Total " + resourceList.size() + " resources")).append("\n");
return sb.toString();
}
@@ -279,11 +279,11 @@ public class McpCommand implements SlashCommand {
registerBridgedTools(entry.getValue(), entry.getKey(), context);
}
return AnsiStyle.green(" ✅ MCP 配置已重新加载: "
+ manager.getClients().size() + " 个服务器, "
+ manager.getAllTools().size() + " 个工具");
return AnsiStyle.green(" ✅ MCP config reloaded: "
+ manager.getClients().size() + " servers, "
+ manager.getAllTools().size() + " tools");
} catch (Exception e) {
return AnsiStyle.red("重载失败: " + e.getMessage());
return AnsiStyle.red("Reload failed: " + e.getMessage());
}
}
@@ -308,20 +308,20 @@ public class McpCommand implements SlashCommand {
private String showHelp() {
return """
\033[1m🔌 MCP 命令帮助\033[0m
\033[1m🔌 MCP Command Help\033[0m
──────────────────────────────────────
/mcp 列出所有 MCP 服务器状态
/mcp connect <name> <cmd> [args] 连接到 MCP 服务器
/mcp disconnect <name> 断开 MCP 服务器
/mcp tools [server] 列出 MCP 工具
/mcp resources [server] 列出 MCP 资源
/mcp reload 从配置文件重新加载
/mcp help 显示此帮助信息
/mcp List all MCP server status
/mcp connect <name> <cmd> [args] Connect to MCP server
/mcp disconnect <name> Disconnect MCP server
/mcp tools [server] List MCP tools
/mcp resources [server] List MCP resources
/mcp reload Reload from config file
/mcp help Show this help
配置文件:
项目级: .mcp.json
全局: ~/.claude-code-java/mcp.json
Config files:
Project: .mcp.json
Global: ~/.claude-code-java/mcp.json
""";
}
@@ -56,13 +56,13 @@ public class MemoryCommand implements SlashCommand {
/** 显示项目级 CLAUDE.md */
private String showProjectMemory() {
Path projectClaudeMd = Path.of(System.getProperty("user.dir"), "CLAUDE.md");
return showMemoryFile(projectClaudeMd, "项目级");
return showMemoryFile(projectClaudeMd, "Project");
}
/** 显示用户级 CLAUDE.md */
private String showUserMemory() {
Path userClaudeMd = Path.of(System.getProperty("user.home"), ".claude", "CLAUDE.md");
return showMemoryFile(userClaudeMd, "用户级");
return showMemoryFile(userClaudeMd, "User");
}
private String showMemoryFile(Path path, String level) {
@@ -76,17 +76,17 @@ public class MemoryCommand implements SlashCommand {
try {
String content = Files.readString(path, StandardCharsets.UTF_8);
if (content.isBlank()) {
sb.append(AnsiStyle.dim(" (文件为空)\n"));
sb.append(AnsiStyle.dim(" (File is empty)\n"));
} else {
content.lines().forEach(line -> sb.append(" ").append(line).append("\n"));
}
} catch (IOException e) {
sb.append(AnsiStyle.red("读取失败: " + e.getMessage() + "\n"));
sb.append(AnsiStyle.red("Read failed: " + e.getMessage() + "\n"));
}
} else {
sb.append(AnsiStyle.dim(" (文件不存在)\n\n"));
sb.append(AnsiStyle.dim(" 使用 /memory add <内容> 创建并添加内容\n"));
sb.append(AnsiStyle.dim(" 或使用 /init 命令初始化\n"));
sb.append(AnsiStyle.dim(" (File does not exist)\n\n"));
sb.append(AnsiStyle.dim(" Use /memory add <content> to create and add content\n"));
sb.append(AnsiStyle.dim(" Or use /init command to initialize\n"));
}
return sb.toString();
@@ -95,7 +95,7 @@ public class MemoryCommand implements SlashCommand {
/** 追加内容到项目级 CLAUDE.md */
private String handleAdd(String content) {
if (content.isEmpty()) {
return AnsiStyle.yellow("请提供要添加的内容:/memory add <内容>");
return AnsiStyle.yellow("Please provide content: /memory add <content>");
}
Path projectClaudeMd = Path.of(System.getProperty("user.dir"), "CLAUDE.md");
@@ -105,7 +105,7 @@ public class MemoryCommand implements SlashCommand {
Files.writeString(projectClaudeMd,
"# CLAUDE.md\n\n" + content + "\n",
StandardCharsets.UTF_8);
return AnsiStyle.green("已创建 CLAUDE.md 并添加内容");
return AnsiStyle.green("Created CLAUDE.md and added content");
}
// 追加内容
@@ -113,9 +113,9 @@ public class MemoryCommand implements SlashCommand {
String newContent = existing.endsWith("\n") ? existing + "\n" + content + "\n" : existing + "\n\n" + content + "\n";
Files.writeString(projectClaudeMd, newContent, StandardCharsets.UTF_8);
return AnsiStyle.green("已追加内容到 CLAUDE.md");
return AnsiStyle.green("Content appended to CLAUDE.md");
} catch (IOException e) {
return AnsiStyle.red("写入失败: " + e.getMessage());
return AnsiStyle.red("Write failed: " + e.getMessage());
}
}
@@ -138,20 +138,20 @@ public class MemoryCommand implements SlashCommand {
pb.inheritIO();
Process p = pb.start();
p.waitFor();
return AnsiStyle.green("编辑器已关闭");
return AnsiStyle.green("Editor closed");
}
// Windows: 尝试 notepad
if (System.getProperty("os.name").toLowerCase().contains("win")) {
ProcessBuilder pb = new ProcessBuilder("notepad", projectClaudeMd.toString());
pb.start(); // 不等待
return AnsiStyle.green("已用记事本打开 CLAUDE.md");
return AnsiStyle.green("Opened CLAUDE.md with Notepad");
}
return AnsiStyle.yellow("未找到编辑器。请设置 EDITOR 环境变量,或手动编辑:\n " + projectClaudeMd);
return AnsiStyle.yellow("No editor found. Set EDITOR environment variable, or manually edit:\n " + projectClaudeMd);
} catch (Exception e) {
return AnsiStyle.red("打开编辑器失败: " + e.getMessage());
return AnsiStyle.red("Failed to open editor: " + e.getMessage());
}
}
}
@@ -47,7 +47,7 @@ public class PluginCommand implements SlashCommand {
public String execute(String args, CommandContext context) {
PluginManager manager = getPluginManager(context);
if (manager == null) {
return AnsiStyle.red("插件系统未初始化");
return AnsiStyle.red("Plugin system not initialized");
}
String trimmed = (args == null) ? "" : args.trim();
@@ -67,7 +67,7 @@ public class PluginCommand implements SlashCommand {
case "unload" -> unloadPlugin(manager, subArgs);
case "reload" -> reloadPlugins(manager);
case "info" -> pluginInfo(manager, subArgs);
default -> AnsiStyle.yellow(" 未知子命令: " + subCommand) + "\n"
default -> AnsiStyle.yellow(" Unknown subcommand: " + subCommand) + "\n"
+ usageHelp();
};
}
@@ -96,12 +96,12 @@ public class PluginCommand implements SlashCommand {
sb.append(String.format(" ID: %s | %s%n",
AnsiStyle.cyan(p.id()),
p.description()));
sb.append(String.format(" 工具: %d | 命令: %d%n",
sb.append(String.format(" Tools: %d | Commands: %d%n",
p.getTools().size(),
p.getCommands().size()));
sb.append("\n");
}
sb.append(AnsiStyle.dim(String.format(" 共 %d 个插件", plugins.size()))).append("\n");
sb.append(AnsiStyle.dim(String.format(" Total %d plugins", plugins.size()))).append("\n");
}
return sb.toString();
}
@@ -111,15 +111,15 @@ public class PluginCommand implements SlashCommand {
*/
private String loadPlugin(PluginManager manager, String pathStr) {
if (pathStr.isEmpty()) {
return AnsiStyle.yellow(" 用法: /plugin load <jar-path>");
return AnsiStyle.yellow(" Usage: /plugin load <jar-path>");
}
Path jarPath = Path.of(pathStr);
boolean success = manager.loadPlugin(jarPath);
if (success) {
return AnsiStyle.green("插件加载成功: " + jarPath.getFileName());
return AnsiStyle.green("Plugin loaded: " + jarPath.getFileName());
} else {
return AnsiStyle.red("插件加载失败: " + jarPath.getFileName())
+ "\n" + AnsiStyle.dim(" 请检查 JAR 是否包含有效的 Plugin-Class 属性");
return AnsiStyle.red("Plugin load failed: " + jarPath.getFileName())
+ "\n" + AnsiStyle.dim(" Please check if JAR contains a valid Plugin-Class attribute");
}
}
@@ -128,13 +128,13 @@ public class PluginCommand implements SlashCommand {
*/
private String unloadPlugin(PluginManager manager, String pluginId) {
if (pluginId.isEmpty()) {
return AnsiStyle.yellow(" 用法: /plugin unload <plugin-id>");
return AnsiStyle.yellow(" Usage: /plugin unload <plugin-id>");
}
boolean success = manager.unload(pluginId);
if (success) {
return AnsiStyle.green("插件已卸载: " + pluginId);
return AnsiStyle.green("Plugin unloaded: " + pluginId);
} else {
return AnsiStyle.red("未找到插件: " + pluginId);
return AnsiStyle.red("Plugin not found: " + pluginId);
}
}
@@ -147,7 +147,7 @@ public class PluginCommand implements SlashCommand {
manager.loadAll();
int afterCount = manager.getPlugins().size();
return AnsiStyle.green(
String.format("插件已重载(之前: %d,现在: %d", beforeCount, afterCount));
String.format("Plugins reloaded (before: %d, now: %d)", beforeCount, afterCount));
}
/**
@@ -155,12 +155,12 @@ public class PluginCommand implements SlashCommand {
*/
private String pluginInfo(PluginManager manager, String pluginId) {
if (pluginId.isEmpty()) {
return AnsiStyle.yellow(" 用法: /plugin info <plugin-id>");
return AnsiStyle.yellow(" Usage: /plugin info <plugin-id>");
}
PluginInfo info = manager.findPlugin(pluginId);
if (info == null) {
return AnsiStyle.red("未找到插件: " + pluginId);
return AnsiStyle.red("Plugin not found: " + pluginId);
}
Plugin p = info.plugin();
@@ -245,11 +245,11 @@ public class PluginCommand implements SlashCommand {
*/
private String usageHelp() {
return AnsiStyle.dim("""
用法:
/plugin 列出所有插件
/plugin load <path> 加载 JAR 插件
/plugin unload <id> 卸载插件
/plugin reload 重载所有插件
/plugin info <id> 查看插件详情""");
Usage:
/plugin List all plugins
/plugin load <path> Load JAR plugin
/plugin unload <id> Unload plugin
/plugin reload Reload all plugins
/plugin info <id> View plugin details""");
}
}
@@ -41,8 +41,8 @@ public class ResumeCommand implements SlashCommand {
args = args == null ? "" : args.strip();
if (conversations.isEmpty()) {
return AnsiStyle.yellow("没有已保存的对话\n")
+ AnsiStyle.dim(" 对话在退出时自动保存到 ~/.claude-code-java/conversations/");
return AnsiStyle.yellow("No saved conversations\n")
+ AnsiStyle.dim(" Conversations are auto-saved on exit to ~/.claude-code-java/conversations/");
}
// /resume list —— 列出所有对话
@@ -56,10 +56,10 @@ public class ResumeCommand implements SlashCommand {
try {
index = Integer.parseInt(args) - 1;
if (index < 0 || index >= conversations.size()) {
return AnsiStyle.red("无效序号(范围 1-" + conversations.size() + "");
return AnsiStyle.red("Invalid index (range 1-" + conversations.size() + ")");
}
} catch (NumberFormatException e) {
return AnsiStyle.yellow("用法: /resume [序号] 或 /resume list");
return AnsiStyle.yellow("Usage: /resume [index] or /resume list");
}
}
@@ -69,7 +69,7 @@ public class ResumeCommand implements SlashCommand {
List<Message> messages = persistence.loadFromFile(file);
if (messages.isEmpty()) {
return AnsiStyle.red("加载对话失败: " + summary.filename());
return AnsiStyle.red("Failed to load conversation: " + summary.filename());
}
// 替换当前消息历史
@@ -77,12 +77,12 @@ public class ResumeCommand implements SlashCommand {
StringBuilder sb = new StringBuilder();
sb.append("\n");
sb.append(AnsiStyle.green("对话已恢复\n"));
sb.append(AnsiStyle.green("Conversation restored\n"));
sb.append(" ").append("".repeat(50)).append("\n");
sb.append(" ").append(AnsiStyle.bold("摘要: ")).append(summary.summary()).append("\n");
sb.append(" ").append(AnsiStyle.bold("时间: ")).append(summary.savedAt()).append("\n");
sb.append(" ").append(AnsiStyle.bold("消息数: ")).append(summary.messageCount()).append("\n");
sb.append(" ").append(AnsiStyle.bold("目录: ")).append(AnsiStyle.dim(summary.workingDir())).append("\n");
sb.append(" ").append(AnsiStyle.bold("Summary: ")).append(summary.summary()).append("\n");
sb.append(" ").append(AnsiStyle.bold("Time: ")).append(summary.savedAt()).append("\n");
sb.append(" ").append(AnsiStyle.bold("Messages: ")).append(summary.messageCount()).append("\n");
sb.append(" ").append(AnsiStyle.bold("Dir: ")).append(AnsiStyle.dim(summary.workingDir())).append("\n");
return sb.toString();
}
@@ -104,10 +104,10 @@ public class ResumeCommand implements SlashCommand {
}
if (conversations.size() > maxShow) {
sb.append(AnsiStyle.dim("\n ... 还有 " + (conversations.size() - maxShow) + " 个对话\n"));
sb.append(AnsiStyle.dim("\n ... and " + (conversations.size() - maxShow) + " more conversations\n"));
}
sb.append(AnsiStyle.dim("\n 使用 /resume [序号] 恢复指定对话\n"));
sb.append(AnsiStyle.dim("\n Use /resume [index] to restore a conversation\n"));
return sb.toString();
}
@@ -43,7 +43,7 @@ public class ReviewCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用,无法执行代码审查。");
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform code review.");
}
String trimmedArgs = args != null ? args.trim() : "";
@@ -55,16 +55,16 @@ public class ReviewCommand implements SlashCommand {
// 检查 diff 是否为空
if (diffOutput.isBlank()) {
return AnsiStyle.yellow("没有检测到代码变更。") + "\n"
+ AnsiStyle.dim(" 提示: 使用 --staged 审查已暂存的变更,或指定文件路径。");
return AnsiStyle.yellow("No code changes detected.") + "\n"
+ AnsiStyle.dim(" Tip: Use --staged to review staged changes, or specify a file path.");
}
// 构建审查提示
String reviewPrompt = buildReviewPrompt(trimmedArgs, diffOutput);
// 输出审查进行中的提示
context.out().println(AnsiStyle.cyan(" 🔍 正在审查代码变更..."));
context.out().println(AnsiStyle.dim(" diff 大小: " + diffOutput.lines().count() + " "));
context.out().println(AnsiStyle.cyan(" 🔍 Reviewing code changes..."));
context.out().println(AnsiStyle.dim(" diff size: " + diffOutput.lines().count() + " lines"));
context.out().println();
// 发送给 AI 进行审查
@@ -72,8 +72,8 @@ public class ReviewCommand implements SlashCommand {
return result;
} catch (Exception e) {
return AnsiStyle.red("代码审查失败: " + e.getMessage()) + "\n"
+ AnsiStyle.dim(" 请确保当前目录是一个 Git 仓库。");
return AnsiStyle.red("Code review failed: " + e.getMessage()) + "\n"
+ AnsiStyle.dim(" Please ensure the current directory is a Git repository.");
}
}
@@ -129,7 +129,7 @@ public class ReviewCommand implements SlashCommand {
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("git diff 执行失败 (exit=" + exitCode + "): " + errorOutput);
throw new RuntimeException("git diff failed (exit=" + exitCode + "): " + errorOutput);
}
return output;
@@ -38,21 +38,21 @@ public class RewindCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用。");
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
// 解析要回滚的消息对数量
int pairsToRemove = parseRewindCount(args);
if (pairsToRemove < 0) {
return AnsiStyle.red("无效的回滚数量。请输入一个正整数。") + "\n"
+ AnsiStyle.dim(" 用法: /rewind [n] n 为要移除的消息对数,默认为 1");
return AnsiStyle.red("Invalid rewind count. Please enter a positive integer.") + "\n"
+ AnsiStyle.dim(" Usage: /rewind [n] (n = number of message pairs to remove, default 1)");
}
List<Message> currentHistory = context.agentLoop().getMessageHistory();
int currentSize = currentHistory.size();
if (currentSize == 0) {
return AnsiStyle.yellow("对话历史为空,无法回滚。");
return AnsiStyle.yellow("Conversation history is empty, cannot rewind.");
}
// 计算需要移除的消息数量
@@ -61,8 +61,8 @@ public class RewindCommand implements SlashCommand {
// 如果要移除的消息数超过总消息数,则清除所有消息
if (messagesToRemove >= currentSize) {
context.agentLoop().replaceHistory(new ArrayList<>());
return AnsiStyle.green("已清除全部 " + currentSize + " 条消息。") + "\n"
+ AnsiStyle.dim(" (请求移除 " + pairsToRemove + " 对,实际清除全部消息)");
return AnsiStyle.green("Cleared all " + currentSize + " messages.") + "\n"
+ AnsiStyle.dim(" (Requested " + pairsToRemove + " pairs, cleared all messages)");
}
// 截断消息历史
@@ -72,9 +72,9 @@ public class RewindCommand implements SlashCommand {
// 构建结果输出
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.green("已回滚 " + pairsToRemove + " 个消息对")).append("\n");
sb.append(AnsiStyle.dim(" 移除: " + messagesToRemove + " 条消息")).append("\n");
sb.append(AnsiStyle.dim(" 剩余: " + newSize + " 条消息")).append("\n");
sb.append(AnsiStyle.green("Rewound " + pairsToRemove + " message pairs")).append("\n");
sb.append(AnsiStyle.dim(" Removed: " + messagesToRemove + " messages")).append("\n");
sb.append(AnsiStyle.dim(" Remaining: " + newSize + " messages")).append("\n");
return sb.toString();
}
@@ -45,7 +45,7 @@ public class SecurityReviewCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用,无法执行安全审查。");
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform security review.");
}
try {
@@ -53,13 +53,13 @@ public class SecurityReviewCommand implements SlashCommand {
String diffOutput = executeGitDiff();
if (diffOutput.isBlank()) {
return AnsiStyle.yellow("没有检测到代码变更。") + "\n"
+ AnsiStyle.dim(" git diff HEAD 未返回任何内容。请确认是否有提交记录。");
return AnsiStyle.yellow("No code changes detected.") + "\n"
+ AnsiStyle.dim(" git diff HEAD returned nothing. Please verify there are commits.");
}
// 输出审查进行中的提示
context.out().println(AnsiStyle.magenta(" 🔒 正在进行安全审查..."));
context.out().println(AnsiStyle.dim(" diff 大小: " + diffOutput.lines().count() + " "));
context.out().println(AnsiStyle.magenta(" 🔒 Performing security review..."));
context.out().println(AnsiStyle.dim(" diff size: " + diffOutput.lines().count() + " lines"));
context.out().println();
// 构建安全审查提示词
@@ -70,8 +70,8 @@ public class SecurityReviewCommand implements SlashCommand {
return result;
} catch (Exception e) {
return AnsiStyle.red("安全审查失败: " + e.getMessage()) + "\n"
+ AnsiStyle.dim(" 请确保当前目录是一个 Git 仓库且有提交历史。");
return AnsiStyle.red("Security review failed: " + e.getMessage()) + "\n"
+ AnsiStyle.dim(" Please ensure the current directory is a Git repository with commit history.");
}
}
@@ -102,7 +102,7 @@ public class SecurityReviewCommand implements SlashCommand {
int exitCode = process.waitFor();
if (exitCode != 0) {
throw new RuntimeException("git diff HEAD 执行失败 (exit=" + exitCode + "): " + errorOutput);
throw new RuntimeException("git diff HEAD failed (exit=" + exitCode + "): " + errorOutput);
}
return output;
@@ -37,20 +37,20 @@ public class SkillsCommand implements SlashCommand {
sb.append(" ").append("".repeat(50)).append("\n\n");
if (skills.isEmpty()) {
sb.append(AnsiStyle.dim(" (无可用技能)\n\n"));
sb.append(AnsiStyle.dim(" 技能文件放置位置:\n"));
sb.append(AnsiStyle.dim(" 用户级: ~/.claude/skills/*.md\n"));
sb.append(AnsiStyle.dim(" 项目级: ./.claude/skills/*.md\n"));
sb.append(AnsiStyle.dim(" 命令级: ./.claude/commands/*.md\n"));
sb.append(AnsiStyle.dim(" (No available skills)\n\n"));
sb.append(AnsiStyle.dim(" Skill file locations:\n"));
sb.append(AnsiStyle.dim(" User: ~/.claude/skills/*.md\n"));
sb.append(AnsiStyle.dim(" Project: ./.claude/skills/*.md\n"));
sb.append(AnsiStyle.dim(" Command: ./.claude/commands/*.md\n"));
} else {
for (SkillLoader.Skill skill : skills) {
sb.append(" ").append(AnsiStyle.cyan("")).append(AnsiStyle.bold(skill.name()));
// 来源标签
String sourceLabel = switch (skill.source()) {
case "user" -> AnsiStyle.dim(" [用户级]");
case "project" -> AnsiStyle.dim(" [项目级]");
case "command" -> AnsiStyle.dim(" [命令]");
case "user" -> AnsiStyle.dim(" [user]");
case "project" -> AnsiStyle.dim(" [project]");
case "command" -> AnsiStyle.dim(" [command]");
default -> AnsiStyle.dim(" [" + skill.source() + "]");
};
sb.append(sourceLabel).append("\n");
@@ -64,7 +64,7 @@ public class SkillsCommand implements SlashCommand {
sb.append(" ").append(AnsiStyle.dim("File: " + skill.filePath())).append("\n");
sb.append("\n");
}
sb.append(AnsiStyle.dim(" " + skills.size() + " 个技能\n"));
sb.append(AnsiStyle.dim(" Total " + skills.size() + " skills\n"));
}
return sb.toString();
@@ -38,12 +38,12 @@ public class StatsCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用。");
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
TokenTracker tracker = context.agentLoop().getTokenTracker();
if (tracker == null) {
return AnsiStyle.yellow(" ⚠ Token 追踪器不可用。");
return AnsiStyle.yellow(" ⚠ Token tracker unavailable.");
}
// 收集统计数据
@@ -43,7 +43,7 @@ public class TagCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
if (context.agentLoop() == null) {
return AnsiStyle.red(" ✗ AgentLoop 不可用。");
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
String trimmedArgs = args != null ? args.trim() : "";
@@ -78,7 +78,7 @@ public class TagCommand implements SlashCommand {
*/
private String createTag(String tagName, CommandContext context) {
if (tagName.isEmpty()) {
return AnsiStyle.red("请指定标签名称。");
return AnsiStyle.red("Please specify tag name.");
}
// 标签名称不能与子命令冲突(虽然 list/goto 已在 switch 中处理)
@@ -88,9 +88,9 @@ public class TagCommand implements SlashCommand {
boolean isOverwrite = tags.containsKey(tagName);
tags.put(tagName, new TagInfo(position, timestamp));
String action = isOverwrite ? "已更新" : "已创建";
return AnsiStyle.green("标签" + action + ": ") + AnsiStyle.bold(tagName) + "\n"
+ AnsiStyle.dim(" 位置: 第 " + position + " 条消息 时间: " + timestamp);
String action = isOverwrite ? "updated" : "created";
return AnsiStyle.green("Tag " + action + ": ") + AnsiStyle.bold(tagName) + "\n"
+ AnsiStyle.dim(" Position: message " + position + " Time: " + timestamp);
}
/**
@@ -101,8 +101,8 @@ public class TagCommand implements SlashCommand {
*/
private String listTags(CommandContext context) {
if (tags.isEmpty()) {
return AnsiStyle.dim(" 没有保存的标签。") + "\n"
+ AnsiStyle.dim(" 使用 /tag <name> 为当前位置打标签。");
return AnsiStyle.dim(" No saved tags.") + "\n"
+ AnsiStyle.dim(" Use /tag <name> to tag the current position.");
}
int currentPosition = context.agentLoop().getMessageHistory().size();
@@ -127,7 +127,7 @@ public class TagCommand implements SlashCommand {
}
sb.append("\n")
.append(AnsiStyle.dim(" 当前位置: " + currentPosition + " 条消息 | 共 " + tags.size() + " 个标签"))
.append(AnsiStyle.dim(" Current position: " + currentPosition + " messages | Total " + tags.size() + " tags"))
.append("\n");
return sb.toString();
}
@@ -144,14 +144,14 @@ public class TagCommand implements SlashCommand {
*/
private String gotoTag(String tagName, CommandContext context) {
if (tagName.isEmpty()) {
return AnsiStyle.red("请指定标签名称。") + "\n"
+ AnsiStyle.dim(" 用法: /tag goto <name>");
return AnsiStyle.red("Please specify tag name.") + "\n"
+ AnsiStyle.dim(" Usage: /tag goto <name>");
}
TagInfo info = tags.get(tagName);
if (info == null) {
return AnsiStyle.red("标签不存在: " + tagName) + "\n"
+ AnsiStyle.dim(" 使用 /tag list 查看所有可用标签。");
return AnsiStyle.red("Tag not found: " + tagName) + "\n"
+ AnsiStyle.dim(" Use /tag list to see all available tags.");
}
List<Message> currentHistory = context.agentLoop().getMessageHistory();
@@ -159,8 +159,8 @@ public class TagCommand implements SlashCommand {
int targetPosition = info.position();
if (targetPosition >= currentSize) {
return AnsiStyle.yellow("标签位置 (" + targetPosition + ") 不小于当前消息数 ("
+ currentSize + "),无需回溯。");
return AnsiStyle.yellow("Tag position (" + targetPosition + ") is not less than current message count ("
+ currentSize + "), no rewind needed.");
}
// 截断到标签位置
@@ -168,8 +168,8 @@ public class TagCommand implements SlashCommand {
context.agentLoop().replaceHistory(truncated);
int removedCount = currentSize - targetPosition;
return AnsiStyle.green("已回溯到标签: ") + AnsiStyle.bold(tagName) + "\n"
+ AnsiStyle.dim(" 移除了 " + removedCount + " 条消息,当前消息数: " + targetPosition);
return AnsiStyle.green("Rewound to tag: ") + AnsiStyle.bold(tagName) + "\n"
+ AnsiStyle.dim(" Removed " + removedCount + " messages, current count: " + targetPosition);
}
/**
@@ -179,10 +179,10 @@ public class TagCommand implements SlashCommand {
*/
private String showUsage() {
StringBuilder sb = new StringBuilder();
sb.append(AnsiStyle.bold("\n 🏷️ Tag — 对话位置标签\n\n"));
sb.append(" ").append(AnsiStyle.cyan("/tag <name>")).append(" 为当前位置打标签\n");
sb.append(" ").append(AnsiStyle.cyan("/tag list")).append(" 列出所有标签\n");
sb.append(" ").append(AnsiStyle.cyan("/tag goto <name>")).append(" 回溯到指定标签位置\n");
sb.append(AnsiStyle.bold("\n 🏷️ Tag — Conversation position tags\n\n"));
sb.append(" ").append(AnsiStyle.cyan("/tag <name>")).append(" Tag current position\n");
sb.append(" ").append(AnsiStyle.cyan("/tag list")).append(" List all tags\n");
sb.append(" ").append(AnsiStyle.cyan("/tag goto <name>")).append(" Rewind to tag position\n");
return sb.toString();
}