branches = new LinkedHashMap<>();
@@ -43,7 +43,7 @@ public class BranchCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
diff --git a/src/main/java/com/claudecode/command/impl/BriefCommand.java b/src/main/java/com/claudecode/command/impl/BriefCommand.java
index 13faf7d..13d3a00 100644
--- a/src/main/java/com/claudecode/command/impl/BriefCommand.java
+++ b/src/main/java/com/claudecode/command/impl/BriefCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /brief 命令 —— 切换简洁输出模式。
*/
-public class BriefCommand implements SlashCommand {
+public class BriefCommand extends BaseSlashCommand {
@Override
public String name() { return "brief"; }
@@ -17,14 +17,14 @@ public class BriefCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
boolean current = Boolean.TRUE.equals(toolCtx.get("BRIEF_MODE"));
- String trimmed = (args == null) ? "" : args.trim();
+ String trimmed = args(args);
boolean newMode = switch (trimmed) {
case "on", "enable", "true" -> true;
case "off", "disable", "false" -> false;
diff --git a/src/main/java/com/claudecode/command/impl/ClearCommand.java b/src/main/java/com/claudecode/command/impl/ClearCommand.java
index 8e8fe80..6688475 100644
--- a/src/main/java/com/claudecode/command/impl/ClearCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ClearCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /clear 命令 —— 清除对话历史。
*/
-public class ClearCommand implements SlashCommand {
+public class ClearCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/CommitCommand.java b/src/main/java/com/claudecode/command/impl/CommitCommand.java
index 8c9dd89..4fd45cd 100644
--- a/src/main/java/com/claudecode/command/impl/CommitCommand.java
+++ b/src/main/java/com/claudecode/command/impl/CommitCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.BufferedReader;
@@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit;
* /commit --pr —— 提交 + push + 创建 PR(使用 gh CLI)
*
*/
-public class CommitCommand implements SlashCommand {
+public class CommitCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -41,7 +41,7 @@ public class CommitCommand implements SlashCommand {
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
}
- args = args == null ? "" : args.strip();
+ args = args(args);
try {
boolean addAll = args.contains("--all") || args.contains("-a");
diff --git a/src/main/java/com/claudecode/command/impl/CompactCommand.java b/src/main/java/com/claudecode/command/impl/CompactCommand.java
index 50b087d..b48a47f 100644
--- a/src/main/java/com/claudecode/command/impl/CompactCommand.java
+++ b/src/main/java/com/claudecode/command/impl/CompactCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenTracker;
import com.claudecode.core.compact.AutoCompactManager;
@@ -23,7 +23,7 @@ import java.util.List;
* /compact --aggressive —— 激进压缩(保留更少上下文)
*
*/
-public class CompactCommand implements SlashCommand {
+public class CompactCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -37,11 +37,11 @@ public class CompactCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.yellow(" ⚠ No active conversation to compact.");
}
- String argStr = (args == null) ? "" : args.strip();
+ String argStr = args(args);
// --stats: only show statistics
if (argStr.equals("--stats") || argStr.equals("-s")) {
diff --git a/src/main/java/com/claudecode/command/impl/ConfigCommand.java b/src/main/java/com/claudecode/command/impl/ConfigCommand.java
index d3b1c52..68c8d64 100644
--- a/src/main/java/com/claudecode/command/impl/ConfigCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ConfigCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.permission.PermissionRuleEngine;
import com.claudecode.permission.PermissionSettings;
@@ -15,7 +15,7 @@ import java.util.Map;
*
* 支持查看当前配置、设置单个配置项,以及权限管理子命令。
*/
-public class ConfigCommand implements SlashCommand {
+public class ConfigCommand extends BaseSlashCommand {
/** 支持的配置项及说明 */
private static final Map CONFIG_KEYS = Map.of(
diff --git a/src/main/java/com/claudecode/command/impl/ContextCommand.java b/src/main/java/com/claudecode/command/impl/ContextCommand.java
index 4c9d868..f73b1cf 100644
--- a/src/main/java/com/claudecode/command/impl/ContextCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ContextCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.context.ClaudeMdLoader;
import com.claudecode.context.GitContext;
@@ -14,7 +14,7 @@ import java.nio.file.Path;
*
* 展示已加载的 CLAUDE.md、Skills、Git 上下文和 Token 预算使用情况。
*/
-public class ContextCommand implements SlashCommand {
+public class ContextCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/ContextVizCommand.java b/src/main/java/com/claudecode/command/impl/ContextVizCommand.java
index 879a578..8b4c8e7 100644
--- a/src/main/java/com/claudecode/command/impl/ContextVizCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ContextVizCommand.java
@@ -1,8 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenEstimationService;
@@ -11,7 +10,7 @@ import java.util.List;
/**
* /ctx-viz 命令 —— 上下文可视化(token 分布、消息结构)。
*/
-public class ContextVizCommand implements SlashCommand {
+public class ContextVizCommand extends BaseSlashCommand {
@Override
public String name() { return "ctx-viz"; }
@@ -25,13 +24,13 @@ public class ContextVizCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
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();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
// Get or create token estimation service
TokenEstimationService estimator = new TokenEstimationService();
diff --git a/src/main/java/com/claudecode/command/impl/CopyCommand.java b/src/main/java/com/claudecode/command/impl/CopyCommand.java
index 29138e4..ac11039 100644
--- a/src/main/java/com/claudecode/command/impl/CopyCommand.java
+++ b/src/main/java/com/claudecode/command/impl/CopyCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
@@ -16,7 +16,7 @@ import java.util.List;
* 从消息历史中提取最后一条 AssistantMessage 的文本内容,
* 使用 AWT 剪贴板 API 复制。
*/
-public class CopyCommand implements SlashCommand {
+public class CopyCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/CostCommand.java b/src/main/java/com/claudecode/command/impl/CostCommand.java
index 10f6392..ef649ef 100644
--- a/src/main/java/com/claudecode/command/impl/CostCommand.java
+++ b/src/main/java/com/claudecode/command/impl/CostCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenTracker;
@@ -12,7 +12,7 @@ import com.claudecode.core.TokenTracker;
* 对应 claude-code/src/commands/cost.ts。
* 从 AgentLoop 的 TokenTracker 获取真实 Token 统计。
*/
-public class CostCommand implements SlashCommand {
+public class CostCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -30,7 +30,7 @@ public class CostCommand implements SlashCommand {
int msgCount = context.agentLoop().getMessageHistory().size();
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("API Calls: ")).append(tracker.getApiCallCount()).append("\n");
diff --git a/src/main/java/com/claudecode/command/impl/DebugCommand.java b/src/main/java/com/claudecode/command/impl/DebugCommand.java
index 982f463..843f610 100644
--- a/src/main/java/com/claudecode/command/impl/DebugCommand.java
+++ b/src/main/java/com/claudecode/command/impl/DebugCommand.java
@@ -1,8 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.InternalLogger;
@@ -11,7 +10,7 @@ import java.util.List;
/**
* /debug 命令 —— 调试模式开关 + 工具调用追踪。
*/
-public class DebugCommand implements SlashCommand {
+public class DebugCommand extends BaseSlashCommand {
@Override
public String name() { return "debug"; }
@@ -24,16 +23,16 @@ public class DebugCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
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();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
if (trimmed.equals("on") || trimmed.equals("enable")) {
toolCtx.set("DEBUG_MODE", true);
diff --git a/src/main/java/com/claudecode/command/impl/DiffCommand.java b/src/main/java/com/claudecode/command/impl/DiffCommand.java
index cf77664..8eeb07e 100644
--- a/src/main/java/com/claudecode/command/impl/DiffCommand.java
+++ b/src/main/java/com/claudecode/command/impl/DiffCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.BufferedReader;
@@ -20,7 +20,7 @@ import java.util.concurrent.TimeUnit;
*
--stat:仅显示文件统计(不含详细diff)
*
*/
-public class DiffCommand implements SlashCommand {
+public class DiffCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -39,7 +39,7 @@ public class DiffCommand implements SlashCommand {
return AnsiStyle.yellow(" ⚠ Current directory is not a Git repository");
}
- args = args == null ? "" : args.strip();
+ args = args(args);
try {
String diffOutput;
diff --git a/src/main/java/com/claudecode/command/impl/DoctorCommand.java b/src/main/java/com/claudecode/command/impl/DoctorCommand.java
index fb1bf27..99d0125 100644
--- a/src/main/java/com/claudecode/command/impl/DoctorCommand.java
+++ b/src/main/java/com/claudecode/command/impl/DoctorCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.File;
@@ -21,7 +21,7 @@ import java.util.List;
* 磁盘空间
*
*/
-public class DoctorCommand implements SlashCommand {
+public class DoctorCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/EnvCommand.java b/src/main/java/com/claudecode/command/impl/EnvCommand.java
index 5da62fa..cfce953 100644
--- a/src/main/java/com/claudecode/command/impl/EnvCommand.java
+++ b/src/main/java/com/claudecode/command/impl/EnvCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.File;
@@ -13,7 +13,7 @@ import java.util.TreeMap;
/**
* /env 命令 —— 显示环境变量和配置信息。
*/
-public class EnvCommand implements SlashCommand {
+public class EnvCommand extends BaseSlashCommand {
@Override
public String name() { return "env"; }
@@ -23,12 +23,12 @@ public class EnvCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
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(" ")
.append(System.getProperty("os.version")).append("\n");
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");
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(" Home: ").append(System.getProperty("user.home")).append("\n");
sb.append(" Config: ").append(System.getProperty("user.home"))
.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 relevantVars = List.of(
"ANTHROPIC_API_KEY", "OPENAI_API_KEY", "CLAUDE_CODE_",
"JAVA_HOME", "PATH", "SHELL", "TERM", "EDITOR"
diff --git a/src/main/java/com/claudecode/command/impl/ExitCommand.java b/src/main/java/com/claudecode/command/impl/ExitCommand.java
index fcd21cf..b1f2c01 100644
--- a/src/main/java/com/claudecode/command/impl/ExitCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ExitCommand.java
@@ -1,14 +1,14 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import java.util.List;
/**
* /exit 命令 —— 退出应用。
*/
-public class ExitCommand implements SlashCommand {
+public class ExitCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/ExportCommand.java b/src/main/java/com/claudecode/command/impl/ExportCommand.java
index 1cef675..a1f0e17 100644
--- a/src/main/java/com/claudecode/command/impl/ExportCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ExportCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import org.springframework.ai.chat.messages.*;
@@ -22,7 +22,7 @@ import java.util.List;
* /export [路径] —— 导出到指定路径
*
*/
-public class ExportCommand implements SlashCommand {
+public class ExportCommand extends BaseSlashCommand {
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;
if (!args.isEmpty()) {
outputPath = Path.of(args);
diff --git a/src/main/java/com/claudecode/command/impl/FeedbackCommand.java b/src/main/java/com/claudecode/command/impl/FeedbackCommand.java
index 67e40a9..4f947ca 100644
--- a/src/main/java/com/claudecode/command/impl/FeedbackCommand.java
+++ b/src/main/java/com/claudecode/command/impl/FeedbackCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
@@ -12,7 +12,7 @@ import java.time.Instant;
/**
* /feedback 命令 —— 提交反馈(本地保存)。
*/
-public class FeedbackCommand implements SlashCommand {
+public class FeedbackCommand extends BaseSlashCommand {
@Override
public String name() { return "feedback"; }
@@ -22,7 +22,7 @@ public class FeedbackCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = (args == null) ? "" : args.trim();
+ String trimmed = args(args);
if (trimmed.isEmpty()) {
return AnsiStyle.yellow(" Usage: /feedback ")
diff --git a/src/main/java/com/claudecode/command/impl/FilesCommand.java b/src/main/java/com/claudecode/command/impl/FilesCommand.java
index fcb4e62..9e32a39 100644
--- a/src/main/java/com/claudecode/command/impl/FilesCommand.java
+++ b/src/main/java/com/claudecode/command/impl/FilesCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
@@ -15,7 +15,7 @@ import java.util.stream.Stream;
* 对应 claude-code/src/commands/files.ts。
* 显示项目目录树(默认2层深度)。
*/
-public class FilesCommand implements SlashCommand {
+public class FilesCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/HeapdumpCommand.java b/src/main/java/com/claudecode/command/impl/HeapdumpCommand.java
index 50c075b..71c0195 100644
--- a/src/main/java/com/claudecode/command/impl/HeapdumpCommand.java
+++ b/src/main/java/com/claudecode/command/impl/HeapdumpCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.lang.management.ManagementFactory;
@@ -16,7 +16,7 @@ import java.time.format.DateTimeFormatter;
/**
* /heapdump 命令 —— JVM 堆转储(Java 独有优势)。
*/
-public class HeapdumpCommand implements SlashCommand {
+public class HeapdumpCommand extends BaseSlashCommand {
@Override
public String name() { return "heapdump"; }
@@ -26,26 +26,26 @@ public class HeapdumpCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("📦", "JVM Heap Dump"));
+ sb.append(header("📦", "JVM Heap Dump"));
if (trimmed.equals("info") || trimmed.isEmpty()) {
MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heap = memBean.getHeapMemoryUsage();
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(" Committed: ").append(CommandUtils.formatBytes(heap.getCommitted())).append("\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(" 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()) {
MemoryUsage usage = pool.getUsage();
if (usage != null && usage.getUsed() > 0) {
@@ -66,12 +66,12 @@ public class HeapdumpCommand implements SlashCommand {
com.sun.management.HotSpotDiagnosticMXBean.class);
hotspot.dumpHeap(dumpPath.toString(), true);
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(" Size: ").append(CommandUtils.formatBytes(fileSize)).append("\n\n");
sb.append(AnsiStyle.dim(" Analyze with: jhat, MAT, or VisualVM"));
} 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)"));
}
@@ -87,7 +87,7 @@ public class HeapdumpCommand implements SlashCommand {
sb.append(" Freed: ").append(AnsiStyle.green(CommandUtils.formatBytes(Math.max(0, freed)))).append("\n");
} 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 dump Generate .hprof file\n");
sb.append(" /heapdump gc Trigger garbage collection\n");
diff --git a/src/main/java/com/claudecode/command/impl/HelpCommand.java b/src/main/java/com/claudecode/command/impl/HelpCommand.java
index ca8c663..1538214 100644
--- a/src/main/java/com/claudecode/command/impl/HelpCommand.java
+++ b/src/main/java/com/claudecode/command/impl/HelpCommand.java
@@ -1,6 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.command.SlashCommand;
import com.claudecode.console.AnsiStyle;
@@ -17,7 +18,7 @@ import java.util.stream.Collectors;
* /help [command] —— 显示特定命令详情
*
*/
-public class HelpCommand implements SlashCommand {
+public class HelpCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -36,7 +37,7 @@ public class HelpCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String query = (args == null) ? "" : args.strip();
+ String query = args(args);
var allCommands = context.commandRegistry().getCommands();
// If a specific command name is given, show details
diff --git a/src/main/java/com/claudecode/command/impl/HistoryCommand.java b/src/main/java/com/claudecode/command/impl/HistoryCommand.java
index 6b9b7a9..c603f9b 100644
--- a/src/main/java/com/claudecode/command/impl/HistoryCommand.java
+++ b/src/main/java/com/claudecode/command/impl/HistoryCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.ConversationPersistence;
@@ -10,7 +10,7 @@ import com.claudecode.core.ConversationPersistence;
*
* 显示最近的对话记录,包括时间、摘要和消息数量。
*/
-public class HistoryCommand implements SlashCommand {
+public class HistoryCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/HooksCommand.java b/src/main/java/com/claudecode/command/impl/HooksCommand.java
index 3e2f60f..19fd56c 100644
--- a/src/main/java/com/claudecode/command/impl/HooksCommand.java
+++ b/src/main/java/com/claudecode/command/impl/HooksCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.HookManager;
import com.claudecode.core.HookManager.HookRegistration;
@@ -17,7 +17,7 @@ import java.util.stream.Collectors;
* 按 Hook 类型分组展示,包含每个 Hook 的名称和优先级。
* 如果没有注册任何 Hook,则显示提示信息。
*/
-public class HooksCommand implements SlashCommand {
+public class HooksCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/InitCommand.java b/src/main/java/com/claudecode/command/impl/InitCommand.java
index 20d0cbc..afdf0b7 100644
--- a/src/main/java/com/claudecode/command/impl/InitCommand.java
+++ b/src/main/java/com/claudecode/command/impl/InitCommand.java
@@ -1,14 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
-import java.util.List;
/**
* /init 命令 —— 初始化项目 CLAUDE.md。
@@ -16,7 +15,7 @@ import java.util.List;
* 对应 claude-code/src/commands/init.ts。
* 检测项目类型并生成 CLAUDE.md 模板文件。
*/
-public class InitCommand implements SlashCommand {
+public class InitCommand extends BaseSlashCommand {
@Override
public String name() {
diff --git a/src/main/java/com/claudecode/command/impl/KeybindingsCommand.java b/src/main/java/com/claudecode/command/impl/KeybindingsCommand.java
index 2bb4930..b58596e 100644
--- a/src/main/java/com/claudecode/command/impl/KeybindingsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/KeybindingsCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.util.List;
@@ -9,7 +9,7 @@ import java.util.List;
/**
* /keybindings 命令 —— 显示和配置快捷键。
*/
-public class KeybindingsCommand implements SlashCommand {
+public class KeybindingsCommand extends BaseSlashCommand {
@Override
public String name() { return "keybindings"; }
diff --git a/src/main/java/com/claudecode/command/impl/McpCommand.java b/src/main/java/com/claudecode/command/impl/McpCommand.java
index 78e5244..a0db39f 100644
--- a/src/main/java/com/claudecode/command/impl/McpCommand.java
+++ b/src/main/java/com/claudecode/command/impl/McpCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.mcp.McpClient;
import com.claudecode.mcp.McpException;
@@ -25,7 +25,7 @@ import java.util.*;
*
{@code /mcp reload} —— 从配置文件重新加载
*
*/
-public class McpCommand implements SlashCommand {
+public class McpCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -48,7 +48,7 @@ public class McpCommand implements SlashCommand {
return AnsiStyle.red(" ❌ MCP manager not initialized");
}
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
if (trimmed.isEmpty()) {
return showStatus(manager);
}
@@ -73,7 +73,7 @@ public class McpCommand implements SlashCommand {
*/
private String showStatus(McpManager manager) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("🔌", "MCP Server Status"));
+ sb.append(header("🔌", "MCP Server Status"));
Map clients = manager.getClients();
if (clients.isEmpty()) {
@@ -192,7 +192,7 @@ public class McpCommand implements SlashCommand {
*/
private String handleTools(McpManager manager, String args) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("🛠️", "MCP Tools"));
+ sb.append(header("🛠️", "MCP Tools"));
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) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("📦", "MCP Resources"));
+ sb.append(header("📦", "MCP Resources"));
String serverFilter = args.isEmpty() ? null : args.split("\\s+")[0];
diff --git a/src/main/java/com/claudecode/command/impl/MemoryCommand.java b/src/main/java/com/claudecode/command/impl/MemoryCommand.java
index 6408740..7ba6e55 100644
--- a/src/main/java/com/claudecode/command/impl/MemoryCommand.java
+++ b/src/main/java/com/claudecode/command/impl/MemoryCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
@@ -21,7 +21,7 @@ import java.util.List;
* /memory user —— 查看用户级 CLAUDE.md
*
*/
-public class MemoryCommand implements SlashCommand {
+public class MemoryCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -40,7 +40,7 @@ public class MemoryCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- args = args == null ? "" : args.strip();
+ args = args(args);
if (args.startsWith("add ")) {
return handleAdd(args.substring(4).strip());
diff --git a/src/main/java/com/claudecode/command/impl/ModelCommand.java b/src/main/java/com/claudecode/command/impl/ModelCommand.java
index c981ebc..53761d5 100644
--- a/src/main/java/com/claudecode/command/impl/ModelCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ModelCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.util.List;
@@ -12,7 +12,7 @@ import java.util.Map;
*
* 支持查看当前模型信息和切换到其他模型。
*/
-public class ModelCommand implements SlashCommand {
+public class ModelCommand extends BaseSlashCommand {
private static final Map AVAILABLE_MODELS = Map.of(
"sonnet", "claude-sonnet-4-20250514",
diff --git a/src/main/java/com/claudecode/command/impl/OutputStyleCommand.java b/src/main/java/com/claudecode/command/impl/OutputStyleCommand.java
index b982603..f269e0c 100644
--- a/src/main/java/com/claudecode/command/impl/OutputStyleCommand.java
+++ b/src/main/java/com/claudecode/command/impl/OutputStyleCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /output-style 命令 —— 输出风格设置。
*/
-public class OutputStyleCommand implements SlashCommand {
+public class OutputStyleCommand extends BaseSlashCommand {
@Override
public String name() { return "output-style"; }
@@ -17,15 +17,15 @@ public class OutputStyleCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
String current = (String) toolCtx.get("OUTPUT_STYLE");
if (current == null) current = "markdown";
- String trimmed = (args == null) ? "" : args.trim().toLowerCase();
+ String trimmed = args(args).toLowerCase();
if (trimmed.isEmpty()) {
return "\n" + AnsiStyle.bold(" 📝 Output Style\n")
diff --git a/src/main/java/com/claudecode/command/impl/PerformanceCommand.java b/src/main/java/com/claudecode/command/impl/PerformanceCommand.java
index dd36363..5e18299 100644
--- a/src/main/java/com/claudecode/command/impl/PerformanceCommand.java
+++ b/src/main/java/com/claudecode/command/impl/PerformanceCommand.java
@@ -2,14 +2,13 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
-import com.claudecode.console.AnsiStyle;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.telemetry.MetricsCollector;
/**
* /performance 命令 —— 性能统计。
*/
-public class PerformanceCommand implements SlashCommand {
+public class PerformanceCommand extends BaseSlashCommand {
@Override
public String name() { return "performance"; }
@@ -25,7 +24,7 @@ public class PerformanceCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("⚡", "Performance Statistics"));
+ sb.append(header("⚡", "Performance Statistics"));
Runtime runtime = Runtime.getRuntime();
long totalMem = runtime.totalMemory();
@@ -33,14 +32,14 @@ public class PerformanceCommand implements SlashCommand {
long usedMem = totalMem - freeMem;
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(" Allocated: ").append(CommandUtils.formatBytes(totalMem)).append("\n");
sb.append(" Max: ").append(CommandUtils.formatBytes(maxMem)).append("\n");
sb.append(" Usage: ").append(CommandUtils.progressBar((double) usedMem / maxMem, 20)).append("\n\n");
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(" Available: ").append(runtime.availableProcessors()).append(" CPUs\n\n");
@@ -50,14 +49,14 @@ public class PerformanceCommand implements SlashCommand {
gcCount += gc.getCollectionCount();
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(" Total time: ").append(CommandUtils.formatMillis(gcTime)).append("\n\n");
if (context.agentLoop() != null) {
- Object metricsObj = context.agentLoop().getToolContext().get("METRICS_COLLECTOR");
+ Object metricsObj = toolCtx(context).get("METRICS_COLLECTOR");
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");
var toolUsage = metrics.getToolUsage();
if (!toolUsage.isEmpty()) {
diff --git a/src/main/java/com/claudecode/command/impl/PermissionsCommand.java b/src/main/java/com/claudecode/command/impl/PermissionsCommand.java
index 5245b53..31e2ad1 100644
--- a/src/main/java/com/claudecode/command/impl/PermissionsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/PermissionsCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.permission.PermissionSettings;
import com.claudecode.permission.PermissionTypes;
@@ -14,7 +14,7 @@ import java.util.List;
* 对应 claude-code/src/commands/permissions.ts。
* 显示当前权限模式和规则列表。
*/
-public class PermissionsCommand implements SlashCommand {
+public class PermissionsCommand extends BaseSlashCommand {
private final PermissionSettings settings;
diff --git a/src/main/java/com/claudecode/command/impl/PlanCommand.java b/src/main/java/com/claudecode/command/impl/PlanCommand.java
index 88374ab..86b0808 100644
--- a/src/main/java/com/claudecode/command/impl/PlanCommand.java
+++ b/src/main/java/com/claudecode/command/impl/PlanCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.permission.PermissionSettings;
import com.claudecode.permission.PermissionTypes.PermissionMode;
@@ -10,7 +10,7 @@ import com.claudecode.permission.PermissionTypes.PermissionMode;
*
* 切换计划模式开关。在计划模式下,AI只能分析不能修改。
*/
-public class PlanCommand implements SlashCommand {
+public class PlanCommand extends BaseSlashCommand {
private final PermissionSettings permissionSettings;
diff --git a/src/main/java/com/claudecode/command/impl/PluginCommand.java b/src/main/java/com/claudecode/command/impl/PluginCommand.java
index aa29cf5..9a84ce9 100644
--- a/src/main/java/com/claudecode/command/impl/PluginCommand.java
+++ b/src/main/java/com/claudecode/command/impl/PluginCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
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.plugin.*;
@@ -32,7 +32,7 @@ import java.util.Optional;
* 通过 {@link com.claudecode.tool.ToolContext} 中 key 为
* {@code "PLUGIN_MANAGER"} 的共享状态获取 {@link PluginManager} 实例。
*/
-public class PluginCommand implements SlashCommand {
+public class PluginCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -56,7 +56,7 @@ public class PluginCommand implements SlashCommand {
return AnsiStyle.red(" ✗ Plugin system not initialized");
}
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
// 无参数:列出所有插件
if (trimmed.isEmpty()) {
@@ -88,7 +88,7 @@ public class PluginCommand implements SlashCommand {
private String listPlugins(PluginManager manager) {
List plugins = manager.getPlugins();
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("🔌", "Loaded Plugins"));
+ sb.append(header("🔌", "Loaded Plugins"));
if (plugins.isEmpty()) {
sb.append(AnsiStyle.dim(" No plugins loaded.")).append("\n");
@@ -173,7 +173,7 @@ public class PluginCommand implements SlashCommand {
Plugin p = info.plugin();
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("ID: ")).append(AnsiStyle.cyan(p.id())).append("\n");
@@ -360,7 +360,7 @@ public class PluginCommand implements SlashCommand {
private PluginInstaller getInstaller(CommandContext context) {
try {
- Object obj = context.agentLoop().getToolContext().get("PLUGIN_INSTALLER");
+ Object obj = toolCtx(context).get("PLUGIN_INSTALLER");
if (obj instanceof PluginInstaller pi) return pi;
} catch (Exception ignored) {}
return null;
@@ -368,7 +368,7 @@ public class PluginCommand implements SlashCommand {
private MarketplaceManager getMarketplace(CommandContext context) {
try {
- Object obj = context.agentLoop().getToolContext().get("MARKETPLACE_MANAGER");
+ Object obj = toolCtx(context).get("MARKETPLACE_MANAGER");
if (obj instanceof MarketplaceManager mm) return mm;
} catch (Exception ignored) {}
return null;
@@ -376,7 +376,7 @@ public class PluginCommand implements SlashCommand {
private PluginAutoUpdate getAutoUpdate(CommandContext context) {
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;
} catch (Exception ignored) {}
return null;
@@ -391,11 +391,11 @@ public class PluginCommand implements SlashCommand {
* @return PluginManager 实例,未找到时返回 null
*/
private PluginManager getPluginManager(CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return null;
}
try {
- Object manager = context.agentLoop().getToolContext().get("PLUGIN_MANAGER");
+ Object manager = toolCtx(context).get("PLUGIN_MANAGER");
if (manager instanceof PluginManager pm) {
return pm;
}
diff --git a/src/main/java/com/claudecode/command/impl/PrivacyCommand.java b/src/main/java/com/claudecode/command/impl/PrivacyCommand.java
index 8ffd4fc..936e678 100644
--- a/src/main/java/com/claudecode/command/impl/PrivacyCommand.java
+++ b/src/main/java/com/claudecode/command/impl/PrivacyCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /privacy 命令 —— 隐私设置查看和修改。
*/
-public class PrivacyCommand implements SlashCommand {
+public class PrivacyCommand extends BaseSlashCommand {
@Override
public String name() { return "privacy"; }
@@ -17,16 +17,16 @@ public class PrivacyCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
boolean telemetryEnabled = Boolean.TRUE.equals(toolCtx.get("TELEMETRY_ENABLED"));
boolean sessionLogging = !Boolean.FALSE.equals(toolCtx.get("SESSION_LOGGING"));
boolean memoryPersist = !Boolean.FALSE.equals(toolCtx.get("MEMORY_PERSIST"));
- String trimmed = (args == null) ? "" : args.trim().toLowerCase();
+ String trimmed = args(args).toLowerCase();
if (trimmed.isEmpty()) {
StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/com/claudecode/command/impl/ReleaseNotesCommand.java b/src/main/java/com/claudecode/command/impl/ReleaseNotesCommand.java
index 7d656e5..c1b9985 100644
--- a/src/main/java/com/claudecode/command/impl/ReleaseNotesCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ReleaseNotesCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.util.List;
@@ -10,7 +10,7 @@ import java.util.List;
/**
* /release-notes 命令 —— 显示版本更新日志。
*/
-public class ReleaseNotesCommand implements SlashCommand {
+public class ReleaseNotesCommand extends BaseSlashCommand {
@Override
public String name() { return "release-notes"; }
@@ -26,7 +26,7 @@ public class ReleaseNotesCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
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(CommandUtils.separator(45)).append("\n");
diff --git a/src/main/java/com/claudecode/command/impl/RenameCommand.java b/src/main/java/com/claudecode/command/impl/RenameCommand.java
index c564b49..8ef1712 100644
--- a/src/main/java/com/claudecode/command/impl/RenameCommand.java
+++ b/src/main/java/com/claudecode/command/impl/RenameCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.util.List;
@@ -12,7 +12,7 @@ import java.util.List;
* 对应 claude-code 的 /rename 命令,给当前会话设置一个友好名称。
* 会话名称在 UI 标题栏和会话列表中显示。
*/
-public class RenameCommand implements SlashCommand {
+public class RenameCommand extends BaseSlashCommand {
/** 当前会话名称(进程级别) */
private static volatile String sessionName = null;
@@ -34,7 +34,7 @@ public class RenameCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- args = args == null ? "" : args.strip();
+ args = args(args);
if (args.isBlank()) {
if (sessionName == null) {
diff --git a/src/main/java/com/claudecode/command/impl/ResetLimitsCommand.java b/src/main/java/com/claudecode/command/impl/ResetLimitsCommand.java
index 12d9bb4..a11aca9 100644
--- a/src/main/java/com/claudecode/command/impl/ResetLimitsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ResetLimitsCommand.java
@@ -1,8 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.RateLimiter;
@@ -11,7 +10,7 @@ import java.util.List;
/**
* /reset-limits 命令 —— 重置速率限制。
*/
-public class ResetLimitsCommand implements SlashCommand {
+public class ResetLimitsCommand extends BaseSlashCommand {
@Override
public String name() { return "reset-limits"; }
@@ -25,13 +24,13 @@ public class ResetLimitsCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
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();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
Object limiterObj = toolCtx.get("RATE_LIMITER");
if (limiterObj instanceof RateLimiter limiter) {
diff --git a/src/main/java/com/claudecode/command/impl/ResumeCommand.java b/src/main/java/com/claudecode/command/impl/ResumeCommand.java
index ebf9dc7..619054f 100644
--- a/src/main/java/com/claudecode/command/impl/ResumeCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ResumeCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.ConversationPersistence;
import com.claudecode.core.ConversationPersistence.ConversationSummary;
@@ -21,7 +21,7 @@ import java.util.List;
* /resume [序号] —— 恢复指定序号的对话
*
*/
-public class ResumeCommand implements SlashCommand {
+public class ResumeCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -38,7 +38,7 @@ public class ResumeCommand implements SlashCommand {
ConversationPersistence persistence = new ConversationPersistence();
List conversations = persistence.listConversations();
- args = args == null ? "" : args.strip();
+ args = args(args);
if (conversations.isEmpty()) {
return AnsiStyle.yellow(" ⚠ No saved conversations\n")
diff --git a/src/main/java/com/claudecode/command/impl/ReviewCommand.java b/src/main/java/com/claudecode/command/impl/ReviewCommand.java
index edb9260..4b21424 100644
--- a/src/main/java/com/claudecode/command/impl/ReviewCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ReviewCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.BufferedReader;
@@ -23,7 +23,7 @@ import java.util.stream.Collectors;
*
* 获取 diff 内容后,发送给 AI 模型进行代码审查。
*/
-public class ReviewCommand implements SlashCommand {
+public class ReviewCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -42,7 +42,7 @@ public class ReviewCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform code review.");
}
diff --git a/src/main/java/com/claudecode/command/impl/RewindCommand.java b/src/main/java/com/claudecode/command/impl/RewindCommand.java
index 8b19c75..8a6222b 100644
--- a/src/main/java/com/claudecode/command/impl/RewindCommand.java
+++ b/src/main/java/com/claudecode/command/impl/RewindCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import org.springframework.ai.chat.messages.Message;
@@ -20,7 +20,7 @@ import java.util.List;
* 使用 {@code agentLoop.getMessageHistory()} 获取当前消息历史,
* 然后通过 {@code agentLoop.replaceHistory()} 用截断后的列表替换。
*/
-public class RewindCommand implements SlashCommand {
+public class RewindCommand extends BaseSlashCommand {
/** 每个消息对包含的消息数(用户消息 + 助手消息) */
private static final int MESSAGES_PER_PAIR = 2;
@@ -37,7 +37,7 @@ public class RewindCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
diff --git a/src/main/java/com/claudecode/command/impl/SandboxCommand.java b/src/main/java/com/claudecode/command/impl/SandboxCommand.java
index 04ef2f9..b963023 100644
--- a/src/main/java/com/claudecode/command/impl/SandboxCommand.java
+++ b/src/main/java/com/claudecode/command/impl/SandboxCommand.java
@@ -1,17 +1,15 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
-import java.util.List;
/**
* /sandbox 命令 —— 沙箱模式切换。
* 控制工具执行的安全隔离级别。
*/
-public class SandboxCommand implements SlashCommand {
+public class SandboxCommand extends BaseSlashCommand {
@Override
public String name() { return "sandbox"; }
@@ -21,16 +19,16 @@ public class SandboxCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
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();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
if (trimmed.equals("on") || trimmed.equals("enable") || trimmed.equals("strict")) {
toolCtx.set("SANDBOX_MODE", "strict");
diff --git a/src/main/java/com/claudecode/command/impl/SecurityReviewCommand.java b/src/main/java/com/claudecode/command/impl/SecurityReviewCommand.java
index 2c19428..c10891c 100644
--- a/src/main/java/com/claudecode/command/impl/SecurityReviewCommand.java
+++ b/src/main/java/com/claudecode/command/impl/SecurityReviewCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.BufferedReader;
@@ -25,7 +25,7 @@ import java.util.stream.Collectors;
*
依赖安全问题
*
*/
-public class SecurityReviewCommand implements SlashCommand {
+public class SecurityReviewCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -44,7 +44,7 @@ public class SecurityReviewCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable, cannot perform security review.");
}
diff --git a/src/main/java/com/claudecode/command/impl/SessionCommand.java b/src/main/java/com/claudecode/command/impl/SessionCommand.java
index bb4b7e4..3247a88 100644
--- a/src/main/java/com/claudecode/command/impl/SessionCommand.java
+++ b/src/main/java/com/claudecode/command/impl/SessionCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.io.IOException;
@@ -24,7 +24,7 @@ import java.util.List;
* /session export —— 导出会话为 JSON
*
*/
-public class SessionCommand implements SlashCommand {
+public class SessionCommand extends BaseSlashCommand {
private static final DateTimeFormatter FMT =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
@@ -49,7 +49,7 @@ public class SessionCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- args = args == null ? "" : args.strip();
+ args = args(args);
if (args.startsWith("save")) {
String sessionName = args.length() > 5 ? args.substring(5).strip() : "";
diff --git a/src/main/java/com/claudecode/command/impl/SkillsCommand.java b/src/main/java/com/claudecode/command/impl/SkillsCommand.java
index e7468d8..63cdada 100644
--- a/src/main/java/com/claudecode/command/impl/SkillsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/SkillsCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.context.SkillLoader;
@@ -14,7 +14,7 @@ import java.util.List;
*
* 扫描并显示从用户级、项目级和命令目录加载的技能文件。
*/
-public class SkillsCommand implements SlashCommand {
+public class SkillsCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -51,7 +51,7 @@ public class SkillsCommand implements SlashCommand {
}
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("🎯", "Available Skills"));
+ sb.append(header("🎯", "Available Skills"));
if (skills.isEmpty()) {
sb.append(AnsiStyle.dim(" (No available skills)\n\n"));
@@ -89,7 +89,7 @@ public class SkillsCommand implements SlashCommand {
private String formatSkillDetail(SkillLoader.Skill skill) {
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");
if (!skill.description().isEmpty()) {
diff --git a/src/main/java/com/claudecode/command/impl/StatsCommand.java b/src/main/java/com/claudecode/command/impl/StatsCommand.java
index df6c639..5f12894 100644
--- a/src/main/java/com/claudecode/command/impl/StatsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/StatsCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenTracker;
@@ -23,7 +23,7 @@ import java.util.concurrent.TimeUnit;
*
JVM 运行时长
*
*/
-public class StatsCommand implements SlashCommand {
+public class StatsCommand extends BaseSlashCommand {
@Override
public String name() {
@@ -37,7 +37,7 @@ public class StatsCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
diff --git a/src/main/java/com/claudecode/command/impl/StatusCommand.java b/src/main/java/com/claudecode/command/impl/StatusCommand.java
index cf27f06..3eacadc 100644
--- a/src/main/java/com/claudecode/command/impl/StatusCommand.java
+++ b/src/main/java/com/claudecode/command/impl/StatusCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenTracker;
@@ -15,7 +15,7 @@ import java.time.Instant;
*
* 展示当前模型、Token 用量、工具数、消息数、内存和运行时间等信息。
*/
-public class StatusCommand implements SlashCommand {
+public class StatusCommand extends BaseSlashCommand {
private final Instant startTime = Instant.now();
@@ -32,7 +32,7 @@ public class StatusCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("📊", "Session Status"));
+ sb.append(header("📊", "Session Status"));
// 模型信息
TokenTracker tracker = context.agentLoop().getTokenTracker();
diff --git a/src/main/java/com/claudecode/command/impl/TagCommand.java b/src/main/java/com/claudecode/command/impl/TagCommand.java
index 3d9b093..8384b84 100644
--- a/src/main/java/com/claudecode/command/impl/TagCommand.java
+++ b/src/main/java/com/claudecode/command/impl/TagCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import org.springframework.ai.chat.messages.Message;
@@ -25,7 +25,7 @@ import java.util.Map;
*
{@code /tag goto } —— 回溯到指定标签位置
*
*/
-public class TagCommand implements SlashCommand {
+public class TagCommand extends BaseSlashCommand {
/** 静态标签存储:标签名称 -> 标签信息 */
private static final Map tags = new LinkedHashMap<>();
@@ -42,7 +42,7 @@ public class TagCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
+ if (requireAgentLoop(context) == null) {
return AnsiStyle.red(" ✗ AgentLoop unavailable.");
}
diff --git a/src/main/java/com/claudecode/command/impl/TasksCommand.java b/src/main/java/com/claudecode/command/impl/TasksCommand.java
index 4d86e38..a43ffb0 100644
--- a/src/main/java/com/claudecode/command/impl/TasksCommand.java
+++ b/src/main/java/com/claudecode/command/impl/TasksCommand.java
@@ -2,7 +2,7 @@ package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TaskManager;
import com.claudecode.core.TaskManager.TaskInfo;
@@ -18,7 +18,7 @@ import java.util.List;
* 对应 claude-code 中的任务管理 UI。
* 显示所有任务的状态、创建时间和结果摘要。
*/
-public class TasksCommand implements SlashCommand {
+public class TasksCommand extends BaseSlashCommand {
private final TaskManager taskManager;
@@ -39,7 +39,7 @@ public class TasksCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
List tasks;
- String filter = CommandUtils.parseArgs(args);
+ String filter = args(args);
// Optional status filter
if (!filter.isEmpty()) {
diff --git a/src/main/java/com/claudecode/command/impl/ThemeCommand.java b/src/main/java/com/claudecode/command/impl/ThemeCommand.java
index 84356e3..e3d2f45 100644
--- a/src/main/java/com/claudecode/command/impl/ThemeCommand.java
+++ b/src/main/java/com/claudecode/command/impl/ThemeCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /theme 命令 —— 切换主题(dark/light/auto)。
*/
-public class ThemeCommand implements SlashCommand {
+public class ThemeCommand extends BaseSlashCommand {
@Override
public String name() { return "theme"; }
@@ -17,15 +17,15 @@ public class ThemeCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
String current = (String) toolCtx.get("THEME");
if (current == null) current = "dark";
- String trimmed = (args == null) ? "" : args.trim().toLowerCase();
+ String trimmed = args(args).toLowerCase();
if (trimmed.isEmpty()) {
return "\n" + AnsiStyle.bold(" 🎨 Theme Settings\n")
diff --git a/src/main/java/com/claudecode/command/impl/TipsCommand.java b/src/main/java/com/claudecode/command/impl/TipsCommand.java
index 08d98b6..daf985d 100644
--- a/src/main/java/com/claudecode/command/impl/TipsCommand.java
+++ b/src/main/java/com/claudecode/command/impl/TipsCommand.java
@@ -1,7 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.util.List;
@@ -10,7 +10,7 @@ import java.util.Random;
/**
* /tips 命令 —— 显示使用技巧和建议。
*/
-public class TipsCommand implements SlashCommand {
+public class TipsCommand extends BaseSlashCommand {
private static final List TIPS = List.of(
"Use /compact when the conversation gets long to reduce token usage",
@@ -45,7 +45,7 @@ public class TipsCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = (args == null) ? "" : args.trim();
+ String trimmed = args(args);
if ("all".equals(trimmed)) {
StringBuilder sb = new StringBuilder();
diff --git a/src/main/java/com/claudecode/command/impl/TraceCommand.java b/src/main/java/com/claudecode/command/impl/TraceCommand.java
index 9f07f67..516f22b 100644
--- a/src/main/java/com/claudecode/command/impl/TraceCommand.java
+++ b/src/main/java/com/claudecode/command/impl/TraceCommand.java
@@ -1,20 +1,18 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import java.time.Instant;
import java.util.LinkedList;
-import java.util.List;
import java.util.concurrent.ConcurrentLinkedDeque;
/**
* /trace 命令 —— 请求/响应追踪。
* 显示 API 调用追踪信息(模型调用、tool 调用链等)。
*/
-public class TraceCommand implements SlashCommand {
+public class TraceCommand extends BaseSlashCommand {
@Override
public String name() { return "trace"; }
@@ -24,16 +22,16 @@ public class TraceCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- String trimmed = CommandUtils.parseArgs(args);
+ String trimmed = args(args);
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();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
if (trimmed.equals("on") || trimmed.equals("enable")) {
toolCtx.set("TRACE_ENABLED", true);
@@ -69,7 +67,7 @@ public class TraceCommand implements SlashCommand {
// Show recent conversation turns
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");
sb.append("\n").append(AnsiStyle.bold(" Subcommands\n"));
diff --git a/src/main/java/com/claudecode/command/impl/UsageCommand.java b/src/main/java/com/claudecode/command/impl/UsageCommand.java
index f8836dd..aaac535 100644
--- a/src/main/java/com/claudecode/command/impl/UsageCommand.java
+++ b/src/main/java/com/claudecode/command/impl/UsageCommand.java
@@ -1,14 +1,14 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
import com.claudecode.core.TokenTracker;
/**
* /usage 命令 —— 详细 token 和费用统计。
*/
-public class UsageCommand implements SlashCommand {
+public class UsageCommand extends BaseSlashCommand {
@Override
public String name() { return "usage"; }
@@ -18,8 +18,8 @@ public class UsageCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
TokenTracker tracker = context.agentLoop().getTokenTracker();
diff --git a/src/main/java/com/claudecode/command/impl/VersionCommand.java b/src/main/java/com/claudecode/command/impl/VersionCommand.java
index 53ab9c9..0990e98 100644
--- a/src/main/java/com/claudecode/command/impl/VersionCommand.java
+++ b/src/main/java/com/claudecode/command/impl/VersionCommand.java
@@ -1,8 +1,7 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.CommandUtils;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
@@ -10,7 +9,7 @@ import com.claudecode.console.AnsiStyle;
*
* 展示 Claude Code Java 版本、运行时环境等信息。
*/
-public class VersionCommand implements SlashCommand {
+public class VersionCommand extends BaseSlashCommand {
/** 当前版本号 */
public static final String VERSION = "1.0.0";
@@ -29,7 +28,7 @@ public class VersionCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
StringBuilder sb = new StringBuilder();
- sb.append(CommandUtils.header("🏷️", "Claude Code Java"));
+ sb.append(header("🏷️", "Claude Code Java"));
sb.append(" ").append(AnsiStyle.bold("Version: "))
.append(AnsiStyle.cyan("v" + VERSION)).append("\n");
diff --git a/src/main/java/com/claudecode/command/impl/VimCommand.java b/src/main/java/com/claudecode/command/impl/VimCommand.java
index 1c6e901..ca895ed 100644
--- a/src/main/java/com/claudecode/command/impl/VimCommand.java
+++ b/src/main/java/com/claudecode/command/impl/VimCommand.java
@@ -1,13 +1,13 @@
package com.claudecode.command.impl;
import com.claudecode.command.CommandContext;
-import com.claudecode.command.SlashCommand;
+import com.claudecode.command.BaseSlashCommand;
import com.claudecode.console.AnsiStyle;
/**
* /vim 命令 —— 切换 Vi 编辑模式(JLine vi-mode)。
*/
-public class VimCommand implements SlashCommand {
+public class VimCommand extends BaseSlashCommand {
@Override
public String name() { return "vim"; }
@@ -17,14 +17,14 @@ public class VimCommand implements SlashCommand {
@Override
public String execute(String args, CommandContext context) {
- if (context.agentLoop() == null) {
- return AnsiStyle.red(" ✗ No active session");
+ if (requireAgentLoop(context) == null) {
+ return noSession();
}
- var toolCtx = context.agentLoop().getToolContext();
+ var toolCtx = toolCtx(context);
boolean current = Boolean.TRUE.equals(toolCtx.get("VIM_MODE"));
- String trimmed = (args == null) ? "" : args.trim();
+ String trimmed = args(args);
boolean newMode = switch (trimmed) {
case "on", "enable" -> true;
case "off", "disable" -> false;
diff --git a/weather_example.py b/weather_example.py
new file mode 100644
index 0000000..e7d3447
--- /dev/null
+++ b/weather_example.py
@@ -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()
\ No newline at end of file