claude-pangu 2.2.8 → 2.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/agents/bianque-high.md +318 -0
- package/agents/build-fixer.md +199 -0
- package/agents/huoshen.md +514 -0
- package/agents/mozi-high.md +245 -0
- package/agents/mozi.md +127 -16
- package/commands/deepwork.md +13 -0
- package/commands/dw.md +13 -0
- package/commands/error.md +1 -1
- package/commands/huoshen.md +192 -0
- package/commands/xuetu.md +185 -22
- package/hooks/keyword-detector.sh +22 -0
- package/hooks/ralph-loop.sh +3 -0
- package/hooks/todo-continuation.sh +3 -0
- package/package.json +1 -1
- package/scripts/install.sh +97 -15
- package/skills/continuous-learning/skill.md +426 -0
- package/skills/deepwork/skill.md +479 -0
package/commands/xuetu.md
CHANGED
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
---
|
|
2
|
+
name: xuetu
|
|
3
|
+
description: |
|
|
4
|
+
学徒命令 - 从经验中学习
|
|
5
|
+
基于持续学习技能,从会话中提取可复用模式。
|
|
6
|
+
别名:/apprentice, /learner, /remember
|
|
7
|
+
arguments:
|
|
8
|
+
- name: action
|
|
9
|
+
description: "操作类型: (空)=分析学习, list=列出, search=搜索, apply=应用, save=保存"
|
|
10
|
+
required: false
|
|
11
|
+
- name: query
|
|
12
|
+
description: "搜索关键词或模式 ID"
|
|
13
|
+
required: false
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
# /xuetu - 学徒命令
|
|
2
17
|
|
|
3
18
|
> 📚 学而时习之,温故而知新
|
|
4
19
|
|
|
@@ -13,49 +28,197 @@
|
|
|
13
28
|
# 指定提取内容
|
|
14
29
|
/xuetu "如何处理认证错误"
|
|
15
30
|
|
|
16
|
-
# 英文命令
|
|
17
|
-
/learner extract "auth error handling"
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
## 管理知识库
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
31
|
# 列出所有已学习的模式
|
|
24
32
|
/xuetu list
|
|
25
33
|
|
|
34
|
+
# 按分类列出
|
|
35
|
+
/xuetu list --category code-patterns
|
|
36
|
+
|
|
26
37
|
# 搜索特定模式
|
|
27
38
|
/xuetu search "认证"
|
|
39
|
+
/xuetu search --tags auth,jwt
|
|
28
40
|
|
|
29
41
|
# 应用已学习的模式
|
|
30
|
-
/xuetu apply
|
|
42
|
+
/xuetu apply jwt-refresh-token
|
|
43
|
+
|
|
44
|
+
# 手动保存模式
|
|
45
|
+
/xuetu save "模式名称" --category code-patterns --tags react,form
|
|
46
|
+
|
|
47
|
+
# 删除过时的学习成果
|
|
48
|
+
/xuetu delete <pattern-id>
|
|
31
49
|
|
|
32
50
|
# 导出知识库
|
|
33
|
-
/xuetu export
|
|
51
|
+
/xuetu export > patterns.json
|
|
34
52
|
|
|
35
53
|
# 导入知识库
|
|
36
|
-
/xuetu import
|
|
54
|
+
/xuetu import patterns.json
|
|
37
55
|
```
|
|
38
56
|
|
|
39
|
-
##
|
|
57
|
+
## 执行流程
|
|
40
58
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
59
|
+
<xuetu>
|
|
60
|
+
## 学徒学习流程
|
|
61
|
+
|
|
62
|
+
### 阶段 1: 会话分析
|
|
63
|
+
|
|
64
|
+
分析当前会话中的:
|
|
65
|
+
- 实现的功能代码
|
|
66
|
+
- 解决的问题
|
|
67
|
+
- 使用的模式
|
|
68
|
+
- 讨论的最佳实践
|
|
69
|
+
|
|
70
|
+
### 阶段 2: 模式识别
|
|
71
|
+
|
|
72
|
+
识别以下信号:
|
|
73
|
+
- [ ] 重复出现的代码结构
|
|
74
|
+
- [ ] 解决了特定问题的方案
|
|
75
|
+
- [ ] 用户肯定的实现方式
|
|
76
|
+
- [ ] 通过验证的代码
|
|
77
|
+
|
|
78
|
+
### 阶段 3: 价值评估
|
|
79
|
+
|
|
80
|
+
对每个候选内容评估:
|
|
81
|
+
| 维度 | 权重 | 说明 |
|
|
82
|
+
|------|------|------|
|
|
83
|
+
| 可复用性 | 40% | 能否在其他场景使用 |
|
|
84
|
+
| 通用性 | 30% | 是否项目无关 |
|
|
85
|
+
| 正确性 | 20% | 是否是正确做法 |
|
|
86
|
+
| 复杂度 | 10% | 是否值得记录 |
|
|
87
|
+
|
|
88
|
+
### 阶段 4: 保存学习成果
|
|
89
|
+
|
|
90
|
+
将有价值的内容保存到 `.claude/learned-patterns/`
|
|
91
|
+
|
|
92
|
+
分类:
|
|
93
|
+
- `code-patterns/` - 代码模式
|
|
94
|
+
- `solutions/` - 问题解决方案
|
|
95
|
+
- `best-practices/` - 最佳实践
|
|
96
|
+
- `project-knowledge/` - 项目知识
|
|
97
|
+
|
|
98
|
+
### 阶段 5: 汇报结果
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
## 📚 学习报告
|
|
102
|
+
|
|
103
|
+
### 本次学习
|
|
104
|
+
从当前会话识别并保存了以下模式:
|
|
105
|
+
|
|
106
|
+
#### 1. [code-pattern] React 表单验证
|
|
107
|
+
- **文件**: `code-patterns/react-form-validation.md`
|
|
108
|
+
- **标签**: react, form, zod
|
|
109
|
+
- **来源**: 实现登录表单
|
|
44
110
|
|
|
45
|
-
|
|
46
|
-
/
|
|
111
|
+
#### 2. [solution] Prisma 连接池配置
|
|
112
|
+
- **文件**: `solutions/prisma-connection-pool.md`
|
|
113
|
+
- **标签**: prisma, database
|
|
114
|
+
- **来源**: 修复连接超时
|
|
47
115
|
|
|
48
|
-
|
|
49
|
-
|
|
116
|
+
### 统计
|
|
117
|
+
- 总学习成果: 42 个
|
|
118
|
+
- 本次新增: 2 个
|
|
50
119
|
```
|
|
120
|
+
</xuetu>
|
|
121
|
+
|
|
122
|
+
## 学习成果格式
|
|
123
|
+
|
|
124
|
+
### 代码模式
|
|
125
|
+
|
|
126
|
+
```markdown
|
|
127
|
+
## 模式:React 表单验证
|
|
128
|
+
|
|
129
|
+
### 触发条件
|
|
130
|
+
- 实现表单验证逻辑
|
|
131
|
+
- 使用 react-hook-form
|
|
132
|
+
|
|
133
|
+
### 模式代码
|
|
134
|
+
\`\`\`typescript
|
|
135
|
+
import { useForm } from 'react-hook-form';
|
|
136
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
137
|
+
import { z } from 'zod';
|
|
138
|
+
|
|
139
|
+
const schema = z.object({
|
|
140
|
+
email: z.string().email('请输入有效邮箱'),
|
|
141
|
+
password: z.string().min(8, '密码至少8位'),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
export function LoginForm() {
|
|
145
|
+
const { register, handleSubmit, formState: { errors } } = useForm({
|
|
146
|
+
resolver: zodResolver(schema),
|
|
147
|
+
});
|
|
148
|
+
// ...
|
|
149
|
+
}
|
|
150
|
+
\`\`\`
|
|
151
|
+
|
|
152
|
+
### 学习来源
|
|
153
|
+
- 会话: 2026-02-03-login
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 解决方案
|
|
157
|
+
|
|
158
|
+
```markdown
|
|
159
|
+
## 解决方案:Next.js Cookies 问题
|
|
160
|
+
|
|
161
|
+
### 问题描述
|
|
162
|
+
在服务端组件中 cookies() 报错
|
|
163
|
+
|
|
164
|
+
### 根本原因
|
|
165
|
+
cookies() 需要在异步上下文中调用
|
|
166
|
+
|
|
167
|
+
### 解决方案
|
|
168
|
+
\`\`\`typescript
|
|
169
|
+
import { cookies } from 'next/headers';
|
|
170
|
+
|
|
171
|
+
export default async function Page() {
|
|
172
|
+
const cookieStore = await cookies();
|
|
173
|
+
const token = cookieStore.get('token');
|
|
174
|
+
}
|
|
175
|
+
\`\`\`
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## 存储位置
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
.claude/
|
|
182
|
+
└── learned-patterns/
|
|
183
|
+
├── index.json # 学习索引
|
|
184
|
+
├── code-patterns/ # 代码模式
|
|
185
|
+
├── solutions/ # 解决方案
|
|
186
|
+
├── best-practices/ # 最佳实践
|
|
187
|
+
└── project-knowledge/ # 项目知识
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## 配置选项
|
|
191
|
+
|
|
192
|
+
在 `.claude/settings.json` 中配置:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"omc": {
|
|
197
|
+
"continuousLearning": {
|
|
198
|
+
"enabled": true,
|
|
199
|
+
"autoLearn": true,
|
|
200
|
+
"storage": "project",
|
|
201
|
+
"minConfidence": 0.7
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## 与其他命令的关系
|
|
208
|
+
|
|
209
|
+
| 命令 | 功能 | 范围 |
|
|
210
|
+
|------|------|------|
|
|
211
|
+
| `/learn` | 项目画像 | 项目结构、技术栈 |
|
|
212
|
+
| `/xuetu` | 个人经验 | 模式、解决方案 |
|
|
213
|
+
| `/snippet` | 代码片段 | 可直接复制的代码 |
|
|
51
214
|
|
|
52
215
|
## 命令别名
|
|
53
216
|
|
|
54
217
|
- `/xuetu` - 学徒(中文)
|
|
55
|
-
- `/
|
|
56
|
-
- `/
|
|
218
|
+
- `/apprentice` - 英文别名
|
|
219
|
+
- `/learner` - 学习者
|
|
57
220
|
- `/remember` - 记住
|
|
58
221
|
|
|
59
222
|
---
|
|
60
223
|
|
|
61
|
-
加载
|
|
224
|
+
加载 continuous-learning 技能以获取详细指南。
|
|
@@ -96,6 +96,28 @@ EOF
|
|
|
96
96
|
exit 0
|
|
97
97
|
fi
|
|
98
98
|
|
|
99
|
+
# ---- 深度工作模式检测 (Deepwork/Hephaestus) ----
|
|
100
|
+
# 特征:目标导向、自主实现、端到端、深度
|
|
101
|
+
deepwork_mode=false
|
|
102
|
+
if echo "$prompt_lower" | grep -qE '(deepwork|huoshen|火神|hephaestus|深度工作)'; then
|
|
103
|
+
deepwork_mode=true
|
|
104
|
+
elif echo "$prompt_lower" | grep -qE '(目标导向|goal[-_]?oriented|自主.{0,5}(实现|完成|开发)|autonomous.{0,5}(implement|develop|complete))' || \
|
|
105
|
+
echo "$prompt_lower" | grep -qE '(端到端|end[-_]?to[-_]?end|e2e).{0,10}(实现|完成|开发|implement|develop|complete)' || \
|
|
106
|
+
echo "$prompt_lower" | grep -qE '(深度.{0,5}(理解|探索|分析)|deep.{0,5}(understand|explore|analyze))' || \
|
|
107
|
+
echo "$prompt_lower" | grep -qE '(给我.{0,5}目标|只需要.{0,5}目标|不用.{0,5}(告诉|说).{0,5}怎么做)' || \
|
|
108
|
+
echo "$prompt_lower" | grep -qE '(你来决定|你自己决定|自己想办法|自己规划)'; then
|
|
109
|
+
deepwork_mode=true
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
if [ "$deepwork_mode" = true ]; then
|
|
113
|
+
cat << 'EOF'
|
|
114
|
+
{
|
|
115
|
+
"systemMessage": "\n\n<deepwork-mode>\n🔥 **火神深度工作模式已激活!**\n\n[目标导向 - 自主探索 - 端到端完成]\n\n## 为什么选择此模式\n检测到需要深度理解和自主完成的任务。\n\n## 核心特性\n\n### 1. 🎯 目标导向\n- 只需提供目标,我来决定实现路径\n- 不需要详细步骤指引\n\n### 2. 🔭 探索优先\n在写任何代码前,并行启动 2-5 个探索任务:\n```\n@wukong - 探索项目结构\n@wukong - 分析代码风格\n@wukong - 搜索相似实现\n@wukong - 识别测试模式\n```\n\n### 3. 🎨 模式匹配\n- 学习项目的编码规范\n- 按照项目风格编写代码\n- 让代码看起来像项目原作者写的\n\n### 4. ✅ 端到端完成\n- 100% 完成才算完成\n- 每个声明都有验证证据\n- 不接受部分完成\n\n## 执行流程\n```\nPhase 0: 目标理解\n ↓\nPhase 1: 并行探索 (2-5 @wukong)\n ↓\nPhase 2: 模式学习 → 项目画像\n ↓\nPhase 3: 自主实现\n ↓\nPhase 4: 验证循环 (测试→修复→测试)\n ↓\nPhase 5: 最终验证 + 证据展示\n```\n\n## 完成标志\n```\n<promise>深度工作完成</promise>\n```\n\n**给我目标,我交付完美。**\n</deepwork-mode>\n"
|
|
116
|
+
}
|
|
117
|
+
EOF
|
|
118
|
+
exit 0
|
|
119
|
+
fi
|
|
120
|
+
|
|
99
121
|
# ---- 蜂群模式检测 (Swarm) ----
|
|
100
122
|
# 特征:批量、所有、N个同类任务
|
|
101
123
|
swarm_mode=false
|
package/hooks/ralph-loop.sh
CHANGED
|
@@ -17,6 +17,8 @@ RALPH_STATE_FILE=".claude/ralph-loop.local.md"
|
|
|
17
17
|
|
|
18
18
|
# 如果状态文件不存在,允许正常退出
|
|
19
19
|
if [ ! -f "$RALPH_STATE_FILE" ]; then
|
|
20
|
+
# Stop hook 需要输出 JSON 响应,即使是允许停止的情况
|
|
21
|
+
echo '{}' 2>/dev/null || true
|
|
20
22
|
exit 0
|
|
21
23
|
fi
|
|
22
24
|
|
|
@@ -34,6 +36,7 @@ COMPLETION_PROMISE=${COMPLETION_PROMISE:-DONE}
|
|
|
34
36
|
if [ "$MAX_ITERATIONS" -gt 0 ] 2>/dev/null && [ "$ITERATION" -ge "$MAX_ITERATIONS" ] 2>/dev/null; then
|
|
35
37
|
echo "🛑 Ralph Loop: 已达最大迭代次数 ($MAX_ITERATIONS)" >&2
|
|
36
38
|
rm -f "$RALPH_STATE_FILE"
|
|
39
|
+
echo '{}' 2>/dev/null || true
|
|
37
40
|
exit 0
|
|
38
41
|
fi
|
|
39
42
|
|
|
@@ -16,6 +16,8 @@ YISHAN_STATE_FILE=".claude/yishan-loop.local.md"
|
|
|
16
16
|
|
|
17
17
|
# 如果状态文件不存在,允许正常退出
|
|
18
18
|
if [ ! -f "$YISHAN_STATE_FILE" ]; then
|
|
19
|
+
# Stop hook 需要输出 JSON 响应,即使是允许停止的情况
|
|
20
|
+
echo '{}' 2>/dev/null || true
|
|
19
21
|
exit 0
|
|
20
22
|
fi
|
|
21
23
|
|
|
@@ -31,6 +33,7 @@ MAX_ITERATIONS=${MAX_ITERATIONS:-50}
|
|
|
31
33
|
if [ "$MAX_ITERATIONS" -gt 0 ] 2>/dev/null && [ "$ITERATION" -ge "$MAX_ITERATIONS" ] 2>/dev/null; then
|
|
32
34
|
echo "🛑 愚公移山: 已达最大迭代次数 ($MAX_ITERATIONS)" >&2
|
|
33
35
|
rm -f "$YISHAN_STATE_FILE"
|
|
36
|
+
echo '{}' 2>/dev/null || true
|
|
34
37
|
exit 0
|
|
35
38
|
fi
|
|
36
39
|
|
package/package.json
CHANGED
package/scripts/install.sh
CHANGED
|
@@ -19,6 +19,21 @@ NC='\033[0m' # No Color
|
|
|
19
19
|
# 配置
|
|
20
20
|
REPO="ZDragon17/oh-my-claude"
|
|
21
21
|
PLUGIN_NAME="oh-my-claude"
|
|
22
|
+
|
|
23
|
+
# Windows 兼容性:确保 HOME 变量正确设置
|
|
24
|
+
if [[ "$(uname -s)" == MINGW* ]] || [[ "$(uname -s)" == MSYS* ]] || [[ "$(uname -s)" == CYGWIN* ]]; then
|
|
25
|
+
# Windows Git Bash 环境
|
|
26
|
+
IS_WINDOWS=true
|
|
27
|
+
# 如果 HOME 未设置或为空,使用 USERPROFILE
|
|
28
|
+
if [ -z "$HOME" ] && [ -n "$USERPROFILE" ]; then
|
|
29
|
+
HOME=$(cygpath -u "$USERPROFILE" 2>/dev/null || echo "$USERPROFILE" | sed 's|\\|/|g' | sed 's|^\([A-Za-z]\):|/\L\1|')
|
|
30
|
+
fi
|
|
31
|
+
# 确保 HOME 是 Unix 风格路径 (/c/Users/xxx)
|
|
32
|
+
HOME=$(cygpath -u "$HOME" 2>/dev/null || echo "$HOME")
|
|
33
|
+
else
|
|
34
|
+
IS_WINDOWS=false
|
|
35
|
+
fi
|
|
36
|
+
|
|
22
37
|
INSTALL_DIR="$HOME/.claude/plugins/$PLUGIN_NAME"
|
|
23
38
|
COMMANDS_DIR="$HOME/.claude/commands"
|
|
24
39
|
SKILLS_DIR="$HOME/.claude/skills"
|
|
@@ -272,10 +287,40 @@ setup_hooks() {
|
|
|
272
287
|
import json
|
|
273
288
|
import sys
|
|
274
289
|
import os
|
|
290
|
+
import platform
|
|
291
|
+
import re
|
|
275
292
|
|
|
276
293
|
settings_file = "$SETTINGS_FILE"
|
|
277
294
|
install_dir = "$INSTALL_DIR"
|
|
278
295
|
|
|
296
|
+
# Windows 兼容性:确保路径格式正确
|
|
297
|
+
# Claude Code 在 Windows 上执行 bash 命令时需要 Unix 风格路径
|
|
298
|
+
def normalize_path_for_hooks(path):
|
|
299
|
+
"""将路径转换为 Git Bash 兼容格式"""
|
|
300
|
+
if platform.system() == 'Windows':
|
|
301
|
+
# 如果是 Windows 原生路径 (C:\Users\...),转换为 /c/Users/...
|
|
302
|
+
if re.match(r'^[A-Za-z]:', path):
|
|
303
|
+
drive = path[0].lower()
|
|
304
|
+
rest = path[2:].replace('\\\\', '/').replace('\\', '/')
|
|
305
|
+
return f'/{drive}{rest}'
|
|
306
|
+
# 如果已经是 Unix 风格但使用了反斜杠
|
|
307
|
+
path = path.replace('\\\\', '/').replace('\\', '/')
|
|
308
|
+
return path
|
|
309
|
+
|
|
310
|
+
install_dir = normalize_path_for_hooks(install_dir)
|
|
311
|
+
|
|
312
|
+
# Windows 上需要使用 bash -c '. script' 格式,否则 stdout 不会被捕获
|
|
313
|
+
is_windows = platform.system() == 'Windows'
|
|
314
|
+
|
|
315
|
+
def make_hook_command(script_path):
|
|
316
|
+
"""生成 hook 命令,Windows 使用特殊格式"""
|
|
317
|
+
if is_windows:
|
|
318
|
+
# Windows: bash -c '. "script.sh"' 格式确保 stdout 正确输出
|
|
319
|
+
return f"bash -c '. \"{script_path}\"'"
|
|
320
|
+
else:
|
|
321
|
+
# macOS/Linux: 标准格式
|
|
322
|
+
return f'bash "{script_path}"'
|
|
323
|
+
|
|
279
324
|
try:
|
|
280
325
|
with open(settings_file, 'r', encoding='utf-8') as f:
|
|
281
326
|
settings = json.load(f)
|
|
@@ -283,30 +328,50 @@ except Exception as e:
|
|
|
283
328
|
print(f"读取 settings.json 失败: {e}", file=sys.stderr)
|
|
284
329
|
sys.exit(1)
|
|
285
330
|
|
|
286
|
-
# 定义核心 hooks
|
|
331
|
+
# 定义核心 hooks(使用绝对路径,不依赖环境变量)
|
|
287
332
|
core_hooks = {
|
|
288
333
|
"PostToolUse": [
|
|
289
334
|
{
|
|
290
|
-
"
|
|
291
|
-
"
|
|
292
|
-
|
|
335
|
+
"matcher": "TodoWrite",
|
|
336
|
+
"hooks": [
|
|
337
|
+
{
|
|
338
|
+
"type": "command",
|
|
339
|
+
"command": make_hook_command(f"{install_dir}/hooks/progress-notifier.sh"),
|
|
340
|
+
"timeout": 3000
|
|
341
|
+
}
|
|
342
|
+
]
|
|
293
343
|
},
|
|
294
344
|
{
|
|
295
|
-
"
|
|
296
|
-
"
|
|
297
|
-
|
|
345
|
+
"matcher": "*",
|
|
346
|
+
"hooks": [
|
|
347
|
+
{
|
|
348
|
+
"type": "command",
|
|
349
|
+
"command": make_hook_command(f"{install_dir}/hooks/context-smart-alert.sh"),
|
|
350
|
+
"timeout": 2000
|
|
351
|
+
}
|
|
352
|
+
]
|
|
298
353
|
}
|
|
299
354
|
],
|
|
300
355
|
"Stop": [
|
|
301
356
|
{
|
|
302
|
-
"
|
|
303
|
-
"
|
|
304
|
-
|
|
357
|
+
"matcher": "*",
|
|
358
|
+
"hooks": [
|
|
359
|
+
{
|
|
360
|
+
"type": "command",
|
|
361
|
+
"command": make_hook_command(f"{install_dir}/hooks/todo-continuation.sh"),
|
|
362
|
+
"timeout": 3000
|
|
363
|
+
}
|
|
364
|
+
]
|
|
305
365
|
},
|
|
306
366
|
{
|
|
307
|
-
"
|
|
308
|
-
"
|
|
309
|
-
|
|
367
|
+
"matcher": "*",
|
|
368
|
+
"hooks": [
|
|
369
|
+
{
|
|
370
|
+
"type": "command",
|
|
371
|
+
"command": make_hook_command(f"{install_dir}/hooks/ralph-loop.sh"),
|
|
372
|
+
"timeout": 3000
|
|
373
|
+
}
|
|
374
|
+
]
|
|
310
375
|
}
|
|
311
376
|
]
|
|
312
377
|
}
|
|
@@ -315,14 +380,31 @@ core_hooks = {
|
|
|
315
380
|
if "hooks" not in settings:
|
|
316
381
|
settings["hooks"] = {}
|
|
317
382
|
|
|
383
|
+
def extract_commands_from_hooks(hooks_list):
|
|
384
|
+
"""从 hooks 列表中提取所有 command"""
|
|
385
|
+
commands = []
|
|
386
|
+
for h in hooks_list:
|
|
387
|
+
if isinstance(h, dict):
|
|
388
|
+
# 新格式: {"matcher": "...", "hooks": [{"command": "..."}]}
|
|
389
|
+
if "hooks" in h:
|
|
390
|
+
for inner_hook in h.get("hooks", []):
|
|
391
|
+
if "command" in inner_hook:
|
|
392
|
+
commands.append(inner_hook["command"])
|
|
393
|
+
# 旧格式: {"type": "command", "command": "..."}
|
|
394
|
+
elif "command" in h:
|
|
395
|
+
commands.append(h["command"])
|
|
396
|
+
return commands
|
|
397
|
+
|
|
318
398
|
for hook_type, hooks in core_hooks.items():
|
|
319
399
|
if hook_type not in settings["hooks"]:
|
|
320
400
|
settings["hooks"][hook_type] = []
|
|
321
401
|
|
|
322
402
|
# 添加新的 hooks(避免重复)
|
|
323
|
-
existing_commands =
|
|
403
|
+
existing_commands = extract_commands_from_hooks(settings["hooks"][hook_type])
|
|
324
404
|
for hook in hooks:
|
|
325
|
-
|
|
405
|
+
# 从新格式中提取 command
|
|
406
|
+
hook_command = hook.get("hooks", [{}])[0].get("command", "")
|
|
407
|
+
if hook_command and hook_command not in existing_commands:
|
|
326
408
|
settings["hooks"][hook_type].append(hook)
|
|
327
409
|
|
|
328
410
|
# 写回文件
|