Skip to content

Commit 0a809a0

Browse files
committed
feat: re-parse if files changed by cc
1 parent b67c7e2 commit 0a809a0

File tree

7 files changed

+288
-128
lines changed

7 files changed

+288
-128
lines changed

.gitignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,5 @@ src/lang/testdata
7777
!ts-parser/**/*.json
7878

7979
tools
80-
abcoder
8180

8281
!testdata/asts/*.json

internal/utils/assets/.claude/README.md

Lines changed: 0 additions & 127 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# ABCoder代码分析工作流
2+
3+
## AST 层次结构
4+
- **模块(Module)**: 仓库中的编译单元,由 `mod_path` 标识;例如: "github.com/cloudwego/kitex"
5+
- **包(Package)**: 符号的命名空间,由 `pkg_path` 标识;例如: "github.com/cloudwego/kitex/pkg/generic"
6+
- **文件(File)**: 代码文件,由 `file_path` 标识;例如: "pkg/generic/closer.go"
7+
- **AST节点**: 语法单元(函数、类型、变量),由 `NodeID` 标识;例如:
8+
```
9+
{
10+
"mod_path": "github.com/cloudwego/kitex",
11+
"pkg_path": "github.com/cloudwego/kitex/pkg/generic",
12+
"name": "Closer"
13+
}
14+
```
15+
16+
## ABCoder工作SOP
17+
1. **问题分析**:
18+
- 基于用户问题分析相关关键词
19+
- 必须使用 `list_repos` 确认repo_name
20+
21+
2. **代码定位** (repo→package→node→ast node relationship):
22+
- 2.1 **定位package**: 基于 `get_repo_structure` 返回的package list选择目标package
23+
- 2.2 **定位文件**: 通过 `get_package_structure` 返回的file信息,确认目标文件
24+
- 2.3 **定位节点**: 通过 `get_files_structure` 返回的node信息,确认目标节点
25+
- 2.4 **确认node详情**: 递归调用 `get_ast_node` 获取node详细(dependencies, references, inheritance, implementation, grouping)
26+
27+
3. **自我反思**:
28+
- 理解完整的code calling-chain、contextual-relationship
29+
- 如果无法清楚解释机制,使用`sequential_thinking` 来帮助分解问题并记录信息,调整选择并重复步骤2
30+
31+
## 注意事项
32+
- 回复应列出相关代码的准确 metadata,包括 AST node(或 package)的 identity、file location 和代码。**必须提供确切的 file location(包括 line numbers)!**
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#!/bin/bash
2+
3+
# 当 Claude Code 执行 Write 操作后,更新 _need_update 文件为 1
4+
5+
# 错误处理
6+
set -euo pipefail
7+
8+
# 获取脚本所在的绝对路径
9+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
11+
# 定义 _need_update 文件路径(放在脚本所在目录)
12+
need_update_file="${script_dir}/_need_update"
13+
14+
# 写入 1 到文件
15+
echo "1" > "$need_update_file"
16+
17+
# 输出 JSON
18+
jq -n --arg file "$need_update_file" '{
19+
"continue": true,
20+
"hookSpecificOutput": {
21+
"hookEventName": "PostToolUse",
22+
"additionalContext": ("Write 操作完成,已更新 " + $file + " 为 1")
23+
}
24+
}'
25+
26+
exit 0
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#!/bin/bash
2+
3+
# 修正并简化的 get_basename 函数
4+
get_basename() {
5+
# 检查输入是否为 "." 或 ".."
6+
if [[ "$1" == "." || "$1" == ".." ]]; then
7+
# 对于当前或上级目录,使用 basename 结合 pwd
8+
basename "$(cd "$1" && pwd)"
9+
else
10+
# 对于其他路径,直接使用 basename
11+
basename "$1"
12+
fi
13+
}
14+
15+
# 新增:检测项目语言并获取仓库标识
16+
detect_project_info() {
17+
local target_dir="$1"
18+
local project_info=""
19+
20+
# 1. 优先检测 Go 项目(判断 go.mod 或 main.go)
21+
if [[ -f "${target_dir}/go.mod" ]]; then
22+
# 从 go.mod 中提取 module name
23+
local module_name=$(grep "^module " "${target_dir}/go.mod" | head -1 | awk '{print $2}')
24+
if [[ -n "$module_name" ]]; then
25+
echo "go|${module_name}"
26+
return 0
27+
fi
28+
# 如果无法获取 module name,使用 main.go 所在目录
29+
if [[ -f "${target_dir}/main.go" ]]; then
30+
echo "go|$(get_basename "$target_dir")"
31+
return 0
32+
fi
33+
fi
34+
35+
# 2. 检测 TypeScript 项目(判断 package.json 或 tsconfig.json)
36+
if [[ -f "${target_dir}/package.json" ]]; then
37+
# 从 package.json 中提取 name
38+
local package_name=$(jq -r '.name // empty' "${target_dir}/package.json" 2>/dev/null)
39+
if [[ -n "$package_name" && "$package_name" != "null" ]]; then
40+
echo "ts|${package_name}"
41+
return 0
42+
fi
43+
fi
44+
45+
# 3. 检测 TypeScript 项目(判断 tsconfig.json 或 .ts/.tsx 文件)
46+
if [[ -f "${target_dir}/tsconfig.json" ]]; then
47+
echo "ts|$(get_basename "$target_dir")"
48+
return 0
49+
fi
50+
51+
# 统计 .ts 和 .tsx 文件数量(排除 node_modules 目录)
52+
local ts_file_count=$(find "${target_dir}" -type f -not -path "*/node_modules/*" \( -name "*.ts" -o -name "*.tsx" \) | wc -l)
53+
if [[ $ts_file_count -gt 0 ]]; then
54+
echo "ts|$(get_basename "$target_dir")"
55+
return 0
56+
fi
57+
58+
# 4. 未检测到目标语言
59+
echo "unknown|$(get_basename "$target_dir")"
60+
return 1
61+
}
62+
63+
# 直接映射 abc 为别名,处理 parse <语言> <仓库路径> 形式的参数
64+
abc() {
65+
# 检查命令格式是否为 "parse <语言> <仓库路径>"
66+
if [ $# -eq 3 ] && [ "$1" = "parse" ]; then
67+
local lang="$2"
68+
local repo_path="$3"
69+
local repo_name=$(get_basename "$repo_path")
70+
71+
# 确保输出目录存在
72+
mkdir -p ~/.asts/
73+
74+
# 执行实际命令
75+
abcoder parse "${lang}" "${repo_path}" -o "~/.asts/${repo_name}.json"
76+
else
77+
# 如果不是预期的 parse 命令格式,直接将参数传递给原始 abcoder 命令
78+
abcoder "$@"
79+
fi
80+
}
81+
82+
# LOG_FILE="/tmp/claude-hook-debug.log"
83+
84+
# 获取脚本所在的绝对路径
85+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
86+
87+
# 定义 _need_update 文件路径
88+
need_update_file="${script_dir}/_need_update"
89+
90+
input=$(cat)
91+
repo_name=$(echo "$input" | jq -r '.tool_input.repo_name // ""')
92+
cwd=$(echo "$input" | jq -r '.cwd // ""')
93+
94+
# echo "=== $(date) ===" >> "$LOG_FILE"
95+
# echo "repo_name: $repo_name" >> "$LOG_FILE"
96+
# echo "cwd: $cwd" >> "$LOG_FILE"
97+
98+
# 读取 _need_update 文件判断是否需要强制更新
99+
force_update=0
100+
if [[ -f "$need_update_file" ]]; then
101+
need_update_value=$(cat "$need_update_file" 2>/dev/null || echo "0")
102+
if [[ "$need_update_value" == "1" ]]; then
103+
force_update=1
104+
# 强制更新后,将 _need_update 重置为 0
105+
echo "0" > "$need_update_file"
106+
fi
107+
fi
108+
109+
# 复用现有的 get_basename 函数
110+
current_base_name=$(get_basename "$cwd")
111+
112+
# 检测项目信息(语言和仓库标识符)
113+
project_info=$(detect_project_info "$cwd")
114+
project_lang=$(echo "$project_info" | cut -d'|' -f1)
115+
project_identifier=$(echo "$project_info" | cut -d'|' -f2)
116+
117+
# 优化判断逻辑:只要检测到有效项目,就执行 parse
118+
if [[ "$project_lang" != "unknown" ]]; then
119+
# 捕获标准输出和错误输出
120+
output_file=$(mktemp)
121+
error_file=$(mktemp)
122+
123+
# 使用检测到的语言执行 parse 命令
124+
# 确保输出目录存在
125+
mkdir -p ~/.asts/
126+
ast_output_file=~/.asts/$(echo "$project_identifier" | sed 's|/|_|g').json
127+
128+
# 检查 AST 文件是否存在且更新时间小于 3 分钟(缓存优化)
129+
# 如果 force_update=1,则跳过缓存检查,强制重新 parse
130+
if [[ $force_update -eq 0 && -f "$ast_output_file" ]]; then
131+
file_age_seconds=$(($(date +%s) - $(stat -f %m "$ast_output_file" 2>/dev/null || stat -c %Y "$ast_output_file" 2>/dev/null)))
132+
if [[ $file_age_seconds -lt 180 ]]; then
133+
jq -n --arg lang "$project_lang" --arg repo "$project_identifier" --arg age "$file_age_seconds" '{
134+
"continue": true,
135+
"systemMessage": ("abcoder AST 缓存命中(语言:" + $lang + ",仓库:" + $repo + ")。文件更新于 " + $age + " 秒前(小于3分钟更新阈值),跳过 parse 操作。")
136+
}'
137+
exit 0
138+
fi
139+
fi
140+
141+
# 使用检测到的语言执行 parse 命令,并输出到 AST 目录
142+
if abcoder parse "$project_lang" . -o "$ast_output_file" >"$output_file" 2>"$error_file"; then
143+
msg_prefix="[定时更新]"
144+
if [[ $force_update -eq 1 ]]; then
145+
msg_prefix="[检测到变更] "
146+
fi
147+
jq -n --arg lang "$project_lang" --arg repo "$project_identifier" --arg prefix "$msg_prefix" '{
148+
"continue": true,
149+
"systemMessage": ($prefix + "abcoder parse 已成功完成(语言:" + $lang + ",仓库:" + $repo + ")。AST文件已生成,可以继续分析代码。")
150+
}'
151+
else
152+
exit_code=$?
153+
# 读取错误信息
154+
error_msg=$(cat "$error_file" | tail -20)
155+
156+
jq -n --arg code "$exit_code" --arg err "$error_msg" --arg lang "$project_lang" --arg repo "$project_identifier" '{
157+
"decision": "block",
158+
"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' 检查"),
159+
"hookSpecificOutput": {
160+
"hookEventName": "PreToolUse",
161+
"additionalContext": "解析失败,需要修复后重试"
162+
}
163+
}'
164+
fi
165+
166+
# 清理临时文件
167+
trash "$output_file" "$error_file" 2>/dev/null || rm -f "$output_file" "$error_file"
168+
else
169+
# 当前目录不是支持的项目,返回空对象
170+
jq -n '{
171+
"decision": "block",
172+
"reason": "当前目录未检测到支持的语言(仅支持 Go 和 TypeScript),请确保项目是 Go 或 TypeScript 类型"
173+
}'
174+
fi
175+
176+
exit 0

0 commit comments

Comments
 (0)