@deepstorm/cli 0.1.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.
Files changed (148) hide show
  1. package/README.md +72 -0
  2. package/dist/agents/reef-inspect-figma.md +77 -0
  3. package/dist/agents/reef-review-backend.md.tmpl +112 -0
  4. package/dist/agents/reef-review-frontend.md.tmpl +78 -0
  5. package/dist/agents/reef-review-infra.md +47 -0
  6. package/dist/agents/reef-review-security.md.tmpl +80 -0
  7. package/dist/agents/reef-scope-analysis.md +64 -0
  8. package/dist/build-registry.js +375 -0
  9. package/dist/cli.js +8581 -0
  10. package/dist/config-schema.json +133 -0
  11. package/dist/env-examples/context7.env-example +19 -0
  12. package/dist/env-examples/feishu-wiki.env-example +16 -0
  13. package/dist/env-examples/figma.env-example +16 -0
  14. package/dist/env-examples/github.env-example +20 -0
  15. package/dist/env-examples/jira.env-example +20 -0
  16. package/dist/hooks/mcp-hook.sh +77 -0
  17. package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
  18. package/dist/hooks/reef-block-dangerous.sh +70 -0
  19. package/dist/hooks/reef-hooks.json +72 -0
  20. package/dist/hooks/reef-intent-detect.sh +129 -0
  21. package/dist/hooks/reef-protect-files.sh +55 -0
  22. package/dist/hooks/reef-run-tests.sh +84 -0
  23. package/dist/hooks/reef-scope-check.sh +386 -0
  24. package/dist/hooks/reef-scope-ci.sh +28 -0
  25. package/dist/hooks/reef-scope-gate.sh +115 -0
  26. package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
  27. package/dist/hooks/reef-scope-setup.sh +204 -0
  28. package/dist/hooks/reef-scope-split.sh +203 -0
  29. package/dist/hooks/sweep-hooks.json +14 -0
  30. package/dist/hooks/sweep-mcp-hook.sh +77 -0
  31. package/dist/hooks/tide-hooks.json +14 -0
  32. package/dist/hooks/tide-session-preload.sh +17 -0
  33. package/dist/mcp/code-hosting/github.json +20 -0
  34. package/dist/mcp/design-tools/figma.json +19 -0
  35. package/dist/mcp/docs-reference/context7.json +28 -0
  36. package/dist/mcp/e2e-testing/playwright.json +13 -0
  37. package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
  38. package/dist/mcp/project-management/jira.json +27 -0
  39. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
  40. package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
  41. package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
  42. package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
  43. package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
  44. package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
  45. package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
  46. package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
  47. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
  48. package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
  49. package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
  50. package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
  51. package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
  52. package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
  53. package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
  54. package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
  55. package/dist/registry.json +818 -0
  56. package/dist/skills/atoll-ops/SKILL.md +46 -0
  57. package/dist/skills/reef-commit/SKILL.md +127 -0
  58. package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
  59. package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
  60. package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
  61. package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
  62. package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
  63. package/dist/skills/reef-harden/EXAMPLES.md +89 -0
  64. package/dist/skills/reef-harden/SKILL.md +136 -0
  65. package/dist/skills/reef-pr/SKILL.md +97 -0
  66. package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
  67. package/dist/skills/reef-scope/SKILL.md +134 -0
  68. package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
  69. package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
  70. package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
  71. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
  72. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
  73. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
  74. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
  75. package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
  76. package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
  77. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
  78. package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
  79. package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
  80. package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
  81. package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
  82. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
  83. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
  84. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
  85. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
  86. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
  87. package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
  88. package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
  89. package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
  90. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
  91. package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
  92. package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
  93. package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
  94. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
  95. package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
  96. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
  97. package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
  98. package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
  99. package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
  100. package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
  101. package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
  102. package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
  103. package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
  104. package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
  105. package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
  106. package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
  107. package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
  108. package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
  109. package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
  110. package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
  111. package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
  112. package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
  113. package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
  114. package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
  115. package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
  116. package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
  117. package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
  118. package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
  119. package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
  120. package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
  121. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
  122. package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
  123. package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
  124. package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
  125. package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
  126. package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
  127. package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
  128. package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
  129. package/dist/skills/reef-testcase/SKILL.md +138 -0
  130. package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
  131. package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
  132. package/dist/skills/sweep-init/SKILL.md +286 -0
  133. package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
  134. package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
  135. package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
  136. package/dist/skills/sweep-run/SKILL.md +437 -0
  137. package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
  138. package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
  139. package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
  140. package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
  141. package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
  142. package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
  143. package/dist/skills/tide-discuss/references/checklists.md +88 -0
  144. package/dist/skills/tide-discuss/references/data-format.md +237 -0
  145. package/dist/skills/tide-discuss/references/prd-template.md +134 -0
  146. package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
  147. package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
  148. package/package.json +38 -0
