jetbrains-cc-gui Documentation

Jetbrains Claude Code and Codex GUI Plugin

5,056 stars TypeScript TypeScript 1 file · ~55,360 tokens
RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy jetbrains-cc-gui on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name jetbrains-cc-gui --restart always zhukunpenglinyutong/jetbrains-cc-gui:latest

1. Project Overview & Quickstart (zhukunpenglinyutong/jetbrains-cc-gui)

File: README.md

To mitigate Claude trademark risks, this project has been renamed to CC GUI (originally Claude Code GUI). The logo has also been updated to reduce Chinese-specific elements. Regarding security, a
/security-review audit will be conducted before each minor version release, and a comprehensive claude-code-security audit will be performed every 10 minor versions.

A powerful IntelliJ IDEA plugin that provides a visual interface for Claude Code and OpenAI Codex dual AI tools, making AI-assisted programming more efficient and intuitive.


Installation

CC GUI(Claude or Codex) Installation


Key Features

Dual AI Engine Support

  • Claude Code - Anthropic's official AI programming assistant, supporting Opus 4.5 and other models
  • OpenAI Codex - OpenAI's powerful code generation engine

Intelligent Conversation

  • Context-aware AI coding assistant
  • @file reference support for precise code context
  • Image sending support for visual requirement description
  • Conversation rewind feature for flexible history adjustment
  • Enhanced prompts for better AI understanding

Agent System

  • Built-in Agent system for automated complex tasks
  • Skills slash command system (/init, /review, etc.)
  • MCP server support to extend AI capabilities

Developer Experience

  • Comprehensive permission management and security controls
  • Code DIFF comparison feature
  • File navigation and code jumping
  • Dark/Light theme switching
  • Font scaling and IDE font synchronization
  • Internationalization support (auto-switch between Chinese/English)

Session Management

  • History session records and search
  • Session favorites
  • Message export support
  • Provider management (cc-switch compatible)
  • Usage statistics analysis

Project Status

The project is under active development with continuous updates. For version history and iteration progress, please read CHANGELOG.md


Collaborative Contributing

For contributing guidelines, please read CONTRIBUTING.md


Local Development and Debugging

1. Install Frontend Dependencies

bash
cd webview
npm install

2. Install ai-bridge Dependencies

bash
cd ai-bridge
npm install

3. Debug Plugin

Run in IDEA:

bash
./gradlew clean runIde

4. Build Plugin

sh
./gradlew clean buildPlugin

# The generated plugin package will be in the build/distributions/ directory (package size approximately 40MB)

License

MIT


Contributing

Thanks to all contributors who help make IDEA-Claude-Code-GUI better!


If this project is helpful to you, you can invite the author to have a KFC or a cup of coffee~

如果这个项目对你有帮助,想请作者吃顿肯德基(KFC)或者喝杯咖啡,都是可以的~

View Sponsors List →


Thanks for the support and feedback from the friends at LINUX DO.

Thank you for AtomGit platform G-Star certification


Acknowledgements

Recently, many bloggers have recommended this project on their own initiative, and I am deeply grateful. Thanks again to bloggers including "沉默的王二", "macrozheng", "JavaGuide", "Java知音", "鲲鹏talk 公众号", and "程序员青戈" for recommending this project. I will keep iterating to make it more comfortable for everyone to use.


File: docs/testing/project-level-prompts-testing-checklist.md

Project-Level Prompts Testing Checklist

Overview

This document provides a comprehensive manual testing checklist for the project-level prompts feature implementation.

Feature: Dual-scope prompt management (Global + Project)
Test Date: To be filled during testing
Tester: To be filled during testing


Pre-Testing Setup

  • Build the project: ./gradlew buildPlugin
  • Install the plugin in IntelliJ IDEA
  • Open a test project in IntelliJ IDEA
  • Ensure no existing .codemoss/prompt.json in home directory or project
  • Clear browser cache for webview

1. Global Prompts Management

1.1 Create Global Prompt

  • Open Settings (Tools → Codemoss → Settings)
  • Navigate to "Prompt Library" section
  • Click "Create" button in "Global Prompts" section
  • Enter prompt name: "Test Global Prompt"
  • Enter prompt content: "This is a test global prompt"
  • Click "Save"
  • Expected: Prompt appears in global prompts list
  • Expected: Success message displayed
  • Verify: File ~/.codemoss/prompt.json exists and contains the prompt

1.2 Edit Global Prompt

  • Click the three-dot menu (⋮) on a global prompt
  • Select "Edit"
  • Change prompt name to "Updated Global Prompt"
  • Change content to "Updated content"
  • Click "Save"
  • Expected: Prompt name and content updated in the list
  • Expected: Success message displayed
  • Verify: Changes persisted in ~/.codemoss/prompt.json

1.3 Delete Global Prompt

  • Click the three-dot menu on a global prompt
  • Select "Delete"
  • Confirm deletion in the dialog
  • Expected: Prompt removed from list
  • Expected: Success message displayed
  • Verify: Prompt removed from ~/.codemoss/prompt.json

1.4 Export Global Prompts

  • Create 2-3 global prompts
  • Click "Export" button in global section
  • Select prompts to export
  • Click "Export"
  • Choose export location
  • Expected: JSON file created with selected prompts
  • Verify: Exported file is valid JSON
  • Verify: Exported prompts match selection

1.5 Import Global Prompts

  • Click "Import" button in global section
  • Select a valid prompt JSON file
  • Review import preview dialog
  • Select conflict resolution strategy (SKIP/OVERWRITE/DUPLICATE)
  • Click "Import"
  • Expected: Prompts imported according to strategy
  • Expected: Import summary message displayed
  • Verify: Imported prompts appear in global list
  • Verify: Conflicts handled correctly

2. Project Prompts Management

