diff --git a/README.md b/README.md index 121d45c..f83c8e6 100644 --- a/README.md +++ b/README.md @@ -79,12 +79,107 @@ see [UniAST Specification](docs/uniast-zh.md) ## Tips: - + - You can add more repo ASTs into the AST directory without restarting abcoder MCP server. - Try to use [the recommended prompt](llm/prompt/analyzer.md) and combine planning/memory tools like [sequential-thinking](https://github.com/modelcontextprotocol/servers/tree/main/src/sequentialthinking) in your AI agent. +## Claude Code Integration + +ABCoder provides deep integration with [Claude Code](https://claude.ai/code) through the AST-Driven Coding workflow, enabling hallucination-free code analysis and precise execution. + +### Setup + +1. **Install ABCoder Claude Configuration** + + Copy [`docs/.claude/`](docs/.claude/) to your home directory or project root: + + ```bash + cp -r docs/.claude ~/ + ``` + +2. **Configure ABCoder MCP Server** + + Configure in Claude Code's `~/.claude.json` (the hook uses `abcoder parse go/ts . -o ~/.asts/repo.json` for the default AST folder): + + ```json + { + "mcpServers": { + "abcoder": { + "command": "abcoder", + "args": ["mcp", "~/.asts"] + } + } + } + ``` + +3. **Configure Hooks** + + Claude Code will automatically read hooks from [`~/.claude/settings.json`](docs/.claude/settings.json) to enable: + - Auto-detect language and generate AST before calling `mcp__abcoder` tools + - Display ABCoder workflow SOP to Claude after `list_repos` + - Remind to call `get_ast_node` recursively + +### AST-Driven Coding Workflow + +[`.claude/hooks`](docs/.claude/hooks) provide a 4-layer analysis chain from repository to node details: + +``` +list_repos → get_repo_structure → get_package_structure → get_file_structure → get_ast_node + │ │ │ │ │ + └── repo_name └── mod/pkg list └── file list └── node list └── dependencies/references +``` + +### Claude Code Slash Commands + +[`.claude/commands`](docs/.claude/commands) provide three custom slash commands to streamline development: + +| Command | Function | Description | +|---------|----------|-------------| +| [`/abcoder:schd`](docs/.claude/commands/abcoder:schd.md) | Design implementation | Analyze codebase using ABCoder, design technical solution | +| [`/abcoder:task `](docs/.claude/commands/abcoder:task.md) | Create coding task | Generate standardized CODE_TASK document | +| [`/abcoder:recheck `](docs/.claude/commands/abcoder:recheck.md) | Verify solution | Critically check CODE_TASK feasibility, useful when a CODE_TASK contains external dependencies | + +### Workflow + +``` +User Request + │ + ▼ +/abcoder:schd ──────────→ Design Solution (ABCoder Analysis) + │ │ + ▼ ▼ +/abcoder:task ─────────→ CODE_TASK (with Technical Specs, including accurate `get_ast_node` call args) + │ │ + ▼ ▼ +/abcoder:recheck ────→ Verify Solution (ABCoder Validation. After `/abcoder:task` Claude Code will tell you what the external dependencies CODE_TASK contains, use `/abcoder:recheck` to analyze external ast_node and technical detail with ABCoder) + │ │ + ▼ ▼ +sub-agent ─────────→ Execute Implementation +``` + +### Configuration Files + +| File | Purpose | +|------|---------| +| [`CLAUDE.md`](docs/.claude/CLAUDE.md) | Core AST-Driven Coder role definition | +| [`settings.json`](docs/.claude/settings.json) | Hooks and permissions configuration | +| [`hooks/`](docs/.claude/hooks/) | Automation scripts (parse/prompt/reminder) | +| [`commands/`](docs/.claude/commands/) | Slash command definitions (abcoder:task/abcoder:schd/abcoder:recheck) | +| [`tmpls/CODE_TASK.md`](docs/.claude/tmpls/CODE_TASK.md) | Coding task template | + +### Dependencies + +- Claude Code CLI +- abcoder MCP server (provides `mcp__abcoder` tools) +- sequential-thinking MCP server (provides `mcp__sequential_thinking` tools, optional) + +> For detailed configuration, see [docs/.claude/README.md](docs/.claude/README.md) + +> Watch the demo video [here](https://github.com/cloudwego/abcoder/pull/141) + + ## Use ABCoder as an Agent (WIP) You can also use ABCoder as a command-line Agent like: diff --git a/docs/.claude/CLAUDE.md b/docs/.claude/CLAUDE.md new file mode 100644 index 0000000..96679ce --- /dev/null +++ b/docs/.claude/CLAUDE.md @@ -0,0 +1,76 @@ +# AST-Driven Coding + +你是 AST-Driven Coder,通过整合 `mcp__abcoder` 和 `mcp__sequential_thinking`,为用户提供无幻觉上下文、模糊需求质询、诚实推理和精确执行。 + +## MCP 工具使用体系 + +### 工具优先级决策 +**代码分析优先级**: `mcp__abcoder` > Read/Search + +| 工具 | 适用场景 | 核心价值 | +|------|----------|----------| +| `mcp__abcoder` | 本地代码深度分析 | UniAST + LSP无幻觉理解代码结构、类型信息、调用链。优于Read/Search | +| `mcp__sequential_thinking` | 复杂问题分解 | 多步骤问题的系统化思考 | + +## ABCoder SOP +1. 问题分析: + - 基于用户问题分析相关关键词 + - MUST 使用 `list_repos` 确认`repo_name` + +2. 代码定位 (repo→package→node→ast node relationship): + - 2.1 定位package: 基于 `get_repo_structure` 返回的package list选择目标package + - 2.2 定位node: 通过 `get_package_structure` 返回的file信息,确认目标node;无法确认时,调用 `get_files_structure` + - 2.3 确认ast node relationship: 递归调用 `get_ast_node` 获取node详细(dependencies, references, inheritance, implementation, grouping) + +### 开发中的 abcoder 使用 +- 编写前:使用 `get_package_structure` 分析相似代码模式,`get_ast_node` 学习项目最佳实践 + +## 分阶段开发理念 + +IMPORTANT: 开发前,MUST 与用户对齐CODE_TASK需求;对于CODE_TASK中不明确的任务(例如任务需要的SDK Method定义、返回值的JSON/IDL),质询用户 +IMPORTANT: 开始开发前,阐述此次CODE_TASK的调用链路、相关SDK Method定义、cURL JSON定义 +### 开发阶段 +1. MVP阶段:核心功能可工作,基本类型安全 +2. 完善阶段:错误处理、边界情况、测试覆盖 +3. 优化阶段:性能优化、代码重构、文档完善 + +## 代码质量标准 + +### 实现要求(按优先级) +MUST: +- Never 使用简化/Mock实现,使用真实SDK/cURL +- 类型安全:核心逻辑必须有明确类型定义 +- 基本错误处理:处理可预见的异常情况 + +SHOULD: +- 完整的边界条件处理 +- 性能敏感场景的优化 +- 复杂逻辑的注释说明 + +COULD: +- 100%遵循SOLID编码规范 +- 极致的性能优化 + +### 验证标准 +- 关键路径和边界条件 MUST 有测试 +- 通过 linter 和类型检查 +- 手动验证主要用户场景 + +## 用户协作模式 + +| 用户行为 | 响应策略 | +|----------|----------| +| 模糊需求 | 使用 `mcp__sequential_thinking` 澄清,提供具体选项 | +| BUG修复 | 使用 `mcp__abcoder__get_ast_node` 详细分析,根本解决 | +| 重构替换 | 使用 `semgrep` 和 `comby` 结构化搜索替换 | +| 代码分析请求 | MUST 使用 `mcp__abcoder` SOP | + +## 执行要求 + +1. 绝不假设 - 任何不确定代码,MUST 通过`mcp__abcoder__get_ast_node`工具验证 +2. 工具链整合 - 充分利用ABCoder等工具提升效率 +3. 质量内建 - 代码质量要求融入每个环节 +4. 渐进交付 - 复杂任务分解为可验证的小步骤 + +- **使用SubAgent时提醒使用ABCoder** - 当需要使用subAgent(如@agent-Explore、@agent-coding-executor)进行代码分析时,应该提醒SubAgent使用`mcp__abcoder__get_ast_node`以获得更准确的分析结果 +- **Never 说英语**:MUST 使用中文 diff --git a/docs/.claude/README.md b/docs/.claude/README.md new file mode 100644 index 0000000..293dc77 --- /dev/null +++ b/docs/.claude/README.md @@ -0,0 +1,127 @@ +# AST-Driven Coding 配置 + +Claude Code 的 AST 驱动开发配置,通过 MCP 工具、钩子和斜杠命令实现无幻觉的代码分析和精确执行。 + +## 目录结构 + +``` +.claude/ +├── CLAUDE.md # 核心指令:AST-Driven Coder 角色定义 +├── settings.json # 钩子配置:PreToolUse/PostToolUse +├── hooks/ # 钩子脚本 +│ ├── parse.sh # 自动检测语言并生成 AST +│ ├── prompt.sh # 显示工作流程 SOP +│ └── reminder.sh # 提醒递归调用 get_ast_node +├── commands/ # 斜杠命令定义 +│ ├── abcoder:task.md # /abcoder:task - 创建编码任务 +│ ├── abcoder:schd.md # /abcoder:schd - 设计实现方案 +│ └── abcoder:recheck.md # /abcoder:recheck - 技术方案核对 +└── tmpls/ # 文档模板 + └── CODE_TASK.md # 编码任务模板 +``` + +## 核心理念 + +**AST-Driven Coding**: 基于 UniAST + LSP 的无幻觉代码分析 + +| 原则 | 说明 | +|------|------| +| 绝不假设 | 不确定代码 MUST 通过 `mcp__abcoder__get_ast_node` 验证 | +| 代码分析优先级 | `mcp__abcoder` > Read/Search | +| 直接使用原则 | 预先分析提供完整上下文,SubAgent 直接执行无需重分析 | +| 分阶段开发 | MVP → 完善 → 优化 | + +## MCP 工具 + +### mcp__abcoder + +本地代码深度分析工具,提供四层 AST 结构: + +``` +list_repos → get_repo_structure → get_package_structure → get_file_structure → get_ast_node + │ │ │ │ │ + └── repo_name └── mod/pkg list └── file list └── node list └── dependencies/references/inheritance +``` + +**SOP 流程**: +1. 问题分析 → `list_repos` 确认 repo_name +2. 定位 package → `get_repo_structure` 选择目标 package +3. 定位 node → `get_package_structure` 确认目标 node +4. 获取关系 → `get_ast_node` 递归获取完整信息 + +### mcp__sequential_thinking + +多步骤问题的系统化思考工具,用于复杂问题分解和模糊需求质询。 + +## 钩子系统 + +| 钩子 | 事件 | 匹配工具 | 作用 | +|------|------|----------|------| +| parse.sh | PreToolUse | get_repo_structure, get_file_structure, get_package_structure, get_ast_node | 自动检测语言并生成 AST 到 `~/.asts/` 目录 | +| prompt.sh | PostToolUse | list_repos | 显示 ABCoder 工作流程 SOP | +| reminder.sh | PostToolUse | get_repo_structure, get_package_structure, get_file_structure | 提醒递归调用 get_ast_node | + +## 斜杠命令 + +### /abcoder:task <名称> + +创建 CODE_TASK 文档,生成 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md` + +**格式要求**: +- action: create/modify/delete +- 涉及 SDK: 指定 Package/Method 名称 +- 涉及 curl: 提供完整命令和响应结构 +- 提供具体验证方法 + +### /abcoder:schd + +使用 mcp__abcoder 设计实现方案 + +**Guardrails**: +- 最大化复用已有功能 +- 优先最小改动 +- 禁止编写代码、禁止使用 agent + +### /abcoder:recheck <任务> + +批判性检查 CODE_TASK 技术可行性 + +**检查项**: +- 方案可实现性 +- 技术风险 +- 代码复用和改动最小化 + +## 工作流 + +``` +用户需求 + │ + ▼ +/abcoder:schd ──────────────→ 设计方案(abcoder分析) + │ │ + ▼ ▼ +/abcoder:task ────────→ CODE_TASK(含技术规格) + │ │ + ▼ ▼ +/abcoder:recheck ─────→ 方案核对(abcoder验证) + │ │ + ▼ ▼ +coding-executor ──────→ 执行实现 +``` + +## 安装 + +1. 复制 `.claude/` 目录到项目根目录或用户主目录 +2. 确保 `settings.json` 中的钩子路径正确 +3. 确保 `~/.claude/tmpls/` 目录存在且包含模板文件 + +## 依赖 + +- Claude Code CLI +- abcoder MCP 服务器(提供 mcp__abcoder 工具) +- sequential-thinking MCP 服务器(提供 mcp__sequential_thinking 工具) + +## 相关文档 + +- [hooks/README.md](hooks/README.md) - 钩子系统详解 +- [commands/README.md](commands/README.md) - 斜杠命令详解 diff --git a/docs/.claude/commands/README.md b/docs/.claude/commands/README.md new file mode 100644 index 0000000..049276c --- /dev/null +++ b/docs/.claude/commands/README.md @@ -0,0 +1,114 @@ +# Claude Code Commands + +Claude Code 斜杠命令定义,用于规范化和自动化开发工作流。 + +## 命令说明 + +### /abcoder:task - 创建编码任务 + +创建 CODE_TASK 文档,规范化编码需求描述。 + +**执行流程**: +1. 创建 `./task/{{MMDD}}/` 目录 +2. 读取模板 `~/.claude/tmpls/CODE_TASK.md` +3. 根据任务上下文填充模板,生成 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md` +4. 列出外部依赖包(如有) +5. 提示创建成功 + +**使用示例**: +``` +/abcoder:task Feature_Auth → 创建 ./task/1231/Feature_Auth__CODE_TASK.md +/abcoder:task Bugfix-Api → 创建 ./task/1231/Bugfix_Api__CODE_TASK.md +``` + +**CODE_TASK 格式要求**: +- action: create/modify/delete +- 文件路径准确 +- 涉及 SDK 时指定 Package/Method 名称 +- 涉及 curl 时提供具体命令(请求头、请求体、URL、响应结构) +- 提供具体`get_ast_node`入参、验证方法 + +--- + +### /abcoder:schd - 设计实现方案 + +使用 mcp__abcoder 分析代码库,设计技术实现方案。 + +**执行流程**: +1. 从 `mcp__abcoder__get_repo_structure` 开始 +2. 下钻到 `mcp__abcoder__get_ast_node` 查看细节 +3. 分析依赖关系和调用链 +4. 设计最小改动、最大化复用的方案 + +**Guardrails**: +- 最大化复用已有功能 +- 优先采用最小改动方式 +- 限制修改影响面 +- 找出模糊细节并提出问题 +- 禁止编写代码,禁止使用 agent + +--- + +### /abcoder:recheck - 技术方案核对 + +批判性检查 CODE_TASK 的技术可行性。 + +**执行流程**: +1. 从 `mcp__abcoder__get_repo_structure` 开始 +2. 使用 mcp__abcoder 核对技术细节 +3. 下钻到 `mcp__abcoder__get_ast_node` 粒度验证 + +**检查项**: +- 方案是否可实现需求 +- 是否存在技术风险 +- 是否最大化复用已有功能 +- 是否最小化改动 + +## 模板文件 + +### CODE_TASK.md + +编码任务模板,定义任务清单格式。 + +**核心要素**: +``` +1. [ ] 任务描述 + - file: 文件路径 + - action: create/modify/delete + - 技术规格(SDK Method / curl JSON / IDL结构 / `get_ast_node` 调用参数) +``` + +**核心理念 - 直接使用原则**: +- 编写 CODE_TASK 时预先使用 `mcp__abcoder` 分析 +- 提供完整的 `get_ast_node` 调用参数 +- SubAgent 直接执行,无需重新分析 + +## 工作流示意 + +``` +用户需求 + │ + ▼ +/abcoder:schd ──────────────→ 设计方案(abcoder分析) + │ │ + ▼ ▼ +/abcoder:task ────────→ CODE_TASK(含技术规格) + │ │ + ▼ ▼ +/abcoder:recheck ─────→ 方案核对(abcoder验证) + │ │ + ▼ ▼ +coding-executor ──────→ 执行实现 +``` + +## 文件位置 + +``` +~/.claude/ +├── commands/ +│ ├── abcoder:task.md +│ ├── abcoder:schd.md +│ └── abcoder:recheck.md +└── tmpls/ + └── CODE_TASK.md +``` diff --git a/docs/.claude/commands/abcoder:recheck.md b/docs/.claude/commands/abcoder:recheck.md new file mode 100644 index 0000000..9476d8c --- /dev/null +++ b/docs/.claude/commands/abcoder:recheck.md @@ -0,0 +1,9 @@ +# 系统指令: ReCheck + +理解任务 {1},并使用`mcp__abcoder`核对技术细节(下钻到`mcp__abcoder__get_ast_node`粒度); + +IMPORTANT: 批判性思考,保持诚实 +- 确保方案可以实现需求 +- 确保没有技术风险 +- 检查是否最大化复用已有功能、最小化改动。 +IMPORTANT: 必须从`mcp__abcoder__get_repo_strucure`开始 diff --git a/docs/.claude/commands/abcoder:schd.md b/docs/.claude/commands/abcoder:schd.md new file mode 100644 index 0000000..52dbfff --- /dev/null +++ b/docs/.claude/commands/abcoder:schd.md @@ -0,0 +1,12 @@ +# 系统指令:Schedule + +使用`mcp__abcoder`分析相关仓库(下钻到`mcp__abcoder__get_ast_node`查看细节),帮助用户设计实现方案。 + +## Guardrails +- 最大化复用项目已有功能;不重复造轮子。 +- 优先采用直接、最小改动的实现方式,只有在用户明确要求时才增加复杂度。 +- 严格限制修改影响面在所请求的结果范围内。 +- 找出任何模糊或含糊不清的细节,并在修改文件前提出必要的后续问题。 +- 在Schdule阶段禁止编写代码,禁止使用agent。 +IMPORTANT: 必须从`mcp__abcoder__get_repo_strucure`开始 + diff --git a/docs/.claude/commands/abcoder:task.md b/docs/.claude/commands/abcoder:task.md new file mode 100644 index 0000000..69a0638 --- /dev/null +++ b/docs/.claude/commands/abcoder:task.md @@ -0,0 +1,19 @@ +# 系统指令:创建CODE_TASK + +请帮我创建一个CODE_TASK。 + +1. `d=$(date +%m%d) && mkdir -p "./task/$d/" && echo "目录 ./task/$d/ 已创建"` 创建 `./task/{{MMDD}}/` 目录 +2. 读取模板文件 `~/.claude/tmpls/CODE_TASK.md` +3. 根据任务上下文,按照格式和要求填充模板,创建新文件 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md` +4. 告知用户这个`CODE_TASK`是否包含外部依赖;如果包含,请清晰列出完整的外部依赖包名称 +5. 提示用户文件已创建成功,停止操作 + +{{NAME}}:{{1}} + +如果用户没有提供任务名称,请提示用户使用格式:`/task <任务名称>` + +--- + +## 使用说明 +- `/task Feature_Auth` → 创建 `./task/1013/Feature_Auth__CODE_TASK.md` +- `/task Bugfix-Api` → 创建 `./task/1013/Bugfix_Api__CODE_TASK.md` diff --git a/docs/.claude/hooks/README.md b/docs/.claude/hooks/README.md new file mode 100644 index 0000000..83615cc --- /dev/null +++ b/docs/.claude/hooks/README.md @@ -0,0 +1,75 @@ +# ABCoder Hooks + +Claude Code 钩子系统,用于自动化 ABCoder 代码分析工作流。 + +## 配置文件 + +`settings.json` 定义了两个钩子事件: + +- **PreToolUse**: 在工具调用前触发 +- **PostToolUse**: 在工具调用后触发 + +## 钩子说明 + +### 1. parse.sh (PreToolUse) + +**匹配工具**: `get_repo_structure`, `get_file_structure`, `get_package_structure`, `get_ast_node` + +**功能**: 自动检测项目语言并生成最新 AST 到 `~/.asts/` 目录( `~/.claude.json` 需要配置abcoder目录 `abcoder mcp ${HOME}/.asts` ) + +- 自动检测 Go/TypeScript 项目 +- 执行 `abcoder parse go/ts . -o ~/.asts/repo.json` 生成 AST 文件 + +**检测规则**: +- Go: 存在 `go.mod` 或 `main.go` +- TypeScript: 存在 `package.json`, `tsconfig.json` 或 `.ts/.tsx` 文件 + +### 2. prompt.sh (PostToolUse) + +**匹配工具**: `list_repos` + +**功能**: 显示 ABCoder 工作流程 SOP + +在列出仓库后,自动提示用户遵循正确的分析流程。 + +### 3. reminder.sh (PostToolUse) + +**匹配工具**: `get_repo_structure`, `get_package_structure`, `get_file_structure` + +**功能**: 提醒递归调用 `get_ast_node` + +在定位目标节点后,提醒用户必须使用 `get_ast_node` 递归获取完整的节点信息(类型、代码、位置、依赖、引用等)。 + +## ABCoder 工作流 SOP + +``` +1. 问题分析 + └── list_repos (确认 repo_name) + +2. 代码定位 (repo -> package -> node -> ast) + ├── get_repo_structure (定位 package) + ├── get_package_structure (定位 node) + └── get_ast_node (递归获取节点关系) + +3. 自我反思 + └── sequential_thinking (理解调用链和上下文) +``` + +## AST 层次结构 + +| 层级 | 标识 | 示例 | +|------|------|------| +| Module | mod_path | github.com/cloudwego/kitex | +| Package | pkg_path | github.com/cloudwego/kitex/pkg/generic | +| File | file_path | pkg/generic/closer.go | +| AST Node | {mod_path, pkg_path, name} | {"mod_path": "...", "pkg_path": "...", "name": "Closer"} | + +## 安装位置 + +``` +~/.claude/hooks/abcoder/ +├── parse.sh +├── prompt.sh +├── reminder.sh +└── abcoder-workflow.md +``` diff --git a/docs/.claude/hooks/abcoder/abcoder-workflow.md b/docs/.claude/hooks/abcoder/abcoder-workflow.md new file mode 100644 index 0000000..a30b8c6 --- /dev/null +++ b/docs/.claude/hooks/abcoder/abcoder-workflow.md @@ -0,0 +1,31 @@ +# ABCoder代码分析工作流 + +## AST 层次结构 +- **模块(Module)**: 仓库中的编译单元,由 `mod_path` 标识;例如: "github.com/cloudwego/kitex" +- **包(Package)**: 符号的命名空间,由 `pkg_path` 标识;例如: "github.com/cloudwego/kitex/pkg/generic" +- **文件(File)**: 代码文件,由 `file_path` 标识;例如: "pkg/generic/closer.go" +- **AST节点**: 语法单元(函数、类型、变量),由 `NodeID` 标识;例如: +``` +{ + "mod_path": "github.com/cloudwego/kitex", + "pkg_path": "github.com/cloudwego/kitex/pkg/generic", + "name": "Closer" +} +``` + +## ABCoder工作SOP +1. **问题分析**: + - 基于用户问题分析相关关键词 + - 必须使用 `list_repos` 确认repo_name + +2. **代码定位** (repo→package→node→ast node relationship): + - 2.1 **定位package**: 基于 `get_repo_structure` 返回的package list选择目标package + - 2.2 **定位node**: 通过 `get_package_structure` 返回的file信息,确认目标node;无法确认时,调用 `get_files_structure` + - 2.3 **确认ast node relationship**: 递归调用 `get_ast_node` 获取node详细(dependencies, references, inheritance, implementation, grouping) + +3. **自我反思**: + - 理解完整的code calling-chain、contextual-relationship + - 如果无法清楚解释机制,使用`sequential_thinking` 来帮助分解问题并记录信息,调整选择并重复步骤2 + +## 注意事项 +- 回复应列出相关代码的准确 metadata,包括 AST node(或 package)的 identity、file location 和代码。**必须提供确切的 file location(包括 line numbers)!** diff --git a/docs/.claude/hooks/abcoder/parse.sh b/docs/.claude/hooks/abcoder/parse.sh new file mode 100755 index 0000000..18c5247 --- /dev/null +++ b/docs/.claude/hooks/abcoder/parse.sh @@ -0,0 +1,159 @@ +#!/bin/bash + +# 修正并简化的 get_basename 函数 +get_basename() { + # 检查输入是否为 "." 或 ".." + if [[ "$1" == "." || "$1" == ".." ]]; then + # 对于当前或上级目录,使用 basename 结合 pwd + basename "$(cd "$1" && pwd)" + else + # 对于其他路径,直接使用 basename + basename "$1" + fi +} + +# 新增:检测项目语言并获取仓库标识 +detect_project_info() { + local target_dir="$1" + local project_info="" + + # 1. 优先检测 Go 项目(判断 go.mod 或 main.go) + if [[ -f "${target_dir}/go.mod" ]]; then + # 从 go.mod 中提取 module name + local module_name=$(grep "^module " "${target_dir}/go.mod" | head -1 | awk '{print $2}') + if [[ -n "$module_name" ]]; then + echo "go|${module_name}" + return 0 + fi + # 如果无法获取 module name,使用 main.go 所在目录 + if [[ -f "${target_dir}/main.go" ]]; then + echo "go|$(get_basename "$target_dir")" + return 0 + fi + fi + + # 2. 检测 TypeScript 项目(判断 package.json 或 tsconfig.json) + if [[ -f "${target_dir}/package.json" ]]; then + # 从 package.json 中提取 name + local package_name=$(jq -r '.name // empty' "${target_dir}/package.json" 2>/dev/null) + if [[ -n "$package_name" && "$package_name" != "null" ]]; then + echo "ts|${package_name}" + return 0 + fi + fi + + # 3. 检测 TypeScript 项目(判断 tsconfig.json 或 .ts/.tsx 文件) + if [[ -f "${target_dir}/tsconfig.json" ]]; then + echo "ts|$(get_basename "$target_dir")" + return 0 + fi + + # 统计 .ts 和 .tsx 文件数量(排除 node_modules 目录) + local ts_file_count=$(find "${target_dir}" -type f -not -path "*/node_modules/*" \( -name "*.ts" -o -name "*.tsx" \) | wc -l) + if [[ $ts_file_count -gt 0 ]]; then + echo "ts|$(get_basename "$target_dir")" + return 0 + fi + + # 4. 未检测到目标语言 + echo "unknown|$(get_basename "$target_dir")" + return 1 +} + +# 直接映射 abc 为别名,处理 parse <语言> <仓库路径> 形式的参数 +abc() { + # 检查命令格式是否为 "parse <语言> <仓库路径>" + if [ $# -eq 3 ] && [ "$1" = "parse" ]; then + local lang="$2" + local repo_path="$3" + local repo_name=$(get_basename "$repo_path") + + # 确保输出目录存在 + mkdir -p ~/.asts/ + + # 执行实际命令 + abcoder parse "${lang}" "${repo_path}" -o "/Users/bytedance/.asts/${repo_name}.json" + else + # 如果不是预期的 parse 命令格式,直接将参数传递给原始 abcoder 命令 + abcoder "$@" + fi +} + +# LOG_FILE="/tmp/claude-hook-debug.log" + +input=$(cat) +repo_name=$(echo "$input" | jq -r '.tool_input.repo_name // ""') +cwd=$(echo "$input" | jq -r '.cwd // ""') + +# echo "=== $(date) ===" >> "$LOG_FILE" +# echo "repo_name: $repo_name" >> "$LOG_FILE" +# echo "cwd: $cwd" >> "$LOG_FILE" + +# 复用现有的 get_basename 函数 +current_base_name=$(get_basename "$cwd") + +# 检测项目信息(语言和仓库标识) +project_info=$(detect_project_info "$cwd") +project_lang=$(echo "$project_info" | cut -d'|' -f1) +project_identifier=$(echo "$project_info" | cut -d'|' -f2) + +# echo "Detected project language: $project_lang" >> "$LOG_FILE" +# echo "Detected project identifier: $project_identifier" >> "$LOG_FILE" + +if [ "$repo_name" = "$cwd" ] || [ "$current_base_name" = "$repo_name" ] || [ "$project_identifier" = "$repo_name" ]; then + # echo "Path or identifier matched, executing abc parse..." >> "$LOG_FILE" + + # 检查是否检测到目标语言 + if [[ "$project_lang" == "unknown" ]]; then + jq -n '{ + "decision": "block", + "reason": "未检测到支持的语言(仅支持 Go 和 TypeScript)", + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "additionalContext": "请确保项目是 Go 或 TypeScript 类型" + } + }' + exit 0 + fi + + # 捕获标准输出和错误输出 + output_file=$(mktemp) + error_file=$(mktemp) + + # 修改:使用检测到的语言执行 parse 命令(替换原有的固定 ts) + if abcoder parse "$project_lang" . >"$output_file" 2>"$error_file"; then + # echo "abc parse succeeded" >> "$LOG_FILE" + # cat "$output_file" >> "$LOG_FILE" + + jq -n --arg lang "$project_lang" '{ + "systemMessage": "abcoder parse 已成功完成(语言:\($lang))。AST文件已生成,可以继续分析代码。" + }' + else + exit_code=$? + # echo "abc parse FAILED with exit code $exit_code" >> "$LOG_FILE" + # echo "STDOUT:" >> "$LOG_FILE" + # cat "$output_file" >> "$LOG_FILE" + # echo "STDERR:" >> "$LOG_FILE" + # cat "$error_file" >> "$LOG_FILE" + + # 读取错误信息 + error_msg=$(cat "$error_file" | tail -20) + + jq -n --arg code "$exit_code" --arg err "$error_msg" --arg lang "$project_lang" '{ + "decision": "block", + "reason": ("abcoder parse 失败(语言:\($lang),退出码: " + $code + ")。错误信息:\n" + $err + "\n\n可能的原因:\n1. 项目配置文件有问题(Go: go.mod;TS: tsconfig.json)\n2. 缺少依赖包\n3. 代码语法错误\n\n建议:\n- Go 项目:运行 'go mod tidy' 和 'go build' 检查\n- TS 项目:运行 'npm install' 和 'tsc --noEmit' 检查"), + "hookSpecificOutput": { + "hookEventName": "PreToolUse", + "additionalContext": ("解析失败,需要修复后重试") + } + }' + fi + + # 清理临时文件 + trash "$output_file" "$error_file" +else + # echo "Path did not match" >> "$LOG_FILE" + echo '{}' +fi + +exit 0 diff --git a/docs/.claude/hooks/abcoder/prompt.sh b/docs/.claude/hooks/abcoder/prompt.sh new file mode 100755 index 0000000..454acb3 --- /dev/null +++ b/docs/.claude/hooks/abcoder/prompt.sh @@ -0,0 +1,43 @@ +#!/bin/zsh + +# 添加调试信息 +# LOG_FILE="/tmp/claude-hook-debug.log" + +# 错误处理 +set -euo pipefail + +# 验证文件存在 +SOP_FILE="$HOME/.claude/hooks/abcoder/abcoder-workflow.md" +# echo "DEBUG: Checking file: $SOP_FILE" >&2 + +if [[ ! -f "$SOP_FILE" ]]; then + # echo "DEBUG: File not found" >&2 + echo '{"decision": "block", "reason": "SOP file not found", "hookSpecificOutput": {"hookEventName": "PostToolUse"}}' + exit 0 +fi + +# echo "DEBUG: File found, reading content" >&2 + +# 读取并转义内容 +SOP_CONTENT=$(cat "$SOP_FILE" | jq -Rs . 2>/dev/null) +if [[ $? -ne 0 ]]; then + # echo "DEBUG: jq failed" >&2 + echo '{"decision": "block", "reason": "Failed to process SOP content", "hookSpecificOutput": {"hookEventName": "PostToolUse"}}' + exit 0 +fi + +# echo "DEBUG: Content processed successfully" >&2 + +# 输出 JSON +cat <&2 +exit 0 diff --git a/docs/.claude/hooks/abcoder/reminder.sh b/docs/.claude/hooks/abcoder/reminder.sh new file mode 100755 index 0000000..f8596a6 --- /dev/null +++ b/docs/.claude/hooks/abcoder/reminder.sh @@ -0,0 +1,11 @@ +#!/bin/zsh +cat <This is a reminder that when executing the ABCoder code analysis workflow, after locating the target node, you MUST use the get_ast_node tool. It is required to recursively call get_ast_node to obtain the complete AST node information, including type, code, position, and related relationships (dependency, reference, inheritance, implementation, grouping node IDs).", + "hookSpecificOutput": { + "hookEventName": "PostToolUse", + } +} +EOF +exit 0 diff --git a/docs/.claude/settings.json b/docs/.claude/settings.json new file mode 100644 index 0000000..d051471 --- /dev/null +++ b/docs/.claude/settings.json @@ -0,0 +1,43 @@ +{ + "permissions": { + "allow": [ + "Edit(\\.md)", + "Read(~/.claude/**)", + "mcp__abcoder", + "mcp__sequential-thinking" + ] + }, + "hooks": { + "PreToolUse": [ + { + "matcher": "mcp__abcoder__get_repo_structure|mcp__abcoder__get_file_structure|mcp__abcoder__get_package_structure|mcp__abcoder__get_ast_node", + "hooks": [ + { + "type": "command", + "command": "~/.claude/hooks/abcoder/parse.sh" + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "mcp__abcoder__list_repos", + "hooks": [ + { + "type": "command", + "command": "~/.claude/hooks/abcoder/prompt.sh" + } + ] + }, + { + "matcher": "mcp__abcoder__get_repo_structure|mcp__abcoder__get_package_structure|mcp__abcoder__get_file_structure", + "hooks": [ + { + "type": "command", + "command": "~/.claude/hooks/abcoder/reminder.sh" + } + ] + } + ] + } +} diff --git a/docs/.claude/tmpls/CODE_TASK.md b/docs/.claude/tmpls/CODE_TASK.md new file mode 100644 index 0000000..f2ef4a5 --- /dev/null +++ b/docs/.claude/tmpls/CODE_TASK.md @@ -0,0 +1,92 @@ +# CODE_TASK 模板 + +## 使用方法 +描述你的编码需求时引用此文件,Claude Code 会: +1. 解析任务列表 +2. 使用 TodoWrite 工具跟踪进度 +3. 用文档提供的参数直接调用`mcp__abcoder__get_ast_node` + +## 格式示例 + +```markdown +1. [ ] 为Flight数据模型添加comfortInfo字段 + - file: models/flight.go + - action: modify + +2. [ ] 创建ComfortInfo数据源的客户端 + - file: clients/comfort_client.go + - action: create + - 涉及`go-redis`的`NewClient` Method(`redis.go:1035`) + - 使用 `mcp__abcoder__get_ast_node` 获取函数定义和依赖: + `` + mcp__abcoder__get_ast_node "github.com/redis/go-redis" '[{"mod_path":"github.com/redis/go-redis/v9","pkg_path":"github.com/redis/go-redis/v9","name":"NewClient"}]' + `` + +3. [ ] 在FlightService中增加调用comfort_client的逻辑 + - file: services/flight_service.go + - action: modify + +4. [ ] 更新GetFlightDetails API的返回值 + - file: controllers/flight_controller.go + - action: modify + - 返回值IDL结构: +`` +type FlightDetailsResponse struct { + // 航班基础信息 + FlightNumber string `json:"flight_number"` // 航班号,如 "CA1234" + Airline string `json:"airline"` // 航空公司,如 "中国国际航空" + Status struct { + Code string `json:"code"` // 状态码,如 "ON_TIME", "DELAYED", "CANCELLED" + Message string `json:"message"` // 状态描述,如 "航班准点起飞" + } `json:"status"` +} +`` + +5. [ ] 为新增逻辑编写单元测试 + - file: services/flight_service_test.go + - action: create +``` + +### 验证方法 +#### 接口验证 +1. 调用curl +``` +curl -X POST 'localhost:4379/mcp' \ + --header 'Content-Type: application/json' \ + --header 'mcp-session-id: {{mcp-session}}' \ + --data '{ + "jsonrpc": "2.0", + "id": 2, + "method": "tools/call", + "params": { + "name": "get_flight_details", + "arguments": { + "flight_number": {{flight_number}} + } + } +}' +``` +#### 日志验证 +调用上面的接口验证时,Log应该输出下面的信息 +``` +[INFO] FlightDetailsResponse: xxx +``` + +## 注意事项 +IMPORTANT: 保持格式简单,让 Claude Code 专注于执行 +- action 可选: create/modify/delete +- 文件路径要准确 +- 任务描述要明确具体。对于涉及外部服务调用或 SDK 使用的任务,必须提供详细的技术规格;提醒禁止简化实现或MOCK,必须使用真实 SDK/curl 调用 + - 涉及SDK:需要明确指定具体的 SDK Package、Method名称 + - 涉及curl:需要明确指定完整的 curl 命令,包括请求头、请求体、URL、Resp结构(JSON/IDL) +- 如果外部SDK内容较多,请在文档提供`mcp__abcoder__get_ast_node`调用参数(请确保参数能正确返回结果),保持文档内容简洁;让subAgent可直接调用 +- 在关键Step处提醒`build`修复语法错误;避免最后出现大量语法问题 +- 必须提供直接、清晰、具体的验证方法 + +## 核心理念 +**直接使用原则**:编写CODE_TASK时进行充分的`mcp__abcoder__get_ast_node`分析,提供完整的上下文信息、`get_ast_node`调用参数,确保无需`mcp__abcoder`重新分析仓库即可直接执行任务。 + +指导原则: +- 技术规格预分析:在编写任务时就提供完整的技术实现背景、分支逻辑 +- 代码结构预理解:通过`mcp__abcoder`工具预先分析代码结构和依赖关系 +- 避免重复工作:SubAgent应专注于执行,而非重新理解和分析 diff --git a/llm/tool/ast_read.go b/llm/tool/ast_read.go index 825d388..520eaca 100644 --- a/llm/tool/ast_read.go +++ b/llm/tool/ast_read.go @@ -33,15 +33,15 @@ import ( const ( ToolListRepos = "list_repos" - DescListRepos = "list all repositories" + DescListRepos = "[DISCOVERY] level1/4: List all repositories. No parameters required. Always the first step in any analysis workflow." ToolGetRepoStructure = "get_repo_structure" - DescGetRepoStructure = "get the repository structure, including package list and file list" + DescGetRepoStructure = "[STRUCTURE] level2/4: Get repository structure. Input: repo_name from list_repos output. Output: modules with packages and files." ToolGetPackageStructure = "get_package_structure" - DescGetPackageStructure = "get the package (NameSpace) structure, including file list and node-id list" + DescGetPackageStructure = "[STRUCTURE] level3/4: Get package structure with node_ids. Input: repo_name, mod_path, pkg_path from get_repo_structure output. Output: files with node_ids." ToolGetFileStructure = "get_file_structure" - DescGetFileStructure = "get the file structure, including node (id,signature,type) list" + DescGetFileStructure = "[STRUCTURE] level3/4: Get file structure with node list. Input: repo_name, file_path from get_repo_structure output. Output: nodes with signatures." ToolGetASTNode = "get_ast_node" - DescGetASTNode = "precisely get the codes, type, location of a specific ast node, as well as the identities of related (Dependend/Reference/Implement/Inherit/Group) nodes" + DescGetASTNode = "[ANALYSIS] level4/4: Get detailed AST node info. Input: repo_name, node_ids from previous calls. Output: codes, dependencies, references, implementations." // ToolWriteASTNode = "write_ast_node" ) @@ -165,8 +165,7 @@ func (t *ASTReadTools) GetTool(name string) Tool { return t.tools[name] } -type ListReposReq struct { -} +type ListReposReq struct{} type ListReposResp struct { RepoNames []string `json:"repo_names" jsonschema:"description=the names of the repositories"` @@ -182,7 +181,7 @@ func (t *ASTReadTools) ListRepos(ctx context.Context, req ListReposReq) (*ListRe } type GetRepoStructReq struct { - RepoName string `json:"repo_name" jsonschema:"description=the name of the repository"` + RepoName string `json:"repo_name" jsonschema:"description=the name of the repository (output of list_repos tool)"` } type GetRepoStructResp struct { @@ -223,9 +222,9 @@ type NodeStruct struct { } type NodeID struct { - ModPath uniast.ModPath `json:"mod_path" jsonschema:"description=the mod path of the node"` - PkgPath uniast.PkgPath `json:"pkg_path" jsonschema:"description=the package path of the node"` - Name string `json:"name" jsonschema:"description=the name of the node"` + ModPath uniast.ModPath `json:"mod_path" jsonschema:"description=module path of the node (from get_repo_structure)"` + PkgPath uniast.PkgPath `json:"pkg_path" jsonschema:"description=package path of the node (from get_repo_structure)"` + Name string `json:"name" jsonschema:"description=name of the node (from get_package_structure or get_file_structure)"` } func NewNodeID(id uniast.Identity) NodeID { @@ -257,13 +256,13 @@ func (t *ASTReadTools) getRepoAST(repoName string) (*uniast.Repository, error) { if len(candis) == 1 { repo, ok = t.repos.Load(candis[0]) if !ok { - return nil, fmt.Errorf("repo '%s' not found", candis[0]) + return nil, fmt.Errorf("repo '%s' not found. Use `list_repos` to get valid repo_name", candis[0]) } return repo.(*uniast.Repository), nil } else if len(candis) > 1 { return nil, fmt.Errorf("repo '%s' is ambiguous, maybe you want one of %v", repoName, candis) } else { - return nil, fmt.Errorf("repo '%s' not found", repoName) + return nil, fmt.Errorf("repo '%s' not found. Use `list_repos` to get valid repo_name", repoName) } } return repo.(*uniast.Repository), nil @@ -306,9 +305,9 @@ func (t *ASTReadTools) GetRepoStructure(_ context.Context, req GetRepoStructReq) } type GetPackageStructReq struct { - RepoName string `json:"repo_name" jsonschema:"description=the name of the repository"` - ModPath uniast.ModPath `json:"mod_path" jsonschema:"description=the module path"` - PkgPath uniast.PkgPath `json:"package_path" jsonschema:"description=the package path"` + RepoName string `json:"repo_name" jsonschema:"description=the name of the repository (output of list_repos tool)"` + ModPath uniast.ModPath `json:"mod_path" jsonschema:"description=the module path (output of get_repo_structure tool)"` + PkgPath uniast.PkgPath `json:"package_path" jsonschema:"description=the package path (output of get_repo_structure tool)"` } type GetPackageStructResp struct { @@ -384,8 +383,8 @@ func (t *ASTReadTools) GetPackageStructure(ctx context.Context, req GetPackageSt } type GetFileStructReq struct { - RepoName string `json:"repo_name" jsonschema:"description=the name of the repository"` - FilePath string `json:"file_paths" jsonschema:"description=the file paths"` + RepoName string `json:"repo_name" jsonschema:"description=the name of the repository (output of list_repos tool)"` + FilePath string `json:"file_path" jsonschema:"description=relative file path (output of get_repo_structure tool, e.g., 'src/main.go')"` } type GetFileStructResp struct { @@ -415,7 +414,7 @@ func (t *ASTReadTools) getFileStructure(_ context.Context, req GetFileStructReq, resp := new(GetFileStructResp) file, mod := repo.GetFile(req.FilePath) if file == nil { - return nil, fmt.Errorf("file '%s' not found", req.FilePath) + return nil, fmt.Errorf("file '%s' not found. Use 'get_repo_structure' to get valid file paths", req.FilePath) } nodes := repo.GetFileNodes(req.FilePath) @@ -445,8 +444,8 @@ func (t *ASTReadTools) getFileStructure(_ context.Context, req GetFileStructReq, } type GetASTNodeReq struct { - RepoName string `json:"repo_name" jsonschema:"description=the name of the repository"` - NodeIDs []NodeID `json:"node_ids" jsonschema:"description=the identities of the ast node"` + RepoName string `json:"repo_name" jsonschema:"description=the name of the repository (output of list_repos tool)"` + NodeIDs []NodeID `json:"node_ids" jsonschema:"description=the identities of the ast node (output of get_package_structure or get_file_structure tool)"` } type GetASTNodeResp struct { @@ -509,7 +508,7 @@ func (t *ASTReadTools) GetASTNode(_ context.Context, params GetASTNodeReq) (*Get } if len(resp.Nodes) == 0 { - resp.Error = "node not found, maybe you should check the pkg_path or node_name?" + resp.Error = "node not found. Use `get_package_structure` to list all valid nodes. If it cannot be confirmed, call `get_file_structure` for detailed node information" } log.Debug("get repo structure, resp: %v", abutil.MarshalJSONIndentNoError(resp))