@winspan/claude-forge 8.2.0 → 8.11.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/README.md +64 -5
- package/dist/claudemd/convention-extractor.js +3 -3
- package/dist/claudemd/convention-extractor.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +1 -0
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/rules.d.ts +8 -0
- package/dist/cli/commands/rules.d.ts.map +1 -0
- package/dist/cli/commands/rules.js +89 -0
- package/dist/cli/commands/rules.js.map +1 -0
- package/dist/cli/commands/stats.d.ts +8 -0
- package/dist/cli/commands/stats.d.ts.map +1 -0
- package/dist/cli/commands/stats.js +79 -0
- package/dist/cli/commands/stats.js.map +1 -0
- package/dist/cli/commands/template.d.ts +8 -0
- package/dist/cli/commands/template.d.ts.map +1 -0
- package/dist/cli/commands/template.js +68 -0
- package/dist/cli/commands/template.js.map +1 -0
- package/dist/cli/index.js +6 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/storage/sqlite.d.ts +25 -0
- package/dist/core/storage/sqlite.d.ts.map +1 -1
- package/dist/core/storage/sqlite.js +26 -0
- package/dist/core/storage/sqlite.js.map +1 -1
- package/dist/core/types.d.ts +3 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.d.ts +6 -1
- package/dist/daemon/handlers/pre-tool-use.d.ts.map +1 -1
- package/dist/daemon/handlers/pre-tool-use.js +45 -46
- package/dist/daemon/handlers/pre-tool-use.js.map +1 -1
- package/dist/daemon/handlers/user-prompt.d.ts.map +1 -1
- package/dist/daemon/handlers/user-prompt.js +8 -3
- package/dist/daemon/handlers/user-prompt.js.map +1 -1
- package/dist/daemon/index.d.ts.map +1 -1
- package/dist/daemon/index.js +34 -2
- package/dist/daemon/index.js.map +1 -1
- package/dist/engine/conventions/basic-security.yaml +15 -0
- package/dist/engine/conventions/database-safety.yaml +74 -0
- package/dist/engine/conventions/docker-safety.yaml +69 -0
- package/dist/engine/conventions/git-safety.yaml +12 -0
- package/dist/engine/conventions/go-best-practices.yaml +84 -0
- package/dist/engine/conventions/python-best-practices.yaml +96 -0
- package/dist/engine/conventions/react-best-practices.yaml +96 -0
- package/dist/engine/dsl/parser.d.ts.map +1 -1
- package/dist/engine/dsl/parser.js +7 -1
- package/dist/engine/dsl/parser.js.map +1 -1
- package/dist/engine/dsl/types.d.ts +26 -1
- package/dist/engine/dsl/types.d.ts.map +1 -1
- package/dist/engine/rule-engine.d.ts +2 -1
- package/dist/engine/rule-engine.d.ts.map +1 -1
- package/dist/engine/rule-engine.js +56 -20
- package/dist/engine/rule-engine.js.map +1 -1
- package/dist/skills/matcher.d.ts +17 -1
- package/dist/skills/matcher.d.ts.map +1 -1
- package/dist/skills/matcher.js +92 -2
- package/dist/skills/matcher.js.map +1 -1
- package/dist/skills/registry.d.ts +26 -5
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +100 -10
- package/dist/skills/registry.js.map +1 -1
- package/dist/skills/semantic-matcher.d.ts +58 -0
- package/dist/skills/semantic-matcher.d.ts.map +1 -0
- package/dist/skills/semantic-matcher.js +189 -0
- package/dist/skills/semantic-matcher.js.map +1 -0
- package/dist/templates/template-manager.d.ts +52 -0
- package/dist/templates/template-manager.d.ts.map +1 -0
- package/dist/templates/template-manager.js +129 -0
- package/dist/templates/template-manager.js.map +1 -0
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +129 -2
- package/dist/web/server.js.map +1 -1
- package/dist/web/static/index.html +337 -0
- package/dist/web/static/vendor/chart.umd.min.js +20 -0
- package/package.json +2 -2
|
@@ -17,6 +17,9 @@ forbidden:
|
|
|
17
17
|
level: block
|
|
18
18
|
message: "禁止强制推送到受保护分支 {{git.branch}}"
|
|
19
19
|
suggestion: "使用 --force-with-lease 或创建新分支后提交 PR"
|
|
20
|
+
operator_guidance: "公共分支改写历史会影响他人协作;优先通过新分支和 PR 完成修复。"
|
|
21
|
+
doc_ref: "CLAUDE.md#Git 操作安全规范"
|
|
22
|
+
tags: ["git", "protected-branch", "history-rewrite"]
|
|
20
23
|
|
|
21
24
|
- id: "warn-force-push-feature"
|
|
22
25
|
when: |
|
|
@@ -26,6 +29,9 @@ forbidden:
|
|
|
26
29
|
level: warn
|
|
27
30
|
message: "检测到 git push --force,这会覆盖远程历史"
|
|
28
31
|
suggestion: "考虑使用 --force-with-lease 以避免覆盖他人提交"
|
|
32
|
+
operator_guidance: "只有在确认远端无人基于该提交继续工作时,才考虑 force push。"
|
|
33
|
+
doc_ref: "CLAUDE.md#Git 操作安全规范"
|
|
34
|
+
tags: ["git", "force-push"]
|
|
29
35
|
|
|
30
36
|
- id: "no-reset-hard-uncommitted"
|
|
31
37
|
when: |
|
|
@@ -35,6 +41,9 @@ forbidden:
|
|
|
35
41
|
level: block
|
|
36
42
|
message: "禁止在有未提交更改时执行 git reset --hard"
|
|
37
43
|
suggestion: "先使用 git stash 保存更改,或提交后再 reset"
|
|
44
|
+
operator_guidance: "先用 git status 确认工作区,再 stash 或提交,避免本地未保存改动丢失。"
|
|
45
|
+
doc_ref: "CLAUDE.md#Git 操作安全规范"
|
|
46
|
+
tags: ["git", "reset-hard", "uncommitted-changes"]
|
|
38
47
|
|
|
39
48
|
- id: "warn-reset-hard"
|
|
40
49
|
when: |
|
|
@@ -43,6 +52,9 @@ forbidden:
|
|
|
43
52
|
level: confirm
|
|
44
53
|
message: "git reset --hard 会丢弃所有未提交的更改"
|
|
45
54
|
suggestion: "确认这是有意为之,或使用 git stash 保存更改"
|
|
55
|
+
operator_guidance: "执行前先确认 HEAD 目标和工作区状态,必要时先创建临时分支保存现场。"
|
|
56
|
+
doc_ref: "CLAUDE.md#Git 操作安全规范"
|
|
57
|
+
tags: ["git", "reset-hard"]
|
|
46
58
|
|
|
47
59
|
- id: "warn-clean-fd"
|
|
48
60
|
when: |
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
id: "go-best-practices"
|
|
2
|
+
name: "Go 最佳实践"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: "Go 开发规范,覆盖错误处理、并发安全、资源管理和性能"
|
|
5
|
+
triggers: ["go", "golang", "goroutine", "channel", "defer", "panic"]
|
|
6
|
+
|
|
7
|
+
variables:
|
|
8
|
+
go_file_patterns: [".go"]
|
|
9
|
+
dangerous_packages: ["unsafe", "reflect"]
|
|
10
|
+
max_function_lines: 50
|
|
11
|
+
|
|
12
|
+
forbidden:
|
|
13
|
+
- id: "no-panic-in-library"
|
|
14
|
+
when: |
|
|
15
|
+
tool.in(["Write", "Edit"])
|
|
16
|
+
&& file_ext == ".go"
|
|
17
|
+
&& !file_path.includes("_test.go")
|
|
18
|
+
&& (args.content ?? args.new_string ?? "").match(/\bpanic\(/).ok()
|
|
19
|
+
level: warn
|
|
20
|
+
message: "库代码中检测到 panic,应返回 error 而非 panic"
|
|
21
|
+
suggestion: "将 panic 改为返回 error,让调用方决定如何处理"
|
|
22
|
+
operator_guidance: "panic 应只用于不可恢复的错误;库代码应返回 error,由调用方处理。"
|
|
23
|
+
doc_ref: "https://go.dev/blog/error-handling-and-go"
|
|
24
|
+
tags: ["go", "error-handling"]
|
|
25
|
+
|
|
26
|
+
- id: "warn-goroutine-leak"
|
|
27
|
+
when: |
|
|
28
|
+
tool.in(["Write", "Edit"])
|
|
29
|
+
&& file_ext == ".go"
|
|
30
|
+
&& (args.content ?? args.new_string ?? "").match(/go\s+func\s*\(/).ok()
|
|
31
|
+
&& !(args.content ?? args.new_string ?? "").match(/context\./).ok()
|
|
32
|
+
level: warn
|
|
33
|
+
message: "检测到 goroutine 但未使用 context,可能导致泄漏"
|
|
34
|
+
suggestion: "传入 context.Context 参数,监听 ctx.Done() 以优雅退出"
|
|
35
|
+
operator_guidance: "goroutine 泄漏会耗尽资源;用 context 控制生命周期,监听 Done() 信号退出。"
|
|
36
|
+
doc_ref: "https://go.dev/blog/context"
|
|
37
|
+
tags: ["go", "concurrency", "resource-leak"]
|
|
38
|
+
|
|
39
|
+
- id: "no-defer-in-loop"
|
|
40
|
+
when: |
|
|
41
|
+
tool.in(["Write", "Edit"])
|
|
42
|
+
&& file_ext == ".go"
|
|
43
|
+
&& (args.content ?? args.new_string ?? "").match(/for\s+[^{]*\{[^}]*defer\s+/).ok()
|
|
44
|
+
level: warn
|
|
45
|
+
message: "检测到循环中使用 defer,会延迟到函数结束才执行"
|
|
46
|
+
suggestion: "将循环体提取为独立函数,或手动调用清理函数"
|
|
47
|
+
operator_guidance: "defer 在函数返回时执行,不是循环迭代结束;循环中的 defer 会累积,可能导致资源耗尽。"
|
|
48
|
+
doc_ref: "https://go.dev/ref/spec#Defer_statements"
|
|
49
|
+
tags: ["go", "defer", "resource-leak"]
|
|
50
|
+
|
|
51
|
+
- id: "warn-error-ignored"
|
|
52
|
+
when: |
|
|
53
|
+
tool.in(["Write", "Edit"])
|
|
54
|
+
&& file_ext == ".go"
|
|
55
|
+
&& (args.content ?? args.new_string ?? "").match(/\w+\([^)]*\)\s*$/).ok()
|
|
56
|
+
&& (args.content ?? args.new_string ?? "").match(/,\s*err\s*:=/).ok()
|
|
57
|
+
level: warn
|
|
58
|
+
message: "检测到可能未检查的 error 返回值"
|
|
59
|
+
suggestion: "显式检查 error:if err != nil { return err }"
|
|
60
|
+
operator_guidance: "Go 的错误处理依赖显式检查;忽略 error 会掩盖问题,用 if err != nil 立即处理。"
|
|
61
|
+
tags: ["go", "error-handling"]
|
|
62
|
+
|
|
63
|
+
- id: "no-unsafe-package"
|
|
64
|
+
when: |
|
|
65
|
+
tool.in(["Write", "Edit"])
|
|
66
|
+
&& file_ext == ".go"
|
|
67
|
+
&& (args.content ?? args.new_string ?? "").match(/import\s+.*"unsafe"/).ok()
|
|
68
|
+
level: warn
|
|
69
|
+
message: "检测到 unsafe 包,会绕过 Go 的类型安全"
|
|
70
|
+
suggestion: "除非性能关键场景,避免使用 unsafe"
|
|
71
|
+
operator_guidance: "unsafe 绕过类型安全和内存安全;只在性能瓶颈且充分测试后使用。"
|
|
72
|
+
tags: ["go", "safety"]
|
|
73
|
+
|
|
74
|
+
escalation: []
|
|
75
|
+
|
|
76
|
+
deliverables: []
|
|
77
|
+
|
|
78
|
+
principles: |
|
|
79
|
+
## Go 最佳实践
|
|
80
|
+
- 库代码返回 error 而非 panic
|
|
81
|
+
- goroutine 使用 context 控制生命周期
|
|
82
|
+
- 避免在循环中使用 defer
|
|
83
|
+
- 显式检查所有 error 返回值
|
|
84
|
+
- 谨慎使用 unsafe 包
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: "python-best-practices"
|
|
2
|
+
name: "Python 最佳实践"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: "Python 开发规范,覆盖代码风格、类型安全、安全性和性能"
|
|
5
|
+
triggers: ["python", "py", "pip", "django", "flask", "fastapi", "虚拟环境"]
|
|
6
|
+
|
|
7
|
+
variables:
|
|
8
|
+
python_file_patterns: [".py"]
|
|
9
|
+
dangerous_imports: ["pickle", "eval", "exec", "compile", "__import__"]
|
|
10
|
+
max_function_lines: 50
|
|
11
|
+
max_class_lines: 300
|
|
12
|
+
|
|
13
|
+
forbidden:
|
|
14
|
+
- id: "no-pickle-untrusted"
|
|
15
|
+
when: |
|
|
16
|
+
tool.in(["Write", "Edit"])
|
|
17
|
+
&& file_ext == ".py"
|
|
18
|
+
&& (args.content ?? args.new_string ?? "").match(/pickle\.loads?\(/).ok()
|
|
19
|
+
level: warn
|
|
20
|
+
message: "检测到 pickle.load/loads,反序列化不可信数据存在代码执行风险"
|
|
21
|
+
suggestion: "使用 JSON 或其他安全的序列化格式,避免 pickle 处理外部数据"
|
|
22
|
+
operator_guidance: "pickle 可以执行任意代码;只用于可信数据,外部数据优先用 JSON/MessagePack。"
|
|
23
|
+
doc_ref: "https://docs.python.org/3/library/pickle.html#module-pickle"
|
|
24
|
+
tags: ["python", "security", "deserialization"]
|
|
25
|
+
|
|
26
|
+
- id: "no-eval-exec"
|
|
27
|
+
when: |
|
|
28
|
+
tool.in(["Write", "Edit"])
|
|
29
|
+
&& file_ext == ".py"
|
|
30
|
+
&& (args.content ?? args.new_string ?? "").match(/\b(eval|exec)\s*\(/).ok()
|
|
31
|
+
level: block
|
|
32
|
+
message: "禁止使用 eval() 或 exec(),存在代码注入风险"
|
|
33
|
+
suggestion: "使用 ast.literal_eval() 解析字面量,或重新设计避免动态执行"
|
|
34
|
+
operator_guidance: "eval/exec 可执行任意代码;用 ast.literal_eval 安全解析,或用字典映射替代动态执行。"
|
|
35
|
+
doc_ref: "https://docs.python.org/3/library/ast.html#ast.literal_eval"
|
|
36
|
+
tags: ["python", "security", "code-injection"]
|
|
37
|
+
|
|
38
|
+
- id: "warn-bare-except"
|
|
39
|
+
when: |
|
|
40
|
+
tool.in(["Write", "Edit"])
|
|
41
|
+
&& file_ext == ".py"
|
|
42
|
+
&& (args.content ?? args.new_string ?? "").match(/except\s*:/).ok()
|
|
43
|
+
level: warn
|
|
44
|
+
message: "检测到裸 except:,会捕获所有异常包括 KeyboardInterrupt"
|
|
45
|
+
suggestion: "使用 except Exception: 或捕获具体异常类型"
|
|
46
|
+
operator_guidance: "裸 except 会捕获系统退出信号;用 except Exception 或具体异常类型。"
|
|
47
|
+
tags: ["python", "error-handling"]
|
|
48
|
+
|
|
49
|
+
- id: "warn-mutable-default-arg"
|
|
50
|
+
when: |
|
|
51
|
+
tool.in(["Write", "Edit"])
|
|
52
|
+
&& file_ext == ".py"
|
|
53
|
+
&& (args.content ?? args.new_string ?? "").match(/def\s+\w+\([^)]*=\s*(\[\]|\{\})\s*[,)]/).ok()
|
|
54
|
+
level: warn
|
|
55
|
+
message: "检测到可变默认参数([] 或 {}),会在调用间共享"
|
|
56
|
+
suggestion: "使用 None 作为默认值,在函数内部初始化"
|
|
57
|
+
operator_guidance: "可变默认参数只创建一次,多次调用会共享同一对象;用 None 并在函数内 if arg is None: arg = []"
|
|
58
|
+
doc_ref: "https://docs.python-guide.org/writing/gotchas/#mutable-default-arguments"
|
|
59
|
+
tags: ["python", "correctness"]
|
|
60
|
+
|
|
61
|
+
- id: "no-shell-injection"
|
|
62
|
+
when: |
|
|
63
|
+
tool.in(["Write", "Edit"])
|
|
64
|
+
&& file_ext == ".py"
|
|
65
|
+
&& (args.content ?? args.new_string ?? "").match(/subprocess\.(call|run|Popen)\([^)]*shell\s*=\s*True/).ok()
|
|
66
|
+
level: warn
|
|
67
|
+
message: "检测到 subprocess 使用 shell=True,存在命令注入风险"
|
|
68
|
+
suggestion: "使用列表形式传递参数,避免 shell=True"
|
|
69
|
+
operator_guidance: "shell=True 会通过 shell 解析命令,存在注入风险;用列表传参:['ls', '-la']"
|
|
70
|
+
doc_ref: "https://docs.python.org/3/library/subprocess.html#security-considerations"
|
|
71
|
+
tags: ["python", "security", "command-injection"]
|
|
72
|
+
|
|
73
|
+
- id: "warn-requirements-no-version"
|
|
74
|
+
when: |
|
|
75
|
+
tool.in(["Write", "Edit"])
|
|
76
|
+
&& file_path != null
|
|
77
|
+
&& file_path.endsWith("requirements.txt")
|
|
78
|
+
&& (args.content ?? args.new_string ?? "").match(/^\w+\s*$/m).ok()
|
|
79
|
+
level: warn
|
|
80
|
+
message: "requirements.txt 中存在未固定版本的依赖"
|
|
81
|
+
suggestion: "使用 == 固定版本号,确保构建可复现"
|
|
82
|
+
operator_guidance: "未固定版本会导致不同环境安装不同版本;用 pip freeze 生成精确版本。"
|
|
83
|
+
tags: ["python", "dependency", "reproducibility"]
|
|
84
|
+
|
|
85
|
+
escalation: []
|
|
86
|
+
|
|
87
|
+
deliverables: []
|
|
88
|
+
|
|
89
|
+
principles: |
|
|
90
|
+
## Python 最佳实践
|
|
91
|
+
- 禁止 pickle 反序列化不可信数据
|
|
92
|
+
- 禁止 eval/exec 动态执行代码
|
|
93
|
+
- 使用 except Exception 而非裸 except
|
|
94
|
+
- 避免可变默认参数
|
|
95
|
+
- subprocess 避免 shell=True
|
|
96
|
+
- requirements.txt 固定依赖版本
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
id: "react-best-practices"
|
|
2
|
+
name: "React 最佳实践"
|
|
3
|
+
version: "1.0.0"
|
|
4
|
+
description: "React/前端开发规范,覆盖组件设计、状态管理、性能优化和安全性"
|
|
5
|
+
triggers: ["react", "jsx", "tsx", "component", "hook", "useState", "useEffect", "组件", "前端"]
|
|
6
|
+
|
|
7
|
+
variables:
|
|
8
|
+
react_file_patterns: [".jsx", ".tsx"]
|
|
9
|
+
hook_names: ["useState", "useEffect", "useCallback", "useMemo", "useRef", "useContext", "useReducer"]
|
|
10
|
+
dangerous_html_props: ["dangerouslySetInnerHTML", "innerHTML"]
|
|
11
|
+
max_component_lines: 300
|
|
12
|
+
max_useEffect_deps: 5
|
|
13
|
+
|
|
14
|
+
forbidden:
|
|
15
|
+
- id: "no-dangerously-set-inner-html"
|
|
16
|
+
when: |
|
|
17
|
+
tool.in(["Write", "Edit"])
|
|
18
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
19
|
+
&& (args.content ?? args.new_string ?? "").match(/dangerouslySetInnerHTML/).ok()
|
|
20
|
+
level: warn
|
|
21
|
+
message: "检测到 dangerouslySetInnerHTML,存在 XSS 风险"
|
|
22
|
+
suggestion: "使用 React 的默认转义机制,或使用 DOMPurify 清理 HTML"
|
|
23
|
+
operator_guidance: "dangerouslySetInnerHTML 绕过了 React 的 XSS 防护;如必须使用,需先用 DOMPurify.sanitize() 清理内容。"
|
|
24
|
+
doc_ref: "https://react.dev/reference/react-dom/components/common#dangerously-setting-the-inner-html"
|
|
25
|
+
tags: ["react", "xss", "security"]
|
|
26
|
+
|
|
27
|
+
- id: "no-inline-function-in-jsx"
|
|
28
|
+
when: |
|
|
29
|
+
tool.in(["Write", "Edit"])
|
|
30
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
31
|
+
&& (args.content ?? args.new_string ?? "").match(/onClick=\{(?:\(\)|function)/).ok()
|
|
32
|
+
level: warn
|
|
33
|
+
message: "检测到 JSX 中的内联函数,可能导致不必要的重渲染"
|
|
34
|
+
suggestion: "使用 useCallback 包裹函数,或将函数提取到组件外"
|
|
35
|
+
operator_guidance: "每次渲染都会创建新函数引用,导致子组件不必要的重渲染;用 useCallback 缓存函数引用。"
|
|
36
|
+
tags: ["react", "performance"]
|
|
37
|
+
|
|
38
|
+
- id: "warn-missing-key-prop"
|
|
39
|
+
when: |
|
|
40
|
+
tool.in(["Write", "Edit"])
|
|
41
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
42
|
+
&& (args.content ?? args.new_string ?? "").match(/\.map\([^)]*=>\s*<[A-Z]/).ok()
|
|
43
|
+
&& !(args.content ?? args.new_string ?? "").match(/key=/).ok()
|
|
44
|
+
level: warn
|
|
45
|
+
message: "检测到 map 渲染列表但可能缺少 key 属性"
|
|
46
|
+
suggestion: "为列表项添加唯一的 key 属性,避免使用数组索引"
|
|
47
|
+
operator_guidance: "key 帮助 React 识别哪些元素改变了;使用稳定的唯一标识符(如 id),不要用数组索引。"
|
|
48
|
+
doc_ref: "https://react.dev/learn/rendering-lists#keeping-list-items-in-order-with-key"
|
|
49
|
+
tags: ["react", "performance", "correctness"]
|
|
50
|
+
|
|
51
|
+
- id: "warn-useEffect-missing-deps"
|
|
52
|
+
when: |
|
|
53
|
+
tool.in(["Write", "Edit"])
|
|
54
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
55
|
+
&& (args.content ?? args.new_string ?? "").match(/useEffect\([^,]+,\s*\[\s*\]\s*\)/).ok()
|
|
56
|
+
level: warn
|
|
57
|
+
message: "检测到 useEffect 使用空依赖数组,可能遗漏依赖"
|
|
58
|
+
suggestion: "检查 effect 中使用的所有外部变量是否都在依赖数组中"
|
|
59
|
+
operator_guidance: "空依赖数组意味着 effect 只在挂载时运行一次;如果 effect 内部使用了 props/state,应添加到依赖数组。"
|
|
60
|
+
doc_ref: "https://react.dev/reference/react/useEffect#specifying-reactive-dependencies"
|
|
61
|
+
tags: ["react", "hooks", "correctness"]
|
|
62
|
+
|
|
63
|
+
- id: "no-direct-state-mutation"
|
|
64
|
+
when: |
|
|
65
|
+
tool.in(["Write", "Edit"])
|
|
66
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
67
|
+
&& (args.content ?? args.new_string ?? "").match(/\bstate\.[a-zA-Z_]+\s*=/).ok()
|
|
68
|
+
level: block
|
|
69
|
+
message: "禁止直接修改 state,必须使用 setState"
|
|
70
|
+
suggestion: "使用 setState 或 useState 返回的 setter 函数更新状态"
|
|
71
|
+
operator_guidance: "直接修改 state 不会触发重渲染,且违反 React 的不可变性原则;始终通过 setState 更新。"
|
|
72
|
+
doc_ref: "https://react.dev/learn/updating-objects-in-state"
|
|
73
|
+
tags: ["react", "state", "correctness"]
|
|
74
|
+
|
|
75
|
+
escalation:
|
|
76
|
+
- id: "large-component-warning"
|
|
77
|
+
when: |
|
|
78
|
+
tool == "Write"
|
|
79
|
+
&& file_ext.in(vars.react_file_patterns)
|
|
80
|
+
&& (args.content ?? "").split("\n").length > vars.max_component_lines
|
|
81
|
+
action: warn
|
|
82
|
+
message: "组件超过 {{vars.max_component_lines}} 行,建议拆分"
|
|
83
|
+
suggestion: "将大组件拆分为多个小组件,提升可维护性"
|
|
84
|
+
operator_guidance: "大组件难以理解和测试;按功能或 UI 区域拆分,每个组件专注单一职责。"
|
|
85
|
+
tags: ["react", "maintainability"]
|
|
86
|
+
|
|
87
|
+
deliverables: []
|
|
88
|
+
|
|
89
|
+
principles: |
|
|
90
|
+
## React 最佳实践
|
|
91
|
+
- 禁止使用 dangerouslySetInnerHTML,防止 XSS 攻击
|
|
92
|
+
- 避免 JSX 中的内联函数,使用 useCallback 优化性能
|
|
93
|
+
- 列表渲染必须添加唯一 key,不使用数组索引
|
|
94
|
+
- useEffect 依赖数组必须完整,避免遗漏依赖
|
|
95
|
+
- 禁止直接修改 state,必须通过 setState 更新
|
|
96
|
+
- 组件超过 300 行时建议拆分
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/engine/dsl/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/engine/dsl/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,OAAO,KAAK,EACV,UAAU,EAKX,MAAM,YAAY,CAAC;AA6DpB,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,cAAc,CAAS;gBAEnB,cAAc,CAAC,EAAE,MAAM;IAWnC,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAkBvC,OAAO,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC;IAyBlC,kBAAkB,CAChB,YAAY,EAAE,MAAM,EACpB,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GACtC,UAAU;IAiBb,OAAO,CAAC,gBAAgB;IAsDxB,OAAO,CAAC,cAAc;CAkBvB"}
|
|
@@ -18,6 +18,9 @@ const RuleSchema = z.object({
|
|
|
18
18
|
level: z.enum(['allow', 'warn', 'confirm', 'block']),
|
|
19
19
|
message: z.string(),
|
|
20
20
|
suggestion: z.string().optional(),
|
|
21
|
+
operator_guidance: z.string().optional(),
|
|
22
|
+
doc_ref: z.string().optional(),
|
|
23
|
+
tags: z.array(z.string()).optional(),
|
|
21
24
|
enabled: z.boolean().optional().default(true),
|
|
22
25
|
});
|
|
23
26
|
const EscalationRuleSchema = z.object({
|
|
@@ -26,6 +29,9 @@ const EscalationRuleSchema = z.object({
|
|
|
26
29
|
action: z.enum(['allow', 'warn', 'confirm', 'block']),
|
|
27
30
|
message: z.string(),
|
|
28
31
|
suggestion: z.string().optional(),
|
|
32
|
+
operator_guidance: z.string().optional(),
|
|
33
|
+
doc_ref: z.string().optional(),
|
|
34
|
+
tags: z.array(z.string()).optional(),
|
|
29
35
|
enabled: z.boolean().optional().default(true),
|
|
30
36
|
});
|
|
31
37
|
const DeliverableSchema = z.object({
|
|
@@ -33,7 +39,7 @@ const DeliverableSchema = z.object({
|
|
|
33
39
|
pattern: z.string().optional(),
|
|
34
40
|
command: z.string().optional(),
|
|
35
41
|
required: z.boolean(),
|
|
36
|
-
check_at: z.enum(['session_end', 'before_commit', 'on_demand']),
|
|
42
|
+
check_at: z.enum(['session_end', 'before_commit', 'on_demand', 'ci']),
|
|
37
43
|
condition: z.string().optional(),
|
|
38
44
|
});
|
|
39
45
|
const RuleOverrideSchema = z.object({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/engine/dsl/parser.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,oBAAoB;AAEpB,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,KAAK,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACpD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC;IACpC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE;IACd,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;IACrD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACjC,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC;CAC9C,CAAC,CAAC;AAEH,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACjC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE;IAChB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC9B,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE;IACrB,QAAQ,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,eAAe,EAAE,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/engine/dsl/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,6 +14,22 @@ export interface GovernanceDecision {
|
|
|
14
14
|
reason?: string;
|
|
15
15
|
suggestion?: string;
|
|
16
16
|
}
|
|
17
|
+
export interface RuleMetadata {
|
|
18
|
+
operator_guidance?: string;
|
|
19
|
+
doc_ref?: string;
|
|
20
|
+
tags?: string[];
|
|
21
|
+
}
|
|
22
|
+
export interface MatchedRule extends RuleMetadata {
|
|
23
|
+
id: string;
|
|
24
|
+
level: DecisionLevel;
|
|
25
|
+
reason: string;
|
|
26
|
+
suggestion?: string;
|
|
27
|
+
source_convention: string;
|
|
28
|
+
}
|
|
29
|
+
export interface RuleEvaluationResult {
|
|
30
|
+
decision: GovernanceDecision;
|
|
31
|
+
matches: MatchedRule[];
|
|
32
|
+
}
|
|
17
33
|
export interface Convention {
|
|
18
34
|
id: string;
|
|
19
35
|
name: string;
|
|
@@ -34,6 +50,9 @@ export interface Rule {
|
|
|
34
50
|
level: DecisionLevel;
|
|
35
51
|
message: string;
|
|
36
52
|
suggestion?: string;
|
|
53
|
+
operator_guidance?: string;
|
|
54
|
+
doc_ref?: string;
|
|
55
|
+
tags?: string[];
|
|
37
56
|
enabled?: boolean;
|
|
38
57
|
}
|
|
39
58
|
export interface EscalationRule {
|
|
@@ -42,6 +61,9 @@ export interface EscalationRule {
|
|
|
42
61
|
action: DecisionLevel;
|
|
43
62
|
message: string;
|
|
44
63
|
suggestion?: string;
|
|
64
|
+
operator_guidance?: string;
|
|
65
|
+
doc_ref?: string;
|
|
66
|
+
tags?: string[];
|
|
45
67
|
enabled?: boolean;
|
|
46
68
|
}
|
|
47
69
|
export interface Deliverable {
|
|
@@ -49,7 +71,7 @@ export interface Deliverable {
|
|
|
49
71
|
pattern?: string;
|
|
50
72
|
command?: string;
|
|
51
73
|
required: boolean;
|
|
52
|
-
check_at: 'session_end' | 'before_commit' | 'on_demand';
|
|
74
|
+
check_at: 'session_end' | 'before_commit' | 'on_demand' | 'ci';
|
|
53
75
|
condition?: string;
|
|
54
76
|
}
|
|
55
77
|
export interface RuleOverride {
|
|
@@ -103,6 +125,9 @@ export interface CompiledRule {
|
|
|
103
125
|
level: DecisionLevel;
|
|
104
126
|
message: string;
|
|
105
127
|
suggestion?: string;
|
|
128
|
+
operator_guidance?: string;
|
|
129
|
+
doc_ref?: string;
|
|
130
|
+
tags?: string[];
|
|
106
131
|
evaluate: CompiledExpression;
|
|
107
132
|
render: (ctx: RuleContext) => string;
|
|
108
133
|
source_convention: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/engine/dsl/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAEnE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAID,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,SAAS,EAAE,IAAI,EAAE,CAAC;IAClB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,SAAS,EAAE,YAAY,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,aAAa,GAAG,eAAe,GAAG,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/engine/dsl/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* - Compile rules to evaluation functions
|
|
7
7
|
* - Real-time evaluation, returning GovernanceDecision
|
|
8
8
|
*/
|
|
9
|
-
import type { Convention, RuleContext, GovernanceDecision, ConventionBinding } from './dsl/types.js';
|
|
9
|
+
import type { Convention, RuleContext, GovernanceDecision, ConventionBinding, RuleEvaluationResult } from './dsl/types.js';
|
|
10
10
|
export declare class RuleEngine {
|
|
11
11
|
private parser;
|
|
12
12
|
private compiler;
|
|
@@ -18,6 +18,7 @@ export declare class RuleEngine {
|
|
|
18
18
|
loadFile(filePath: string): void;
|
|
19
19
|
applyBinding(binding: ConventionBinding): void;
|
|
20
20
|
evaluate(ctx: RuleContext): GovernanceDecision;
|
|
21
|
+
evaluateDetailed(ctx: RuleContext): RuleEvaluationResult;
|
|
21
22
|
getConventions(): Map<string, Convention>;
|
|
22
23
|
/**
|
|
23
24
|
* Get conventions relevant to the given keywords (for proactive injection)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,UAAU,EAGV,WAAW,EAGX,kBAAkB,EAElB,iBAAiB,
|
|
1
|
+
{"version":3,"file":"rule-engine.d.ts","sourceRoot":"","sources":["../../src/engine/rule-engine.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,UAAU,EAGV,WAAW,EAGX,kBAAkB,EAElB,iBAAiB,EACjB,oBAAoB,EAErB,MAAM,gBAAgB,CAAC;AAWxB,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,QAAQ,CAAmB;IACnC,OAAO,CAAC,WAAW,CAAsC;IACzD,OAAO,CAAC,KAAK,CAAgD;gBAEjD,cAAc,CAAC,EAAE,MAAM;IAMnC,OAAO,IAAI,IAAI;IAKf,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAMhC,YAAY,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI;IAiC9C,QAAQ,CAAC,GAAG,EAAE,WAAW,GAAG,kBAAkB;IAI9C,gBAAgB,CAAC,GAAG,EAAE,WAAW,GAAG,oBAAoB;IA+ExD,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC;IAIzC;;OAEG;IACH,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,UAAU,EAAE;IAuBxD,OAAO,CAAC,YAAY;IAuBpB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,iBAAiB;IAqBzB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,UAAU;CA2BnB"}
|
|
@@ -70,12 +70,15 @@ export class RuleEngine {
|
|
|
70
70
|
this.rebuildIndex();
|
|
71
71
|
}
|
|
72
72
|
evaluate(ctx) {
|
|
73
|
+
return this.evaluateDetailed(ctx).decision;
|
|
74
|
+
}
|
|
75
|
+
evaluateDetailed(ctx) {
|
|
73
76
|
const candidates = [
|
|
74
77
|
...(this.index.byTool.get(ctx.tool) ?? []),
|
|
75
78
|
...this.index.global,
|
|
76
79
|
];
|
|
77
80
|
candidates.sort((a, b) => LEVEL_PRIORITY[b.level] - LEVEL_PRIORITY[a.level]);
|
|
78
|
-
const
|
|
81
|
+
const matches = [];
|
|
79
82
|
for (const rule of candidates) {
|
|
80
83
|
let matched = false;
|
|
81
84
|
try {
|
|
@@ -86,30 +89,57 @@ export class RuleEngine {
|
|
|
86
89
|
}
|
|
87
90
|
if (!matched)
|
|
88
91
|
continue;
|
|
89
|
-
|
|
90
|
-
|
|
92
|
+
matches.push({
|
|
93
|
+
id: rule.id,
|
|
94
|
+
level: rule.level,
|
|
95
|
+
reason: rule.render(ctx),
|
|
96
|
+
suggestion: rule.suggestion,
|
|
97
|
+
source_convention: rule.source_convention,
|
|
98
|
+
operator_guidance: rule.operator_guidance,
|
|
99
|
+
doc_ref: rule.doc_ref,
|
|
100
|
+
tags: rule.tags,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
const blocking = matches.find((match) => match.level === 'block');
|
|
104
|
+
if (blocking) {
|
|
105
|
+
return {
|
|
106
|
+
decision: {
|
|
91
107
|
level: 'block',
|
|
92
|
-
rule_id:
|
|
93
|
-
reason:
|
|
94
|
-
suggestion:
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
108
|
+
rule_id: blocking.id,
|
|
109
|
+
reason: blocking.reason,
|
|
110
|
+
suggestion: blocking.suggestion,
|
|
111
|
+
},
|
|
112
|
+
matches,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
const confirm = matches.find((match) => match.level === 'confirm');
|
|
116
|
+
if (confirm) {
|
|
117
|
+
return {
|
|
118
|
+
decision: {
|
|
99
119
|
level: 'confirm',
|
|
100
|
-
rule_id:
|
|
101
|
-
reason:
|
|
102
|
-
suggestion:
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
warnings.push(rule.render(ctx));
|
|
107
|
-
}
|
|
120
|
+
rule_id: confirm.id,
|
|
121
|
+
reason: confirm.reason,
|
|
122
|
+
suggestion: confirm.suggestion,
|
|
123
|
+
},
|
|
124
|
+
matches,
|
|
125
|
+
};
|
|
108
126
|
}
|
|
127
|
+
const warnings = matches.filter((match) => match.level === 'warn');
|
|
109
128
|
if (warnings.length > 0) {
|
|
110
|
-
return {
|
|
129
|
+
return {
|
|
130
|
+
decision: {
|
|
131
|
+
level: 'warn',
|
|
132
|
+
rule_id: warnings[0]?.id,
|
|
133
|
+
reason: warnings.map((warning) => warning.reason).join('\n'),
|
|
134
|
+
suggestion: warnings[0]?.suggestion,
|
|
135
|
+
},
|
|
136
|
+
matches,
|
|
137
|
+
};
|
|
111
138
|
}
|
|
112
|
-
return {
|
|
139
|
+
return {
|
|
140
|
+
decision: { level: 'allow' },
|
|
141
|
+
matches,
|
|
142
|
+
};
|
|
113
143
|
}
|
|
114
144
|
getConventions() {
|
|
115
145
|
return this.conventions;
|
|
@@ -159,6 +189,9 @@ export class RuleEngine {
|
|
|
159
189
|
level: rule.level,
|
|
160
190
|
message: rule.message,
|
|
161
191
|
suggestion: rule.suggestion,
|
|
192
|
+
operator_guidance: rule.operator_guidance,
|
|
193
|
+
doc_ref: rule.doc_ref,
|
|
194
|
+
tags: rule.tags,
|
|
162
195
|
evaluate,
|
|
163
196
|
render: (ctx) => renderer.render(rule.message, ctx),
|
|
164
197
|
source_convention: conventionId,
|
|
@@ -172,6 +205,9 @@ export class RuleEngine {
|
|
|
172
205
|
level: rule.action,
|
|
173
206
|
message: rule.message,
|
|
174
207
|
suggestion: rule.suggestion,
|
|
208
|
+
operator_guidance: rule.operator_guidance,
|
|
209
|
+
doc_ref: rule.doc_ref,
|
|
210
|
+
tags: rule.tags,
|
|
175
211
|
evaluate,
|
|
176
212
|
render: (ctx) => renderer.render(rule.message, ctx),
|
|
177
213
|
source_convention: conventionId,
|