2.1 Create Project Prompt

  • Open Settings
  • Navigate to "Prompt Library" section
  • Verify "Project Prompts - {ProjectName}" section appears
  • Click "Create" button in project section
  • Enter prompt name: "Test Project Prompt"
  • Enter prompt content: "This is a test project prompt"
  • Click "Save"
  • Expected: Prompt appears in project prompts list
  • Expected: Success message displayed
  • Verify: File /.codemoss/prompt.json exists and contains the prompt

2.2 Edit Project Prompt

  • Click the three-dot menu on a project prompt
  • Select "Edit"
  • Change prompt name to "Updated Project Prompt"
  • Change content to "Updated project content"
  • Click "Save"
  • Expected: Prompt updated in project list
  • Expected: Success message displayed
  • Verify: Changes persisted in /.codemoss/prompt.json

2.3 Delete Project Prompt

  • Click the three-dot menu on a project prompt
  • Select "Delete"
  • Confirm deletion
  • Expected: Prompt removed from project list
  • Expected: Success message displayed
  • Verify: Prompt removed from /.codemoss/prompt.json

2.4 Export Project Prompts

  • Create 2-3 project prompts
  • Click "Export" button in project section
  • Select prompts to export
  • Click "Export"
  • Choose export location
  • Expected: JSON file created with selected prompts
  • Verify: Exported file is valid JSON
  • Verify: Exported prompts match selection

2.5 Import Project Prompts

  • Click "Import" button in project section
  • Select a valid prompt JSON file
  • Review import preview dialog
  • Select conflict resolution strategy
  • Click "Import"
  • Expected: Prompts imported to project scope
  • Expected: Import summary displayed
  • Verify: Imported prompts appear in project list

2.6 Copy Project Prompt to Global

  • Create a project prompt
  • Click the three-dot menu on the prompt
  • Select "Copy to Global"
  • Expected: Prompt copied to global scope
  • Expected: Success message displayed
  • Verify: Prompt appears in both project and global lists
  • Verify: Both copies have unique IDs

3. Scope Isolation

3.1 Global and Project Independence

  • Create a prompt named "Test" in global scope
  • Create a prompt named "Test" in project scope
  • Expected: Both prompts exist independently
  • Edit global "Test" prompt
  • Expected: Project "Test" prompt remains unchanged
  • Delete global "Test" prompt
  • Expected: Project "Test" prompt still exists

3.2 ID Uniqueness

  • Create multiple prompts in global scope
  • Create multiple prompts in project scope
  • Verify: All prompt IDs are unique within each scope
  • Verify: IDs don't conflict between scopes

4. Multi-Project Support

4.1 Switch Between Projects

  • Open Project A
  • Create project prompts in Project A
  • Note project prompt names
  • Close Project A
  • Open Project B
  • Open Settings
  • Expected: Project section shows "Project Prompts - Project B"
  • Expected: Project A's prompts NOT visible
  • Create different project prompts in Project B
  • Close Project B
  • Reopen Project A
  • Expected: Project A's prompts visible again
  • Expected: Project B's prompts NOT visible

4.2 No Active Project

  • Close all projects
  • Open the plugin without a project
  • Open Settings
  • Expected: "No Active Project" message displayed
  • Expected: Global prompts still accessible
  • Expected: Project section disabled or hidden

5. Chat Integration

5.1 Autocomplete with Global Prompts

  • Create 2-3 global prompts with distinct names
  • Open chat input
  • Type "/" to trigger autocomplete
  • Expected: Global prompts appear with "[全局]" label
  • Select a global prompt
  • Expected: Prompt content inserted into chat

5.2 Autocomplete with Project Prompts

  • Create 2-3 project prompts with distinct names
  • Open chat input
  • Type "/" to trigger autocomplete
  • Expected: Project prompts appear with "[项目]" label
  • Expected: Project prompts appear BEFORE global prompts
  • Select a project prompt
  • Expected: Prompt content inserted into chat

5.3 Autocomplete with Both Scopes

  • Ensure both global and project prompts exist
  • Open chat input
  • Type "/" to trigger autocomplete
  • Expected: Both scopes visible
  • Expected: Project prompts listed first
  • Expected: Clear scope labels distinguish prompts
  • Expected: Prompts with same name in different scopes both visible

6. Error Handling

6.1 Invalid JSON Import

  • Click "Import" in either section
  • Select an invalid JSON file
  • Expected: Error message displayed
  • Expected: No prompts imported
  • Expected: Existing prompts unaffected

6.2 Permission Errors

  • Make ~/.codemoss directory read-only
  • Try to create a global prompt
  • Expected: Error message displayed
  • Restore permissions

6.3 Empty Project Path

  • Create a project without a base path (if possible)
  • Try to create a project prompt
  • Expected: Error message or graceful degradation
  • Expected: Global prompts still functional

7. UI/UX Validation

7.1 Layout and Styling

  • Verify: Two distinct sections (Global and Project)
  • Verify: Section headers clearly labeled
  • Verify: Action buttons properly aligned
  • Verify: Prompt cards have consistent styling
  • Verify: Dropdown menus positioned correctly
  • Verify: Loading spinners appear during operations

7.2 Internationalization

  • Switch language to English (en-US)
  • Verify: All labels translated to English
  • Verify: "Global Prompts" and "Project Prompts" labels correct
  • Switch language to Chinese (zh-CN)
  • Verify: All labels translated to Chinese
  • Verify: "全局提示词" and "项目提示词" labels correct

7.3 Responsive Behavior

  • Resize settings window to minimum width
  • Verify: UI remains usable
  • Verify: No text overflow or layout breaks
  • Expand window to maximum width
  • Verify: UI scales appropriately

8. Edge Cases

