sumulige-claude 1.1.2 → 1.2.0
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/hooks/code-formatter.cjs +7 -2
- package/.claude/hooks/multi-session.cjs +9 -3
- package/.claude/hooks/pre-commit.cjs +0 -0
- package/.claude/hooks/pre-push.cjs +0 -0
- package/.claude/hooks/project-kickoff.cjs +22 -11
- package/.claude/hooks/rag-skill-loader.cjs +7 -0
- package/.claude/hooks/thinking-silent.cjs +9 -3
- package/.claude/hooks/todo-manager.cjs +19 -13
- package/.claude/hooks/verify-work.cjs +10 -4
- package/.claude/quality-gate.json +9 -3
- package/.claude/settings.local.json +16 -1
- package/.claude/templates/hooks/README.md +302 -0
- package/.claude/templates/hooks/hook.sh.template +94 -0
- package/.claude/templates/hooks/user-prompt-submit.cjs.template +116 -0
- package/.claude/templates/hooks/user-response-submit.cjs.template +94 -0
- package/.claude/templates/hooks/validate.js +173 -0
- package/.claude/workflow/document-scanner.js +426 -0
- package/.claude/workflow/knowledge-engine.js +941 -0
- package/.claude/workflow/notebooklm/browser.js +1028 -0
- package/.claude/workflow/phases/phase1-research.js +578 -0
- package/.claude/workflow/phases/phase1-research.ts +465 -0
- package/.claude/workflow/phases/phase2-approve.js +722 -0
- package/.claude/workflow/phases/phase3-plan.js +1200 -0
- package/.claude/workflow/phases/phase4-develop.js +894 -0
- package/.claude/workflow/search-cache.js +230 -0
- package/.claude/workflow/templates/approval.md +315 -0
- package/.claude/workflow/templates/development.md +377 -0
- package/.claude/workflow/templates/planning.md +328 -0
- package/.claude/workflow/templates/research.md +250 -0
- package/.claude/workflow/types.js +37 -0
- package/.claude/workflow/web-search.js +278 -0
- package/.claude-plugin/marketplace.json +2 -2
- package/AGENTS.md +176 -0
- package/CHANGELOG.md +7 -14
- package/cli.js +20 -0
- package/config/quality-gate.json +9 -3
- package/development/cache/web-search/search_1193d605f8eb364651fc2f2041b58a31.json +36 -0
- package/development/cache/web-search/search_3798bf06960edc125f744a1abb5b72c5.json +36 -0
- package/development/cache/web-search/search_37c7d4843a53f0d83f1122a6f908a2a3.json +36 -0
- package/development/cache/web-search/search_44166fa0153709ee168485a22aa0ab40.json +36 -0
- package/development/cache/web-search/search_4deaebb1f77e86a8ca066dc5a49c59fd.json +36 -0
- package/development/cache/web-search/search_94da91789466070a7f545612e73c7372.json +36 -0
- package/development/cache/web-search/search_dd5de8491b8b803a3cb01339cd210fb0.json +36 -0
- package/development/knowledge-base/.index.clean.json +0 -0
- package/development/knowledge-base/.index.json +486 -0
- package/development/knowledge-base/test-best-practices.md +29 -0
- package/development/projects/proj_mkh1pazz_ixmt1/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jvnb_z7rwf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4jxkd_ewz5a/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4k84n_ni73k/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wfyd_u9w88/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/development/projects/proj_mkh4xbpg_4na5w/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4wsbo_iahvf/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xulg_1ka8x/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4xwhj_gch8j/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/DEVELOPMENT_LOG.md +35 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/TASKS.md +34 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/.env.example +5 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/README.md +60 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/package.json +25 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/index.js +70 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/src/routes/index.js +48 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/health.test.js +20 -0
- package/development/projects/proj_mkh4y2qk_9lm8z/phase4/source/tests/jest.config.js +21 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase2/requirements.md +226 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/PRD.md +345 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/TASK_PLAN.md +284 -0
- package/development/projects/proj_mkh7veqg_3lypc/phase3/prototype/README.md +14 -0
- package/development/projects/proj_mkh8k8fo_rmqn5/phase1/feasibility-report.md +160 -0
- package/development/projects/proj_mkh8xyhy_1vshq/phase1/feasibility-report.md +178 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase1/feasibility-report.md +377 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase2/requirements.md +442 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/api-design.md +800 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/architecture.md +625 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/data-model.md +830 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/risks.md +957 -0
- package/development/projects/proj_mkh8zddd_dhamf/phase3/wbs.md +381 -0
- package/development/todos/.state.json +14 -1
- package/development/todos/INDEX.md +31 -73
- package/development/todos/completed/develop/local-knowledge-index.md +85 -0
- package/development/todos/{active → completed/develop}/todo-system.md +13 -3
- package/development/todos/completed/develop/web-search-integration.md +83 -0
- package/development/todos/completed/test/phase1-e2e-test.md +103 -0
- package/lib/commands.js +388 -0
- package/package.json +3 -2
- package/tests/config-manager.test.js +677 -0
- package/tests/config-validator.test.js +436 -0
- package/tests/errors.test.js +477 -0
- package/tests/manual/phase1-e2e.sh +389 -0
- package/tests/manual/phase2-test-cases.md +311 -0
- package/tests/manual/phase3-test-cases.md +309 -0
- package/tests/manual/phase4-test-cases.md +414 -0
- package/tests/manual/test-cases.md +417 -0
- package/tests/quality-gate.test.js +679 -0
- package/tests/quality-rules.test.js +619 -0
- package/tests/version-check.test.js +75 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# [Hook Name] - Shell Hook Template
|
|
4
|
+
#
|
|
5
|
+
# 触发时机: [说明触发时机]
|
|
6
|
+
# 环境变量:
|
|
7
|
+
# - CLAUDE_PROJECT_DIR: 项目根目录
|
|
8
|
+
#
|
|
9
|
+
# @version 1.0.0
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
set -euo pipefail # 严格模式
|
|
13
|
+
|
|
14
|
+
# ============================================================
|
|
15
|
+
# 标准头部 - 所有 hooks 必须包含
|
|
16
|
+
# ============================================================
|
|
17
|
+
|
|
18
|
+
# 获取项目目录,提供 fallback
|
|
19
|
+
CLAUDE_PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
|
20
|
+
|
|
21
|
+
# 如果不在项目环境中,静默退出
|
|
22
|
+
[[ -z "${CLAUDE_PROJECT_DIR:-}" ]] && exit 0
|
|
23
|
+
|
|
24
|
+
# ============================================================
|
|
25
|
+
# 配置区域
|
|
26
|
+
# ============================================================
|
|
27
|
+
|
|
28
|
+
VERBOSE="${VERBOSE:-false}"
|
|
29
|
+
HOOKS_DIR="$CLAUDE_PROJECT_DIR/.claude/hooks"
|
|
30
|
+
DATA_DIR="$HOOKS_DIR/.data"
|
|
31
|
+
|
|
32
|
+
# ============================================================
|
|
33
|
+
# 工具函数
|
|
34
|
+
// ============================================================
|
|
35
|
+
|
|
36
|
+
# 安全日志
|
|
37
|
+
log() {
|
|
38
|
+
if [[ "$VERBOSE" == "true" ]]; then
|
|
39
|
+
echo "[Hook] $*" >&2
|
|
40
|
+
fi
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# 确保目录存在
|
|
44
|
+
ensure_dir() {
|
|
45
|
+
local dir="$1"
|
|
46
|
+
mkdir -p "$dir" 2>/dev/null || true
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# 安全读取文件
|
|
50
|
+
safe_read() {
|
|
51
|
+
local file="$1"
|
|
52
|
+
local default="${2:-}"
|
|
53
|
+
if [[ -f "$file" ]]; then
|
|
54
|
+
cat "$file" 2>/dev/null || echo "$default"
|
|
55
|
+
else
|
|
56
|
+
echo "$default"
|
|
57
|
+
fi
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
# 安全写入文件
|
|
61
|
+
safe_write() {
|
|
62
|
+
local file="$1"
|
|
63
|
+
local content="$2"
|
|
64
|
+
ensure_dir "$(dirname "$file")"
|
|
65
|
+
echo "$content" > "$file" 2>/dev/null || true
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
# ============================================================
|
|
69
|
+
# Hook 核心逻辑
|
|
70
|
+
# ============================================================
|
|
71
|
+
|
|
72
|
+
main() {
|
|
73
|
+
# 错误处理:任何错误都静默处理,不影响 Claude Code
|
|
74
|
+
{
|
|
75
|
+
|
|
76
|
+
log "Project: $CLAUDE_PROJECT_DIR"
|
|
77
|
+
|
|
78
|
+
# ===== 在这里实现你的 hook 逻辑 =====
|
|
79
|
+
|
|
80
|
+
# 示例: 记录执行时间
|
|
81
|
+
local timestamp_file="$DATA_DIR/last-run.txt"
|
|
82
|
+
safe_write "$timestamp_file" "$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
83
|
+
|
|
84
|
+
# ===== Hook 逻辑结束 =====
|
|
85
|
+
|
|
86
|
+
} || true
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
# ============================================================
|
|
90
|
+
# 执行
|
|
91
|
+
# ============================================================
|
|
92
|
+
|
|
93
|
+
main
|
|
94
|
+
exit 0
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* [Hook Name] - UserPromptSubmit Hook Template
|
|
4
|
+
*
|
|
5
|
+
* 触发时机: 用户每次提交提示时
|
|
6
|
+
* 环境变量:
|
|
7
|
+
* - CLAUDE_PROJECT_DIR: 项目根目录
|
|
8
|
+
* - CLAUDE_TOOL_NAME: 当前调用的工具名
|
|
9
|
+
* - CLAUDE_TOOL_INPUT: 工具输入内容
|
|
10
|
+
*
|
|
11
|
+
* @version 1.0.0
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
|
|
17
|
+
// ============================================================
|
|
18
|
+
// 标准头部 - 所有 hooks 必须包含
|
|
19
|
+
// ============================================================
|
|
20
|
+
|
|
21
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
22
|
+
|
|
23
|
+
// 如果不在 Claude Code 环境中运行,静默退出
|
|
24
|
+
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================
|
|
29
|
+
// 配置区域
|
|
30
|
+
// ============================================================
|
|
31
|
+
|
|
32
|
+
const CONFIG = {
|
|
33
|
+
// 你的 hook 配置
|
|
34
|
+
enabled: true,
|
|
35
|
+
verbose: false, // 设为 true 开启调试输出
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// ============================================================
|
|
39
|
+
// 工具函数
|
|
40
|
+
// ============================================================
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* 安全日志 - 只有 verbose=true 时才输出
|
|
44
|
+
*/
|
|
45
|
+
function log(...args) {
|
|
46
|
+
if (CONFIG.verbose) {
|
|
47
|
+
console.error('[Hook]', ...args);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 安全读取文件
|
|
53
|
+
*/
|
|
54
|
+
function safeReadFile(filePath, defaultValue = '') {
|
|
55
|
+
try {
|
|
56
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return defaultValue;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 安全写入文件
|
|
64
|
+
*/
|
|
65
|
+
function safeWriteFile(filePath, content) {
|
|
66
|
+
try {
|
|
67
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
68
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
69
|
+
return true;
|
|
70
|
+
} catch (e) {
|
|
71
|
+
log('Write error:', e.message);
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ============================================================
|
|
77
|
+
// Hook 核心逻辑
|
|
78
|
+
// ============================================================
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* 主处理函数
|
|
82
|
+
*/
|
|
83
|
+
function main() {
|
|
84
|
+
try {
|
|
85
|
+
// 获取环境变量
|
|
86
|
+
const toolName = process.env.CLAUDE_TOOL_NAME || '';
|
|
87
|
+
const toolInput = process.env.CLAUDE_TOOL_INPUT || '';
|
|
88
|
+
|
|
89
|
+
log('Tool:', toolName);
|
|
90
|
+
log('Project:', PROJECT_DIR);
|
|
91
|
+
|
|
92
|
+
// ===== 在这里实现你的 hook 逻辑 =====
|
|
93
|
+
|
|
94
|
+
// 示例: 追踪操作
|
|
95
|
+
const trackerFile = path.join(PROJECT_DIR, '.claude/hooks/.tracker.json');
|
|
96
|
+
const data = JSON.parse(safeReadFile(trackerFile, '{}'));
|
|
97
|
+
data.lastRun = new Date().toISOString();
|
|
98
|
+
data.runs = (data.runs || 0) + 1;
|
|
99
|
+
safeWriteFile(trackerFile, JSON.stringify(data, null, 2));
|
|
100
|
+
|
|
101
|
+
// ===== Hook 逻辑结束 =====
|
|
102
|
+
|
|
103
|
+
} catch (error) {
|
|
104
|
+
// 静默处理错误,不干扰用户对话
|
|
105
|
+
log('Error:', error.message);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// 永远输出成功,不影响 Claude Code 正常运行
|
|
109
|
+
process.exit(0);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================================
|
|
113
|
+
// 执行
|
|
114
|
+
// ============================================================
|
|
115
|
+
|
|
116
|
+
main();
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* [Hook Name] - UserResponseSubmit Hook Template
|
|
4
|
+
*
|
|
5
|
+
* 触发时机: AI 返回响应后
|
|
6
|
+
* 环境变量:
|
|
7
|
+
* - CLAUDE_PROJECT_DIR: 项目根目录
|
|
8
|
+
* - CLAUDE_RESPONSE_CONTENT: 响应内容
|
|
9
|
+
*
|
|
10
|
+
* @version 1.0.0
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
// ============================================================
|
|
17
|
+
// 标准头部 - 所有 hooks 必须包含
|
|
18
|
+
// ============================================================
|
|
19
|
+
|
|
20
|
+
const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
21
|
+
|
|
22
|
+
// 如果不在 Claude Code 环境中运行,静默退出
|
|
23
|
+
if (!process.env.CLAUDE_PROJECT_DIR) {
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// ============================================================
|
|
28
|
+
// 配置区域
|
|
29
|
+
// ============================================================
|
|
30
|
+
|
|
31
|
+
const CONFIG = {
|
|
32
|
+
enabled: true,
|
|
33
|
+
verbose: false,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ============================================================
|
|
37
|
+
// 工具函数
|
|
38
|
+
// ============================================================
|
|
39
|
+
|
|
40
|
+
function log(...args) {
|
|
41
|
+
if (CONFIG.verbose) {
|
|
42
|
+
console.error('[Hook]', ...args);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function safeReadFile(filePath, defaultValue = '') {
|
|
47
|
+
try {
|
|
48
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
49
|
+
} catch (e) {
|
|
50
|
+
return defaultValue;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function safeWriteFile(filePath, content) {
|
|
55
|
+
try {
|
|
56
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
57
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
58
|
+
return true;
|
|
59
|
+
} catch (e) {
|
|
60
|
+
log('Write error:', e.message);
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ============================================================
|
|
66
|
+
// Hook 核心逻辑
|
|
67
|
+
// ============================================================
|
|
68
|
+
|
|
69
|
+
function main() {
|
|
70
|
+
try {
|
|
71
|
+
const responseContent = process.env.CLAUDE_RESPONSE_CONTENT || '';
|
|
72
|
+
|
|
73
|
+
log('Response length:', responseContent.length);
|
|
74
|
+
|
|
75
|
+
// ===== 在这里实现你的 hook 逻辑 =====
|
|
76
|
+
|
|
77
|
+
// 示例: 记录响应统计
|
|
78
|
+
const statsFile = path.join(PROJECT_DIR, '.claude/hooks/.response-stats.json');
|
|
79
|
+
const stats = JSON.parse(safeReadFile(statsFile, '{}'));
|
|
80
|
+
stats.totalResponses = (stats.totalResponses || 0) + 1;
|
|
81
|
+
stats.totalChars = (stats.totalChars || 0) + responseContent.length;
|
|
82
|
+
stats.lastUpdate = new Date().toISOString();
|
|
83
|
+
safeWriteFile(statsFile, JSON.stringify(stats, null, 2));
|
|
84
|
+
|
|
85
|
+
// ===== Hook 逻辑结束 =====
|
|
86
|
+
|
|
87
|
+
} catch (error) {
|
|
88
|
+
log('Error:', error.message);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
process.exit(0);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
main();
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook Validator - 检查 hook 是否符合最佳实践
|
|
4
|
+
*
|
|
5
|
+
* 使用方法:
|
|
6
|
+
* node validate.js [hook-file]
|
|
7
|
+
*
|
|
8
|
+
* 示例:
|
|
9
|
+
* node validate.js ../my-hook.cjs
|
|
10
|
+
* node validate.js *.cjs
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const path = require('path');
|
|
15
|
+
|
|
16
|
+
const CHECKS = {
|
|
17
|
+
ENV_FALLBACK: {
|
|
18
|
+
name: '环境变量 fallback',
|
|
19
|
+
pattern: /process\.env\.CLAUDE_PROJECT_DIR\s*\|\|/,
|
|
20
|
+
required: true,
|
|
21
|
+
error: '缺少 CLAUDE_PROJECT_DIR fallback (|| process.cwd())'
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
SILENT_EXIT: {
|
|
25
|
+
name: '静默退出检查',
|
|
26
|
+
pattern: /if\s*\(\s*!process\.env\.CLAUDE_PROJECT_DIR/,
|
|
27
|
+
required: true,
|
|
28
|
+
error: '缺少非项目环境的静默退出检查'
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
ERROR_HANDLING: {
|
|
32
|
+
name: '错误处理',
|
|
33
|
+
pattern: /try\s*\{/,
|
|
34
|
+
required: true,
|
|
35
|
+
error: '缺少 try-catch 错误处理'
|
|
36
|
+
},
|
|
37
|
+
|
|
38
|
+
SAFE_EXIT: {
|
|
39
|
+
name: '安全退出',
|
|
40
|
+
pattern: /process\.exit\(0\)/,
|
|
41
|
+
required: true,
|
|
42
|
+
error: '缺少明确的 process.exit(0)'
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
NO_STDOUT: {
|
|
46
|
+
name: '避免 stdout 输出',
|
|
47
|
+
pattern: /console\.log\(/,
|
|
48
|
+
required: false,
|
|
49
|
+
error: '检测到 console.log - 建议使用 console.error 用于调试'
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
SAFE_MKDIR: {
|
|
53
|
+
name: '安全创建目录',
|
|
54
|
+
pattern: /mkdirSync.*recursive/,
|
|
55
|
+
required: true,
|
|
56
|
+
error: '目录创建缺少 { recursive: true } 选项'
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* 验证单个 hook 文件
|
|
62
|
+
*/
|
|
63
|
+
function validateHook(filePath) {
|
|
64
|
+
const results = {
|
|
65
|
+
file: filePath,
|
|
66
|
+
passed: 0,
|
|
67
|
+
failed: 0,
|
|
68
|
+
warnings: 0,
|
|
69
|
+
checks: []
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (!fs.existsSync(filePath)) {
|
|
73
|
+
results.error = '文件不存在';
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
78
|
+
|
|
79
|
+
for (const [key, check] of Object.entries(CHECKS)) {
|
|
80
|
+
const passed = check.pattern.test(content);
|
|
81
|
+
|
|
82
|
+
if (check.required) {
|
|
83
|
+
if (passed) {
|
|
84
|
+
results.passed++;
|
|
85
|
+
} else {
|
|
86
|
+
results.failed++;
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
// 可选检查作为警告
|
|
90
|
+
if (passed) {
|
|
91
|
+
results.warnings++;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
results.checks.push({
|
|
96
|
+
name: check.name,
|
|
97
|
+
passed: check.required ? passed : !passed,
|
|
98
|
+
required: check.required,
|
|
99
|
+
error: check.error
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return results;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* 打印验证结果
|
|
108
|
+
*/
|
|
109
|
+
function printResults(results) {
|
|
110
|
+
console.log(`\n📎 ${path.basename(results.file)}`);
|
|
111
|
+
console.log('─'.repeat(50));
|
|
112
|
+
|
|
113
|
+
if (results.error) {
|
|
114
|
+
console.log(`❌ ${results.error}`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
for (const check of results.checks) {
|
|
119
|
+
if (check.passed) {
|
|
120
|
+
console.log(`✅ ${check.name}`);
|
|
121
|
+
} else if (check.required) {
|
|
122
|
+
console.log(`❌ ${check.name}`);
|
|
123
|
+
console.log(` ${check.error}`);
|
|
124
|
+
} else {
|
|
125
|
+
console.log(`⚠️ ${check.name}`);
|
|
126
|
+
console.log(` ${check.error}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const total = results.passed + results.failed;
|
|
131
|
+
const score = total > 0 ? Math.round((results.passed / total) * 100) : 0;
|
|
132
|
+
|
|
133
|
+
console.log('─'.repeat(50));
|
|
134
|
+
console.log(`得分: ${score}% | ✅ ${results.passed} | ❌ ${results.failed} | ⚠️ ${results.warnings}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 主函数
|
|
139
|
+
*/
|
|
140
|
+
function main() {
|
|
141
|
+
const args = process.argv.slice(2);
|
|
142
|
+
|
|
143
|
+
if (args.length === 0) {
|
|
144
|
+
console.log(`
|
|
145
|
+
🔍 Hook Validator - 检查 hook 最佳实践
|
|
146
|
+
|
|
147
|
+
使用方法:
|
|
148
|
+
node validate.js <hook-file>
|
|
149
|
+
node validate.js *.cjs
|
|
150
|
+
|
|
151
|
+
示例:
|
|
152
|
+
node validate.js my-hook.cjs
|
|
153
|
+
node validate.js ../*.cjs
|
|
154
|
+
`);
|
|
155
|
+
process.exit(0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let allPassed = true;
|
|
159
|
+
|
|
160
|
+
for (const arg of args) {
|
|
161
|
+
const results = validateHook(arg);
|
|
162
|
+
printResults(results);
|
|
163
|
+
|
|
164
|
+
if (results.failed > 0) {
|
|
165
|
+
allPassed = false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
console.log('');
|
|
170
|
+
process.exit(allPassed ? 0 : 1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
main();
|