@@ -0,0 +1,55 @@
1
+ #!/bin/bash
2
+ # reef-protect-files.sh
3
+ # PreToolUse: 保护敏感文件不被修改
4
+
5
+ INPUT=$(cat)
6
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""') || exit 0
7
+
8
+ [ -z "$FILE_PATH" ] && exit 0
9
+
10
+ FILENAME=$(basename "$FILE_PATH")
11
+
12
+ PROTECTED_PATTERNS=(
13
+ ".env"
14
+ "package-lock.json"
15
+ "yarn.lock"
16
+ "pnpm-lock.yaml"
17
+ )
18
+
19
+ PROTECTED_DIRS=(
20
+ ".git/"
21
+ "node_modules/"
22
+ )
23
+
24
+ for dir in "${PROTECTED_DIRS[@]}"; do
25
+ if [[ "$FILE_PATH" == *"$dir"* ]]; then
26
+ cat <<EOF
27
+ {
28
+ "hookSpecificOutput": {
29
+ "hookEventName": "PreToolUse",
30
+ "permissionDecision": "deny",
31
+ "permissionDecisionReason": "Cannot modify files in protected directory: $dir"
32
+ }
33
+ }
34
+ EOF
35
+ exit 0
36
+ fi
37
+ done
38
+
39
+ for pattern in "${PROTECTED_PATTERNS[@]}"; do
40
+ if [[ "$FILENAME" == "$pattern" ]]; then
41
+ cat <<EOF
42
+ {
43
+ "hookSpecificOutput": {
44
+ "hookEventName": "PreToolUse",
45
+ "permissionDecision": "deny",
46
+ "permissionDecisionReason": "Cannot modify protected file: $pattern"
47
+ }
48
+ }
49
+ EOF
50
+ exit 0
51
+ fi
52
+ done
53
+
54
+ # 默认放行
55
+ exit 0
@@ -0,0 +1,84 @@
1
+ #!/bin/bash
2
+ # reef-run-tests.sh
3
+ # Stop hook: 停止前运行测试,超时后放行
4
+ # 使用 timeout 避免长时间阻塞退出流程;无 timeout 命令时回退为普通运行
5
+
6
+ export PATH="$HOME/bin:/usr/local/bin:$PATH"
7
+
8
+ cd "$CLAUDE_PROJECT_DIR" 2>/dev/null || cd "$(dirname "$0")/../.." 2>/dev/null || true
9
+
10
+ TIMEOUT=30
11
+ FRONTEND_FAILED=false
12
+ BACKEND_FAILED=false
13
+
14
+ # timeout 命令兼容(macOS: gtimeout, Linux: timeout)
15
+ if command -v gtimeout &>/dev/null; then
16
+ TIMEOUT_CMD="gtimeout $TIMEOUT"
17
+ elif command -v timeout &>/dev/null; then
18
+ TIMEOUT_CMD="timeout $TIMEOUT"
19
+ fi
20
+
21
+ # Python 测试(pytest)
22
+ if [ -f "pyproject.toml" ] || [ -f "pytest.ini" ] || [ -f "setup.cfg" ]; then
23
+ if ! command -v pytest &>/dev/null && ! python -m pytest --version &>/dev/null 2>&1; then
24
+ exit 0
25
+ fi
26
+ echo "Running Python tests (python -m pytest, timeout ${TIMEOUT}s)..." >&2
27
+ if $TIMEOUT_CMD python -m pytest 2>&1; then
28
+ echo "Python tests passed." >&2
29
+ else
30
+ rc=$?
31
+ if [ -n "$TIMEOUT_CMD" ] && [ $rc -eq 124 ]; then
32
+ echo "[reef-run-tests] ⏱ Python tests timed out after ${TIMEOUT}s, continuing..." >&2
33
+ else
34
+ BACKEND_FAILED=true
35
+ echo "Python tests FAILED." >&2
36
+ fi
37
+ fi
38
+ fi
39
+
40
+ # 前端测试(pnpm)
41
+ if [ -f "package.json" ]; then
42
+ echo "Running frontend tests (pnpm test, timeout ${TIMEOUT}s)..." >&2
43
+ if $TIMEOUT_CMD pnpm test 2>&1; then
44
+ echo "Frontend tests passed." >&2
45
+ else
46
+ rc=$?
47
+ if [ -n "$TIMEOUT_CMD" ] && [ $rc -eq 124 ]; then
48
+ echo "[reef-run-tests] ⏱ Frontend tests timed out after ${TIMEOUT}s, continuing..." >&2
49
+ else
50
+ FRONTEND_FAILED=true
51
+ echo "Frontend tests FAILED." >&2
52
+ fi
53
+ fi
54
+ fi
55
+
56
+ # 后端测试(Gradle)
57
+ if [ -f "build.gradle.kts" ] || [ -f "build.gradle" ]; then
58
+ echo "Running backend tests (./gradlew test, timeout ${TIMEOUT}s)..." >&2
59
+ if $TIMEOUT_CMD ./gradlew test 2>&1; then
60
+ echo "Backend tests passed." >&2
61
+ else
62
+ rc=$?
63
+ if [ -n "$TIMEOUT_CMD" ] && [ $rc -eq 124 ]; then
64
+ echo "[reef-run-tests] ⏱ Backend tests timed out after ${TIMEOUT}s, continuing..." >&2
65
+ else
66
+ BACKEND_FAILED=true
67
+ echo "Backend tests FAILED." >&2
68
+ fi
69
+ fi
70
+ fi
71
+
72
+ if [ "$FRONTEND_FAILED" = false ] && [ "$BACKEND_FAILED" = false ]; then
73
+ echo '{"decision": "approve", "reason": "All tests passed."}'
74
+ exit 0
75
+ else
76
+ cat <<EOF
77
+ {
78
+ "decision": "block",
79
+ "reason": "Tests are failing. Please fix the issues before stopping.",
80
+ "continue": true
81
+ }
82
+ EOF
83
+ exit 0
84
+ fi
@@ -0,0 +1,386 @@
1
+ #!/bin/bash
2
+ # reef-scope-check.sh
3
+ # Core: AI 语义分析 git diff,检测业务领域范围
4
+ #
5
+ # 功能:
6
+ # 1. 接收 git diff 输入(stdin 或自动获取)
7
+ # 2. 调用 LLM API(默认 Claude)分析涉及的业务领域
8
+ # 3. 输出结构化 JSON 报告
9
+ #
10
+ # 用法:
11
+ # git diff main...HEAD | bash reef-scope-check.sh
12
+ # bash reef-scope-check.sh --diff-from main
13
+ #
14
+ # 依赖:
15
+ # - .env 文件中的 ANTHROPIC_API_KEY(或其他 LLM API key)
16
+ # - curl (用于 API 调用)
17
+
18
+ set -euo pipefail
19
+
20
+ # ── 配置 ──────────────────────────────────────────────────────────
21
+
22
+ # 默认 LLM API 配置
23
+ LLM_API_URL="${LLM_API_URL:-https://api.anthropic.com/v1/messages}"
24
+ LLM_API_KEY="${ANTHROPIC_API_KEY:-${LLM_API_KEY:-}}"
25
+ LLM_MODEL="${LLM_MODEL:-claude-sonnet-4-20250514}"
26
+
27
+ # 项目根目录(自动检测 git 仓库根目录)
28
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
29
+
30
+ # 项目配置文件
31
+ SCOPE_CONFIG="${PROJECT_ROOT}/.deepflow/scope-config.json"
32
+
33
+ # diff 大小限制(字符数),超过则截断采样
34
+ MAX_DIFF_CHARS="${MAX_DIFF_CHARS:-20000}"
35
+
36
+ # ── 辅助函数 ─────────────────────────────────────────────────────
37
+
38
+ # 读取配置文件(支持 enable/disable、领域对齐列表)
39
+ read_config() {
40
+ if [ -f "$SCOPE_CONFIG" ]; then
41
+ cat "$SCOPE_CONFIG"
42
+ else
43
+ echo '{"enabled": true, "domains": [], "ciEnabled": true}'
44
+ fi
45
+ }
46
+
47
+ # 检查 scope 检查是否启用
48
+ is_enabled() {
49
+ local mode="${1:-local}" # local 或 ci
50
+ local config
51
+ config="$(read_config)"
52
+
53
+ if [ "$mode" = "ci" ]; then
54
+ echo "$config" | python3 -c "import json,sys; c=json.load(sys.stdin); print(str(c.get('ciEnabled', c.get('enabled', True))).lower())" 2>/dev/null || echo "true"
55
+ else
56
+ echo "$config" | python3 -c "import json,sys; c=json.load(sys.stdin); print(str(c.get('enabled', True)).lower())" 2>/dev/null || echo "true"
57
+ fi
58
+ }
59
+
60
+ # 获取分支 diff
61
+ get_diff() {
62
+ local from_branch="${1:-main}"
63
+
64
+ # 若通过 stdin 提供 diff,直接读取
65
+ if [ ! -t 0 ]; then
66
+ cat
67
+ return
68
+ fi
69
+
70
+ # 否则自动获取当前分支与目标分支的 diff
71
+ local fork_point
72
+ fork_point="$(git merge-base "$from_branch" HEAD 2>/dev/null || echo "")"
73
+ if [ -n "$fork_point" ]; then
74
+ git diff "$fork_point"..HEAD 2>/dev/null || git diff HEAD 2>/dev/null || true
75
+ else
76
+ git diff HEAD 2>/dev/null || true
77
+ fi
78
+ }
79
+
80
+ # 截断大 diff — 保留文件头和部分上下文
81
+ truncate_diff() {
82
+ local input
83
+ input="$(cat)"
84
+ local len="${#input}"
85
+
86
+ if [ "$len" -le "$MAX_DIFF_CHARS" ]; then
87
+ echo "$input"
88
+ return
89
+ fi
90
+
91
+ # 截断:保留文件变更列表 + 每文件的部分 diff
92
+ echo "$input" | head -c "$MAX_DIFF_CHARS"
93
+ echo ""
94
+ echo "... [diff truncated at ${MAX_DIFF_CHARS} chars, ${len} total]"
95
+ }
96
+
97
+ # 调用 LLM API 进行领域分析
98
+ call_llm_analysis() {
99
+ local diff_content="$1"
100
+ local config
101
+ config="$(read_config)"
102
+
103
+ # 获取领域对齐列表(如果有)
104
+ local domains_hint=""
105
+ domains_hint="$(echo "$config" | python3 -c "
106
+ import json, sys
107
+ try:
108
+ c = json.load(sys.stdin)
109
+ domains = c.get('domains', [])
110
+ if domains:
111
+ print('项目已定义的业务领域:' + ', '.join(domains))
112
+ else:
113
+ print('')
114
+ except:
115
+ print('')
116
+ " 2>/dev/null || echo "")"
117
+
118
+ # 构建分析 prompt
119
+ local prompt
120
+ prompt=$(cat <<PROMPT
121
+ 你是一个代码变更分析专家。你需要分析以下 git diff 内容,判断它涉及哪些业务领域。
122
+
123
+ $domains_hint
124
+
125
+ 请按以下 JSON 格式输出分析结果(不要输出其他内容):
126
+
127
+ {
128
+ "domains": [
129
+ {
130
+ "name": "业务领域英文名(kebab-case)",
131
+ "confidence": 0.0-1.0,
132
+ "explanation": "为什么这段代码属于这个领域(中文)"
133
+ }
134
+ ],
135
+ "summary": "整体变更说明(中文,一句话)",
136
+ "suggested_split": [
137
+ {
138
+ "branch_name": "建议的分支名",
139
+ "files_count": 0,
140
+ "description": "该分支的变更说明"
141
+ }
142
+ ]
143
+ }
144
+
145
+ 规则:
146
+ 1. 领域数量 = 实际涉及的业务领域数量。如果只有一个领域,suggested_split 可以是空数组。
147
+ 2. 可信度 < 0.6 时,标注 "需人工确认"
148
+ 3. 文档变更归类为 "documentation" 领域
149
+ 4. 不要编造领域,仅基于 diff 内容判断
150
+ 5. 每个领域必须有对应的文件变更支撑
151
+
152
+ 以下是 diff 内容:
153
+
154
+ \`\`\`diff
155
+ ${diff_content}
156
+ \`\`\`
157
+ PROMPT
158
+ )
159
+
160
+ # API 调用 - 支持不同 LLM 提供商
161
+ if [ -n "$ANTHROPIC_API_KEY" ]; then
162
+ # Claude API
163
+ local response
164
+ response=$(curl -s -w "\n%{http_code}" "$LLM_API_URL" \
165
+ -H "Content-Type: application/json" \
166
+ -H "x-api-key: $ANTHROPIC_API_KEY" \
167
+ -H "anthropic-version: 2023-06-01" \
168
+ -d "$(cat <<JSONBODY
169
+ {
170
+ "model": "$LLM_MODEL",
171
+ "max_tokens": 2048,
172
+ "system": "你是一个代码变更分析专家。你的任务是通过分析 git diff 来确定代码变更涉及的业务领域。只输出 JSON。",
173
+ "messages": [
174
+ {"role": "user", "content": "$(echo "$prompt" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 2>/dev/null || echo "$prompt")"}
175
+ ]
176
+ }
177
+ JSONBODY
178
+ )" 2>/dev/null || echo '{"error":"API call failed"}')
179
+
180
+ local http_code
181
+ http_code="$(echo "$response" | tail -1)"
182
+ local body
183
+ body="$(echo "$response" | sed '$d')"
184
+
185
+ if [ "$http_code" != "200" ]; then
186
+ echo "{\"error\":\"API returned $http_code\",\"domains\":[],\"summary\":\"API 调用失败,跳过范围检查\",\"suggested_split\":[]}"
187
+ return
188
+ fi
189
+
190
+ # 提取 content 中的 JSON
191
+ echo "$body" | python3 -c "
192
+ import json, sys
193
+ try:
194
+ resp = json.load(sys.stdin)
195
+ content = resp.get('content', [])
196
+ for block in content:
197
+ if block.get('type') == 'text':
198
+ text = block.get('text', '').strip()
199
+ # 尝试提取 JSON(可能在 markdown 代码块中)
200
+ if text.startswith('{'):
201
+ print(text)
202
+ elif '\`\`\`json' in text:
203
+ start = text.index('\`\`\`json') + 7
204
+ end = text.index('\`\`\`', start)
205
+ print(text[start:end].strip())
206
+ elif '\`\`\`' in text:
207
+ start = text.index('\`\`\`') + 3
208
+ end = text.index('\`\`\`', start)
209
+ print(text[start:end].strip())
210
+ else:
211
+ print(text)
212
+ except Exception as e:
213
+ print(json.dumps({'error': str(e), 'domains': [], 'summary': '解析失败', 'suggested_split': []}))
214
+ " 2>/dev/null || echo "{\"domains\":[],\"summary\":\"分析失败\",\"suggested_split\":[]}"
215
+
216
+ elif [ -n "$OPENAI_API_KEY" ]; then
217
+ # OpenAI API(备选)
218
+ local response
219
+ response=$(curl -s -w "\n%{http_code}" "https://api.openai.com/v1/chat/completions" \
220
+ -H "Content-Type: application/json" \
221
+ -H "Authorization: Bearer $OPENAI_API_KEY" \
222
+ -d "$(cat <<JSONBODY
223
+ {
224
+ "model": "gpt-4o",
225
+ "max_tokens": 2048,
226
+ "messages": [
227
+ {"role": "system", "content": "你是一个代码变更分析专家。分析 git diff 涉及的 business domain,只输出 JSON。"},
228
+ {"role": "user", "content": "$(echo "$prompt" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 2>/dev/null || echo "$prompt")"}
229
+ ]
230
+ }
231
+ JSONBODY
232
+ )" 2>/dev/null || echo '{"error":"API call failed"}')
233
+
234
+ local http_code
235
+ http_code="$(echo "$response" | tail -1)"
236
+ local body
237
+ body="$(echo "$response" | sed '$d')"
238
+
239
+ if [ "$http_code" != "200" ]; then
240
+ echo "{\"error\":\"API returned $http_code\",\"domains\":[],\"summary\":\"API 调用失败,跳过范围检查\",\"suggested_split\":[]}"
241
+ return
242
+ fi
243
+
244
+ echo "$body" | python3 -c "
245
+ import json, sys
246
+ try:
247
+ resp = json.load(sys.stdin)
248
+ text = resp['choices'][0]['message']['content']
249
+ print(text)
250
+ except Exception as e:
251
+ print(json.dumps({'error': str(e), 'domains': [], 'summary': '解析失败', 'suggested_split': []}))
252
+ " 2>/dev/null || echo "{\"domains\":[],\"summary\":\"分析失败\",\"suggested_split\":[]}"
253
+
254
+ else
255
+ # 无 API key 配置 — fallback 模式
256
+ echo "{\"domains\":[],\"summary\":\"未配置 LLM API Key,跳过范围检查\",\"suggested_split\":[]}"
257
+ fi
258
+ }
259
+
260
+ # ── 主逻辑 ────────────────────────────────────────────────────────
261
+
262
+ main() {
263
+ local from_branch="main"
264
+ local mode="local" # local 或 ci
265
+ local raw_output=false
266
+
267
+ # 参数解析
268
+ while [ $# -gt 0 ]; do
269
+ case "$1" in
270
+ --diff-from) from_branch="$2"; shift 2 ;;
271
+ --ci-mode) mode="ci"; shift ;;
272
+ --raw) raw_output=true; shift ;;
273
+ --help|-h)
274
+ echo "用法: reef-scope-check.sh [选项]"
275
+ echo " --diff-from <分支> 指定比较基准分支(默认: main)"
276
+ echo " --ci-mode CI 模式,检查 CI 配置门禁"
277
+ echo " --raw 输出原始 JSON,不加格式"
278
+ echo " 从 stdin 传入 diff: git diff main...HEAD | reef-scope-check.sh"
279
+ exit 0
280
+ ;;
281
+ *) shift ;;
282
+ esac
283
+ done
284
+
285
+ # 检查是否启用
286
+ local enabled
287
+ enabled="$(is_enabled "$mode")"
288
+ if [ "$enabled" != "true" ]; then
289
+ [ "$raw_output" = false ] && echo "ℹ️ Scope 检查已禁用(可通过 .deepflow/scope-config.json 启用)"
290
+ echo "{\"enabled\":false,\"domains\":[],\"summary\":\"检查已禁用\",\"suggested_split\":[]}"
291
+ exit 0
292
+ fi
293
+
294
+ # 获取 diff
295
+ local diff
296
+ diff="$(get_diff "$from_branch")"
297
+
298
+ if [ -z "$diff" ]; then
299
+ [ "$raw_output" = false ] && echo "ℹ️ 无 diff 可分析"
300
+ echo "{\"domains\":[],\"summary\":\"无变更\",\"suggested_split\":[]}"
301
+ exit 0
302
+ fi
303
+
304
+ # 截断大 diff
305
+ local truncated_diff
306
+ truncated_diff="$(echo "$diff" | truncate_diff)"
307
+
308
+ # 调用 LLM 分析
309
+ local result
310
+ result="$(call_llm_analysis "$truncated_diff")"
311
+
312
+ # 输出结果
313
+ if [ "$raw_output" = true ]; then
314
+ echo "$result"
315
+ else
316
+ # 格式化输出
317
+ local domain_count
318
+ domain_count="$(echo "$result" | python3 -c "
319
+ import json, sys
320
+ try:
321
+ r = json.load(sys.stdin)
322
+ print(len(r.get('domains', [])))
323
+ except:
324
+ print('0')
325
+ " 2>/dev/null || echo "0")"
326
+
327
+ echo ""
328
+ if [ "$domain_count" = "0" ]; then
329
+ echo "✅ 未检测到明确的业务领域"
330
+ elif [ "$domain_count" = "1" ]; then
331
+ echo "✅ 检测到 1 个业务领域:"
332
+ else
333
+ echo "⚠️ 检测到 ${domain_count} 个业务领域:"
334
+ fi
335
+ echo ""
336
+ echo "$result" | python3 -c "
337
+ import json, sys
338
+ r = json.load(sys.stdin)
339
+ for d in r.get('domains', []):
340
+ conf = d.get('confidence', 0)
341
+ flag = ' ⚠️ 需人工确认' if conf < 0.6 else ''
342
+ print(f' • {d[\"name\"]} ({conf:.2f}){flag}')
343
+ print(f' {d.get(\"explanation\", \"\")}')
344
+ print()
345
+ print(f' 总结:{r.get(\"summary\", \"\")}')
346
+ if r.get('suggested_split'):
347
+ print()
348
+ print(' 建议拆分方案:')
349
+ for s in r['suggested_split']:
350
+ print(f' → {s[\"branch_name\"]}: {s.get(\"description\", \"\")}')
351
+ " 2>/dev/null || echo "$result"
352
+ fi
353
+
354
+ # 返回 domain 数量(用于 exit code)
355
+ local count
356
+ count="$(echo "$result" | python3 -c "
357
+ import json, sys
358
+ try:
359
+ r = json.load(sys.stdin)
360
+ print(len(r.get('domains', [])))
361
+ except:
362
+ print('0')
363
+ " 2>/dev/null || echo "0")"
364
+
365
+ # 纯检测模式:基于 domain 数量返回 exit code
366
+ # 0 = 单领域或无领域(通过),1+ = 多领域(阻断)
367
+ # documentation 领域不计入多领域阻断
368
+ local non_doc_count
369
+ non_doc_count="$(echo "$result" | python3 -c "
370
+ import json, sys
371
+ try:
372
+ r = json.load(sys.stdin)
373
+ nd = [d for d in r.get('domains', []) if d.get('name') != 'documentation']
374
+ print(len(nd))
375
+ except:
376
+ print('0')
377
+ " 2>/dev/null || echo "0")"
378
+
379
+ if [ "$non_doc_count" -gt 1 ]; then
380
+ exit 2 # 多领域 — 阻断
381
+ else
382
+ exit 0 # 通过
383
+ fi
384
+ }
385
+
386
+ main "$@"
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ # reef-scope-ci.sh
3
+ # CI 门禁脚本 — 在 CI/PR 中检查分支范围
4
+ #
5
+ # 用法:
6
+ # 方式1(自动获取 PR diff):
7
+ # reef-scope-ci.sh --base main
8
+ #
9
+ # 方式2(从 stdin 传入 diff):
10
+ # git diff main...HEAD | reef-scope-ci.sh
11
+ #
12
+ # 适用于:
13
+ # GitHub Actions, GitLab CI, Jenkins 等
14
+ #
15
+ # exit code:
16
+ # 0 = 通过
17
+ # 2 = 阻断(多领域)
18
+
19
+ set -euo pipefail
20
+
21
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
22
+ SCOPE_GATE="${SCRIPT_DIR}/reef-scope-gate.sh"
23
+
24
+ echo "🔍 [Scope Check] 正在检查分支范围..."
25
+ echo ""
26
+
27
+ # 传给 gate 脚本,加上 --ci 标记
28
+ exec "$SCOPE_GATE" --ci "$@"
@@ -0,0 +1,115 @@
1
+ #!/bin/bash
2
+ # reef-scope-gate.sh
3
+ # 门禁执行层:在 git commit / CI 时运行范围检查并阻断
4
+ #
5
+ # 用法:
6
+ # bash reef-scope-gate.sh # pre-commit 模式
7
+ # bash reef-scope-gate.sh --ci # CI 模式
8
+ #
9
+ # exit code:
10
+ # 0 = 通过(单领域或无变更)
11
+ # 2 = 阻断(多领域)
12
+
13
+ set -euo pipefail
14
+
15
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
17
+ SCOPE_CHECK="${SCRIPT_DIR}/reef-scope-check.sh"
18
+
19
+ # ── 主逻辑 ──────────────────────────────────────────────────────
20
+
21
+ main() {
22
+ local mode="local"
23
+ local from_branch="main"
24
+
25
+ while [ $# -gt 0 ]; do
26
+ case "$1" in
27
+ --ci) mode="ci"; shift ;;
28
+ --diff-from) from_branch="$2"; shift 2 ;;
29
+ --help|-h)
30
+ echo "用法: reef-scope-gate.sh [选项]"
31
+ echo " --ci CI 模式"
32
+ echo " --diff-from <分支> 指定比较基准分支"
33
+ exit 0
34
+ ;;
35
+ *) shift ;;
36
+ esac
37
+ done
38
+
39
+ # 运行范围检查,获取原始 JSON 结果
40
+ local result
41
+ result="$("$SCOPE_CHECK" --diff-from "$from_branch" --raw 2>/dev/null)" || true
42
+
43
+ # 提取 domain 列表(排除 documentation)
44
+ local domains_json
45
+ domains_json="$(echo "$result" | python3 -c "
46
+ import json, sys
47
+ try:
48
+ r = json.load(sys.stdin)
49
+ nd = [d for d in r.get('domains', []) if d.get('name') != 'documentation']
50
+ print(json.dumps(nd, ensure_ascii=False))
51
+ except:
52
+ print('[]')
53
+ " 2>/dev/null || echo "[]")"
54
+
55
+ # 判断是否需要阻断
56
+ local domain_count
57
+ domain_count="$(echo "$domains_json" | python3 -c "import json,sys; print(len(json.load(sys.stdin)))" 2>/dev/null || echo "0")"
58
+
59
+ if [ "$domain_count" -le 1 ]; then
60
+ # 通过
61
+ exit 0
62
+ fi
63
+
64
+ # ── 阻断 — 输出格式化报告 ──────────────────────────────────
65
+
66
+ echo ""
67
+ echo "╔══════════════════════════════════════════════════════════╗"
68
+ echo "║ 🚫 分支范围检查未通过 ║"
69
+ echo "║ 当前分支涉及 $domain_count 个业务领域 ║"
70
+ echo "╚══════════════════════════════════════════════════════════╝"
71
+ echo ""
72
+
73
+ echo "$domains_json" | python3 -c "
74
+ import json, sys
75
+ domains = json.load(sys.stdin)
76
+ for d in domains:
77
+ name = d.get('name', '?')
78
+ conf = d.get('confidence', 0)
79
+ expl = d.get('explanation', '')
80
+ flag = ' ⚠️ 需人工确认' if conf < 0.6 else ''
81
+ print(f' 领域: {name} (可信度: {conf:.2f}){flag}')
82
+ print(f' 说明: {expl}')
83
+ print()
84
+ " 2>/dev/null
85
+
86
+ echo " ── 建议 ──"
87
+ echo " 每个分支应只专注于一个业务领域。建议将当前变更拆分为"
88
+ echo " 多个独立分支,分别提交。"
89
+ echo ""
90
+
91
+ # 获取拆分建议
92
+ local split_info
93
+ split_info="$(echo "$result" | python3 -c "
94
+ import json, sys
95
+ try:
96
+ r = json.load(sys.stdin)
97
+ for s in r.get('suggested_split', []):
98
+ print(f' → {s.get(\"branch_name\", \"?\")}: {s.get(\"description\", \"\")}')
99
+ except:
100
+ print(' (暂无拆分建议)')
101
+ " 2>/dev/null)"
102
+
103
+ if [ -n "$split_info" ] && [ "$split_info" != " (暂无拆分建议)" ]; then
104
+ echo " ── 建议的拆分方案 ──"
105
+ echo "$split_info"
106
+ echo ""
107
+ fi
108
+
109
+ echo " 提示: 使用 'reef scope split' 命令可自动执行拆分"
110
+ echo ""
111
+
112
+ exit 2
113
+ }
114
+
115
+ main "$@"
@@ -0,0 +1,28 @@
1
+ #!/bin/bash
2
+ # reef-scope pre-commit hook
3
+ # 由 `reef scope setup` 安装到 .git/hooks/pre-commit
4
+ # 在 git commit 时自动检查分支范围
5
+
6
+ set -euo pipefail
7
+
8
+ # 获取 DeepFlow 插件路径
9
+ DEEPFLOW_PLUGIN_ROOT="${DEEPFLOW_PLUGIN_ROOT:-${CLAUDE_PLUGIN_ROOT:-}}"
10
+
11
+ # 如果无法获取插件根目录,尝试从 .git 上级查找
12
+ if [ -z "$DEEPFLOW_PLUGIN_ROOT" ]; then
13
+ PROJECT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || true)"
14
+ if [ -n "$PROJECT_ROOT" ]; then
15
+ # 检查是否在 reef 的安装路径中
16
+ if [ -f "${PROJECT_ROOT}/.deepflow/scope-config.json" ]; then
17
+ DEEPFLOW_PLUGIN_ROOT="$PROJECT_ROOT"
18
+ fi
19
+ fi
20
+ fi
21
+
22
+ # 如果还是找不到 reef 路径,跳过检查
23
+ if [ ! -f "${DEEPFLOW_PLUGIN_ROOT}/hooks/reef-scope-gate.sh" ]; then
24
+ exit 0
25
+ fi
26
+
27
+ # 执行门禁检查
28
+ bash "${DEEPFLOW_PLUGIN_ROOT}/hooks/reef-scope-gate.sh" "$@"