8.1 Empty States

  • Delete all global prompts
  • Expected: "No prompts available" message in global section
  • Delete all project prompts
  • Expected: "No prompts available" message in project section

8.2 Large Number of Prompts

  • Import 50+ prompts into global scope
  • Verify: List scrollable
  • Verify: Performance acceptable
  • Verify: Search/filter works (if implemented)

8.3 Special Characters in Names

  • Create prompts with special characters: @#$%^&*()
  • Verify: Names saved and displayed correctly
  • Verify: No encoding issues

8.4 Very Long Prompt Content

  • Create a prompt with 10,000+ characters of content
  • Verify: Content saved successfully
  • Verify: UI truncates display appropriately
  • Verify: Full content accessible on edit

9. Backward Compatibility

9.1 Existing Global Prompts

  • Manually create ~/.codemoss/prompt.json with old format prompts
  • Open Settings
  • Verify: Existing prompts load in global section
  • Verify: Can edit and delete existing prompts
  • Verify: New prompts saved in compatible format

9.2 Migration from Single Scope

  • Start with only old global prompts
  • Create first project prompt
  • Verify: Global prompts unaffected
  • Verify: Project prompts stored in correct location

10. Performance

10.1 Load Time

  • Measure time to open Settings with 100 global prompts
  • Expected: < 2 seconds to fully load
  • Measure time to open Settings with 100 project prompts
  • Expected: < 2 seconds to fully load

10.2 Operation Speed

  • Measure time to create a prompt
  • Expected: < 500ms
  • Measure time to delete a prompt
  • Expected: < 500ms
  • Measure time to import 50 prompts
  • Expected: < 3 seconds

Bug Tracker

# Description Severity Steps to Reproduce Status
1
2
3

Test Summary

Total Test Cases: 80+

Passed: ___
Failed: ___
Blocked: ___
Not Tested: ___

Pass Rate: ___%


Notes

Add any additional observations, suggestions, or issues encountered during testing.


Sign-off

Tested by: _______________
Date: _______________
Approved by: _______________
Date: _______________


File: docs/skills/avoiding-tmp-writes.md

解决 Claude CLI 在 IDEA 插件中写入 `/tmp` 的问题

在 IDE 插件中调用 claude-code CLI 时,模型经常会把 Write/Edit 等工具的 file_path 指向 /tmp/xxx。如果直接将 CLI 入口点切换到 claude-vscode,虽然会沿用 VSCode 的“工作区信任”策略,但也会强制要求通过 /login 获取官方凭证,并且只支持官方 https://api.anthropic.com。对于使用自建网关或手动配置 ANTHROPIC_API_KEY 的场景,这条路不可行。

最终有效的方案有两部分:

1. 使用 `sdk-ts` 入口点并注入自有 API 凭证

  1. 在 claude-bridge/channel-manager.js 中把入口点固定为 sdk-ts:

    java
    process.env.CLAUDE_CODE_ENTRYPOINT = "sdk-ts";
  2. 调用 CLI 前,从 ~/.claude/settings.json 或环境变量读取 ANTHROPIC_AUTH_TOKEN / ANTHROPIC_BASE_URL,写入 process.env。这样 CLI 就不会提示 /login,自建 API 也能正常工作。

  3. 仍然可以保留现有的 cwd/TMPDIR 设置与 additionalDirectories,便于调试和记录日志,但不再依赖 WorkspaceTrustManager 自动改写 settings.json。

2. 在 `permission-handler` 中重写工具的写入路径

  1. 在 claude-bridge/permission-handler.js 的 canUseTool 里增加一段重写逻辑:当检测到工具输入中出现 file_path 且指向 /tmp、/var/tmp 等临时目录时,自动改写为当前工作区根 (IDEA_PROJECT_PATH 或 PROJECT_PATH) 下的同名文件。

    javascript
    rewriteToolInputPaths(toolName, input);
  2. rewriteToolInputPaths 会递归检查 input,遇到 /tmp/foo.js 这类路径时将其换成 path.join(workdir, "foo.js"),并在日志中打印所有改写记录,方便验证。

  3. 由于这一步发生在 CLI 真正执行工具之前,Claude 即使仍然生成 /tmp/... 也会被我们拦下,从而保证所有写入都落在项目目录内。

3. 验证方式

  1. 在 IDE 中触发“创建文件”之类的工作流。
  2. 观察插件日志,确认:
    • CLAUDE_CODE_ENTRYPOINT 为 sdk-ts;
    • permission-handler 输出了 [PERMISSION] Rewrote paths ...,说明临时路径被成功改写;
    • CLI tool_use 日志中仍可能出现 /tmp/...,但实际执行时使用的则是被改写后的路径。

通过这套方案,可以继续使用自管的 API/网关,并根除 CLI 在 IDEA 环境下写入 /tmp 的问题,同时避免依赖 VSCode 专用的认证流程。


File: docs/skills/cmdline-argument-escaping-bug.md

命令行参数特殊字符解析问题

问题描述

在 Windows PowerShell 环境中,通过 ProcessBuilder 将用户输入作为命令行参数传递给 Node.js 进程时,如果用户输入包含特殊字符(如括号、引号、换行符等),会导致参数被错误解析和拆分。

典型错误表现

用户发送包含代码片段的消息:

go
queryBuild := data.DB.Table("flight_record").
    Where("deleted_at is null").
    Order(fmt.Sprintf("CASE WHEN inspection_status = %d THEN 0..."))

在 Node.js 端收到的参数会被错误拆分:

  • args[0] (message): 只包含部分消息
  • args[1] (sessionId): 变成 "null).\r\n\t\tOrder(fmt.Sprintf(CASE"
  • args[2] (cwd): 变成 "inspection_status"

问题原因

