diff --git a/src/main/java/com/claudecode/command/impl/PlanCommand.java b/src/main/java/com/claudecode/command/impl/PlanCommand.java new file mode 100644 index 0000000..88374ab --- /dev/null +++ b/src/main/java/com/claudecode/command/impl/PlanCommand.java @@ -0,0 +1,48 @@ +package com.claudecode.command.impl; + +import com.claudecode.command.CommandContext; +import com.claudecode.command.SlashCommand; +import com.claudecode.permission.PermissionSettings; +import com.claudecode.permission.PermissionTypes.PermissionMode; + +/** + * /plan 命令 —— 对应 claude-code/src/commands/plan/plan.tsx。 + *
+ * 切换计划模式开关。在计划模式下,AI只能分析不能修改。
+ */
+public class PlanCommand implements SlashCommand {
+
+ private final PermissionSettings permissionSettings;
+
+ public PlanCommand(PermissionSettings permissionSettings) {
+ this.permissionSettings = permissionSettings;
+ }
+
+ @Override
+ public String name() {
+ return "plan";
+ }
+
+ @Override
+ public String description() {
+ return "Toggle plan mode (analysis only, no file modifications)";
+ }
+
+ @Override
+ public String execute(String args, CommandContext context) {
+ PermissionMode currentMode = permissionSettings.getCurrentMode();
+
+ if (currentMode == PermissionMode.PLAN) {
+ // Exit plan mode
+ permissionSettings.setCurrentMode(PermissionMode.DEFAULT);
+ return "📋 Exited plan mode. Normal permissions restored.\n" +
+ "All tools are now available.";
+ } else {
+ // Enter plan mode
+ permissionSettings.setCurrentMode(PermissionMode.PLAN);
+ return "📋 Entered plan mode.\n" +
+ "Only read-only tools are available. Use /plan again to exit.\n" +
+ "Or ask the AI to call EnterPlanMode for the full workflow.";
+ }
+ }
+}
diff --git a/src/main/java/com/claudecode/config/AppConfig.java b/src/main/java/com/claudecode/config/AppConfig.java
index 11b6a88..d57f0e5 100644
--- a/src/main/java/com/claudecode/config/AppConfig.java
+++ b/src/main/java/com/claudecode/config/AppConfig.java
@@ -79,10 +79,11 @@ public class AppConfig {
@Bean
public ToolRegistry toolRegistry(TaskManager taskManager, McpManager mcpManager,
- ToolContext toolContext) {
+ ToolContext toolContext, PermissionSettings permissionSettings) {
// 将 TaskManager 和 McpManager 注册到 ToolContext 供工具使用
toolContext.set("TASK_MANAGER", taskManager);
toolContext.set("MCP_MANAGER", mcpManager);
+ toolContext.set("PERMISSION_SETTINGS", permissionSettings);
ToolRegistry registry = new ToolRegistry();
registry.registerAll(
@@ -110,7 +111,9 @@ public class AppConfig {
new ConfigTool(),
// P2: 实用工具
new SleepTool(),
- new ToolSearchTool()
+ new ToolSearchTool(),
+ new EnterPlanModeTool(),
+ new ExitPlanModeTool()
);
// P2: 注册 MCP 工具桥接(将远程 MCP 工具映射为本地工具)
@@ -156,6 +159,7 @@ public class AppConfig {
new FilesCommand(),
new PermissionsCommand(permissionSettings),
new TasksCommand(taskManager),
+ new PlanCommand(permissionSettings),
// P2 命令
new HooksCommand(),
new ReviewCommand(),
diff --git a/src/main/java/com/claudecode/context/SystemPromptBuilder.java b/src/main/java/com/claudecode/context/SystemPromptBuilder.java
index 4a7658c..b006e6b 100644
--- a/src/main/java/com/claudecode/context/SystemPromptBuilder.java
+++ b/src/main/java/com/claudecode/context/SystemPromptBuilder.java
@@ -30,6 +30,9 @@ public class SystemPromptBuilder {
private String skillsSummary;
private String gitSummary;
private String languagePreference;
+ private boolean planMode;
+ private String planFilePath;
+ private String sessionMemory;
public SystemPromptBuilder() {
this.workDir = System.getProperty("user.dir");
@@ -67,6 +70,17 @@ public class SystemPromptBuilder {
return this;
}
+ public SystemPromptBuilder planMode(boolean active, String planFilePath) {
+ this.planMode = active;
+ this.planFilePath = planFilePath;
+ return this;
+ }
+
+ public SystemPromptBuilder sessionMemory(String sessionMemory) {
+ this.sessionMemory = sessionMemory;
+ return this;
+ }
+
/**
* 构建完整的系统提示词。
*/
@@ -130,6 +144,18 @@ public class SystemPromptBuilder {
sb.append(customInstructions).append("\n\n");
}
+ // ── 10. Plan Mode Instructions (对应 TS getPlanModeInstructions) ──
+ if (planMode) {
+ sb.append(getPlanModeSection());
+ }
+
+ // ── 11. Session Memory (对应 TS SessionMemory) ──
+ if (sessionMemory != null && !sessionMemory.isBlank()) {
+ sb.append("# Session Memory\n");
+ sb.append("The following is a summary of key information from this conversation:\n");
+ sb.append(sessionMemory).append("\n\n");
+ }
+
return sb.toString();
}
@@ -363,4 +389,62 @@ public class SystemPromptBuilder {
sb.append("\n");
return sb.toString();
}
+
+ /**
+ * 对应 TS getPlanModeInstructions()。
+ * 计划模式5阶段工作流指导。
+ */
+ private String getPlanModeSection() {
+ String planPath = planFilePath != null ? planFilePath : "~/.claude/projects/PLAN.md";
+ return """
+ # Plan Mode Active
+
+ The user indicated they do NOT want execution yet. They want you to analyze and plan first.
+ YOU MUST NOT make any edits (except the plan file), run shell commands, or make changes.
+
+ ## Plan File
+ Location: %s
+ This is the ONLY file you may create or edit in plan mode.
+
+ ## Plan Workflow (5 Phases)
+
+ ### Phase 1: Initial Understanding
+ - Use read-only tools (Read, Grep, Glob, ListFiles) to explore the codebase
+ - Find existing implementations and reusable patterns
+ - Understand the project structure and conventions
+
+ ### Phase 2: Design
+ - Based on your understanding, design the implementation approach
+ - Consider multiple perspectives and trade-offs
+ - Identify potential risks and edge cases
+
+ ### Phase 3: Review
+ - Read critical files you identified
+ - Ensure your plan aligns with the user's original request
+ - Use AskUserQuestion to clarify any ambiguous requirements
+
+ ### Phase 4: Write the Plan
+ - Write your plan to the plan file ONLY
+ - Include these sections:
+ - **Context**: Why this change is needed (problem/need/outcome)
+ - **Recommended Approach**: Single recommended approach (not all alternatives)
+ - **File Paths**: Critical files to be modified
+ - **Existing Utilities**: Functions to reuse (with file paths)
+ - **Verification**: Command to test the changes end-to-end
+ - Keep the plan concise but detailed (~40 lines typical)
+
+ ### Phase 5: Exit Plan Mode
+ - Call ExitPlanMode with a brief summary
+ - The user will review and approve the plan
+ - Do NOT use AskUserQuestion for plan approval — call ExitPlanMode instead
+
+ ## Important Reminders
+ - You can ONLY use: Read, Grep, Glob, ListFiles, WebFetch, WebSearch, AskUserQuestion
+ - You can ONLY write to: %s
+ - Do NOT run Bash commands
+ - Do NOT edit any source files
+ - Do NOT use FileWrite or FileEdit except for the plan file
+
+ """.formatted(planPath, planPath);
+ }
}
diff --git a/src/main/java/com/claudecode/permission/PermissionRuleEngine.java b/src/main/java/com/claudecode/permission/PermissionRuleEngine.java
index 8554c27..f6486ed 100644
--- a/src/main/java/com/claudecode/permission/PermissionRuleEngine.java
+++ b/src/main/java/com/claudecode/permission/PermissionRuleEngine.java
@@ -1,6 +1,7 @@
package com.claudecode.permission;
import com.claudecode.permission.PermissionTypes.*;
+import com.claudecode.tool.impl.EnterPlanModeTool;
import java.util.List;
import java.util.Map;
@@ -25,9 +26,13 @@ public class PermissionRuleEngine {
private static final Set
+ * 将权限切换到只读模式,AI只能分析代码不能修改,
+ * 只有计划文件(PLAN.md)可以编辑。
+ */
+public class EnterPlanModeTool implements Tool {
+
+ public static final String PLAN_MODE_KEY = "PLAN_MODE_ACTIVE";
+ public static final String PLAN_FILE_PATH_KEY = "PLAN_FILE_PATH";
+ public static final String PRE_PLAN_MODE_KEY = "PRE_PLAN_MODE";
+
+ @Override
+ public String name() {
+ return "EnterPlanMode";
+ }
+
+ @Override
+ public String description() {
+ return """
+ Enter plan mode to analyze the codebase and design an implementation plan WITHOUT making changes.
+
+ When to use:
+ - User asks you to "plan" or "think about" a change before implementing
+ - User wants to understand approach before committing to it
+ - Complex multi-file changes that need careful design
+
+ In plan mode:
+ - You can ONLY use read-only tools (Read, Grep, Glob, ListFiles, WebFetch, WebSearch)
+ - You can ONLY write to the plan file (PLAN.md)
+ - All other file modifications and shell commands are BLOCKED
+ - Use AskUserQuestion to clarify requirements
+ - Call ExitPlanMode when the plan is complete
+
+ The plan file location is determined automatically based on the project path.
+ """;
+ }
+
+ @Override
+ public String inputSchema() {
+ return """
+ {
+ "type": "object",
+ "properties": {
+ "reason": {
+ "type": "string",
+ "description": "Brief reason for entering plan mode"
+ }
+ },
+ "required": []
+ }
+ """;
+ }
+
+ @Override
+ public String execute(Map
+ * 验证计划文件内容,恢复之前的权限模式。
+ */
+public class ExitPlanModeTool implements Tool {
+
+ @Override
+ public String name() {
+ return "ExitPlanMode";
+ }
+
+ @Override
+ public String description() {
+ return """
+ Exit plan mode after completing your implementation plan.
+
+ When to use:
+ - You have finished writing the plan to the plan file
+ - The plan includes: context, approach, file paths, and verification steps
+ - You are ready for the user to review and approve the plan
+
+ This will:
+ - Restore normal permission mode (all tools available again)
+ - Present the plan to the user for review
+ - The user can then ask you to implement the plan
+
+ Do NOT call this tool until the plan is written to the plan file.
+ """;
+ }
+
+ @Override
+ public String inputSchema() {
+ return """
+ {
+ "type": "object",
+ "properties": {
+ "summary": {
+ "type": "string",
+ "description": "Brief summary of the plan (1-2 sentences)"
+ }
+ },
+ "required": ["summary"]
+ }
+ """;
+ }
+
+ @Override
+ public String execute(Map