@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.
- package/README.md +72 -0
- package/dist/agents/reef-inspect-figma.md +77 -0
- package/dist/agents/reef-review-backend.md.tmpl +112 -0
- package/dist/agents/reef-review-frontend.md.tmpl +78 -0
- package/dist/agents/reef-review-infra.md +47 -0
- package/dist/agents/reef-review-security.md.tmpl +80 -0
- package/dist/agents/reef-scope-analysis.md +64 -0
- package/dist/build-registry.js +375 -0
- package/dist/cli.js +8581 -0
- package/dist/config-schema.json +133 -0
- package/dist/env-examples/context7.env-example +19 -0
- package/dist/env-examples/feishu-wiki.env-example +16 -0
- package/dist/env-examples/figma.env-example +16 -0
- package/dist/env-examples/github.env-example +20 -0
- package/dist/env-examples/jira.env-example +20 -0
- package/dist/hooks/mcp-hook.sh +77 -0
- package/dist/hooks/reef-auto-format.sh.tmpl +72 -0
- package/dist/hooks/reef-block-dangerous.sh +70 -0
- package/dist/hooks/reef-hooks.json +72 -0
- package/dist/hooks/reef-intent-detect.sh +129 -0
- package/dist/hooks/reef-protect-files.sh +55 -0
- package/dist/hooks/reef-run-tests.sh +84 -0
- package/dist/hooks/reef-scope-check.sh +386 -0
- package/dist/hooks/reef-scope-ci.sh +28 -0
- package/dist/hooks/reef-scope-gate.sh +115 -0
- package/dist/hooks/reef-scope-pre-commit.sh.tmpl +28 -0
- package/dist/hooks/reef-scope-setup.sh +204 -0
- package/dist/hooks/reef-scope-split.sh +203 -0
- package/dist/hooks/sweep-hooks.json +14 -0
- package/dist/hooks/sweep-mcp-hook.sh +77 -0
- package/dist/hooks/tide-hooks.json +14 -0
- package/dist/hooks/tide-session-preload.sh +17 -0
- package/dist/mcp/code-hosting/github.json +20 -0
- package/dist/mcp/design-tools/figma.json +19 -0
- package/dist/mcp/docs-reference/context7.json +28 -0
- package/dist/mcp/e2e-testing/playwright.json +13 -0
- package/dist/mcp/knowledge-base/feishu-wiki.json +19 -0
- package/dist/mcp/project-management/jira.json +27 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepflow-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepflow-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepflow-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepflow-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepflow-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepflow-mcp-playwright-read/SKILL.md +79 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-read/SKILL.md +65 -0
- package/dist/mcp-skills/deepstorm-mcp-feishu-wiki-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-figma-read/SKILL.md +98 -0
- package/dist/mcp-skills/deepstorm-mcp-github-read/SKILL.md +62 -0
- package/dist/mcp-skills/deepstorm-mcp-github-write/SKILL.md +63 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-read/SKILL.md +80 -0
- package/dist/mcp-skills/deepstorm-mcp-jira-write/SKILL.md +74 -0
- package/dist/mcp-skills/deepstorm-mcp-playwright-read/SKILL.md +79 -0
- package/dist/registry.json +818 -0
- package/dist/skills/atoll-ops/SKILL.md +46 -0
- package/dist/skills/reef-commit/SKILL.md +127 -0
- package/dist/skills/reef-gen-backend/SKILL.md.tmpl +87 -0
- package/dist/skills/reef-gen-backend/variants/java/steps.md +28 -0
- package/dist/skills/reef-gen-backend/variants/python/steps.md +70 -0
- package/dist/skills/reef-gen-frontend/SKILL.md.tmpl +83 -0
- package/dist/skills/reef-gen-frontend/variants/angular/steps.md +30 -0
- package/dist/skills/reef-harden/EXAMPLES.md +89 -0
- package/dist/skills/reef-harden/SKILL.md +136 -0
- package/dist/skills/reef-pr/SKILL.md +97 -0
- package/dist/skills/reef-review/SKILL.md.tmpl +107 -0
- package/dist/skills/reef-scope/SKILL.md +134 -0
- package/dist/skills/reef-start/SKILL.md.tmpl +562 -0
- package/dist/skills/reef-start/references/jira-start-subagent.md +60 -0
- package/dist/skills/reef-style-backend/SKILL.md.tmpl +134 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/chat-client.md +96 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/rag.md +94 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/structured-output.md +62 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/examples/tool-calling.md +68 -0
- package/dist/skills/reef-style-backend/fragments/java/ai/spring-ai/quick-reference.md +220 -0
- package/dist/skills/reef-style-backend/fragments/java/api-spec/quick-reference.md +148 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/examples/database-migration.md +131 -0
- package/dist/skills/reef-style-backend/fragments/java/db-migration/liquibase/quick-reference.md +103 -0
- package/dist/skills/reef-style-backend/fragments/java/dependency-management/quick-reference.md +119 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/examples/error-code-enum.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/exception-handling/quick-reference.md +181 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/controller.md +95 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/dto-mapper.md +121 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/infrastructure.md +179 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/service-entity.md +202 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/examples/testing.md +107 -0
- package/dist/skills/reef-style-backend/fragments/java/framework/spring-boot/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/java/orm/hibernate/quick-reference.md +150 -0
- package/dist/skills/reef-style-backend/fragments/java/security-redlines/quick-reference.md +197 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/examples/user-repository-test.md +69 -0
- package/dist/skills/reef-style-backend/fragments/java/test/data-jpa-test/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/examples/user-service-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/junit5/quick-reference.md +100 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/examples/user-controller-test.md +61 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-mvc-test/quick-reference.md +85 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/examples/user-service-integration-test.md +56 -0
- package/dist/skills/reef-style-backend/fragments/java/test/spring-service-test/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/alembic-migration/quick-reference.md +77 -0
- package/dist/skills/reef-style-backend/fragments/python/api-spec/quick-reference.md +164 -0
- package/dist/skills/reef-style-backend/fragments/python/dependency-management/quick-reference.md +139 -0
- package/dist/skills/reef-style-backend/fragments/python/exception-handling/quick-reference.md +177 -0
- package/dist/skills/reef-style-backend/fragments/python/fastapi-quick-reference/quick-reference.md +101 -0
- package/dist/skills/reef-style-backend/fragments/python/langchain/quick-reference.md +135 -0
- package/dist/skills/reef-style-backend/fragments/python/pytest-testing/quick-reference.md +111 -0
- package/dist/skills/reef-style-backend/fragments/python/ruff-mypy-toolchain/quick-reference.md +83 -0
- package/dist/skills/reef-style-backend/fragments/python/security-redlines/quick-reference.md +207 -0
- package/dist/skills/reef-style-backend/fragments/python/sqlalchemy-orm/quick-reference.md +91 -0
- package/dist/skills/reef-style-backend/variants/java/examples/code-wrapping.md +227 -0
- package/dist/skills/reef-style-backend/variants/java/examples/contributor-pattern.md +97 -0
- package/dist/skills/reef-style-backend/variants/java/quick-reference.md +117 -0
- package/dist/skills/reef-style-backend/variants/python/examples/crud-router.md +71 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pydantic-schema.md +45 -0
- package/dist/skills/reef-style-backend/variants/python/examples/pytest-fixture.md +60 -0
- package/dist/skills/reef-style-backend/variants/python/examples/sqlalchemy-model.md +31 -0
- package/dist/skills/reef-style-backend/variants/python/quick-reference.md +111 -0
- package/dist/skills/reef-style-frontend/SKILL.md.tmpl +70 -0
- package/dist/skills/reef-style-frontend/fragments/css/tailwind/quick-reference.md +79 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/examples/testing.md +150 -0
- package/dist/skills/reef-style-frontend/fragments/test/vitest/quick-reference.md +141 -0
- package/dist/skills/reef-style-frontend/fragments/ts-config/strict/quick-reference.md +112 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/examples/ui-components.md +134 -0
- package/dist/skills/reef-style-frontend/fragments/ui-lib/primeng/quick-reference.md +66 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/code-wrapping.md +252 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/component-types-pipes.md +56 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/entity-types.md +100 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/forms-layer.md +119 -0
- package/dist/skills/reef-style-frontend/variants/angular/examples/service-routing.md +95 -0
- package/dist/skills/reef-style-frontend/variants/angular/quick-reference.md +100 -0
- package/dist/skills/reef-testcase/SKILL.md +138 -0
- package/dist/skills/reef-testcase/references/coverage-dimensions.md +99 -0
- package/dist/skills/reef-testcase/references/test-case-template.md +63 -0
- package/dist/skills/sweep-init/SKILL.md +286 -0
- package/dist/skills/sweep-init/scripts/flow-selector.mjs +305 -0
- package/dist/skills/sweep-plan/SKILL.md.tmpl +312 -0
- package/dist/skills/sweep-plan/references/test-flow-template.md +46 -0
- package/dist/skills/sweep-run/SKILL.md +437 -0
- package/dist/skills/sweep-run/scripts/env-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/flow-parser.mjs +329 -0
- package/dist/skills/sweep-run/scripts/flow-selector.mjs +483 -0
- package/dist/skills/sweep-run/scripts/mcp-manager.mjs +208 -0
- package/dist/skills/sweep-run/scripts/spec-compiler.mjs +303 -0
- package/dist/skills/tide-discuss/SKILL.md.tmpl +449 -0
- package/dist/skills/tide-discuss/references/checklists.md +88 -0
- package/dist/skills/tide-discuss/references/data-format.md +237 -0
- package/dist/skills/tide-discuss/references/prd-template.md +134 -0
- package/dist/skills/tide-discuss/references/publish-flow.md +167 -0
- package/dist/skills/tide-discuss/references/role-prompts.md +105 -0
- 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" "$@"
|