Windows PowerShell 对命令行参数有特殊的解析规则:

  1. 括号 () 被解释为子表达式
  2. 引号 " 需要特殊转义
  3. 换行符 \r\n 会分割参数
  4. 百分号 % 可能触发变量替换

解决方案

通过 stdin 传递参数,而不是命令行参数。

Java 端实现

java
// 1. 构建 JSON 对象
JsonObject stdinInput = new JsonObject();
stdinInput.addProperty("message", message);
stdinInput.addProperty("sessionId", sessionId);
// ... 其他参数
String stdinJson = gson.toJson(stdinInput);

// 2. 构建命令(不包含用户输入)
List<String> command = new ArrayList<>();
command.add(node);
command.add(scriptPath);
command.add("send");  // 只传固定的命令名称

// 3. 设置环境变量
ProcessBuilder pb = new ProcessBuilder(command);
pb.environment().put("CLAUDE_USE_STDIN", "true");

// 4. 启动进程后写入 stdin
Process process = pb.start();
try (OutputStream stdin = process.getOutputStream()) {
    stdin.write(stdinJson.getBytes(StandardCharsets.UTF_8));
    stdin.flush();
}

Node.js 端实现

javascript
// 读取 stdin 数据
async function readStdinData() {
  if (process.env.CLAUDE_USE_STDIN !== 'true') {
    return null;
  }
  
  return new Promise((resolve) => {
    let data = '';
    process.stdin.setEncoding('utf8');
    process.stdin.on('data', chunk => data += chunk);
    process.stdin.on('end', () => {
      try {
        resolve(JSON.parse(data));
      } catch {
        resolve(null);
      }
    });
    
    // 超时回退
    setTimeout(() => resolve(null), 100);
  });
}

// 使用时
const stdinData = await readStdinData();
if (stdinData && stdinData.message !== undefined) {
  // 从 stdin 读取参数
  const { message, sessionId, cwd } = stdinData;
} else {
  // 向后兼容:从命令行参数读取
  const [message, sessionId, cwd] = process.argv.slice(3);
}

受影响的文件

已修复的文件列表:

文件 方法/函数
ClaudeSDKBridge.java sendMessageWithChannel()
ClaudeSDKBridge.java executeQuerySync()
ClaudeSDKBridge.java executeQueryStream()
ClaudeSDKTest.java executeQuery()
channel-manager.js send 和 sendWithAttachments 命令
simple-query.js getUserPrompt()

开发注意事项

⚠️ 重要规则:

  1. 永远不要通过命令行参数传递用户输入的自由文本
  2. 始终使用 stdin + JSON 的方式传递可能包含特殊字符的数据
  3. 只有固定的、已知安全的值(如命令名称、UUID)可以通过命令行参数传递
  4. 新增任何调用 Node.js 脚本的代码时,必须检查是否涉及用户输入

复现 Bug 的测试用例

以下消息可以在未修复版本中稳定复现此 bug:

测试用例 1:包含括号和引号的 Go 代码

text
queryBuild := data.DB.Table("flight_record").Where("deleted_at is null")

测试用例 2:包含 fmt.Sprintf 的代码

text
fmt.Sprintf("CASE WHEN status = %d THEN 0", constant.StatusRunning)

测试用例 3:包含换行和缩进的多行代码

text
if err != nil {
    return fmt.Errorf("failed: %w", err)
}

测试用例 4:包含 SQL 语句

text
SELECT * FROM users WHERE name = 'test' AND status IN (1, 2, 3)

测试用例 5:包含 JSON 字符串

text
{"key": "value", "array": [1, 2, 3]}

测试用例 6:Windows 路径

text
C:\Users\test\Documents\file (1).txt

File: docs/skills/multimodal-permission-bug.md

多模态消息权限弹窗失效问题

问题描述

当用户发送包含图片的多模态消息时,Claude 在执行工具(如 Write、Bash)时不会弹出权限确认对话框,工具直接执行。而发送纯文本消息时权限弹窗正常工作。

表现形式

消息类型 权限弹窗 结果
纯文本消息 ✅ 正常显示 用户可以确认或拒绝
图片+文本消息 ❌ 不显示 工具直接执行,无需确认
先发图片,AI回复后再发纯文本 ✅ 正常显示 用户可以确认或拒绝

根本原因

Claude Agent SDK 的 `query()` 函数签名

typescript
function query({
  prompt,
  options
}: {
  prompt: string | AsyncIterable<SDKUserMessage>;
  options?: Options;
}): Query

prompt 参数有两种模式:

  1. 字符串模式 (string):直接传递文本消息
  2. 流式模式 (AsyncIterable<SDKUserMessage>):用于发送复杂的多模态消息

问题所在

当使用 AsyncIterable<SDKUserMessage> 作为 prompt 时,SDK 的 canUseTool 回调不会被触发。

这是 SDK 在流式输入模式下的一个行为特性(或 bug)。在这种模式下,options.canUseTool 虽然被设置,但 SDK 内部不会调用它。

代码对比

纯文本消息(正常工作):

javascript
const result = query({
  prompt: message,  // string 类型
  options: {
    canUseTool: canUseTool  // ✅ 会被调用
  }
});

多模态消息(权限失效):

javascript
const inputStream = new AsyncStream();
inputStream.enqueue(userMessage);  // SDKUserMessage 包含图片
inputStream.done();

const result = query({
  prompt: inputStream,  // AsyncIterable 类型
  options: {
    canUseTool: canUseTool  // ❌ 不会被调用
  }
});

解决方案

使用 PreToolUse Hook 替代 canUseTool 回调。

SDK 提供了 hooks 机制,其中 PreToolUse hook 会在工具执行前被调用,无论 prompt 是什么类型。

修复代码

