diff --git a/.gitignore b/.gitignore
index 89d64fa..99f6ff5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,10 +73,8 @@ testdata/repos
testdata/jsons
src/lang/testdata
-*.json
!ts-parser/**/*.json
tools
-abcoder
!testdata/asts/*.json
\ No newline at end of file
diff --git a/README.md b/README.md
index f83c8e6..cc6a62f 100644
--- a/README.md
+++ b/README.md
@@ -25,101 +25,40 @@ see [UniAST Specification](docs/uniast-zh.md)
# Quick Start
-## Use ABCoder as a MCP server
-
-1. Install ABCoder:
-
- ```bash
- go install github.com/cloudwego/abcoder@latest
- ```
-
-2. Use ABCoder to parse a repository to UniAST (JSON)
-
- ```bash
- abcoder parse {language} {repo-path} -o xxx.json
- ```
-
- ABCoder will try to install any dependency automatically.
- In case of failure (or if you want to customize installation), refer to the [docs](./docs/lsp-installation-en.md).
-
- For example, to parse a Go repository:
-
- ```bash
- git clone https://github.com/cloudwego/localsession.git localsession
- abcoder parse go localsession -o /abcoder-asts/localsession.json
- ```
-
-
-3. Integrate ABCoder's MCP tools into your AI agent.
-
- ```json
- {
- "mcpServers": {
- "abcoder": {
- "command": "abcoder",
- "args": [
- "mcp",
- "{the-AST-directory}"
- ]
- }
- }
- }
- ```
-
-
-4. Enjoy it!
-
- Try to click and watch the video below:
-
-
-
- [

](https://www.bilibili.com/video/BV14ggJzCEnK)
-
-
-
-
-## 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.
+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. Check [Claude Code Specification](docs/claude-code-spec.md) for more details.
### Setup
-1. **Install ABCoder Claude Configuration**
-
- Copy [`docs/.claude/`](docs/.claude/) to your home directory or project root:
+Use the `init-spec` command to automatically configure Claude Code integration for your project:
- ```bash
- cp -r docs/.claude ~/
- ```
+```bash
+# Install ABCoder
+go install github.com/cloudwego/abcoder@latest
-2. **Configure ABCoder MCP Server**
+# Run init-spec in your project directory (optional: specify target path)
+cd /path/to/your/project
+abcoder init-spec
+```
- Configure in Claude Code's `~/.claude.json` (the hook uses `abcoder parse go/ts . -o ~/.asts/repo.json` for the default AST folder):
+The `init-spec` command will:
+1. Copy `.claude` directory to your project root
+2. Configure MCP servers in `~/.claude.json`:
+ - `abcoder`: for code analysis using AST
+ - `sequential-thinking`: for complex problem decomposition
+3. Replace all `{{CLAUDE_HOME_PATH}}` placeholders with actual project paths
- ```json
- {
- "mcpServers": {
- "abcoder": {
- "command": "abcoder",
- "args": ["mcp", "~/.asts"]
- }
- }
- }
- ```
+### Start Coding with Claude Code
-3. **Configure Hooks**
+Once setup, you can start coding with Claude Code:
- 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
+1. Start Claude Code in your project directory
+2. Use slash common `/abcoder:schedule ` to address your feature/requirement/issue, and ABCoder will help you analyze the codebase and design a technical solution.
+3. Once all questions are set, use slash common `/abcoder:task ` to create a coding task(specification)
+4. Recheck the task using `/abcoder:recheck ` before real implementation
+5. Begin coding! Claude Code will process the task step by step according to the specification, leveraging the power of AST-driven analysis.
### AST-Driven Coding Workflow
@@ -137,7 +76,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
| Command | Function | Description |
|---------|----------|-------------|
-| [`/abcoder:schd`](docs/.claude/commands/abcoder:schd.md) | Design implementation | Analyze codebase using ABCoder, design technical solution |
+| [`/abcoder:schedule` ](docs/.claude/commands/abcoder:schedule.md) | Design implementation | Analyze codebase by 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 |
@@ -147,7 +86,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
User Request
│
▼
-/abcoder:schd ──────────→ Design Solution (ABCoder Analysis)
+/abcoder:schedule ──────────→ Design Solution (ABCoder Analysis)
│ │
▼ ▼
/abcoder:task ─────────→ CODE_TASK (with Technical Specs, including accurate `get_ast_node` call args)
@@ -156,7 +95,7 @@ User Request
/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
+Start coding(sub-agent) ─────────→ Execute Implementation
```
### Configuration Files
@@ -166,19 +105,78 @@ sub-agent ─────────→ Execute Implementation
| [`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 |
+| [`commands/`](docs/.claude/commands/) | Slash command definitions (abcoder:task/abcoder:schedule/abcoder:recheck) |
+| [`tmpls/ABCODER_CODE_TASK.md`](docs/.claude/tmpls/ABCODER_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)
+- ABCoder MCP server (provides `mcp__abcoder` tools)
+- Sequential-thinking MCP server (provides `mcp__sequential_thinking` tools, automatically configured by init-spec)
> 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 a MCP server
+
+1. Install ABCoder:
+
+ ```bash
+ go install github.com/cloudwego/abcoder@latest
+ ```
+
+2. Use ABCoder to parse a repository to UniAST (JSON)
+
+ ```bash
+ abcoder parse {language} {repo-path} -o xxx.json
+ ```
+
+ ABCoder will try to install any dependency automatically.
+ In case of failure (or if you want to customize installation), refer to the [docs](./docs/lsp-installation-en.md).
+
+ For example, to parse a Go repository:
+
+ ```bash
+ git clone https://github.com/cloudwego/localsession.git localsession
+ abcoder parse go localsession -o /abcoder-asts/localsession.json
+ ```
+
+
+3. Integrate ABCoder's MCP tools into your AI agent.
+
+ ```json
+ {
+ "mcpServers": {
+ "abcoder": {
+ "command": "abcoder",
+ "args": [
+ "mcp",
+ "{the-AST-directory}"
+ ]
+ }
+ }
+ }
+ ```
+
+
+4. Enjoy it!
+
+ Try to click and watch the video below:
+
+
+
+ [

](https://www.bilibili.com/video/BV14ggJzCEnK)
+
+
+
+
+## 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.
+
## Use ABCoder as an Agent (WIP)
diff --git a/docs/.claude/hooks/abcoder/reminder.sh b/docs/.claude/hooks/abcoder/reminder.sh
deleted file mode 100755
index f8596a6..0000000
--- a/docs/.claude/hooks/abcoder/reminder.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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
deleted file mode 100644
index d051471..0000000
--- a/docs/.claude/settings.json
+++ /dev/null
@@ -1,43 +0,0 @@
-{
- "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/README.md b/docs/claude-code-spec.md
similarity index 93%
rename from docs/.claude/README.md
rename to docs/claude-code-spec.md
index 293dc77..4f72806 100644
--- a/docs/.claude/README.md
+++ b/docs/claude-code-spec.md
@@ -14,7 +14,7 @@ Claude Code 的 AST 驱动开发配置,通过 MCP 工具、钩子和斜杠命
│ └── reminder.sh # 提醒递归调用 get_ast_node
├── commands/ # 斜杠命令定义
│ ├── abcoder:task.md # /abcoder:task - 创建编码任务
-│ ├── abcoder:schd.md # /abcoder:schd - 设计实现方案
+│ ├── abcoder:schedule.md # /abcoder:schedule - 设计实现方案
│ └── abcoder:recheck.md # /abcoder:recheck - 技术方案核对
└── tmpls/ # 文档模板
└── CODE_TASK.md # 编码任务模板
@@ -63,7 +63,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
## 斜杠命令
-### /abcoder:task <名称>
+### /abcoder:task <任务名称>
创建 CODE_TASK 文档,生成 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md`
@@ -73,7 +73,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
- 涉及 curl: 提供完整命令和响应结构
- 提供具体验证方法
-### /abcoder:schd
+### /abcoder:schedule <任务描述>
使用 mcp__abcoder 设计实现方案
@@ -82,7 +82,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
- 优先最小改动
- 禁止编写代码、禁止使用 agent
-### /abcoder:recheck <任务>
+### /abcoder:recheck <任务名称>
批判性检查 CODE_TASK 技术可行性
@@ -97,7 +97,7 @@ list_repos → get_repo_structure → get_package_structure → get_file_structu
用户需求
│
▼
-/abcoder:schd ──────────────→ 设计方案(abcoder分析)
+/abcoder:schedule ──────────────→ 设计方案(abcoder分析)
│ │
▼ ▼
/abcoder:task ────────→ CODE_TASK(含技术规格)
diff --git a/docs/.claude/CLAUDE.md b/internal/utils/assets/.claude/CLAUDE.md
similarity index 100%
rename from docs/.claude/CLAUDE.md
rename to internal/utils/assets/.claude/CLAUDE.md
diff --git a/docs/.claude/commands/README.md b/internal/utils/assets/.claude/commands/README.md
similarity index 89%
rename from docs/.claude/commands/README.md
rename to internal/utils/assets/.claude/commands/README.md
index 049276c..86fe9d9 100644
--- a/docs/.claude/commands/README.md
+++ b/internal/utils/assets/.claude/commands/README.md
@@ -10,7 +10,7 @@ Claude Code 斜杠命令定义,用于规范化和自动化开发工作流。
**执行流程**:
1. 创建 `./task/{{MMDD}}/` 目录
-2. 读取模板 `~/.claude/tmpls/CODE_TASK.md`
+2. 读取模板 `{{CLAUDE_HOME_PATH}}/.claude/tmpls/ABCODER_CODE_TASK.md`
3. 根据任务上下文填充模板,生成 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md`
4. 列出外部依赖包(如有)
5. 提示创建成功
@@ -30,7 +30,7 @@ Claude Code 斜杠命令定义,用于规范化和自动化开发工作流。
---
-### /abcoder:schd - 设计实现方案
+### /abcoder:schedule - 设计实现方案
使用 mcp__abcoder 分析代码库,设计技术实现方案。
@@ -66,7 +66,7 @@ Claude Code 斜杠命令定义,用于规范化和自动化开发工作流。
## 模板文件
-### CODE_TASK.md
+### ABCODER_CODE_TASK.md
编码任务模板,定义任务清单格式。
@@ -89,7 +89,7 @@ Claude Code 斜杠命令定义,用于规范化和自动化开发工作流。
用户需求
│
▼
-/abcoder:schd ──────────────→ 设计方案(abcoder分析)
+/abcoder:schedule ──────────────→ 设计方案(abcoder分析)
│ │
▼ ▼
/abcoder:task ────────→ CODE_TASK(含技术规格)
@@ -104,11 +104,11 @@ coding-executor ──────→ 执行实现
## 文件位置
```
-~/.claude/
+{{CLAUDE_HOME_PATH}}/.claude/
├── commands/
│ ├── abcoder:task.md
-│ ├── abcoder:schd.md
+│ ├── abcoder:schedule.md
│ └── abcoder:recheck.md
└── tmpls/
- └── CODE_TASK.md
+ └── ABCODER_CODE_TASK.md
```
diff --git a/docs/.claude/commands/abcoder:recheck.md b/internal/utils/assets/.claude/commands/recheck.md
similarity index 85%
rename from docs/.claude/commands/abcoder:recheck.md
rename to internal/utils/assets/.claude/commands/recheck.md
index 9476d8c..326f190 100644
--- a/docs/.claude/commands/abcoder:recheck.md
+++ b/internal/utils/assets/.claude/commands/recheck.md
@@ -1,4 +1,5 @@
-# 系统指令: ReCheck
+# 系统指令: 核对任务实现方案(二次对齐)
+
理解任务 {1},并使用`mcp__abcoder`核对技术细节(下钻到`mcp__abcoder__get_ast_node`粒度);
diff --git a/docs/.claude/commands/abcoder:schd.md b/internal/utils/assets/.claude/commands/schedule.md
similarity index 81%
rename from docs/.claude/commands/abcoder:schd.md
rename to internal/utils/assets/.claude/commands/schedule.md
index 52dbfff..0d6bef6 100644
--- a/docs/.claude/commands/abcoder:schd.md
+++ b/internal/utils/assets/.claude/commands/schedule.md
@@ -1,4 +1,4 @@
-# 系统指令:Schedule
+# 系统指令:规划一个任务(输入:任务描述)
使用`mcp__abcoder`分析相关仓库(下钻到`mcp__abcoder__get_ast_node`查看细节),帮助用户设计实现方案。
@@ -7,6 +7,7 @@
- 优先采用直接、最小改动的实现方式,只有在用户明确要求时才增加复杂度。
- 严格限制修改影响面在所请求的结果范围内。
- 找出任何模糊或含糊不清的细节,并在修改文件前提出必要的后续问题。
-- 在Schdule阶段禁止编写代码,禁止使用agent。
+- 在Schedule阶段禁止编写代码,禁止使用agent。
+
IMPORTANT: 必须从`mcp__abcoder__get_repo_strucure`开始
diff --git a/docs/.claude/commands/abcoder:task.md b/internal/utils/assets/.claude/commands/task.md
similarity index 79%
rename from docs/.claude/commands/abcoder:task.md
rename to internal/utils/assets/.claude/commands/task.md
index 69a0638..d212e89 100644
--- a/docs/.claude/commands/abcoder:task.md
+++ b/internal/utils/assets/.claude/commands/task.md
@@ -1,9 +1,9 @@
-# 系统指令:创建CODE_TASK
+# 系统指令:基于当前上下文创建一个 SPEC 驱动的 CODE_TASK(输入:任务名称)
请帮我创建一个CODE_TASK。
1. `d=$(date +%m%d) && mkdir -p "./task/$d/" && echo "目录 ./task/$d/ 已创建"` 创建 `./task/{{MMDD}}/` 目录
-2. 读取模板文件 `~/.claude/tmpls/CODE_TASK.md`
+2. 读取模板文件 `{{CLAUDE_HOME_PATH}}/.claude/tmpls/ABCODER_CODE_TASK.md`
3. 根据任务上下文,按照格式和要求填充模板,创建新文件 `./task/{{MMDD}}/{{NAME}}__CODE_TASK.md`
4. 告知用户这个`CODE_TASK`是否包含外部依赖;如果包含,请清晰列出完整的外部依赖包名称
5. 提示用户文件已创建成功,停止操作
diff --git a/docs/.claude/hooks/README.md b/internal/utils/assets/.claude/hooks/README.md
similarity index 97%
rename from docs/.claude/hooks/README.md
rename to internal/utils/assets/.claude/hooks/README.md
index 83615cc..d131e04 100644
--- a/docs/.claude/hooks/README.md
+++ b/internal/utils/assets/.claude/hooks/README.md
@@ -67,7 +67,7 @@ Claude Code 钩子系统,用于自动化 ABCoder 代码分析工作流。
## 安装位置
```
-~/.claude/hooks/abcoder/
+{{CLAUDE_HOME_PATH}}/.claude/hooks/abcoder/
├── parse.sh
├── prompt.sh
├── reminder.sh
diff --git a/docs/.claude/hooks/abcoder/abcoder-workflow.md b/internal/utils/assets/.claude/hooks/abcoder/abcoder-workflow.md
similarity index 79%
rename from docs/.claude/hooks/abcoder/abcoder-workflow.md
rename to internal/utils/assets/.claude/hooks/abcoder/abcoder-workflow.md
index a30b8c6..1d3bf62 100644
--- a/docs/.claude/hooks/abcoder/abcoder-workflow.md
+++ b/internal/utils/assets/.claude/hooks/abcoder/abcoder-workflow.md
@@ -20,8 +20,9 @@
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)
+ - 2.2 **定位文件**: 通过 `get_package_structure` 返回的file信息,确认目标文件
+ - 2.3 **定位节点**: 通过 `get_files_structure` 返回的node信息,确认目标节点
+ - 2.4 **确认node详情**: 递归调用 `get_ast_node` 获取node详细(dependencies, references, inheritance, implementation, grouping)
3. **自我反思**:
- 理解完整的code calling-chain、contextual-relationship
diff --git a/internal/utils/assets/.claude/hooks/abcoder/need_update.sh b/internal/utils/assets/.claude/hooks/abcoder/need_update.sh
new file mode 100755
index 0000000..2899692
--- /dev/null
+++ b/internal/utils/assets/.claude/hooks/abcoder/need_update.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# 当 Claude Code 执行 Write 操作后,更新 _need_update 文件为 1
+
+# 错误处理
+set -euo pipefail
+
+# 获取脚本所在的绝对路径
+script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# 定义 _need_update 文件路径(放在脚本所在目录)
+need_update_file="${script_dir}/_need_update"
+
+# 写入 1 到文件
+echo "1" > "$need_update_file"
+
+# 输出 JSON
+jq -n --arg file "$need_update_file" '{
+ "continue": true,
+ "hookSpecificOutput": {
+ "hookEventName": "PostToolUse",
+ "additionalContext": ("Write 操作完成,已更新 " + $file + " 为 1")
+ }
+}'
+
+exit 0
diff --git a/docs/.claude/hooks/abcoder/parse.sh b/internal/utils/assets/.claude/hooks/abcoder/parse.sh
similarity index 51%
rename from docs/.claude/hooks/abcoder/parse.sh
rename to internal/utils/assets/.claude/hooks/abcoder/parse.sh
index 18c5247..3661e93 100755
--- a/docs/.claude/hooks/abcoder/parse.sh
+++ b/internal/utils/assets/.claude/hooks/abcoder/parse.sh
@@ -72,7 +72,7 @@ abc() {
mkdir -p ~/.asts/
# 执行实际命令
- abcoder parse "${lang}" "${repo_path}" -o "/Users/bytedance/.asts/${repo_name}.json"
+ abcoder parse "${lang}" "${repo_path}" -o "~/.asts/${repo_name}.json"
else
# 如果不是预期的 parse 命令格式,直接将参数传递给原始 abcoder 命令
abcoder "$@"
@@ -81,6 +81,12 @@ abc() {
# LOG_FILE="/tmp/claude-hook-debug.log"
+# 获取脚本所在的绝对路径
+script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+
+# 定义 _need_update 文件路径
+need_update_file="${script_dir}/_need_update"
+
input=$(cat)
repo_name=$(echo "$input" | jq -r '.tool_input.repo_name // ""')
cwd=$(echo "$input" | jq -r '.cwd // ""')
@@ -89,71 +95,82 @@ cwd=$(echo "$input" | jq -r '.cwd // ""')
# echo "repo_name: $repo_name" >> "$LOG_FILE"
# echo "cwd: $cwd" >> "$LOG_FILE"
+# 读取 _need_update 文件判断是否需要强制更新
+force_update=0
+if [[ -f "$need_update_file" ]]; then
+ need_update_value=$(cat "$need_update_file" 2>/dev/null || echo "0")
+ if [[ "$need_update_value" == "1" ]]; then
+ force_update=1
+ # 强制更新后,将 _need_update 重置为 0
+ echo "0" > "$need_update_file"
+ fi
+fi
+
# 复用现有的 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
-
+# 优化判断逻辑:只要检测到有效项目,就执行 parse
+if [[ "$project_lang" != "unknown" ]]; then
# 捕获标准输出和错误输出
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"
+ # 使用检测到的语言执行 parse 命令
+ # 确保输出目录存在
+ mkdir -p ~/.asts/
+ ast_output_file=~/.asts/$(echo "$project_identifier" | sed 's|/|_|g').json
+
+ # 检查 AST 文件是否存在且更新时间小于 3 分钟(缓存优化)
+ # 如果 force_update=1,则跳过缓存检查,强制重新 parse
+ if [[ $force_update -eq 0 && -f "$ast_output_file" ]]; then
+ file_age_seconds=$(($(date +%s) - $(stat -f %m "$ast_output_file" 2>/dev/null || stat -c %Y "$ast_output_file" 2>/dev/null)))
+ if [[ $file_age_seconds -lt 180 ]]; then
+ jq -n --arg lang "$project_lang" --arg repo "$project_identifier" --arg age "$file_age_seconds" '{
+ "continue": true,
+ "systemMessage": ("abcoder AST 缓存命中(语言:" + $lang + ",仓库:" + $repo + ")。文件更新于 " + $age + " 秒前(小于3分钟更新阈值),跳过 parse 操作。")
+ }'
+ exit 0
+ fi
+ fi
- jq -n --arg lang "$project_lang" '{
- "systemMessage": "abcoder parse 已成功完成(语言:\($lang))。AST文件已生成,可以继续分析代码。"
- }'
+ # 使用检测到的语言执行 parse 命令,并输出到 AST 目录
+ if abcoder parse "$project_lang" . -o "$ast_output_file" >"$output_file" 2>"$error_file"; then
+ msg_prefix="[定时更新]"
+ if [[ $force_update -eq 1 ]]; then
+ msg_prefix="[检测到变更] "
+ fi
+ jq -n --arg lang "$project_lang" --arg repo "$project_identifier" --arg prefix "$msg_prefix" '{
+ "continue": true,
+ "systemMessage": ($prefix + "abcoder parse 已成功完成(语言:" + $lang + ",仓库:" + $repo + ")。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" '{
+ jq -n --arg code "$exit_code" --arg err "$error_msg" --arg lang "$project_lang" --arg repo "$project_identifier" '{
"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' 检查"),
+ "reason": ("abcoder parse 失败(语言:" + $lang + ",仓库:" + $repo + ",退出码: " + $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": ("解析失败,需要修复后重试")
+ "additionalContext": "解析失败,需要修复后重试"
}
}'
fi
# 清理临时文件
- trash "$output_file" "$error_file"
+ trash "$output_file" "$error_file" 2>/dev/null || rm -f "$output_file" "$error_file"
else
- # echo "Path did not match" >> "$LOG_FILE"
- echo '{}'
+ # 当前目录不是支持的项目,返回空对象
+ jq -n '{
+ "decision": "block",
+ "reason": "当前目录未检测到支持的语言(仅支持 Go 和 TypeScript),请确保项目是 Go 或 TypeScript 类型"
+ }'
fi
exit 0
diff --git a/docs/.claude/hooks/abcoder/prompt.sh b/internal/utils/assets/.claude/hooks/abcoder/prompt.sh
similarity index 58%
rename from docs/.claude/hooks/abcoder/prompt.sh
rename to internal/utils/assets/.claude/hooks/abcoder/prompt.sh
index 454acb3..eb52d1c 100755
--- a/docs/.claude/hooks/abcoder/prompt.sh
+++ b/internal/utils/assets/.claude/hooks/abcoder/prompt.sh
@@ -7,12 +7,12 @@
set -euo pipefail
# 验证文件存在
-SOP_FILE="$HOME/.claude/hooks/abcoder/abcoder-workflow.md"
+SOP_FILE="/Users/bytedance/github/github.com/cloudwego/abcoder2/.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"}}'
+ echo '{"ok": false, "reason": "SOP file not found", "hookSpecificOutput": {"hookEventName": "PostToolUse"}}'
exit 0
fi
@@ -22,7 +22,7 @@ fi
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"}}'
+ echo '{"ok": false, "reason": "Failed to process SOP content", "hookSpecificOutput": {"hookEventName": "PostToolUse"}}'
exit 0
fi
@@ -31,10 +31,10 @@ fi
# 输出 JSON
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/internal/utils/assets/.claude/settings.json b/internal/utils/assets/.claude/settings.json
new file mode 100644
index 0000000..d1175b1
--- /dev/null
+++ b/internal/utils/assets/.claude/settings.json
@@ -0,0 +1,52 @@
+{
+ "permissions": {
+ "allow": [
+ "Edit(\\.md)",
+ "Read({{CLAUDE_HOME_PATH}}/.claude/**)",
+ "mcp__abcoder",
+ "mcp__sequential-thinking"
+ ]
+ },
+ "hooks": {
+ "PreToolUse": [
+ {
+ "matcher": "mcp__abcoder__list_repos|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_HOME_PATH}}/.claude/hooks/abcoder/parse.sh"
+ }
+ ]
+ }
+ ],
+ "PostToolUse": [
+ {
+ "matcher": "Write|Edit",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "{{CLAUDE_HOME_PATH}}/.claude/hooks/abcoder/need_update.sh"
+ }
+ ]
+ },
+ {
+ "matcher": "mcp__abcoder__list_repos",
+ "hooks": [
+ {
+ "type": "command",
+ "command": "{{CLAUDE_HOME_PATH}}/.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_HOME_PATH}}/.claude/hooks/abcoder/reminder.sh"
+ }
+ ]
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/docs/.claude/tmpls/CODE_TASK.md b/internal/utils/assets/.claude/tmpls/ABCODER_CODE_TASK.md
similarity index 100%
rename from docs/.claude/tmpls/CODE_TASK.md
rename to internal/utils/assets/.claude/tmpls/ABCODER_CODE_TASK.md
diff --git a/internal/utils/cmd_init_spec.go b/internal/utils/cmd_init_spec.go
new file mode 100644
index 0000000..a5fdf26
--- /dev/null
+++ b/internal/utils/cmd_init_spec.go
@@ -0,0 +1,274 @@
+/**
+ * Copyright 2026 ByteDance Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package utils
+
+import (
+ "embed"
+ "encoding/json"
+ "fmt"
+ "io/fs"
+ "os"
+ "path/filepath"
+ "strings"
+
+ "github.com/cloudwego/abcoder/llm/log"
+)
+
+//go:embed assets/.claude
+var claudeFS embed.FS
+
+// claudeConfig represents the Claude Code configuration structure
+type claudeConfig struct {
+ MCPServers map[string]mcpServerConfig `json:"mcpServers"`
+}
+
+type mcpServerConfig struct {
+ Command string `json:"command"`
+ Args []string `json:"args"`
+}
+
+// runInitSpec implements the init-spec command
+func RunInitSpec(targetDir string) error {
+ if targetDir == "" {
+ // Default to current directory if not specified
+ cwd, err := os.Getwd()
+ if err != nil {
+ return fmt.Errorf("failed to get current directory: %w", err)
+ }
+ targetDir = cwd
+ }
+
+ // Ensure targetDir is absolute
+ targetDirAbs, err := filepath.Abs(targetDir)
+ if err != nil {
+ return fmt.Errorf("failed to get absolute path: %w", err)
+ }
+
+ // 1. Copy assets/.claude to targetDir/.claude
+ claudeDestDir := filepath.Join(targetDirAbs, ".claude")
+ if err := copyEmbeddedDir("assets/.claude", claudeDestDir, targetDirAbs); err != nil {
+ return fmt.Errorf("failed to copy .claude directory: %w", err)
+ }
+ log.Info("Copied .claude directory to %s", claudeDestDir)
+
+ // 2. Get home directory for MCP server configuration
+ homeDir, err := os.UserHomeDir()
+ if err != nil {
+ return fmt.Errorf("failed to get home directory: %w", err)
+ }
+
+ // 3. Configure MCP servers in ~/.claude.json
+ // Get asts directory path from parse.sh hook (default ~/.asts)
+ astsDir := filepath.Join(homeDir, ".asts")
+
+ // Create asts directory if it doesn't exist
+ if err := os.MkdirAll(astsDir, 0755); err != nil {
+ return fmt.Errorf("failed to create asts directory: %w", err)
+ }
+
+ claudeConfigPath := filepath.Join(homeDir, ".claude.json")
+ if err := configureMCPServers(claudeConfigPath, astsDir); err != nil {
+ return fmt.Errorf("failed to configure MCP servers: %w", err)
+ }
+ log.Info("Configured MCP servers in %s", claudeConfigPath)
+
+ // 4. Print success message
+ printSuccessMessage(targetDirAbs, claudeConfigPath, astsDir)
+
+ return nil
+}
+
+// copyEmbeddedDir copies an embedded directory to a destination directory
+func copyEmbeddedDir(srcPath string, destDir string, projectRootDir string) error {
+ // First, ensure the destination directory exists
+ if err := os.MkdirAll(destDir, 0755); err != nil {
+ return fmt.Errorf("failed to create destination directory %s: %w", destDir, err)
+ }
+
+ // Track md files to process after copying
+ var mdFilesToReplace []string
+
+ err := fs.WalkDir(claudeFS, srcPath, func(path string, d fs.DirEntry, err error) error {
+ if err != nil {
+ return err
+ }
+
+ // Calculate relative path from srcPath
+ relPath, err := filepath.Rel(srcPath, path)
+ if err != nil {
+ return err
+ }
+
+ // Skip the root directory itself
+ if relPath == "." {
+ return nil
+ }
+
+ destPath := filepath.Join(destDir, relPath)
+
+ if d.IsDir() {
+ // Create directory
+ return os.MkdirAll(destPath, 0755)
+ }
+
+ // Ensure parent directory exists before writing file
+ parentDir := filepath.Dir(destPath)
+ if err := os.MkdirAll(parentDir, 0755); err != nil {
+ return fmt.Errorf("failed to create parent directory %s: %w", parentDir, err)
+ }
+
+ // Rename command files with abcoder: prefix
+ if strings.HasPrefix(relPath, "commands/") {
+ baseName := filepath.Base(relPath)
+ switch baseName {
+ case "recheck.md":
+ destPath = filepath.Join(filepath.Dir(destPath), "abcoder:recheck.md")
+ case "schedule.md":
+ destPath = filepath.Join(filepath.Dir(destPath), "abcoder:schedule.md")
+ case "task.md":
+ destPath = filepath.Join(filepath.Dir(destPath), "abcoder:task.md")
+ }
+ }
+
+ // Copy file
+ data, err := claudeFS.ReadFile(path)
+ if err != nil {
+ return fmt.Errorf("failed to read embedded file %s: %w", path, err)
+ }
+
+ if err := os.WriteFile(destPath, data, 0644); err != nil {
+ return fmt.Errorf("failed to write file %s: %w", destPath, err)
+ }
+
+ // Set executable permission for shell scripts
+ if strings.HasSuffix(relPath, ".sh") {
+ if err := os.Chmod(destPath, 0755); err != nil {
+ return fmt.Errorf("failed to set executable permission for %s: %w", destPath, err)
+ }
+ }
+
+ // Track md and json files for placeholder replacement
+ if strings.HasSuffix(relPath, ".md") || strings.HasSuffix(relPath, ".json") || strings.HasSuffix(relPath, "prompt.sh") {
+ mdFilesToReplace = append(mdFilesToReplace, destPath)
+ }
+
+ return nil
+ })
+
+ if err != nil {
+ return err
+ }
+
+ // Replace {{CLAUDE_HOME_PATH}} placeholder in md files with project root directory
+ for _, mdFile := range mdFilesToReplace {
+ if err := replaceClaudeHomePlaceholder(mdFile, projectRootDir); err != nil {
+ log.Info("Failed to replace placeholder in %s: %v", mdFile, err)
+ }
+ }
+
+ return nil
+}
+
+// replaceClaudeHomePlaceholder replaces {{CLAUDE_HOME_PATH}} with actual project root directory path
+func replaceClaudeHomePlaceholder(filePath string, projectRootDir string) error {
+ data, err := os.ReadFile(filePath)
+ if err != nil {
+ return fmt.Errorf("failed to read file %s: %w", filePath, err)
+ }
+
+ content := string(data)
+ newContent := strings.ReplaceAll(content, "{{CLAUDE_HOME_PATH}}", projectRootDir)
+
+ if err := os.WriteFile(filePath, []byte(newContent), 0644); err != nil {
+ return fmt.Errorf("failed to write file %s: %w", filePath, err)
+ }
+
+ return nil
+}
+
+// configureMCPServers configures MCP servers in the Claude config file
+func configureMCPServers(configPath string, astsDir string) error {
+ var config claudeConfig
+
+ // Read existing config if it exists
+ if data, err := os.ReadFile(configPath); err == nil {
+ if err := json.Unmarshal(data, &config); err != nil {
+ return fmt.Errorf("failed to parse existing config: %w", err)
+ }
+ } else if !os.IsNotExist(err) {
+ return fmt.Errorf("failed to read config file: %w", err)
+ }
+
+ // Initialize mcpServers map if nil
+ if config.MCPServers == nil {
+ config.MCPServers = make(map[string]mcpServerConfig)
+ }
+
+ // Add/Update abcoder MCP server
+ config.MCPServers["abcoder"] = mcpServerConfig{
+ Command: "abcoder",
+ Args: []string{"mcp", astsDir},
+ }
+
+ // Add sequential-thinking MCP server
+ config.MCPServers["sequential-thinking"] = mcpServerConfig{
+ Command: "npx",
+ Args: []string{"-y", "@modelcontextprotocol/server-sequential-thinking"},
+ }
+
+ // Write the config file
+ data, err := json.MarshalIndent(config, "", " ")
+ if err != nil {
+ return fmt.Errorf("failed to marshal config: %w", err)
+ }
+
+ if err := os.WriteFile(configPath, data, 0644); err != nil {
+ return fmt.Errorf("failed to write config file: %w", err)
+ }
+
+ return nil
+}
+
+// printSuccessMessage prints a success message with instructions
+func printSuccessMessage(targetDir string, configPath string, astsDir string) {
+ fmt.Printf(`
+✓ ABCoder Claude Code integration setup completed!
+
+Configuration files:
+ .claude directory: %s
+ Claude Code config: %s
+ AST storage directory: %s
+
+MCP servers configured:
+ - abcoder: for code analysis using AST
+ - sequential-thinking: for complex problem decomposition
+
+Next steps:
+ 1. Ensure abcoder is installed and in your PATH:
+ go install github.com/cloudwego/abcoder@latest
+
+ 2. Restart Claude Code to apply the configuration
+
+ 3. Use ABCoder tools in Claude Code:
+ - /abcoder:schedule - Analyze codebase and design solution
+ - /abcoder:task - Create coding task
+ - /abcoder:recheck - Verify solution
+
+For more information, see:
+ - https://github.com/cloudwego/abcoder
+`, targetDir, configPath, astsDir)
+}
diff --git a/main.go b/main.go
index ffab2ea..9148f15 100644
--- a/main.go
+++ b/main.go
@@ -39,6 +39,7 @@ import (
"path/filepath"
"strings"
+ interutils "github.com/cloudwego/abcoder/internal/utils"
"github.com/cloudwego/abcoder/lang"
"github.com/cloudwego/abcoder/lang/log"
"github.com/cloudwego/abcoder/lang/uniast"
@@ -56,6 +57,7 @@ Action:
write write the specific UniAST back to codes
mcp run as a MCP server for all repo ASTs (*.json) in the specific directory
agent run as an Agent for all repo ASTs (*.json) in the specific directory. WIP: only support code-analyzing at present.
+ init-spec initialize ABCoder integration for Claude Code (copies .claude directory and configures MCP servers)
version print the version of abcoder
Language:
go for golang codes
@@ -195,6 +197,29 @@ func main() {
os.Exit(1)
}
+ case "init-spec":
+ // Parse flags only, uri is optional and defaults to current directory
+ flags.Parse(os.Args[2:])
+
+ var uri string
+ if flagHelp != nil && *flagHelp {
+ flags.Usage()
+ os.Exit(0)
+ }
+
+ if flagVerbose != nil && *flagVerbose {
+ log.SetLogLevel(log.DebugLevel)
+ }
+
+ if len(os.Args) > 2 && !strings.HasPrefix(os.Args[2], "-") {
+ uri = os.Args[2]
+ }
+
+ if err := interutils.RunInitSpec(uri); err != nil {
+ log.Error("Failed to init-spec: %v\n", err)
+ os.Exit(1)
+ }
+
case "agent":
_, uri := parseArgsAndFlags(flags, false, flagHelp, flagVerbose)
if uri == "" {