refactor: apply BaseSlashCommand to all 54 command implementations
- implements SlashCommand -> extends BaseSlashCommand (all 54 files) - Replace CommandUtils.parseArgs/args null checks -> args(args) - Replace agentLoop null checks -> requireAgentLoop(context)/noSession() - Replace context.agentLoop().getToolContext() -> toolCtx(context) - Replace CommandUtils.success/error/header/subtitle/warning/info -> instance methods - Remove unused CommandUtils/AnsiStyle imports where applicable - 87 tests passing, zero compilation errors Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
简单的天气查询脚本
|
||||||
|
使用OpenWeatherMap API
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_weather(api_key, city="Beijing", units="metric"):
|
||||||
|
"""
|
||||||
|
获取指定城市的天气
|
||||||
|
"""
|
||||||
|
url = "http://api.openweathermap.org/data/2.5/weather"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"q": city,
|
||||||
|
"appid": api_key,
|
||||||
|
"units": units,
|
||||||
|
"lang": "zh_cn"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(url, params=params, timeout=10)
|
||||||
|
data = response.json()
|
||||||
|
|
||||||
|
if data.get("cod") != 200:
|
||||||
|
print(f"错误: {data.get('message', '未知错误')}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
return data
|
||||||
|
except Exception as e:
|
||||||
|
print(f"请求失败: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def main():
|
||||||
|
# 请在这里输入您的API密钥
|
||||||
|
API_KEY = "YOUR_API_KEY_HERE"
|
||||||
|
|
||||||
|
if API_KEY == "YOUR_API_KEY_HERE":
|
||||||
|
print("请先获取OpenWeatherMap API密钥:")
|
||||||
|
print("1. 访问 https://openweathermap.org/api")
|
||||||
|
print("2. 注册免费账户")
|
||||||
|
print("3. 获取API密钥并替换代码中的 'YOUR_API_KEY_HERE'")
|
||||||
|
print("\n免费套餐:每天60次调用")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 查询城市(可以修改为您所在的城市)
|
||||||
|
city = input("请输入城市名称(英文,如 Beijing, Shanghai, New York): ").strip()
|
||||||
|
if not city:
|
||||||
|
city = "Beijing"
|
||||||
|
|
||||||
|
print(f"\n正在查询 {city} 的天气...")
|
||||||
|
|
||||||
|
weather_data = get_weather(API_KEY, city)
|
||||||
|
|
||||||
|
if weather_data:
|
||||||
|
print(f"\n=== {weather_data['name']} 天气 ===")
|
||||||
|
print(f"温度: {weather_data['main']['temp']}°C")
|
||||||
|
print(f"体感温度: {weather_data['main']['feels_like']}°C")
|
||||||
|
print(f"天气: {weather_data['weather'][0]['description']}")
|
||||||
|
print(f"湿度: {weather_data['main']['humidity']}%")
|
||||||
|
print(f"风速: {weather_data['wind']['speed']} m/s")
|
||||||
|
print(f"气压: {weather_data['main']['pressure']} hPa")
|
||||||
|
else:
|
||||||
|
print("无法获取天气数据")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package com.claudecode.command;
|
||||||
|
|
||||||
|
import com.claudecode.core.AgentLoop;
|
||||||
|
import com.claudecode.tool.ToolContext;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 命令抽象基类 —— 消除 54 个命令实现中的重复模式。
|
||||||
|
* <p>
|
||||||
|
* 提供:
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link #args(String)} — 安全解析参数</li>
|
||||||
|
* <li>{@link #requireAgentLoop(CommandContext)} — null 检查并返回错误</li>
|
||||||
|
* <li>{@link #toolCtx(CommandContext)} — 快速获取 ToolContext</li>
|
||||||
|
* <li>{@link #success(String)}, {@link #error(String)}, {@link #warning(String)} — 格式化消息</li>
|
||||||
|
* <li>默认 {@link #aliases()} 返回空列表</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* 子类只需实现 {@link #name()}, {@link #description()}, {@link #execute(String, CommandContext)}。
|
||||||
|
* 可选覆盖 {@link #aliases()}。
|
||||||
|
*/
|
||||||
|
public abstract class BaseSlashCommand implements SlashCommand {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> aliases() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 参数解析 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全解析命令参数(null → 空字符串,去首尾空白)。
|
||||||
|
*/
|
||||||
|
protected String args(String raw) {
|
||||||
|
return CommandUtils.parseArgs(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 上下文访问 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查 AgentLoop 是否可用。如果为 null,返回错误消息;否则返回 null。
|
||||||
|
* 典型用法:
|
||||||
|
* <pre>
|
||||||
|
* AgentLoop loop = requireAgentLoop(context);
|
||||||
|
* if (loop == null) return noSession();
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
protected AgentLoop requireAgentLoop(CommandContext context) {
|
||||||
|
return context.agentLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标准 "无会话" 错误消息。
|
||||||
|
*/
|
||||||
|
protected String noSession() {
|
||||||
|
return CommandUtils.error("No active session");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 快速获取 ToolContext。如果 agentLoop 为 null 则返回 null。
|
||||||
|
*/
|
||||||
|
protected ToolContext toolCtx(CommandContext context) {
|
||||||
|
return context.agentLoop() != null ? context.agentLoop().getToolContext() : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==================== 格式化输出 ====================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 成功消息(绿色 ✓)。
|
||||||
|
*/
|
||||||
|
protected String success(String message) {
|
||||||
|
return CommandUtils.success(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 错误消息(红色 ✗)。
|
||||||
|
*/
|
||||||
|
protected String error(String message) {
|
||||||
|
return CommandUtils.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 警告消息(黄色 ⚠)。
|
||||||
|
*/
|
||||||
|
protected String warning(String message) {
|
||||||
|
return CommandUtils.warning(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息消息(蓝色 ℹ)。
|
||||||
|
*/
|
||||||
|
protected String info(String message) {
|
||||||
|
return CommandUtils.info(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化标题行。
|
||||||
|
*/
|
||||||
|
protected String header(String emoji, String title) {
|
||||||
|
return CommandUtils.header(emoji, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化子标题。
|
||||||
|
*/
|
||||||
|
protected String subtitle(String title) {
|
||||||
|
return CommandUtils.subtitle(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TaskManager;
|
import com.claudecode.core.TaskManager;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import java.util.List;
|
|||||||
* <li>/agent stop-all —— 停止所有 agent</li>
|
* <li>/agent stop-all —— 停止所有 agent</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class AgentCommand implements SlashCommand {
|
public class AgentCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -39,7 +39,7 @@ public class AgentCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
// Get TaskManager from AgentLoop's tool context
|
// Get TaskManager from AgentLoop's tool context
|
||||||
TaskManager taskManager = getTaskManager(context);
|
TaskManager taskManager = getTaskManager(context);
|
||||||
@@ -183,8 +183,8 @@ public class AgentCommand implements SlashCommand {
|
|||||||
private TaskManager getTaskManager(CommandContext context) {
|
private TaskManager getTaskManager(CommandContext context) {
|
||||||
// The TaskManager is typically accessible from the tool context
|
// The TaskManager is typically accessible from the tool context
|
||||||
// via AgentLoop's tool context. Try to access it.
|
// via AgentLoop's tool context. Try to access it.
|
||||||
if (context.agentLoop() == null) return null;
|
if (requireAgentLoop(context) == null) return null;
|
||||||
var toolContext = context.agentLoop().getToolContext();
|
var toolContext = toolCtx(context);
|
||||||
if (toolContext == null) return null;
|
if (toolContext == null) return null;
|
||||||
return toolContext.getOrDefault("TASK_MANAGER", null);
|
return toolContext.getOrDefault("TASK_MANAGER", null);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ import java.util.Map;
|
|||||||
* <li>{@code /branch delete <name>} —— 删除指定分支</li>
|
* <li>{@code /branch delete <name>} —— 删除指定分支</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class BranchCommand implements SlashCommand {
|
public class BranchCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 静态分支存储:分支名称 -> 消息快照 */
|
/** 静态分支存储:分支名称 -> 消息快照 */
|
||||||
private static final Map<String, BranchSnapshot> branches = new LinkedHashMap<>();
|
private static final Map<String, BranchSnapshot> branches = new LinkedHashMap<>();
|
||||||
@@ -43,7 +43,7 @@ public class BranchCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /brief 命令 —— 切换简洁输出模式。
|
* /brief 命令 —— 切换简洁输出模式。
|
||||||
*/
|
*/
|
||||||
public class BriefCommand implements SlashCommand {
|
public class BriefCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "brief"; }
|
public String name() { return "brief"; }
|
||||||
@@ -17,14 +17,14 @@ public class BriefCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
boolean current = Boolean.TRUE.equals(toolCtx.get("BRIEF_MODE"));
|
boolean current = Boolean.TRUE.equals(toolCtx.get("BRIEF_MODE"));
|
||||||
|
|
||||||
String trimmed = (args == null) ? "" : args.trim();
|
String trimmed = args(args);
|
||||||
boolean newMode = switch (trimmed) {
|
boolean newMode = switch (trimmed) {
|
||||||
case "on", "enable", "true" -> true;
|
case "on", "enable", "true" -> true;
|
||||||
case "off", "disable", "false" -> false;
|
case "off", "disable", "false" -> false;
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /clear 命令 —— 清除对话历史。
|
* /clear 命令 —— 清除对话历史。
|
||||||
*/
|
*/
|
||||||
public class ClearCommand implements SlashCommand {
|
public class ClearCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* <li>/commit --pr —— 提交 + push + 创建 PR(使用 gh CLI)</li>
|
* <li>/commit --pr —— 提交 + push + 创建 PR(使用 gh CLI)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class CommitCommand implements SlashCommand {
|
public class CommitCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -41,7 +41,7 @@ public class CommitCommand implements SlashCommand {
|
|||||||
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
|
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
|
||||||
}
|
}
|
||||||
|
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
boolean addAll = args.contains("--all") || args.contains("-a");
|
boolean addAll = args.contains("--all") || args.contains("-a");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenTracker;
|
import com.claudecode.core.TokenTracker;
|
||||||
import com.claudecode.core.compact.AutoCompactManager;
|
import com.claudecode.core.compact.AutoCompactManager;
|
||||||
@@ -23,7 +23,7 @@ import java.util.List;
|
|||||||
* <li>/compact --aggressive —— 激进压缩(保留更少上下文)</li>
|
* <li>/compact --aggressive —— 激进压缩(保留更少上下文)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class CompactCommand implements SlashCommand {
|
public class CompactCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -37,11 +37,11 @@ public class CompactCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.yellow(" ⚠ No active conversation to compact.");
|
return AnsiStyle.yellow(" ⚠ No active conversation to compact.");
|
||||||
}
|
}
|
||||||
|
|
||||||
String argStr = (args == null) ? "" : args.strip();
|
String argStr = args(args);
|
||||||
|
|
||||||
// --stats: only show statistics
|
// --stats: only show statistics
|
||||||
if (argStr.equals("--stats") || argStr.equals("-s")) {
|
if (argStr.equals("--stats") || argStr.equals("-s")) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.permission.PermissionRuleEngine;
|
import com.claudecode.permission.PermissionRuleEngine;
|
||||||
import com.claudecode.permission.PermissionSettings;
|
import com.claudecode.permission.PermissionSettings;
|
||||||
@@ -15,7 +15,7 @@ import java.util.Map;
|
|||||||
* <p>
|
* <p>
|
||||||
* 支持查看当前配置、设置单个配置项,以及权限管理子命令。
|
* 支持查看当前配置、设置单个配置项,以及权限管理子命令。
|
||||||
*/
|
*/
|
||||||
public class ConfigCommand implements SlashCommand {
|
public class ConfigCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 支持的配置项及说明 */
|
/** 支持的配置项及说明 */
|
||||||
private static final Map<String, String> CONFIG_KEYS = Map.of(
|
private static final Map<String, String> CONFIG_KEYS = Map.of(
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.context.ClaudeMdLoader;
|
import com.claudecode.context.ClaudeMdLoader;
|
||||||
import com.claudecode.context.GitContext;
|
import com.claudecode.context.GitContext;
|
||||||
@@ -14,7 +14,7 @@ import java.nio.file.Path;
|
|||||||
* <p>
|
* <p>
|
||||||
* 展示已加载的 CLAUDE.md、Skills、Git 上下文和 Token 预算使用情况。
|
* 展示已加载的 CLAUDE.md、Skills、Git 上下文和 Token 预算使用情况。
|
||||||
*/
|
*/
|
||||||
public class ContextCommand implements SlashCommand {
|
public class ContextCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenEstimationService;
|
import com.claudecode.core.TokenEstimationService;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* /ctx-viz 命令 —— 上下文可视化(token 分布、消息结构)。
|
* /ctx-viz 命令 —— 上下文可视化(token 分布、消息结构)。
|
||||||
*/
|
*/
|
||||||
public class ContextVizCommand implements SlashCommand {
|
public class ContextVizCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "ctx-viz"; }
|
public String name() { return "ctx-viz"; }
|
||||||
@@ -25,13 +24,13 @@ public class ContextVizCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("📊", "Context Window Visualization"));
|
sb.append(header("📊", "Context Window Visualization"));
|
||||||
|
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return sb.append(" No active agent loop\n").toString();
|
return sb.append(" No active agent loop\n").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
|
|
||||||
// Get or create token estimation service
|
// Get or create token estimation service
|
||||||
TokenEstimationService estimator = new TokenEstimationService();
|
TokenEstimationService estimator = new TokenEstimationService();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import org.springframework.ai.chat.messages.AssistantMessage;
|
import org.springframework.ai.chat.messages.AssistantMessage;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
@@ -16,7 +16,7 @@ import java.util.List;
|
|||||||
* 从消息历史中提取最后一条 AssistantMessage 的文本内容,
|
* 从消息历史中提取最后一条 AssistantMessage 的文本内容,
|
||||||
* 使用 AWT 剪贴板 API 复制。
|
* 使用 AWT 剪贴板 API 复制。
|
||||||
*/
|
*/
|
||||||
public class CopyCommand implements SlashCommand {
|
public class CopyCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenTracker;
|
import com.claudecode.core.TokenTracker;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ import com.claudecode.core.TokenTracker;
|
|||||||
* 对应 claude-code/src/commands/cost.ts。
|
* 对应 claude-code/src/commands/cost.ts。
|
||||||
* 从 AgentLoop 的 TokenTracker 获取真实 Token 统计。
|
* 从 AgentLoop 的 TokenTracker 获取真实 Token 统计。
|
||||||
*/
|
*/
|
||||||
public class CostCommand implements SlashCommand {
|
public class CostCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -30,7 +30,7 @@ public class CostCommand implements SlashCommand {
|
|||||||
int msgCount = context.agentLoop().getMessageHistory().size();
|
int msgCount = context.agentLoop().getMessageHistory().size();
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("💰", "Token Usage & Cost"));
|
sb.append(header("💰", "Token Usage & Cost"));
|
||||||
|
|
||||||
sb.append(" ").append(AnsiStyle.bold("Model: ")).append(AnsiStyle.cyan(tracker.getModelName())).append("\n");
|
sb.append(" ").append(AnsiStyle.bold("Model: ")).append(AnsiStyle.cyan(tracker.getModelName())).append("\n");
|
||||||
sb.append(" ").append(AnsiStyle.bold("API Calls: ")).append(tracker.getApiCallCount()).append("\n");
|
sb.append(" ").append(AnsiStyle.bold("API Calls: ")).append(tracker.getApiCallCount()).append("\n");
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.InternalLogger;
|
import com.claudecode.core.InternalLogger;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* /debug 命令 —— 调试模式开关 + 工具调用追踪。
|
* /debug 命令 —— 调试模式开关 + 工具调用追踪。
|
||||||
*/
|
*/
|
||||||
public class DebugCommand implements SlashCommand {
|
public class DebugCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "debug"; }
|
public String name() { return "debug"; }
|
||||||
@@ -24,16 +23,16 @@ public class DebugCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🐛", "Debug Mode"));
|
sb.append(header("🐛", "Debug Mode"));
|
||||||
|
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return sb.append(" No active agent loop\n").toString();
|
return sb.append(" No active agent loop\n").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
|
|
||||||
if (trimmed.equals("on") || trimmed.equals("enable")) {
|
if (trimmed.equals("on") || trimmed.equals("enable")) {
|
||||||
toolCtx.set("DEBUG_MODE", true);
|
toolCtx.set("DEBUG_MODE", true);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* <li>--stat:仅显示文件统计(不含详细diff)</li>
|
* <li>--stat:仅显示文件统计(不含详细diff)</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class DiffCommand implements SlashCommand {
|
public class DiffCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -39,7 +39,7 @@ public class DiffCommand implements SlashCommand {
|
|||||||
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
|
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
|
||||||
}
|
}
|
||||||
|
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String diffOutput;
|
String diffOutput;
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
* <li>磁盘空间</li>
|
* <li>磁盘空间</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class DoctorCommand implements SlashCommand {
|
public class DoctorCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -13,7 +13,7 @@ import java.util.TreeMap;
|
|||||||
/**
|
/**
|
||||||
* /env 命令 —— 显示环境变量和配置信息。
|
* /env 命令 —— 显示环境变量和配置信息。
|
||||||
*/
|
*/
|
||||||
public class EnvCommand implements SlashCommand {
|
public class EnvCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "env"; }
|
public String name() { return "env"; }
|
||||||
@@ -23,12 +23,12 @@ public class EnvCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔧", "Environment"));
|
sb.append(header("🔧", "Environment"));
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("System")).append("\n");
|
sb.append(subtitle("System")).append("\n");
|
||||||
sb.append(" OS: ").append(System.getProperty("os.name")).append(" ")
|
sb.append(" OS: ").append(System.getProperty("os.name")).append(" ")
|
||||||
.append(System.getProperty("os.version")).append("\n");
|
.append(System.getProperty("os.version")).append("\n");
|
||||||
sb.append(" Java: ").append(System.getProperty("java.version"))
|
sb.append(" Java: ").append(System.getProperty("java.version"))
|
||||||
@@ -38,13 +38,13 @@ public class EnvCommand implements SlashCommand {
|
|||||||
.append(" / ").append(CommandUtils.formatBytes(Runtime.getRuntime().maxMemory())).append("\n");
|
.append(" / ").append(CommandUtils.formatBytes(Runtime.getRuntime().maxMemory())).append("\n");
|
||||||
sb.append(" PID: ").append(ProcessHandle.current().pid()).append("\n\n");
|
sb.append(" PID: ").append(ProcessHandle.current().pid()).append("\n\n");
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Paths")).append("\n");
|
sb.append(subtitle("Paths")).append("\n");
|
||||||
sb.append(" WorkDir: ").append(System.getProperty("user.dir")).append("\n");
|
sb.append(" WorkDir: ").append(System.getProperty("user.dir")).append("\n");
|
||||||
sb.append(" Home: ").append(System.getProperty("user.home")).append("\n");
|
sb.append(" Home: ").append(System.getProperty("user.home")).append("\n");
|
||||||
sb.append(" Config: ").append(System.getProperty("user.home"))
|
sb.append(" Config: ").append(System.getProperty("user.home"))
|
||||||
.append(File.separator).append(".claude-code-java").append("\n\n");
|
.append(File.separator).append(".claude-code-java").append("\n\n");
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Environment Variables")).append("\n");
|
sb.append(subtitle("Environment Variables")).append("\n");
|
||||||
List<String> relevantVars = List.of(
|
List<String> relevantVars = List.of(
|
||||||
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "CLAUDE_CODE_",
|
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "CLAUDE_CODE_",
|
||||||
"JAVA_HOME", "PATH", "SHELL", "TERM", "EDITOR"
|
"JAVA_HOME", "PATH", "SHELL", "TERM", "EDITOR"
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /exit 命令 —— 退出应用。
|
* /exit 命令 —— 退出应用。
|
||||||
*/
|
*/
|
||||||
public class ExitCommand implements SlashCommand {
|
public class ExitCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import org.springframework.ai.chat.messages.*;
|
import org.springframework.ai.chat.messages.*;
|
||||||
|
|
||||||
@@ -22,7 +22,7 @@ import java.util.List;
|
|||||||
* <li>/export [路径] —— 导出到指定路径</li>
|
* <li>/export [路径] —— 导出到指定路径</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class ExportCommand implements SlashCommand {
|
public class ExportCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private static final DateTimeFormatter TIMESTAMP_FMT = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
private static final DateTimeFormatter TIMESTAMP_FMT = DateTimeFormatter.ofPattern("yyyyMMdd_HHmmss");
|
||||||
|
|
||||||
@@ -46,7 +46,7 @@ public class ExportCommand implements SlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 确定输出路径
|
// 确定输出路径
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
Path outputPath;
|
Path outputPath;
|
||||||
if (!args.isEmpty()) {
|
if (!args.isEmpty()) {
|
||||||
outputPath = Path.of(args);
|
outputPath = Path.of(args);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -12,7 +12,7 @@ import java.time.Instant;
|
|||||||
/**
|
/**
|
||||||
* /feedback 命令 —— 提交反馈(本地保存)。
|
* /feedback 命令 —— 提交反馈(本地保存)。
|
||||||
*/
|
*/
|
||||||
public class FeedbackCommand implements SlashCommand {
|
public class FeedbackCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "feedback"; }
|
public String name() { return "feedback"; }
|
||||||
@@ -22,7 +22,7 @@ public class FeedbackCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = (args == null) ? "" : args.trim();
|
String trimmed = args(args);
|
||||||
|
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
return AnsiStyle.yellow(" Usage: /feedback <your feedback text>")
|
return AnsiStyle.yellow(" Usage: /feedback <your feedback text>")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -15,7 +15,7 @@ import java.util.stream.Stream;
|
|||||||
* 对应 claude-code/src/commands/files.ts。
|
* 对应 claude-code/src/commands/files.ts。
|
||||||
* 显示项目目录树(默认2层深度)。
|
* 显示项目目录树(默认2层深度)。
|
||||||
*/
|
*/
|
||||||
public class FilesCommand implements SlashCommand {
|
public class FilesCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
@@ -16,7 +16,7 @@ import java.time.format.DateTimeFormatter;
|
|||||||
/**
|
/**
|
||||||
* /heapdump 命令 —— JVM 堆转储(Java 独有优势)。
|
* /heapdump 命令 —— JVM 堆转储(Java 独有优势)。
|
||||||
*/
|
*/
|
||||||
public class HeapdumpCommand implements SlashCommand {
|
public class HeapdumpCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "heapdump"; }
|
public String name() { return "heapdump"; }
|
||||||
@@ -26,26 +26,26 @@ public class HeapdumpCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("📦", "JVM Heap Dump"));
|
sb.append(header("📦", "JVM Heap Dump"));
|
||||||
|
|
||||||
if (trimmed.equals("info") || trimmed.isEmpty()) {
|
if (trimmed.equals("info") || trimmed.isEmpty()) {
|
||||||
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
|
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
|
||||||
MemoryUsage heap = memBean.getHeapMemoryUsage();
|
MemoryUsage heap = memBean.getHeapMemoryUsage();
|
||||||
MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();
|
MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Heap Memory")).append("\n");
|
sb.append(subtitle("Heap Memory")).append("\n");
|
||||||
sb.append(" Used: ").append(CommandUtils.formatBytes(heap.getUsed())).append("\n");
|
sb.append(" Used: ").append(CommandUtils.formatBytes(heap.getUsed())).append("\n");
|
||||||
sb.append(" Committed: ").append(CommandUtils.formatBytes(heap.getCommitted())).append("\n");
|
sb.append(" Committed: ").append(CommandUtils.formatBytes(heap.getCommitted())).append("\n");
|
||||||
sb.append(" Max: ").append(CommandUtils.formatBytes(heap.getMax())).append("\n\n");
|
sb.append(" Max: ").append(CommandUtils.formatBytes(heap.getMax())).append("\n\n");
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Non-Heap Memory")).append("\n");
|
sb.append(subtitle("Non-Heap Memory")).append("\n");
|
||||||
sb.append(" Used: ").append(CommandUtils.formatBytes(nonHeap.getUsed())).append("\n");
|
sb.append(" Used: ").append(CommandUtils.formatBytes(nonHeap.getUsed())).append("\n");
|
||||||
sb.append(" Committed: ").append(CommandUtils.formatBytes(nonHeap.getCommitted())).append("\n\n");
|
sb.append(" Committed: ").append(CommandUtils.formatBytes(nonHeap.getCommitted())).append("\n\n");
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Memory Pools")).append("\n");
|
sb.append(subtitle("Memory Pools")).append("\n");
|
||||||
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
|
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
|
||||||
MemoryUsage usage = pool.getUsage();
|
MemoryUsage usage = pool.getUsage();
|
||||||
if (usage != null && usage.getUsed() > 0) {
|
if (usage != null && usage.getUsed() > 0) {
|
||||||
@@ -66,12 +66,12 @@ public class HeapdumpCommand implements SlashCommand {
|
|||||||
com.sun.management.HotSpotDiagnosticMXBean.class);
|
com.sun.management.HotSpotDiagnosticMXBean.class);
|
||||||
hotspot.dumpHeap(dumpPath.toString(), true);
|
hotspot.dumpHeap(dumpPath.toString(), true);
|
||||||
long fileSize = dumpPath.toFile().length();
|
long fileSize = dumpPath.toFile().length();
|
||||||
sb.append(CommandUtils.success("Heap dump saved to:")).append("\n");
|
sb.append(success("Heap dump saved to:")).append("\n");
|
||||||
sb.append(" ").append(AnsiStyle.cyan(dumpPath.toString())).append("\n");
|
sb.append(" ").append(AnsiStyle.cyan(dumpPath.toString())).append("\n");
|
||||||
sb.append(" Size: ").append(CommandUtils.formatBytes(fileSize)).append("\n\n");
|
sb.append(" Size: ").append(CommandUtils.formatBytes(fileSize)).append("\n\n");
|
||||||
sb.append(AnsiStyle.dim(" Analyze with: jhat, MAT, or VisualVM"));
|
sb.append(AnsiStyle.dim(" Analyze with: jhat, MAT, or VisualVM"));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
sb.append(CommandUtils.error("Failed to create heap dump: " + e.getMessage())).append("\n");
|
sb.append(error("Failed to create heap dump: " + e.getMessage())).append("\n");
|
||||||
sb.append(AnsiStyle.dim(" Requires HotSpot JVM (OpenJDK or Oracle JDK)"));
|
sb.append(AnsiStyle.dim(" Requires HotSpot JVM (OpenJDK or Oracle JDK)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ public class HeapdumpCommand implements SlashCommand {
|
|||||||
sb.append(" Freed: ").append(AnsiStyle.green(CommandUtils.formatBytes(Math.max(0, freed)))).append("\n");
|
sb.append(" Freed: ").append(AnsiStyle.green(CommandUtils.formatBytes(Math.max(0, freed)))).append("\n");
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
sb.append(CommandUtils.subtitle("Subcommands")).append("\n");
|
sb.append(subtitle("Subcommands")).append("\n");
|
||||||
sb.append(" /heapdump Show memory pool info\n");
|
sb.append(" /heapdump Show memory pool info\n");
|
||||||
sb.append(" /heapdump dump Generate .hprof file\n");
|
sb.append(" /heapdump dump Generate .hprof file\n");
|
||||||
sb.append(" /heapdump gc Trigger garbage collection\n");
|
sb.append(" /heapdump gc Trigger garbage collection\n");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.SlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ import java.util.stream.Collectors;
|
|||||||
* <li>/help [command] —— 显示特定命令详情</li>
|
* <li>/help [command] —— 显示特定命令详情</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class HelpCommand implements SlashCommand {
|
public class HelpCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -36,7 +37,7 @@ public class HelpCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String query = (args == null) ? "" : args.strip();
|
String query = args(args);
|
||||||
var allCommands = context.commandRegistry().getCommands();
|
var allCommands = context.commandRegistry().getCommands();
|
||||||
|
|
||||||
// If a specific command name is given, show details
|
// If a specific command name is given, show details
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.ConversationPersistence;
|
import com.claudecode.core.ConversationPersistence;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import com.claudecode.core.ConversationPersistence;
|
|||||||
* <p>
|
* <p>
|
||||||
* 显示最近的对话记录,包括时间、摘要和消息数量。
|
* 显示最近的对话记录,包括时间、摘要和消息数量。
|
||||||
*/
|
*/
|
||||||
public class HistoryCommand implements SlashCommand {
|
public class HistoryCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.HookManager;
|
import com.claudecode.core.HookManager;
|
||||||
import com.claudecode.core.HookManager.HookRegistration;
|
import com.claudecode.core.HookManager.HookRegistration;
|
||||||
@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
|
|||||||
* 按 Hook 类型分组展示,包含每个 Hook 的名称和优先级。
|
* 按 Hook 类型分组展示,包含每个 Hook 的名称和优先级。
|
||||||
* 如果没有注册任何 Hook,则显示提示信息。
|
* 如果没有注册任何 Hook,则显示提示信息。
|
||||||
*/
|
*/
|
||||||
public class HooksCommand implements SlashCommand {
|
public class HooksCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /init 命令 —— 初始化项目 CLAUDE.md。
|
* /init 命令 —— 初始化项目 CLAUDE.md。
|
||||||
@@ -16,7 +15,7 @@ import java.util.List;
|
|||||||
* 对应 claude-code/src/commands/init.ts。
|
* 对应 claude-code/src/commands/init.ts。
|
||||||
* 检测项目类型并生成 CLAUDE.md 模板文件。
|
* 检测项目类型并生成 CLAUDE.md 模板文件。
|
||||||
*/
|
*/
|
||||||
public class InitCommand implements SlashCommand {
|
public class InitCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -9,7 +9,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* /keybindings 命令 —— 显示和配置快捷键。
|
* /keybindings 命令 —— 显示和配置快捷键。
|
||||||
*/
|
*/
|
||||||
public class KeybindingsCommand implements SlashCommand {
|
public class KeybindingsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "keybindings"; }
|
public String name() { return "keybindings"; }
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.mcp.McpClient;
|
import com.claudecode.mcp.McpClient;
|
||||||
import com.claudecode.mcp.McpException;
|
import com.claudecode.mcp.McpException;
|
||||||
@@ -25,7 +25,7 @@ import java.util.*;
|
|||||||
* <li>{@code /mcp reload} —— 从配置文件重新加载</li>
|
* <li>{@code /mcp reload} —— 从配置文件重新加载</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class McpCommand implements SlashCommand {
|
public class McpCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -48,7 +48,7 @@ public class McpCommand implements SlashCommand {
|
|||||||
return AnsiStyle.red(" ❌ MCP manager not initialized");
|
return AnsiStyle.red(" ❌ MCP manager not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
return showStatus(manager);
|
return showStatus(manager);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ public class McpCommand implements SlashCommand {
|
|||||||
*/
|
*/
|
||||||
private String showStatus(McpManager manager) {
|
private String showStatus(McpManager manager) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔌", "MCP Server Status"));
|
sb.append(header("🔌", "MCP Server Status"));
|
||||||
|
|
||||||
Map<String, McpClient> clients = manager.getClients();
|
Map<String, McpClient> clients = manager.getClients();
|
||||||
if (clients.isEmpty()) {
|
if (clients.isEmpty()) {
|
||||||
@@ -192,7 +192,7 @@ public class McpCommand implements SlashCommand {
|
|||||||
*/
|
*/
|
||||||
private String handleTools(McpManager manager, String args) {
|
private String handleTools(McpManager manager, String args) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🛠️", "MCP Tools"));
|
sb.append(header("🛠️", "MCP Tools"));
|
||||||
|
|
||||||
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
|
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
|
||||||
|
|
||||||
@@ -231,7 +231,7 @@ public class McpCommand implements SlashCommand {
|
|||||||
*/
|
*/
|
||||||
private String handleResources(McpManager manager, String args) {
|
private String handleResources(McpManager manager, String args) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("📦", "MCP Resources"));
|
sb.append(header("📦", "MCP Resources"));
|
||||||
|
|
||||||
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
|
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
* <li>/memory user —— 查看用户级 CLAUDE.md</li>
|
* <li>/memory user —— 查看用户级 CLAUDE.md</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class MemoryCommand implements SlashCommand {
|
public class MemoryCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -40,7 +40,7 @@ public class MemoryCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
if (args.startsWith("add ")) {
|
if (args.startsWith("add ")) {
|
||||||
return handleAdd(args.substring(4).strip());
|
return handleAdd(args.substring(4).strip());
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -12,7 +12,7 @@ import java.util.Map;
|
|||||||
* <p>
|
* <p>
|
||||||
* 支持查看当前模型信息和切换到其他模型。
|
* 支持查看当前模型信息和切换到其他模型。
|
||||||
*/
|
*/
|
||||||
public class ModelCommand implements SlashCommand {
|
public class ModelCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private static final Map<String, String> AVAILABLE_MODELS = Map.of(
|
private static final Map<String, String> AVAILABLE_MODELS = Map.of(
|
||||||
"sonnet", "claude-sonnet-4-20250514",
|
"sonnet", "claude-sonnet-4-20250514",
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /output-style 命令 —— 输出风格设置。
|
* /output-style 命令 —— 输出风格设置。
|
||||||
*/
|
*/
|
||||||
public class OutputStyleCommand implements SlashCommand {
|
public class OutputStyleCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "output-style"; }
|
public String name() { return "output-style"; }
|
||||||
@@ -17,15 +17,15 @@ public class OutputStyleCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
String current = (String) toolCtx.get("OUTPUT_STYLE");
|
String current = (String) toolCtx.get("OUTPUT_STYLE");
|
||||||
if (current == null) current = "markdown";
|
if (current == null) current = "markdown";
|
||||||
|
|
||||||
String trimmed = (args == null) ? "" : args.trim().toLowerCase();
|
String trimmed = args(args).toLowerCase();
|
||||||
|
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
return "\n" + AnsiStyle.bold(" 📝 Output Style\n")
|
return "\n" + AnsiStyle.bold(" 📝 Output Style\n")
|
||||||
|
|||||||
@@ -2,14 +2,13 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
|
||||||
import com.claudecode.telemetry.MetricsCollector;
|
import com.claudecode.telemetry.MetricsCollector;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /performance 命令 —— 性能统计。
|
* /performance 命令 —— 性能统计。
|
||||||
*/
|
*/
|
||||||
public class PerformanceCommand implements SlashCommand {
|
public class PerformanceCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "performance"; }
|
public String name() { return "performance"; }
|
||||||
@@ -25,7 +24,7 @@ public class PerformanceCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("⚡", "Performance Statistics"));
|
sb.append(header("⚡", "Performance Statistics"));
|
||||||
|
|
||||||
Runtime runtime = Runtime.getRuntime();
|
Runtime runtime = Runtime.getRuntime();
|
||||||
long totalMem = runtime.totalMemory();
|
long totalMem = runtime.totalMemory();
|
||||||
@@ -33,14 +32,14 @@ public class PerformanceCommand implements SlashCommand {
|
|||||||
long usedMem = totalMem - freeMem;
|
long usedMem = totalMem - freeMem;
|
||||||
long maxMem = runtime.maxMemory();
|
long maxMem = runtime.maxMemory();
|
||||||
|
|
||||||
sb.append(CommandUtils.subtitle("Memory")).append("\n");
|
sb.append(subtitle("Memory")).append("\n");
|
||||||
sb.append(" Used: ").append(CommandUtils.formatBytes(usedMem)).append("\n");
|
sb.append(" Used: ").append(CommandUtils.formatBytes(usedMem)).append("\n");
|
||||||
sb.append(" Allocated: ").append(CommandUtils.formatBytes(totalMem)).append("\n");
|
sb.append(" Allocated: ").append(CommandUtils.formatBytes(totalMem)).append("\n");
|
||||||
sb.append(" Max: ").append(CommandUtils.formatBytes(maxMem)).append("\n");
|
sb.append(" Max: ").append(CommandUtils.formatBytes(maxMem)).append("\n");
|
||||||
sb.append(" Usage: ").append(CommandUtils.progressBar((double) usedMem / maxMem, 20)).append("\n\n");
|
sb.append(" Usage: ").append(CommandUtils.progressBar((double) usedMem / maxMem, 20)).append("\n\n");
|
||||||
|
|
||||||
int threadCount = Thread.activeCount();
|
int threadCount = Thread.activeCount();
|
||||||
sb.append(CommandUtils.subtitle("Threads")).append("\n");
|
sb.append(subtitle("Threads")).append("\n");
|
||||||
sb.append(" Active: ").append(threadCount).append("\n");
|
sb.append(" Active: ").append(threadCount).append("\n");
|
||||||
sb.append(" Available: ").append(runtime.availableProcessors()).append(" CPUs\n\n");
|
sb.append(" Available: ").append(runtime.availableProcessors()).append(" CPUs\n\n");
|
||||||
|
|
||||||
@@ -50,14 +49,14 @@ public class PerformanceCommand implements SlashCommand {
|
|||||||
gcCount += gc.getCollectionCount();
|
gcCount += gc.getCollectionCount();
|
||||||
gcTime += gc.getCollectionTime();
|
gcTime += gc.getCollectionTime();
|
||||||
}
|
}
|
||||||
sb.append(CommandUtils.subtitle("GC")).append("\n");
|
sb.append(subtitle("GC")).append("\n");
|
||||||
sb.append(" Collections: ").append(gcCount).append("\n");
|
sb.append(" Collections: ").append(gcCount).append("\n");
|
||||||
sb.append(" Total time: ").append(CommandUtils.formatMillis(gcTime)).append("\n\n");
|
sb.append(" Total time: ").append(CommandUtils.formatMillis(gcTime)).append("\n\n");
|
||||||
|
|
||||||
if (context.agentLoop() != null) {
|
if (context.agentLoop() != null) {
|
||||||
Object metricsObj = context.agentLoop().getToolContext().get("METRICS_COLLECTOR");
|
Object metricsObj = toolCtx(context).get("METRICS_COLLECTOR");
|
||||||
if (metricsObj instanceof MetricsCollector metrics) {
|
if (metricsObj instanceof MetricsCollector metrics) {
|
||||||
sb.append(CommandUtils.subtitle("Session Metrics")).append("\n");
|
sb.append(subtitle("Session Metrics")).append("\n");
|
||||||
sb.append(" Duration: ").append(CommandUtils.formatDuration(metrics.getSessionDurationSeconds())).append("\n");
|
sb.append(" Duration: ").append(CommandUtils.formatDuration(metrics.getSessionDurationSeconds())).append("\n");
|
||||||
var toolUsage = metrics.getToolUsage();
|
var toolUsage = metrics.getToolUsage();
|
||||||
if (!toolUsage.isEmpty()) {
|
if (!toolUsage.isEmpty()) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.permission.PermissionSettings;
|
import com.claudecode.permission.PermissionSettings;
|
||||||
import com.claudecode.permission.PermissionTypes;
|
import com.claudecode.permission.PermissionTypes;
|
||||||
@@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
* 对应 claude-code/src/commands/permissions.ts。
|
* 对应 claude-code/src/commands/permissions.ts。
|
||||||
* 显示当前权限模式和规则列表。
|
* 显示当前权限模式和规则列表。
|
||||||
*/
|
*/
|
||||||
public class PermissionsCommand implements SlashCommand {
|
public class PermissionsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private final PermissionSettings settings;
|
private final PermissionSettings settings;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.permission.PermissionSettings;
|
import com.claudecode.permission.PermissionSettings;
|
||||||
import com.claudecode.permission.PermissionTypes.PermissionMode;
|
import com.claudecode.permission.PermissionTypes.PermissionMode;
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ import com.claudecode.permission.PermissionTypes.PermissionMode;
|
|||||||
* <p>
|
* <p>
|
||||||
* 切换计划模式开关。在计划模式下,AI只能分析不能修改。
|
* 切换计划模式开关。在计划模式下,AI只能分析不能修改。
|
||||||
*/
|
*/
|
||||||
public class PlanCommand implements SlashCommand {
|
public class PlanCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private final PermissionSettings permissionSettings;
|
private final PermissionSettings permissionSettings;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.SlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.plugin.*;
|
import com.claudecode.plugin.*;
|
||||||
@@ -32,7 +32,7 @@ import java.util.Optional;
|
|||||||
* 通过 {@link com.claudecode.tool.ToolContext} 中 key 为
|
* 通过 {@link com.claudecode.tool.ToolContext} 中 key 为
|
||||||
* {@code "PLUGIN_MANAGER"} 的共享状态获取 {@link PluginManager} 实例。
|
* {@code "PLUGIN_MANAGER"} 的共享状态获取 {@link PluginManager} 实例。
|
||||||
*/
|
*/
|
||||||
public class PluginCommand implements SlashCommand {
|
public class PluginCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -56,7 +56,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
return AnsiStyle.red(" ✗ Plugin system not initialized");
|
return AnsiStyle.red(" ✗ Plugin system not initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
// 无参数:列出所有插件
|
// 无参数:列出所有插件
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
@@ -88,7 +88,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
private String listPlugins(PluginManager manager) {
|
private String listPlugins(PluginManager manager) {
|
||||||
List<PluginInfo> plugins = manager.getPlugins();
|
List<PluginInfo> plugins = manager.getPlugins();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔌", "Loaded Plugins"));
|
sb.append(header("🔌", "Loaded Plugins"));
|
||||||
|
|
||||||
if (plugins.isEmpty()) {
|
if (plugins.isEmpty()) {
|
||||||
sb.append(AnsiStyle.dim(" No plugins loaded.")).append("\n");
|
sb.append(AnsiStyle.dim(" No plugins loaded.")).append("\n");
|
||||||
@@ -173,7 +173,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
|
|
||||||
Plugin p = info.plugin();
|
Plugin p = info.plugin();
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔌", "Plugin Details"));
|
sb.append(header("🔌", "Plugin Details"));
|
||||||
|
|
||||||
sb.append(" ").append(AnsiStyle.bold("Name: ")).append(p.name()).append("\n");
|
sb.append(" ").append(AnsiStyle.bold("Name: ")).append(p.name()).append("\n");
|
||||||
sb.append(" ").append(AnsiStyle.bold("ID: ")).append(AnsiStyle.cyan(p.id())).append("\n");
|
sb.append(" ").append(AnsiStyle.bold("ID: ")).append(AnsiStyle.cyan(p.id())).append("\n");
|
||||||
@@ -360,7 +360,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
|
|
||||||
private PluginInstaller getInstaller(CommandContext context) {
|
private PluginInstaller getInstaller(CommandContext context) {
|
||||||
try {
|
try {
|
||||||
Object obj = context.agentLoop().getToolContext().get("PLUGIN_INSTALLER");
|
Object obj = toolCtx(context).get("PLUGIN_INSTALLER");
|
||||||
if (obj instanceof PluginInstaller pi) return pi;
|
if (obj instanceof PluginInstaller pi) return pi;
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
return null;
|
return null;
|
||||||
@@ -368,7 +368,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
|
|
||||||
private MarketplaceManager getMarketplace(CommandContext context) {
|
private MarketplaceManager getMarketplace(CommandContext context) {
|
||||||
try {
|
try {
|
||||||
Object obj = context.agentLoop().getToolContext().get("MARKETPLACE_MANAGER");
|
Object obj = toolCtx(context).get("MARKETPLACE_MANAGER");
|
||||||
if (obj instanceof MarketplaceManager mm) return mm;
|
if (obj instanceof MarketplaceManager mm) return mm;
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
return null;
|
return null;
|
||||||
@@ -376,7 +376,7 @@ public class PluginCommand implements SlashCommand {
|
|||||||
|
|
||||||
private PluginAutoUpdate getAutoUpdate(CommandContext context) {
|
private PluginAutoUpdate getAutoUpdate(CommandContext context) {
|
||||||
try {
|
try {
|
||||||
Object obj = context.agentLoop().getToolContext().get("PLUGIN_AUTO_UPDATE");
|
Object obj = toolCtx(context).get("PLUGIN_AUTO_UPDATE");
|
||||||
if (obj instanceof PluginAutoUpdate pau) return pau;
|
if (obj instanceof PluginAutoUpdate pau) return pau;
|
||||||
} catch (Exception ignored) {}
|
} catch (Exception ignored) {}
|
||||||
return null;
|
return null;
|
||||||
@@ -391,11 +391,11 @@ public class PluginCommand implements SlashCommand {
|
|||||||
* @return PluginManager 实例,未找到时返回 null
|
* @return PluginManager 实例,未找到时返回 null
|
||||||
*/
|
*/
|
||||||
private PluginManager getPluginManager(CommandContext context) {
|
private PluginManager getPluginManager(CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
Object manager = context.agentLoop().getToolContext().get("PLUGIN_MANAGER");
|
Object manager = toolCtx(context).get("PLUGIN_MANAGER");
|
||||||
if (manager instanceof PluginManager pm) {
|
if (manager instanceof PluginManager pm) {
|
||||||
return pm;
|
return pm;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /privacy 命令 —— 隐私设置查看和修改。
|
* /privacy 命令 —— 隐私设置查看和修改。
|
||||||
*/
|
*/
|
||||||
public class PrivacyCommand implements SlashCommand {
|
public class PrivacyCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "privacy"; }
|
public String name() { return "privacy"; }
|
||||||
@@ -17,16 +17,16 @@ public class PrivacyCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
boolean telemetryEnabled = Boolean.TRUE.equals(toolCtx.get("TELEMETRY_ENABLED"));
|
boolean telemetryEnabled = Boolean.TRUE.equals(toolCtx.get("TELEMETRY_ENABLED"));
|
||||||
boolean sessionLogging = !Boolean.FALSE.equals(toolCtx.get("SESSION_LOGGING"));
|
boolean sessionLogging = !Boolean.FALSE.equals(toolCtx.get("SESSION_LOGGING"));
|
||||||
boolean memoryPersist = !Boolean.FALSE.equals(toolCtx.get("MEMORY_PERSIST"));
|
boolean memoryPersist = !Boolean.FALSE.equals(toolCtx.get("MEMORY_PERSIST"));
|
||||||
|
|
||||||
String trimmed = (args == null) ? "" : args.trim().toLowerCase();
|
String trimmed = args(args).toLowerCase();
|
||||||
|
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -10,7 +10,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* /release-notes 命令 —— 显示版本更新日志。
|
* /release-notes 命令 —— 显示版本更新日志。
|
||||||
*/
|
*/
|
||||||
public class ReleaseNotesCommand implements SlashCommand {
|
public class ReleaseNotesCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "release-notes"; }
|
public String name() { return "release-notes"; }
|
||||||
@@ -26,7 +26,7 @@ public class ReleaseNotesCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("📋", "Release Notes"));
|
sb.append(header("📋", "Release Notes"));
|
||||||
|
|
||||||
sb.append(AnsiStyle.bold(" v0.4.0 — Phase 4: Commands, Tools & Services\n"));
|
sb.append(AnsiStyle.bold(" v0.4.0 — Phase 4: Commands, Tools & Services\n"));
|
||||||
sb.append(CommandUtils.separator(45)).append("\n");
|
sb.append(CommandUtils.separator(45)).append("\n");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -12,7 +12,7 @@ import java.util.List;
|
|||||||
* 对应 claude-code 的 /rename 命令,给当前会话设置一个友好名称。
|
* 对应 claude-code 的 /rename 命令,给当前会话设置一个友好名称。
|
||||||
* 会话名称在 UI 标题栏和会话列表中显示。
|
* 会话名称在 UI 标题栏和会话列表中显示。
|
||||||
*/
|
*/
|
||||||
public class RenameCommand implements SlashCommand {
|
public class RenameCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 当前会话名称(进程级别) */
|
/** 当前会话名称(进程级别) */
|
||||||
private static volatile String sessionName = null;
|
private static volatile String sessionName = null;
|
||||||
@@ -34,7 +34,7 @@ public class RenameCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
if (args.isBlank()) {
|
if (args.isBlank()) {
|
||||||
if (sessionName == null) {
|
if (sessionName == null) {
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.RateLimiter;
|
import com.claudecode.core.RateLimiter;
|
||||||
|
|
||||||
@@ -11,7 +10,7 @@ import java.util.List;
|
|||||||
/**
|
/**
|
||||||
* /reset-limits 命令 —— 重置速率限制。
|
* /reset-limits 命令 —— 重置速率限制。
|
||||||
*/
|
*/
|
||||||
public class ResetLimitsCommand implements SlashCommand {
|
public class ResetLimitsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "reset-limits"; }
|
public String name() { return "reset-limits"; }
|
||||||
@@ -25,13 +24,13 @@ public class ResetLimitsCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔄", "Rate Limit Reset"));
|
sb.append(header("🔄", "Rate Limit Reset"));
|
||||||
|
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return sb.append(" No active agent loop\n").toString();
|
return sb.append(" No active agent loop\n").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
Object limiterObj = toolCtx.get("RATE_LIMITER");
|
Object limiterObj = toolCtx.get("RATE_LIMITER");
|
||||||
|
|
||||||
if (limiterObj instanceof RateLimiter limiter) {
|
if (limiterObj instanceof RateLimiter limiter) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.ConversationPersistence;
|
import com.claudecode.core.ConversationPersistence;
|
||||||
import com.claudecode.core.ConversationPersistence.ConversationSummary;
|
import com.claudecode.core.ConversationPersistence.ConversationSummary;
|
||||||
@@ -21,7 +21,7 @@ import java.util.List;
|
|||||||
* <li>/resume [序号] —— 恢复指定序号的对话</li>
|
* <li>/resume [序号] —— 恢复指定序号的对话</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class ResumeCommand implements SlashCommand {
|
public class ResumeCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -38,7 +38,7 @@ public class ResumeCommand implements SlashCommand {
|
|||||||
ConversationPersistence persistence = new ConversationPersistence();
|
ConversationPersistence persistence = new ConversationPersistence();
|
||||||
List<ConversationSummary> conversations = persistence.listConversations();
|
List<ConversationSummary> conversations = persistence.listConversations();
|
||||||
|
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
if (conversations.isEmpty()) {
|
if (conversations.isEmpty()) {
|
||||||
return AnsiStyle.yellow(" ⚠ No saved conversations\n")
|
return AnsiStyle.yellow(" ⚠ No saved conversations\n")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -23,7 +23,7 @@ import java.util.stream.Collectors;
|
|||||||
* <p>
|
* <p>
|
||||||
* 获取 diff 内容后,发送给 AI 模型进行代码审查。
|
* 获取 diff 内容后,发送给 AI 模型进行代码审查。
|
||||||
*/
|
*/
|
||||||
public class ReviewCommand implements SlashCommand {
|
public class ReviewCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -42,7 +42,7 @@ public class ReviewCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform code review.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform code review.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ import java.util.List;
|
|||||||
* 使用 {@code agentLoop.getMessageHistory()} 获取当前消息历史,
|
* 使用 {@code agentLoop.getMessageHistory()} 获取当前消息历史,
|
||||||
* 然后通过 {@code agentLoop.replaceHistory()} 用截断后的列表替换。
|
* 然后通过 {@code agentLoop.replaceHistory()} 用截断后的列表替换。
|
||||||
*/
|
*/
|
||||||
public class RewindCommand implements SlashCommand {
|
public class RewindCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 每个消息对包含的消息数(用户消息 + 助手消息) */
|
/** 每个消息对包含的消息数(用户消息 + 助手消息) */
|
||||||
private static final int MESSAGES_PER_PAIR = 2;
|
private static final int MESSAGES_PER_PAIR = 2;
|
||||||
@@ -37,7 +37,7 @@ public class RewindCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,15 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /sandbox 命令 —— 沙箱模式切换。
|
* /sandbox 命令 —— 沙箱模式切换。
|
||||||
* 控制工具执行的安全隔离级别。
|
* 控制工具执行的安全隔离级别。
|
||||||
*/
|
*/
|
||||||
public class SandboxCommand implements SlashCommand {
|
public class SandboxCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "sandbox"; }
|
public String name() { return "sandbox"; }
|
||||||
@@ -21,16 +19,16 @@ public class SandboxCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🏖", "Sandbox Mode"));
|
sb.append(header("🏖", "Sandbox Mode"));
|
||||||
|
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return sb.append(" No active agent loop\n").toString();
|
return sb.append(" No active agent loop\n").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
|
|
||||||
if (trimmed.equals("on") || trimmed.equals("enable") || trimmed.equals("strict")) {
|
if (trimmed.equals("on") || trimmed.equals("enable") || trimmed.equals("strict")) {
|
||||||
toolCtx.set("SANDBOX_MODE", "strict");
|
toolCtx.set("SANDBOX_MODE", "strict");
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
|
|||||||
* <li>依赖安全问题</li>
|
* <li>依赖安全问题</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class SecurityReviewCommand implements SlashCommand {
|
public class SecurityReviewCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -44,7 +44,7 @@ public class SecurityReviewCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform security review.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform security review.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -24,7 +24,7 @@ import java.util.List;
|
|||||||
* <li>/session export —— 导出会话为 JSON</li>
|
* <li>/session export —— 导出会话为 JSON</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class SessionCommand implements SlashCommand {
|
public class SessionCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private static final DateTimeFormatter FMT =
|
private static final DateTimeFormatter FMT =
|
||||||
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||||
@@ -49,7 +49,7 @@ public class SessionCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
args = args == null ? "" : args.strip();
|
args = args(args);
|
||||||
|
|
||||||
if (args.startsWith("save")) {
|
if (args.startsWith("save")) {
|
||||||
String sessionName = args.length() > 5 ? args.substring(5).strip() : "";
|
String sessionName = args.length() > 5 ? args.substring(5).strip() : "";
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.context.SkillLoader;
|
import com.claudecode.context.SkillLoader;
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ import java.util.List;
|
|||||||
* <p>
|
* <p>
|
||||||
* 扫描并显示从用户级、项目级和命令目录加载的技能文件。
|
* 扫描并显示从用户级、项目级和命令目录加载的技能文件。
|
||||||
*/
|
*/
|
||||||
public class SkillsCommand implements SlashCommand {
|
public class SkillsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -51,7 +51,7 @@ public class SkillsCommand implements SlashCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🎯", "Available Skills"));
|
sb.append(header("🎯", "Available Skills"));
|
||||||
|
|
||||||
if (skills.isEmpty()) {
|
if (skills.isEmpty()) {
|
||||||
sb.append(AnsiStyle.dim(" (No available skills)\n\n"));
|
sb.append(AnsiStyle.dim(" (No available skills)\n\n"));
|
||||||
@@ -89,7 +89,7 @@ public class SkillsCommand implements SlashCommand {
|
|||||||
|
|
||||||
private String formatSkillDetail(SkillLoader.Skill skill) {
|
private String formatSkillDetail(SkillLoader.Skill skill) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🎯", "Skill: " + skill.name()));
|
sb.append(header("🎯", "Skill: " + skill.name()));
|
||||||
|
|
||||||
sb.append(" ").append(AnsiStyle.bold("Source: ")).append(skill.source()).append("\n");
|
sb.append(" ").append(AnsiStyle.bold("Source: ")).append(skill.source()).append("\n");
|
||||||
if (!skill.description().isEmpty()) {
|
if (!skill.description().isEmpty()) {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenTracker;
|
import com.claudecode.core.TokenTracker;
|
||||||
|
|
||||||
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
* <li>JVM 运行时长</li>
|
* <li>JVM 运行时长</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class StatsCommand implements SlashCommand {
|
public class StatsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() {
|
public String name() {
|
||||||
@@ -37,7 +37,7 @@ public class StatsCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenTracker;
|
import com.claudecode.core.TokenTracker;
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ import java.time.Instant;
|
|||||||
* <p>
|
* <p>
|
||||||
* 展示当前模型、Token 用量、工具数、消息数、内存和运行时间等信息。
|
* 展示当前模型、Token 用量、工具数、消息数、内存和运行时间等信息。
|
||||||
*/
|
*/
|
||||||
public class StatusCommand implements SlashCommand {
|
public class StatusCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private final Instant startTime = Instant.now();
|
private final Instant startTime = Instant.now();
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ public class StatusCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("📊", "Session Status"));
|
sb.append(header("📊", "Session Status"));
|
||||||
|
|
||||||
// 模型信息
|
// 模型信息
|
||||||
TokenTracker tracker = context.agentLoop().getTokenTracker();
|
TokenTracker tracker = context.agentLoop().getTokenTracker();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import org.springframework.ai.chat.messages.Message;
|
import org.springframework.ai.chat.messages.Message;
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ import java.util.Map;
|
|||||||
* <li>{@code /tag goto <name>} —— 回溯到指定标签位置</li>
|
* <li>{@code /tag goto <name>} —— 回溯到指定标签位置</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*/
|
*/
|
||||||
public class TagCommand implements SlashCommand {
|
public class TagCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 静态标签存储:标签名称 -> 标签信息 */
|
/** 静态标签存储:标签名称 -> 标签信息 */
|
||||||
private static final Map<String, TagInfo> tags = new LinkedHashMap<>();
|
private static final Map<String, TagInfo> tags = new LinkedHashMap<>();
|
||||||
@@ -42,7 +42,7 @@ public class TagCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
|
|||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.CommandUtils;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TaskManager;
|
import com.claudecode.core.TaskManager;
|
||||||
import com.claudecode.core.TaskManager.TaskInfo;
|
import com.claudecode.core.TaskManager.TaskInfo;
|
||||||
@@ -18,7 +18,7 @@ import java.util.List;
|
|||||||
* 对应 claude-code 中的任务管理 UI。
|
* 对应 claude-code 中的任务管理 UI。
|
||||||
* 显示所有任务的状态、创建时间和结果摘要。
|
* 显示所有任务的状态、创建时间和结果摘要。
|
||||||
*/
|
*/
|
||||||
public class TasksCommand implements SlashCommand {
|
public class TasksCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private final TaskManager taskManager;
|
private final TaskManager taskManager;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ public class TasksCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
List<TaskInfo> tasks;
|
List<TaskInfo> tasks;
|
||||||
String filter = CommandUtils.parseArgs(args);
|
String filter = args(args);
|
||||||
|
|
||||||
// Optional status filter
|
// Optional status filter
|
||||||
if (!filter.isEmpty()) {
|
if (!filter.isEmpty()) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /theme 命令 —— 切换主题(dark/light/auto)。
|
* /theme 命令 —— 切换主题(dark/light/auto)。
|
||||||
*/
|
*/
|
||||||
public class ThemeCommand implements SlashCommand {
|
public class ThemeCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "theme"; }
|
public String name() { return "theme"; }
|
||||||
@@ -17,15 +17,15 @@ public class ThemeCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
String current = (String) toolCtx.get("THEME");
|
String current = (String) toolCtx.get("THEME");
|
||||||
if (current == null) current = "dark";
|
if (current == null) current = "dark";
|
||||||
|
|
||||||
String trimmed = (args == null) ? "" : args.trim().toLowerCase();
|
String trimmed = args(args).toLowerCase();
|
||||||
|
|
||||||
if (trimmed.isEmpty()) {
|
if (trimmed.isEmpty()) {
|
||||||
return "\n" + AnsiStyle.bold(" 🎨 Theme Settings\n")
|
return "\n" + AnsiStyle.bold(" 🎨 Theme Settings\n")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -10,7 +10,7 @@ import java.util.Random;
|
|||||||
/**
|
/**
|
||||||
* /tips 命令 —— 显示使用技巧和建议。
|
* /tips 命令 —— 显示使用技巧和建议。
|
||||||
*/
|
*/
|
||||||
public class TipsCommand implements SlashCommand {
|
public class TipsCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
private static final List<String> TIPS = List.of(
|
private static final List<String> TIPS = List.of(
|
||||||
"Use /compact when the conversation gets long to reduce token usage",
|
"Use /compact when the conversation gets long to reduce token usage",
|
||||||
@@ -45,7 +45,7 @@ public class TipsCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = (args == null) ? "" : args.trim();
|
String trimmed = args(args);
|
||||||
|
|
||||||
if ("all".equals(trimmed)) {
|
if ("all".equals(trimmed)) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|||||||
@@ -1,20 +1,18 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /trace 命令 —— 请求/响应追踪。
|
* /trace 命令 —— 请求/响应追踪。
|
||||||
* 显示 API 调用追踪信息(模型调用、tool 调用链等)。
|
* 显示 API 调用追踪信息(模型调用、tool 调用链等)。
|
||||||
*/
|
*/
|
||||||
public class TraceCommand implements SlashCommand {
|
public class TraceCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "trace"; }
|
public String name() { return "trace"; }
|
||||||
@@ -24,16 +22,16 @@ public class TraceCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
String trimmed = CommandUtils.parseArgs(args);
|
String trimmed = args(args);
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🔍", "Request Tracing"));
|
sb.append(header("🔍", "Request Tracing"));
|
||||||
|
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return sb.append(" No active agent loop\n").toString();
|
return sb.append(" No active agent loop\n").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
|
|
||||||
if (trimmed.equals("on") || trimmed.equals("enable")) {
|
if (trimmed.equals("on") || trimmed.equals("enable")) {
|
||||||
toolCtx.set("TRACE_ENABLED", true);
|
toolCtx.set("TRACE_ENABLED", true);
|
||||||
@@ -69,7 +67,7 @@ public class TraceCommand implements SlashCommand {
|
|||||||
|
|
||||||
// Show recent conversation turns
|
// Show recent conversation turns
|
||||||
sb.append("\n").append(AnsiStyle.bold(" Conversation State\n"));
|
sb.append("\n").append(AnsiStyle.bold(" Conversation State\n"));
|
||||||
sb.append(" Session ID: ").append(context.agentLoop().getToolContext()
|
sb.append(" Session ID: ").append(toolCtx(context)
|
||||||
.get("SESSION_ID") != null ? toolCtx.get("SESSION_ID") : "default").append("\n");
|
.get("SESSION_ID") != null ? toolCtx.get("SESSION_ID") : "default").append("\n");
|
||||||
|
|
||||||
sb.append("\n").append(AnsiStyle.bold(" Subcommands\n"));
|
sb.append("\n").append(AnsiStyle.bold(" Subcommands\n"));
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
import com.claudecode.core.TokenTracker;
|
import com.claudecode.core.TokenTracker;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /usage 命令 —— 详细 token 和费用统计。
|
* /usage 命令 —— 详细 token 和费用统计。
|
||||||
*/
|
*/
|
||||||
public class UsageCommand implements SlashCommand {
|
public class UsageCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "usage"; }
|
public String name() { return "usage"; }
|
||||||
@@ -18,8 +18,8 @@ public class UsageCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
TokenTracker tracker = context.agentLoop().getTokenTracker();
|
TokenTracker tracker = context.agentLoop().getTokenTracker();
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.CommandUtils;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.command.SlashCommand;
|
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -10,7 +9,7 @@ import com.claudecode.console.AnsiStyle;
|
|||||||
* <p>
|
* <p>
|
||||||
* 展示 Claude Code Java 版本、运行时环境等信息。
|
* 展示 Claude Code Java 版本、运行时环境等信息。
|
||||||
*/
|
*/
|
||||||
public class VersionCommand implements SlashCommand {
|
public class VersionCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
/** 当前版本号 */
|
/** 当前版本号 */
|
||||||
public static final String VERSION = "1.0.0";
|
public static final String VERSION = "1.0.0";
|
||||||
@@ -29,7 +28,7 @@ public class VersionCommand implements SlashCommand {
|
|||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append(CommandUtils.header("🏷️", "Claude Code Java"));
|
sb.append(header("🏷️", "Claude Code Java"));
|
||||||
|
|
||||||
sb.append(" ").append(AnsiStyle.bold("Version: "))
|
sb.append(" ").append(AnsiStyle.bold("Version: "))
|
||||||
.append(AnsiStyle.cyan("v" + VERSION)).append("\n");
|
.append(AnsiStyle.cyan("v" + VERSION)).append("\n");
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package com.claudecode.command.impl;
|
package com.claudecode.command.impl;
|
||||||
|
|
||||||
import com.claudecode.command.CommandContext;
|
import com.claudecode.command.CommandContext;
|
||||||
import com.claudecode.command.SlashCommand;
|
import com.claudecode.command.BaseSlashCommand;
|
||||||
import com.claudecode.console.AnsiStyle;
|
import com.claudecode.console.AnsiStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* /vim 命令 —— 切换 Vi 编辑模式(JLine vi-mode)。
|
* /vim 命令 —— 切换 Vi 编辑模式(JLine vi-mode)。
|
||||||
*/
|
*/
|
||||||
public class VimCommand implements SlashCommand {
|
public class VimCommand extends BaseSlashCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String name() { return "vim"; }
|
public String name() { return "vim"; }
|
||||||
@@ -17,14 +17,14 @@ public class VimCommand implements SlashCommand {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String execute(String args, CommandContext context) {
|
public String execute(String args, CommandContext context) {
|
||||||
if (context.agentLoop() == null) {
|
if (requireAgentLoop(context) == null) {
|
||||||
return AnsiStyle.red(" ✗ No active session");
|
return noSession();
|
||||||
}
|
}
|
||||||
|
|
||||||
var toolCtx = context.agentLoop().getToolContext();
|
var toolCtx = toolCtx(context);
|
||||||
boolean current = Boolean.TRUE.equals(toolCtx.get("VIM_MODE"));
|
boolean current = Boolean.TRUE.equals(toolCtx.get("VIM_MODE"));
|
||||||
|
|
||||||
String trimmed = (args == null) ? "" : args.trim();
|
String trimmed = args(args);
|
||||||
boolean newMode = switch (trimmed) {
|
boolean newMode = switch (trimmed) {
|
||||||
case "on", "enable" -> true;
|
case "on", "enable" -> true;
|
||||||
case "off", "disable" -> false;
|
case "off", "disable" -> false;
|
||||||
|
|||||||
@@ -0,0 +1,171 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
使用OpenWeatherMap API查询天气的示例代码
|
||||||
|
需要先注册获取API密钥:https://openweathermap.org/api
|
||||||
|
"""
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def get_weather_by_city(city_name, api_key, units="metric"):
|
||||||
|
"""
|
||||||
|
根据城市名称查询天气
|
||||||
|
|
||||||
|
Args:
|
||||||
|
city_name: 城市名称(如:"Beijing")
|
||||||
|
api_key: OpenWeatherMap API密钥
|
||||||
|
units: 温度单位,"metric"为摄氏度,"imperial"为华氏度
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
天气数据的字典
|
||||||
|
"""
|
||||||
|
base_url = "http://api.openweathermap.org/data/2.5/weather"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"q": city_name,
|
||||||
|
"appid": api_key,
|
||||||
|
"units": units,
|
||||||
|
"lang": "zh_cn" # 中文返回
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(base_url, params=params, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"请求错误: {e}")
|
||||||
|
return None
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"JSON解析错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_weather_by_coords(lat, lon, api_key, units="metric"):
|
||||||
|
"""
|
||||||
|
根据经纬度坐标查询天气
|
||||||
|
|
||||||
|
Args:
|
||||||
|
lat: 纬度
|
||||||
|
lon: 经度
|
||||||
|
api_key: OpenWeatherMap API密钥
|
||||||
|
units: 温度单位
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
天气数据的字典
|
||||||
|
"""
|
||||||
|
base_url = "http://api.openweathermap.org/data/2.5/weather"
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"lat": lat,
|
||||||
|
"lon": lon,
|
||||||
|
"appid": api_key,
|
||||||
|
"units": units,
|
||||||
|
"lang": "zh_cn"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.get(base_url, params=params, timeout=10)
|
||||||
|
response.raise_for_status()
|
||||||
|
return response.json()
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"请求错误: {e}")
|
||||||
|
return None
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
print(f"JSON解析错误: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
def display_weather(weather_data):
|
||||||
|
"""
|
||||||
|
显示天气信息
|
||||||
|
|
||||||
|
Args:
|
||||||
|
weather_data: 天气数据字典
|
||||||
|
"""
|
||||||
|
if not weather_data or "cod" in weather_data and weather_data["cod"] != 200:
|
||||||
|
print("无法获取天气数据")
|
||||||
|
if weather_data and "message" in weather_data:
|
||||||
|
print(f"错误信息: {weather_data['message']}")
|
||||||
|
return
|
||||||
|
|
||||||
|
print("\n=== 天气信息 ===")
|
||||||
|
print(f"城市: {weather_data.get('name', '未知')}")
|
||||||
|
|
||||||
|
if "sys" in weather_data:
|
||||||
|
country = weather_data["sys"].get("country", "")
|
||||||
|
print(f"国家: {country}")
|
||||||
|
|
||||||
|
if "main" in weather_data:
|
||||||
|
main = weather_data["main"]
|
||||||
|
print(f"温度: {main.get('temp', '未知')}°C")
|
||||||
|
print(f"体感温度: {main.get('feels_like', '未知')}°C")
|
||||||
|
print(f"最低温度: {main.get('temp_min', '未知')}°C")
|
||||||
|
print(f"最高温度: {main.get('temp_max', '未知')}°C")
|
||||||
|
print(f"湿度: {main.get('humidity', '未知')}%")
|
||||||
|
print(f"气压: {main.get('pressure', '未知')} hPa")
|
||||||
|
|
||||||
|
if "weather" in weather_data and len(weather_data["weather"]) > 0:
|
||||||
|
weather = weather_data["weather"][0]
|
||||||
|
print(f"天气状况: {weather.get('description', '未知')}")
|
||||||
|
print(f"天气图标: http://openweathermap.org/img/wn/{weather.get('icon', '')}@2x.png")
|
||||||
|
|
||||||
|
if "wind" in weather_data:
|
||||||
|
wind = weather_data["wind"]
|
||||||
|
print(f"风速: {wind.get('speed', '未知')} m/s")
|
||||||
|
if "deg" in wind:
|
||||||
|
print(f"风向: {wind.get('deg', '未知')}°")
|
||||||
|
|
||||||
|
if "clouds" in weather_data:
|
||||||
|
print(f"云量: {weather_data['clouds'].get('all', '未知')}%")
|
||||||
|
|
||||||
|
if "visibility" in weather_data:
|
||||||
|
visibility = weather_data["visibility"]
|
||||||
|
if visibility >= 1000:
|
||||||
|
print(f"能见度: {visibility/1000:.1f} km")
|
||||||
|
else:
|
||||||
|
print(f"能见度: {visibility} m")
|
||||||
|
|
||||||
|
if "dt" in weather_data:
|
||||||
|
from datetime import datetime
|
||||||
|
dt = datetime.fromtimestamp(weather_data["dt"])
|
||||||
|
print(f"数据时间: {dt.strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
|
|
||||||
|
if "timezone" in weather_data:
|
||||||
|
offset = weather_data["timezone"] / 3600
|
||||||
|
print(f"时区: UTC{offset:+g}")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
主函数:演示如何使用API
|
||||||
|
"""
|
||||||
|
print("OpenWeatherMap API 天气查询示例")
|
||||||
|
print("=" * 40)
|
||||||
|
|
||||||
|
# 您需要在这里填写您的API密钥
|
||||||
|
API_KEY = "YOUR_API_KEY_HERE" # 请替换为您的实际API密钥
|
||||||
|
|
||||||
|
if API_KEY == "YOUR_API_KEY_HERE":
|
||||||
|
print("\n⚠️ 请先注册OpenWeatherMap并获取API密钥:")
|
||||||
|
print(" https://openweathermap.org/api")
|
||||||
|
print("\n注册步骤:")
|
||||||
|
print("1. 访问 https://openweathermap.org/api")
|
||||||
|
print("2. 点击 'Subscribe' 或 'Sign Up'")
|
||||||
|
print("3. 注册免费账户(Current Weather Data API 免费套餐每天可调用60次)")
|
||||||
|
print("4. 在控制台获取您的API密钥")
|
||||||
|
print("\n获取API密钥后,请替换代码中的 'YOUR_API_KEY_HERE'")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 示例:查询北京的天气
|
||||||
|
print("\n示例1:查询北京天气")
|
||||||
|
weather_data = get_weather_by_city("Beijing", API_KEY)
|
||||||
|
if weather_data:
|
||||||
|
display_weather(weather_data)
|
||||||
|
|
||||||
|
# 示例:使用经纬度查询(纽约的坐标)
|
||||||
|
print("\n" + "=" * 40)
|
||||||
|
print("示例2:使用经纬度查询纽约天气")
|
||||||
|
weather_data = get_weather_by_coords(40.7128, -74.0060, API_KEY)
|
||||||
|
if weather_data:
|
||||||
|
display_weather(weather_data)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user