javascript
// PreToolUse hook 用于权限控制
const preToolUseHook = async (input, toolUseID, options) => {
  console.log('[HOOK] PreToolUse called:', input.tool_name);
  
  if (normalizedPermissionMode !== 'default') {
    return { decision: 'approve' };
  }
  
  // 调用原有的 canUseTool 进行权限检查
  const result = await canUseTool(input.tool_name, input.tool_input);
  
  if (result.behavior === 'allow') {
    return { decision: 'approve' };
  } else if (result.behavior === 'deny') {
    return { 
      decision: 'block',
      reason: result.message || 'Permission denied'
    };
  }
  return {};
};

const options = {
  // ... 其他配置
  hooks: {
    PreToolUse: [{
      hooks: [preToolUseHook]
    }]
  }
};

后续开发注意事项

1. 多模态消息必须使用 hooks

当需要发送包含图片的消息时,不要依赖 canUseTool,必须同时配置 PreToolUse hook:

javascript
const options = {
  canUseTool: canUseTool,        // 可能不生效
  hooks: {
    PreToolUse: [{ hooks: [preToolUseHook] }]  // 必须配置
  }
};

2. Hook 的返回值格式

typescript
// 允许执行
return { decision: 'approve' };

// 拒绝执行
return { decision: 'block', reason: '拒绝原因' };

// 让 SDK 自行决定(显示默认权限提示)
return {};

3. Hook 输入参数

typescript
interface PreToolUseHookInput {
  hook_event_name: 'PreToolUse';
  tool_name: string;      // 工具名称:Write, Bash, Edit 等
  tool_input: ToolInput;  // 工具参数
  session_id: string;
  cwd: string;
}

4. 测试清单

添加多模态功能时,务必测试以下场景:

  • 纯文本消息 + Write 工具 → 权限弹窗
  • 纯文本消息 + Bash 工具 → 权限弹窗
  • 图片+文本消息 + Write 工具 → 权限弹窗
  • 图片+文本消息 + Bash 工具 → 权限弹窗
  • 拒绝权限后工具不执行

相关文件

  • claude-bridge/services/message-service.js - 消息发送逻辑
  • claude-bridge/permission-handler.js - 权限处理逻辑
  • docs/claude-agent-sdk.md - SDK 官方文档参考

参考资料


File: docs/skills/SLASH_COMMANDS.md

斜杠指令系统 - 完整技术文档

IDEA 插件版 Claude Code 的斜杠指令(Slash Commands)实现分析

版本: v0.1.1
更新时间: 2025-12-14
技术栈: React + TypeScript + IntelliJ IDEA Plugin


目录

  1. 一句话解释
  2. 系统架构
  3. 核心流程
  4. 五大核心模块
  5. 数据结构定义
  6. 关键实现细节
  7. 性能优化
  8. 文件映射表
  9. 常见问题

一句话解释

斜杠指令就是:当用户在输入框行首打 / 时,弹出一个命令菜单让用户选择。

就像你在微信里 @某人 会弹出好友列表一样,这里打 / 会弹出命令列表。


系统架构

架构拓扑图

text
/* Detailed source-code truncated for AI context efficiency. */

技术栈

层级 技术 文件位置
后端 Java + IntelliJ SDK src/main/java/com/github/claudecodegui/
桥接 JCEF Bridge ai-bridge/
前端框架 React 18 + TypeScript webview/src/
状态管理 React Hooks webview/src/components/ChatInputBox/hooks/
UI 组件 自定义 React 组件 webview/src/components/ChatInputBox/

核心流程

完整数据流程图

text
/* Detailed source-code truncated for AI context efficiency. */

简化流程图

text
用户输入 "/" (行首)
      │
      ▼
┌─────────────┐
│  检测到 /   │  ← useTriggerDetection
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 获取命令列表 │  ← slashCommandProvider (缓存 + 后端请求)
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 弹出下拉菜单 │  ← CompletionDropdown
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 用户选择命令 │  ← 键盘 ↑↓ 或鼠标点击
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 替换输入文本 │  ← replaceText()
└──────┬──────┘
       │
       ▼
┌─────────────┐
│ 执行命令    │  ← 用户按回车后发送给后端
└─────────────┘

五大核心模块

用 餐厅点餐 来比喻:

模块 比喻 做什么
触发检测器 服务员的眼睛 监视用户有没有在行首输入 /
命令缓存 菜单本 存储从后端获取的所有可用命令
数据提供者 后厨传菜员 根据用户输入过滤命令,隐藏不适用的命令
下拉菜单 菜单展示板 显示可选的命令列表
键盘导航 点餐手势 处理上下键和回车选择

模块 1:触发检测器 (useTriggerDetection)

位置: webview/src/components/ChatInputBox/hooks/useTriggerDetection.ts

核心特点:

  • 仅检测行首的 /(与原 VSCode 版本不同)
  • 同时支持 @ 文件引用触发
typescript
/**
 * 检测 / 斜杠命令触发(仅行首)
 */
function detectSlashTrigger(text: string, cursorPosition: number): TriggerQuery | null {
  // 从光标位置向前查找 /
  let start = cursorPosition - 1;
  while (start >= 0) {
    const char = text[start];

    // 遇到空格或换行,停止搜索
    if (char === ' ' || char === '\t' || char === '\n') {
      return null;
    }

    // 找到 /
    if (char === '/') {
      // 检查 / 前是否为行首
      const isLineStart = start === 0 || text[start - 1] === '\n';
      if (isLineStart) {
        return {
          trigger: '/',
          query: text.slice(start + 1, cursorPosition),
          start,
          end: cursorPosition,
        };
      }
      return null; // / 不在行首
    }
    start--;
  }
  return null;
}

检测示例:

输入 光标位置 检测结果
/cle 4 ✓ { query: "cle", start: 0 }
hello /cle 10 ✗ null (/ 不在行首)
行1\n/cle 6 ✓ { query: "cle", start: 3 }

