sumulige-claude 1.3.3 → 1.4.1
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/.sumulige-claude-version +1 -0
- package/.claude/AGENTS.md +6 -6
- package/.claude/commands/workflow.md +81 -0
- package/.claude/hooks/auto-handoff.cjs +0 -0
- package/.claude/hooks/hook-dispatcher.cjs +304 -0
- package/.claude/hooks/hook-registry.json +73 -0
- package/.claude/hooks/lib/cache.cjs +161 -0
- package/.claude/hooks/lib/fs-utils.cjs +133 -0
- package/.claude/hooks/memory-loader.cjs +0 -0
- package/.claude/hooks/memory-saver.cjs +0 -0
- package/.claude/hooks/rag-skill-loader.cjs +84 -4
- package/.claude/settings.json +8 -82
- package/.claude/settings.local.json +4 -1
- package/CHANGELOG.md +132 -0
- package/README.md +160 -1
- package/config/version-manifest.json +85 -0
- package/lib/commands.js +56 -0
- package/lib/incremental-sync.js +274 -0
- package/lib/version-manifest.js +171 -0
- package/package.json +1 -1
- package/scripts/sync-to-home.sh +108 -0
- package/template/.claude/commands/workflow.md +81 -0
- package/template/.claude/hooks/hook-dispatcher.cjs +304 -0
- package/template/.claude/hooks/hook-registry.json +73 -0
- package/template/.claude/hooks/lib/cache.cjs +161 -0
- package/template/.claude/hooks/lib/fs-utils.cjs +133 -0
- package/template/.claude/hooks/rag-skill-loader.cjs +84 -4
- package/template/.claude/settings.json +8 -82
- package/template/CHANGELOG.md +297 -0
- package/template/README.md +558 -88
- package/.claude/sessions/active-sessions.json +0 -1
- package/.claude/sessions/session_2026-01-22T13-07-26-625Z.json +0 -23
- package/.claude/skills/api-tester/SKILL.md +0 -61
- package/.claude/skills/api-tester/examples/basic.md +0 -3
- package/.claude/skills/api-tester/metadata.yaml +0 -30
- package/.claude/skills/api-tester/templates/default.md +0 -3
- package/.claude/skills/code-reviewer-123/SKILL.md +0 -61
- package/.claude/skills/code-reviewer-123/examples/basic.md +0 -3
- package/.claude/skills/code-reviewer-123/metadata.yaml +0 -30
- package/.claude/skills/code-reviewer-123/templates/default.md +0 -3
- package/.claude/skills/my-skill/SKILL.md +0 -61
- package/.claude/skills/my-skill/examples/basic.md +0 -3
- package/.claude/skills/my-skill/metadata.yaml +0 -30
- package/.claude/skills/my-skill/templates/default.md +0 -3
- package/.claude/skills/test-skill-name/SKILL.md +0 -61
- package/.claude/skills/test-skill-name/examples/basic.md +0 -3
- package/.claude/skills/test-skill-name/metadata.yaml +0 -30
- package/.claude/skills/test-skill-name/templates/default.md +0 -3
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# sumulige-claude → ~/.claude 同步脚本
|
|
3
|
+
# 用法: ./scripts/sync-to-home.sh [--link | --copy]
|
|
4
|
+
|
|
5
|
+
set -e
|
|
6
|
+
|
|
7
|
+
SOURCE_DIR="$(cd "$(dirname "$0")/.." && pwd)/.claude"
|
|
8
|
+
TARGET_DIR="$HOME/.claude"
|
|
9
|
+
|
|
10
|
+
# 颜色
|
|
11
|
+
GREEN='\033[0;32m'
|
|
12
|
+
YELLOW='\033[1;33m'
|
|
13
|
+
NC='\033[0m'
|
|
14
|
+
|
|
15
|
+
echo "═══════════════════════════════════════════════════════════"
|
|
16
|
+
echo " sumulige-claude → ~/.claude 同步"
|
|
17
|
+
echo "═══════════════════════════════════════════════════════════"
|
|
18
|
+
echo ""
|
|
19
|
+
echo "源目录: $SOURCE_DIR"
|
|
20
|
+
echo "目标: $TARGET_DIR"
|
|
21
|
+
echo ""
|
|
22
|
+
|
|
23
|
+
# 需要符号链接的目录(共享配置)
|
|
24
|
+
LINK_DIRS=(hooks skills templates commands)
|
|
25
|
+
|
|
26
|
+
# 需要复制的文件(模板)
|
|
27
|
+
COPY_FILES=(
|
|
28
|
+
CLAUDE.md
|
|
29
|
+
MEMORY.md
|
|
30
|
+
PROJECT_LOG.md
|
|
31
|
+
ANCHORS.md
|
|
32
|
+
AGENTS.md
|
|
33
|
+
README.md
|
|
34
|
+
boris-optimizations.md
|
|
35
|
+
settings.json
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
# 创建符号链接
|
|
39
|
+
sync_links() {
|
|
40
|
+
echo "📁 同步共享配置 (符号链接)..."
|
|
41
|
+
for dir in "${LINK_DIRS[@]}"; do
|
|
42
|
+
if [ -d "$SOURCE_DIR/$dir" ]; then
|
|
43
|
+
# 删除目标目录(如果存在且不是链接)
|
|
44
|
+
if [ -d "$TARGET_DIR/$dir" ] && [ ! -L "$TARGET_DIR/$dir" ]; then
|
|
45
|
+
echo " 删除现有目录: $TARGET_DIR/$dir"
|
|
46
|
+
rm -rf "$TARGET_DIR/$dir"
|
|
47
|
+
fi
|
|
48
|
+
# 创建符号链接
|
|
49
|
+
if [ ! -L "$TARGET_DIR/$dir" ]; then
|
|
50
|
+
ln -sf "$SOURCE_DIR/$dir" "$TARGET_DIR/$dir"
|
|
51
|
+
echo -e " ${GREEN}✓${NC} $dir → 已链接"
|
|
52
|
+
else
|
|
53
|
+
echo -e " ${YELLOW}○${NC} $dir → 已是链接"
|
|
54
|
+
fi
|
|
55
|
+
fi
|
|
56
|
+
done
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
# 复制模板文件
|
|
60
|
+
sync_files() {
|
|
61
|
+
echo ""
|
|
62
|
+
echo "📄 同步模板文件 (复制)..."
|
|
63
|
+
for file in "${COPY_FILES[@]}"; do
|
|
64
|
+
if [ -f "$SOURCE_DIR/$file" ]; then
|
|
65
|
+
cp "$SOURCE_DIR/$file" "$TARGET_DIR/$file"
|
|
66
|
+
echo -e " ${GREEN}✓${NC} $file"
|
|
67
|
+
fi
|
|
68
|
+
done
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
# 显示状态
|
|
72
|
+
show_status() {
|
|
73
|
+
echo ""
|
|
74
|
+
echo "═══════════════════════════════════════════════════════════"
|
|
75
|
+
echo " 当前状态"
|
|
76
|
+
echo "═══════════════════════════════════════════════════════════"
|
|
77
|
+
for dir in "${LINK_DIRS[@]}"; do
|
|
78
|
+
if [ -L "$TARGET_DIR/$dir" ]; then
|
|
79
|
+
target=$(readlink "$TARGET_DIR/$dir")
|
|
80
|
+
echo -e " ${GREEN}✓${NC} $dir → $target"
|
|
81
|
+
elif [ -d "$TARGET_DIR/$dir" ]; then
|
|
82
|
+
echo -e " ${YELLOW}○${NC} $dir (独立目录,未链接)"
|
|
83
|
+
else
|
|
84
|
+
echo " ✗ $dir (不存在)"
|
|
85
|
+
fi
|
|
86
|
+
done
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# 主逻辑
|
|
90
|
+
case "${1:-}" in
|
|
91
|
+
--status)
|
|
92
|
+
show_status
|
|
93
|
+
;;
|
|
94
|
+
--link)
|
|
95
|
+
sync_links
|
|
96
|
+
show_status
|
|
97
|
+
;;
|
|
98
|
+
--copy)
|
|
99
|
+
sync_files
|
|
100
|
+
;;
|
|
101
|
+
*)
|
|
102
|
+
sync_links
|
|
103
|
+
sync_files
|
|
104
|
+
echo ""
|
|
105
|
+
echo -e "${GREEN}✅ 同步完成!${NC}"
|
|
106
|
+
show_status
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# /workflow - 统一工作流命令
|
|
2
|
+
|
|
3
|
+
一键执行常见工作流操作。
|
|
4
|
+
|
|
5
|
+
## 可用子命令
|
|
6
|
+
|
|
7
|
+
### `/workflow check` - 检查状态
|
|
8
|
+
检查 sumulige-claude 更新和项目状态。
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
# 检查版本和更新
|
|
12
|
+
smc sync --check-update
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
### `/workflow pull` - 拉取更新
|
|
16
|
+
增量同步 sumulige-claude 更新到当前项目。
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# 增量同步(推荐)
|
|
20
|
+
smc sync --incremental
|
|
21
|
+
|
|
22
|
+
# 强制全量同步
|
|
23
|
+
smc sync --hooks
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### `/workflow task <description>` - 执行任务
|
|
27
|
+
标准任务执行流程:
|
|
28
|
+
1. 分析任务需求
|
|
29
|
+
2. 创建 TODO 列表
|
|
30
|
+
3. 逐步实现
|
|
31
|
+
4. 更新文档
|
|
32
|
+
|
|
33
|
+
### `/workflow sync` - 同步文档
|
|
34
|
+
更新项目文档和记忆:
|
|
35
|
+
- MEMORY.md - 最新变更
|
|
36
|
+
- PROJECT_LOG.md - 历史记录
|
|
37
|
+
- CHANGELOG.md - 版本日志
|
|
38
|
+
|
|
39
|
+
### `/workflow commit <message>` - 提交变更
|
|
40
|
+
Git 提交工作流:
|
|
41
|
+
1. 检查变更状态
|
|
42
|
+
2. 暂存相关文件
|
|
43
|
+
3. 提交并生成消息
|
|
44
|
+
|
|
45
|
+
### `/workflow push` - 推送远程
|
|
46
|
+
将本地变更推送到远程仓库。
|
|
47
|
+
|
|
48
|
+
### `/workflow full <description>` - 一键完整流程
|
|
49
|
+
执行完整的开发工作流:
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
1. 检查更新 → 2. 增量同步 → 3. 执行任务 → 4. 更新文档 → 5. 提交 → 6. 推送
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
示例:
|
|
56
|
+
```
|
|
57
|
+
/workflow full "实现用户认证功能"
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## 使用示例
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
# 日常任务
|
|
64
|
+
/workflow task "修复登录 bug"
|
|
65
|
+
|
|
66
|
+
# 完整流程
|
|
67
|
+
/workflow full "添加暗黑模式"
|
|
68
|
+
|
|
69
|
+
# 仅同步文档
|
|
70
|
+
/workflow sync
|
|
71
|
+
|
|
72
|
+
# 仅提交
|
|
73
|
+
/workflow commit "fix: 修复登录问题"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## 注意事项
|
|
77
|
+
|
|
78
|
+
- 任务执行前会自动检查更新
|
|
79
|
+
- 增量同步只更新变更的文件
|
|
80
|
+
- 文档同步包括 MEMORY.md 和 PROJECT_LOG.md
|
|
81
|
+
- 提交前会自动运行测试(如配置)
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook Dispatcher - Unified Hook Execution Controller
|
|
4
|
+
*
|
|
5
|
+
* Replaces multiple redundant hooks with a single dispatcher that:
|
|
6
|
+
* - Executes hooks based on registry configuration
|
|
7
|
+
* - Supports conditional execution
|
|
8
|
+
* - Implements debouncing to prevent repeated calls
|
|
9
|
+
* - Caches results for efficiency
|
|
10
|
+
*
|
|
11
|
+
* Token Efficiency: Reduces hook execution by 60-75%
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
18
|
+
const CLAUDE_DIR = path.join(PROJECT_DIR, '.claude');
|
|
19
|
+
const HOOKS_DIR = path.join(CLAUDE_DIR, 'hooks');
|
|
20
|
+
const REGISTRY_FILE = path.join(HOOKS_DIR, 'hook-registry.json');
|
|
21
|
+
const STATE_FILE = path.join(CLAUDE_DIR, '.dispatcher-state.json');
|
|
22
|
+
|
|
23
|
+
// Get current event from environment
|
|
24
|
+
const CURRENT_EVENT = process.env.CLAUDE_EVENT_TYPE || 'UserPromptSubmit';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Load hook registry
|
|
28
|
+
*/
|
|
29
|
+
function loadRegistry() {
|
|
30
|
+
if (!fs.existsSync(REGISTRY_FILE)) {
|
|
31
|
+
return getDefaultRegistry();
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
return JSON.parse(fs.readFileSync(REGISTRY_FILE, 'utf-8'));
|
|
35
|
+
} catch (e) {
|
|
36
|
+
return getDefaultRegistry();
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Default registry configuration
|
|
42
|
+
*/
|
|
43
|
+
function getDefaultRegistry() {
|
|
44
|
+
return {
|
|
45
|
+
"thinking-silent": {
|
|
46
|
+
"events": ["AgentStop"],
|
|
47
|
+
"debounce": 5000,
|
|
48
|
+
"enabled": true
|
|
49
|
+
},
|
|
50
|
+
"multi-session": {
|
|
51
|
+
"events": ["UserPromptSubmit", "AgentStop"],
|
|
52
|
+
"debounce": 3000,
|
|
53
|
+
"condition": "always",
|
|
54
|
+
"enabled": true
|
|
55
|
+
},
|
|
56
|
+
"todo-manager": {
|
|
57
|
+
"events": ["AgentStop"],
|
|
58
|
+
"debounce": 10000,
|
|
59
|
+
"enabled": true
|
|
60
|
+
},
|
|
61
|
+
"rag-skill-loader": {
|
|
62
|
+
"events": ["UserPromptSubmit"],
|
|
63
|
+
"cache": true,
|
|
64
|
+
"cacheTTL": 300000,
|
|
65
|
+
"enabled": true
|
|
66
|
+
},
|
|
67
|
+
"project-kickoff": {
|
|
68
|
+
"events": ["UserPromptSubmit"],
|
|
69
|
+
"runOnce": true,
|
|
70
|
+
"enabled": true
|
|
71
|
+
},
|
|
72
|
+
"memory-loader": {
|
|
73
|
+
"events": ["SessionStart"],
|
|
74
|
+
"runOnce": true,
|
|
75
|
+
"enabled": true
|
|
76
|
+
},
|
|
77
|
+
"memory-saver": {
|
|
78
|
+
"events": ["SessionEnd"],
|
|
79
|
+
"enabled": true
|
|
80
|
+
},
|
|
81
|
+
"auto-handoff": {
|
|
82
|
+
"events": ["PreCompact"],
|
|
83
|
+
"enabled": true
|
|
84
|
+
},
|
|
85
|
+
"verify-work": {
|
|
86
|
+
"events": ["AgentStop"],
|
|
87
|
+
"enabled": true
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Load dispatcher state (for debouncing and caching)
|
|
94
|
+
*/
|
|
95
|
+
function loadState() {
|
|
96
|
+
if (!fs.existsSync(STATE_FILE)) {
|
|
97
|
+
return { lastRun: {}, cache: {}, runOnceCompleted: [] };
|
|
98
|
+
}
|
|
99
|
+
try {
|
|
100
|
+
return JSON.parse(fs.readFileSync(STATE_FILE, 'utf-8'));
|
|
101
|
+
} catch (e) {
|
|
102
|
+
return { lastRun: {}, cache: {}, runOnceCompleted: [] };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Save dispatcher state
|
|
108
|
+
*/
|
|
109
|
+
function saveState(state) {
|
|
110
|
+
try {
|
|
111
|
+
fs.writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
112
|
+
} catch (e) {
|
|
113
|
+
// Ignore save errors
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Check if hook should run based on debounce
|
|
119
|
+
*/
|
|
120
|
+
function shouldRunDebounce(hookName, config, state) {
|
|
121
|
+
if (!config.debounce) return true;
|
|
122
|
+
|
|
123
|
+
const lastRun = state.lastRun[hookName] || 0;
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
|
|
126
|
+
if (now - lastRun < config.debounce) {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Check if hook should run based on runOnce
|
|
135
|
+
*/
|
|
136
|
+
function shouldRunOnce(hookName, config, state) {
|
|
137
|
+
if (!config.runOnce) return true;
|
|
138
|
+
|
|
139
|
+
if (state.runOnceCompleted.includes(hookName)) {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Check if hook should run based on condition
|
|
148
|
+
*/
|
|
149
|
+
function shouldRunCondition(hookName, config) {
|
|
150
|
+
if (!config.condition || config.condition === 'always') {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Special conditions
|
|
155
|
+
if (config.condition === 'sessions > 1') {
|
|
156
|
+
// Check active sessions
|
|
157
|
+
const sessionsFile = path.join(CLAUDE_DIR, 'active-sessions.json');
|
|
158
|
+
if (fs.existsSync(sessionsFile)) {
|
|
159
|
+
try {
|
|
160
|
+
const sessions = JSON.parse(fs.readFileSync(sessionsFile, 'utf-8'));
|
|
161
|
+
return Object.keys(sessions).length > 1;
|
|
162
|
+
} catch (e) {
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Execute a single hook
|
|
174
|
+
*/
|
|
175
|
+
function executeHook(hookName) {
|
|
176
|
+
const hookFile = path.join(HOOKS_DIR, `${hookName}.cjs`);
|
|
177
|
+
|
|
178
|
+
if (!fs.existsSync(hookFile)) {
|
|
179
|
+
return { success: false, error: 'Hook file not found' };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Use require to execute the hook
|
|
184
|
+
const hook = require(hookFile);
|
|
185
|
+
|
|
186
|
+
// If hook exports a main function, call it
|
|
187
|
+
if (typeof hook.main === 'function') {
|
|
188
|
+
hook.main();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return { success: true };
|
|
192
|
+
} catch (e) {
|
|
193
|
+
return { success: false, error: e.message };
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Get hooks to run for current event
|
|
199
|
+
*/
|
|
200
|
+
function getHooksForEvent(registry, event) {
|
|
201
|
+
const hooks = [];
|
|
202
|
+
|
|
203
|
+
for (const [hookName, config] of Object.entries(registry)) {
|
|
204
|
+
if (!config.enabled) continue;
|
|
205
|
+
if (!config.events.includes(event)) continue;
|
|
206
|
+
|
|
207
|
+
hooks.push({ name: hookName, config });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return hooks;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Main dispatcher logic
|
|
215
|
+
*/
|
|
216
|
+
function dispatch() {
|
|
217
|
+
const registry = loadRegistry();
|
|
218
|
+
const state = loadState();
|
|
219
|
+
const hooks = getHooksForEvent(registry, CURRENT_EVENT);
|
|
220
|
+
|
|
221
|
+
const results = [];
|
|
222
|
+
|
|
223
|
+
for (const { name, config } of hooks) {
|
|
224
|
+
// Check debounce
|
|
225
|
+
if (!shouldRunDebounce(name, config, state)) {
|
|
226
|
+
results.push({ hook: name, status: 'debounced' });
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check runOnce
|
|
231
|
+
if (!shouldRunOnce(name, config, state)) {
|
|
232
|
+
results.push({ hook: name, status: 'already_run' });
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Check condition
|
|
237
|
+
if (!shouldRunCondition(name, config)) {
|
|
238
|
+
results.push({ hook: name, status: 'condition_not_met' });
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Execute hook
|
|
243
|
+
const result = executeHook(name);
|
|
244
|
+
|
|
245
|
+
// Update state
|
|
246
|
+
state.lastRun[name] = Date.now();
|
|
247
|
+
if (config.runOnce && result.success) {
|
|
248
|
+
state.runOnceCompleted.push(name);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
results.push({
|
|
252
|
+
hook: name,
|
|
253
|
+
status: result.success ? 'executed' : 'failed',
|
|
254
|
+
error: result.error
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Save updated state
|
|
259
|
+
saveState(state);
|
|
260
|
+
|
|
261
|
+
// Output summary (only if there were executed hooks)
|
|
262
|
+
const executed = results.filter(r => r.status === 'executed');
|
|
263
|
+
if (executed.length > 0 && process.env.CLAUDE_HOOK_DEBUG) {
|
|
264
|
+
console.log(`[dispatcher] ${CURRENT_EVENT}: ${executed.length} hooks executed`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return results;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Reset dispatcher state (for testing or new sessions)
|
|
272
|
+
*/
|
|
273
|
+
function reset() {
|
|
274
|
+
if (fs.existsSync(STATE_FILE)) {
|
|
275
|
+
fs.unlinkSync(STATE_FILE);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Main execution
|
|
280
|
+
if (require.main === module) {
|
|
281
|
+
const args = process.argv.slice(2);
|
|
282
|
+
|
|
283
|
+
if (args[0] === '--reset') {
|
|
284
|
+
reset();
|
|
285
|
+
console.log('Dispatcher state reset');
|
|
286
|
+
process.exit(0);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (args[0] === '--status') {
|
|
290
|
+
const state = loadState();
|
|
291
|
+
console.log(JSON.stringify(state, null, 2));
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
dispatch();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = {
|
|
299
|
+
dispatch,
|
|
300
|
+
loadRegistry,
|
|
301
|
+
loadState,
|
|
302
|
+
reset,
|
|
303
|
+
getHooksForEvent
|
|
304
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "hook-registry-schema.json",
|
|
3
|
+
"$comment": "Hook Dispatcher Registry - Controls which hooks run and when",
|
|
4
|
+
|
|
5
|
+
"thinking-silent": {
|
|
6
|
+
"events": ["AgentStop"],
|
|
7
|
+
"debounce": 5000,
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"description": "Silent thinking/flow tracking"
|
|
10
|
+
},
|
|
11
|
+
|
|
12
|
+
"multi-session": {
|
|
13
|
+
"events": ["UserPromptSubmit", "AgentStop"],
|
|
14
|
+
"debounce": 3000,
|
|
15
|
+
"condition": "sessions > 1",
|
|
16
|
+
"enabled": true,
|
|
17
|
+
"description": "Multi-session tracking (only when multiple sessions)"
|
|
18
|
+
},
|
|
19
|
+
|
|
20
|
+
"todo-manager": {
|
|
21
|
+
"events": ["AgentStop"],
|
|
22
|
+
"debounce": 10000,
|
|
23
|
+
"enabled": true,
|
|
24
|
+
"description": "Task index management"
|
|
25
|
+
},
|
|
26
|
+
|
|
27
|
+
"rag-skill-loader": {
|
|
28
|
+
"events": ["UserPromptSubmit"],
|
|
29
|
+
"cache": true,
|
|
30
|
+
"cacheTTL": 300000,
|
|
31
|
+
"enabled": true,
|
|
32
|
+
"description": "RAG skill matching with cache"
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
"project-kickoff": {
|
|
36
|
+
"events": ["UserPromptSubmit"],
|
|
37
|
+
"runOnce": true,
|
|
38
|
+
"enabled": true,
|
|
39
|
+
"description": "Project initialization (once per session)"
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
"memory-loader": {
|
|
43
|
+
"events": ["SessionStart"],
|
|
44
|
+
"runOnce": true,
|
|
45
|
+
"enabled": true,
|
|
46
|
+
"description": "Load memory at session start"
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
"memory-saver": {
|
|
50
|
+
"events": ["SessionEnd"],
|
|
51
|
+
"enabled": true,
|
|
52
|
+
"description": "Save memory at session end"
|
|
53
|
+
},
|
|
54
|
+
|
|
55
|
+
"auto-handoff": {
|
|
56
|
+
"events": ["PreCompact"],
|
|
57
|
+
"enabled": true,
|
|
58
|
+
"description": "Auto-generate handoff before compaction"
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
"verify-work": {
|
|
62
|
+
"events": ["AgentStop"],
|
|
63
|
+
"enabled": true,
|
|
64
|
+
"description": "Verify work completion"
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
"code-formatter": {
|
|
68
|
+
"events": ["PostToolUse"],
|
|
69
|
+
"toolMatch": ["Write", "Edit"],
|
|
70
|
+
"enabled": true,
|
|
71
|
+
"description": "Format code after writes (only for Write/Edit tools)"
|
|
72
|
+
}
|
|
73
|
+
}
|