模块 2:命令缓存 (slashCommandProvider)

位置: webview/src/components/ChatInputBox/providers/slashCommandProvider.ts

核心特点:

  • 从后端接收命令并缓存
  • 过滤隐藏命令(IDEA 插件不适用的命令)
  • 首次加载显示加载提示
typescript
/**
 * 隐藏的命令列表
 * 这些命令是 Claude Code CLI 内置命令,不应在 IDEA 插件中显示
 */
const HIDDEN_COMMANDS = new Set([
  '/clear',        // 清除对话历史
  '/context',      // 上下文可视化
  '/cost',         // 显示费用
  '/init',         // 初始化 CLAUDE.md
  '/pr-comments',  // PR 评论
  '/release-notes',// 发布说明
  '/review',       // 审查 PR
  '/security-review', // 安全审查
  '/todo',         // 待办事项
]);

// SDK 命令缓存
let cachedSdkCommands: CommandItem[] = [];
let isInitialLoading = true;

/**
 * 斜杠命令数据提供者
 */
export async function slashCommandProvider(
  query: string,
  signal: AbortSignal
): Promise<CommandItem[]> {
  // 确保回调已设置
  setupSlashCommandsCallback();

  // 如果缓存为空且正在首次加载,请求后端刷新
  if (cachedSdkCommands.length < 20 && !refreshRequested && isInitialLoading) {
    sendBridgeEvent('refresh_slash_commands');
    refreshRequested = true;
  }

  // 如果缓存为空,返回加载提示
  if (cachedSdkCommands.length === 0 && isInitialLoading) {
    return [{
      id: '__loading__',
      label: '正在加载斜杠指令...',
      category: 'system',
    }];
  }

  // 过滤隐藏命令和搜索
  return filterCommands(cachedSdkCommands, query);
}

模块 3:数据提供者与转换

位置: webview/src/components/ChatInputBox/providers/slashCommandProvider.ts

typescript
/**
 * 将 CommandItem 转换为 DropdownItemData
 */
export function commandToDropdownItem(command: CommandItem): DropdownItemData {
  return {
    id: command.id,
    label: command.label,
    icon: 'codicon-terminal',
    type: 'command',
    data: { command },
  };
}

模块 4:下拉菜单 (CompletionDropdown)

位置: webview/src/components/ChatInputBox/Dropdown/

关键功能:

  • 位置计算(锚定在 / 字符下方)
  • 高亮状态管理
  • 键盘/鼠标导航模式切换
  • 加载状态和空状态显示

模块 5:补全管理 (useCompletionDropdown)

位置: webview/src/components/ChatInputBox/hooks/useCompletionDropdown.ts

核心功能:

  • 防抖搜索(200ms)
  • AbortController 取消请求
  • 竞态条件防护
  • 键盘事件处理
typescript
export function useCompletionDropdown<T>({
  trigger,
  provider,
  toDropdownItem,
  onSelect,
  debounceMs = 200,
}: CompletionDropdownOptions<T>) {
  const [state, setState] = useState<CompletionDropdownState>({...});

  // AbortController 用于取消请求
  const abortControllerRef = useRef<AbortController | null>(null);

  /**
   * 防抖搜索
   */
  const debouncedSearch = useCallback((query: string) => {
    if (debounceTimerRef.current) {
      clearTimeout(debounceTimerRef.current);
    }

    debounceTimerRef.current = window.setTimeout(() => {
      search(query);
    }, debounceMs);
  }, [search, debounceMs]);

  /**
   * 处理键盘事件
   */
  const handleKeyDown = useCallback((e: KeyboardEvent): boolean => {
    if (!currentState.isOpen) return false;

    switch (e.key) {
      case 'ArrowDown':
        e.preventDefault();
        setState(prev => ({
          ...prev,
          activeIndex: (prev.activeIndex + 1) % selectableCount,
        }));
        return true;

      case 'ArrowUp':
        e.preventDefault();
        setState(prev => ({
          ...prev,
          activeIndex: (prev.activeIndex - 1 + selectableCount) % selectableCount,
        }));
        return true;

      case 'Enter':
      case 'Tab':
        e.preventDefault();
        selectActive();
        return true;

      case 'Escape':
        e.preventDefault();
        close();
        return true;
    }
    return false;
  }, [selectActive, close]);

  return {
    isOpen: state.isOpen,
    items: state.items,
    activeIndex: state.activeIndex,
    // ...
    handleKeyDown,
    selectActive,
  };
}

数据结构定义

TriggerQuery (触发查询)

typescript
interface TriggerQuery {
  trigger: string;   // 触发符,'/' 或 '@'
  query: string;     // 查询文本 (不包括触发符)
  start: number;     // 触发符在文本中的位置
  end: number;       // 查询结束位置(光标位置)
}

// 示例:
// 输入: "/workflow"
// TriggerQuery: { trigger: '/', query: 'workflow', start: 0, end: 9 }

CommandItem (命令项)

typescript
interface CommandItem {
  id: string;          // 命令 ID
  label: string;       // 显示标签,如 "/workflow:plan"
  category: string;    // 分类: 'workflow', 'memory', 'task', 'user' 等
}
typescript
interface DropdownItemData {
  id: string;
  label: string;
  description?: string;
  icon?: string;
  type: 'command' | 'file' | 'separator' | 'section-header';
  data?: any;
}
typescript
interface DropdownPosition {
  top: number;
  left: number;
  width: number;
  height: number;
}

关键实现细节

1. 行首检测(与 VSCode 版本的区别)

原 VSCode 版本:

  • 使用正则 (?:^|\s)/[^\s/]* 匹配行首或空格后的 /

IDEA 插件版本:

  • 仅检测行首的 /
  • 空格后的 / 不会触发
typescript
// 检查 / 前是否为行首
const isLineStart = start === 0 || text[start - 1] === '\n';
if (isLineStart) {
  return { trigger: '/', query, start, end };
}
return null; // / 不在行首

2. 命令过滤

IDEA 插件会过滤掉不适用的命令:

typescript
const HIDDEN_COMMANDS = new Set([
  '/clear',        // 在 IDEA 中无意义
  '/context',      // CLI 特定功能
  '/cost',         // CLI 特定功能
  '/init',         // CLI 特定功能
  '/pr-comments',  // CLI 特定功能
  '/release-notes',// CLI 特定功能
  '/review',       // CLI 特定功能
  '/security-review', // CLI 特定功能
  '/todo',         // CLI 特定功能
]);

3. 后端通信机制

typescript
// 前端发送请求
sendBridgeEvent('refresh_slash_commands');

// 后端返回数据(通过 JCEF Bridge)
window.updateSlashCommands(jsonString);

// 前端注册回调
window.updateSlashCommands = (json: string) => {
  const commands = JSON.parse(json);
  cachedSdkCommands = commands.map(cmd => ({...}));
};

4. 防抖与取消

typescript
// 1. 清除之前的定时器
if (debounceTimerRef.current) {
  clearTimeout(debounceTimerRef.current);
}

// 2. 取消之前的请求
if (abortControllerRef.current) {
  abortControllerRef.current.abort();
}

// 3. 设置新的定时器
debounceTimerRef.current = window.setTimeout(() => {
  abortControllerRef.current = new AbortController();
  search(query);
}, 200);

性能优化

优化 位置 效果
防抖 (200ms) useCompletionDropdown.ts 减少搜索次数
AbortController useCompletionDropdown.ts 取消前一个请求
命令缓存 slashCommandProvider.ts 避免重复请求后端
首次加载提示 slashCommandProvider.ts 改善用户体验
导航模式隔离 useCompletionDropdown.ts 减少不必要的样式更新

文件映射表

功能 文件位置
主组件 webview/src/components/ChatInputBox/ChatInputBox.tsx
触发检测 webview/src/components/ChatInputBox/hooks/useTriggerDetection.ts
补全管理 webview/src/components/ChatInputBox/hooks/useCompletionDropdown.ts
斜杠命令提供者 webview/src/components/ChatInputBox/providers/slashCommandProvider.ts
文件引用提供者 webview/src/components/ChatInputBox/providers/fileReferenceProvider.ts
下拉菜单组件 webview/src/components/ChatInputBox/Dropdown/
类型定义 webview/src/components/ChatInputBox/types.ts
样式 webview/src/components/ChatInputBox/styles.css
Java 后端桥接 src/main/java/com/github/claudecodegui/ClaudeSDKBridge.java
命令缓存 src/main/java/com/github/claudecodegui/cache/SlashCommandCache.java

常见问题

Q1: 为什么只在行首触发?

A: IDEA 插件版本设计为仅在行首触发斜杠命令,这与 VSCode 版本不同。这是为了:

  1. 避免在输入路径时误触发(如 /Users/...)
  2. 更符合命令行的使用习惯

Q2: 为什么有些命令被隐藏?

A: HIDDEN_COMMANDS 中的命令是 Claude Code CLI 的内置命令,它们在 IDEA 插件环境中:

  • 没有意义(如 /clear 清除 CLI 历史)
  • 不可用(如 /pr-comments 需要 Git 集成)
  • 由 IDEA 提供替代方案

Q3: 首次加载为什么需要时间?

A: 首次加载时需要:

  1. 通过 JCEF Bridge 向 Java 后端发送请求
  2. Java 后端调用 Claude SDK 获取命令列表
  3. 命令列表通过 Bridge 返回前端
  4. 前端缓存命令以供后续使用

后续使用会直接使用缓存,无需等待。

Q4: 如何添加新的斜杠命令?

A: 斜杠命令来自 Claude SDK,由用户的 .claude/commands/ 目录定义。插件会自动加载这些命令。

如果要在前端过滤或修改命令显示:

  1. 编辑 slashCommandProvider.ts 中的 HIDDEN_COMMANDS
  2. 或修改 commandToDropdownItem() 函数

Q5: 键盘导航支持哪些按键?

按键 功能
↓ 选择下一项
↑ 选择上一项
Enter 确认选择
Tab 确认选择
Escape 关闭菜单

总结

IDEA 插件版的斜杠指令系统是一个模块化设计:

  1. 触发检测: 独立的检测逻辑,仅行首触发 ✓
  2. 命令缓存: 从后端获取并缓存命令 ✓
  3. 命令过滤: 隐藏不适用的 CLI 命令 ✓
  4. 补全管理: 通用的下拉菜单管理器 ✓
  5. 键盘导航: 独立的导航处理 ✓
  6. UI 组件: 完全解耦的显示层 ✓

这个架构使得添加新功能(如标签补全、代码片段等)变得非常简单。


文档最后更新: 2025-12-14
项目版本: v0.1.1


File: docs/skills/tempdir-permission-sync.md

Claude 临时目录与权限通道改进说明

背景

此前 claude-agent-sdk 会在运行 Bash/Write 等工具时:

  • 把当前 process.cwd() 作为临时目录根路径并生成 claude-xxxx-cwd 标记文件;
  • 在 TMPDIR/claude-permission/ 下与 IDE 交换权限请求。

这会带来两个问题:

  1. 项目根目录持续出现大量 claude-*-cwd 文件,影响使用体验;
  2. 若我们把 TMPDIR 改到其它位置,Node 侧的 permission-handler 与 Java 侧 PermissionService 将监听不同目录,最终导致所有写入请求超时并被默认拒绝。

改动概览

1. 定制临时目录

  • 在 ClaudeSDKBridge 启动任何 Node 子进程时,统一创建 System.getProperty("java.io.tmpdir")/claude-agent-tmp,并通过 TMPDIR/TEMP/TMP 环境变量强制使用该目录;
  • 记录进程前后的 claude-*-cwd 文件,待进程结束后只保留原有文件,其余全部清理。

这样 IDE 项目目录不再出现无意义的临时标记文件,真实的临时数据会集中在系统 tmp 目录下,并在每次任务结束时自动回收。

2. 同步权限通道目录

  • 新增 CLAUDE_PERMISSION_DIR 环境变量,并在 updateProcessEnvironment 中默认指向 System.getProperty("java.io.tmpdir")/claude-permission;
  • claude-bridge/permission-handler.js 读取该变量,若不存在则回退到 os.tmpdir();
  • Java 端的 PermissionService 依然监听系统 tmp/claude-permission,因此 Node/Java 再次共享同一个通信目录,权限弹窗能正常弹出。

使用方式

  1. 无需额外配置:安装新版插件后自动启用上述逻辑;
  2. 自定义路径(可选):若想把权限目录指向其它位置,可在 IDE 进程环境中设置 CLAUDE_PERMISSION_DIR=/your/path/claude-permission,Node 侧会按此目录写入,Java 侧同步读取;
  3. 验证:
    • 运行任意会写文件的指令(例如 “创建 hello.js”),应能看到权限弹窗;
    • 权限通过后,文件应成功写入,且项目根目录不再出现 claude-*-cwd;
    • 系统 tmp/claude-agent-tmp 目录中的残留文件会在指令结束后清理;若需排查,可手动查看该目录。

相关文件

  • src/main/java/com/github/claudecodegui/ClaudeSDKBridge.java:负责设置 TMPDIR、清理临时文件、注入 CLAUDE_PERMISSION_DIR;
  • claude-bridge/permission-handler.js:读取 CLAUDE_PERMISSION_DIR 并在该目录下与 Java 交互;
  • src/main/java/com/github/claudecodegui/permission/PermissionService.java:继续监听系统 tmp/claude-permission 目录(无需修改)。

如需进一步扩展(例如把权限记录持久化到项目目录),可以基于 CLAUDE_PERMISSION_DIR 再封装自定义路径逻辑。


File: docs/skills/windows-cli-path-bug.md

Windows 下 Claude CLI 路径问题

问题描述

在 Windows 环境下,插件启动时会报错:

text
process exited with code 1

日志显示 ENOENT 错误:

text
[Node.js] [DEBUG] Error in message loop: Failed to spawn Claude Code process: spawn D:\Apps\Scoop\apps\nvm\current\nodejs\nodejs\claude ENOENT

问题原因

1. `where claude` 命令返回的路径问题

在 Windows 上,where claude 命令会返回多个匹配结果:

text
D:\Apps\Scoop\apps\nvm\current\nodejs\nodejs\claude
D:\Apps\Scoop\apps\nvm\current\nodejs\nodejs\claude.cmd

原代码取第一个结果 claude(无扩展名),但这个路径:

  • 在 Windows 上不能直接被 spawn() 执行
  • 它实际上是一个 shim 文件,需要通过 .cmd 包装器来调用
  • 直接 spawn 会导致 ENOENT(文件不存在)错误

2. SDK 的 spawn 机制

Claude Agent SDK 使用 Node.js 的 child_process.spawn() 来启动 CLI 进程。在 Windows 上:

  • .cmd 文件可以通过 shell: true 选项执行
  • 但无扩展名的 shim 文件无法直接执行

解决方案

完全使用 SDK 内置的 cli.js,不再查找系统安装的 Claude CLI。

SDK 内置了官方 CLI:

  • 路径:node_modules/@anthropic-ai/claude-agent-sdk/cli.js
  • 这是 Anthropic 官方 Claude Code CLI 的 Node 版本
  • 当不传递 pathToClaudeCodeExecutable 选项时,SDK 会自动使用它

修改内容

1. `claude-bridge/services/message-service.js`

移除 getClaudeCliPath 相关代码:

javascript
// 之前
import { mapModelIdToSdkName, getClaudeCliPath } from '../utils/model-utils.js';
const claudeCliPath = getClaudeCliPath();
const options = {
  // ...
  pathToClaudeCodeExecutable: claudeCliPath,
};

// 之后
import { mapModelIdToSdkName } from '../utils/model-utils.js';
const options = {
  // ...
  // 不传递 pathToClaudeCodeExecutable,SDK 将自动使用内置 cli.js
};

2. `claude-bridge/utils/model-utils.js`

删除整个 getClaudeCliPath() 函数(约 60 行代码),包括:

  • where claude / which claude 命令调用
  • 常见路径检测逻辑
  • 相关的 fs 和 child_process import

使用内置 CLI 的优点

  1. 跨平台最稳定:不依赖 where/which 等平台相关命令
  2. 版本对齐:CLI 版本与 SDK 完全一致,避免兼容性问题
  3. 自定义 Base URL 仍然有效:SDK 会继承 process.env 中的 ANTHROPIC_BASE_URL
  4. 配置来源一致:内置 CLI 同样读取 ~/.claude/settings.json

注意事项

  • 如果将来需要支持"使用系统 CLI"的高级选项,需要在 Windows 上:
    • 优先选择 .cmd 结尾的路径
    • 或者使用 shell: true 选项执行
  • 当前方案是最简单可靠的修复,适用于大多数用户场景

File: docs/sdk/claude-agent-sdk.md

Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt
Use this file to discover all available pages before exploring further.

Agent SDK reference - TypeScript

Complete API reference for the TypeScript Agent SDK, including all functions, types, and interfaces.