@chenmk/superflow 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/INSTALL.en.md +106 -0
- package/INSTALL.md +664 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/README.zh-CN.md +117 -0
- package/assets/context-templates/business-rules.md +98 -0
- package/assets/context-templates/decisions.md +153 -0
- package/assets/context-templates/external-systems.md +166 -0
- package/assets/context-templates/incidents.md +89 -0
- package/assets/manifest.json +53 -0
- package/assets/prompts/superflow-archive.md +9 -0
- package/assets/prompts/superflow-clarify.md +10 -0
- package/assets/prompts/superflow-design.md +10 -0
- package/assets/prompts/superflow-docs.md +10 -0
- package/assets/prompts/superflow-implement.md +10 -0
- package/assets/prompts/superflow-pipeline.md +13 -0
- package/assets/prompts/superflow-verify.md +10 -0
- package/assets/rules/superflow-phase-guard.md +50 -0
- package/assets/scripts/claude-auto-backup-hook.sh +313 -0
- package/assets/scripts/codex-auto-backup-hook.sh +361 -0
- package/assets/scripts/install-sql-pre-commit.sh +44 -0
- package/assets/scripts/superflow-contract-hooks.sh +744 -0
- package/assets/scripts/superflow-delivery-check.sh +315 -0
- package/assets/scripts/superflow-dependency-update-hook.sh +161 -0
- package/assets/scripts/superflow-enforce-hook.sh +70 -0
- package/assets/scripts/superflow-hook-guard.sh +132 -0
- package/assets/scripts/superflow-integration-evidence-hook.sh +80 -0
- package/assets/scripts/superflow-sql-sync-hook.py +950 -0
- package/assets/scripts/superflow-test-report-lint.py +433 -0
- package/assets/scripts/superflow-verify-integration.sh +90 -0
- package/assets/scripts/sync-settings-json.py +52 -0
- package/assets/skills/api-doc-changelog/SKILL.md +193 -0
- package/assets/skills/openspec-apply-change/SKILL.md +156 -0
- package/assets/skills/openspec-archive-change/SKILL.md +114 -0
- package/assets/skills/openspec-explore/SKILL.md +288 -0
- package/assets/skills/openspec-propose/SKILL.md +110 -0
- package/assets/skills/superflow-archive/SKILL.md +61 -0
- package/assets/skills/superflow-clarify/SKILL.md +146 -0
- package/assets/skills/superflow-clarify/agents/openai.yaml +4 -0
- package/assets/skills/superflow-design/SKILL.md +83 -0
- package/assets/skills/superflow-design/agents/openai.yaml +4 -0
- package/assets/skills/superflow-docs/SKILL.md +316 -0
- package/assets/skills/superflow-docs/agents/openai.yaml +4 -0
- package/assets/skills/superflow-hotfix/SKILL.md +48 -0
- package/assets/skills/superflow-implement/SKILL.md +461 -0
- package/assets/skills/superflow-implement/agents/openai.yaml +4 -0
- package/assets/skills/superflow-pipeline/SKILL.md +844 -0
- package/assets/skills/superflow-pipeline/agents/openai.yaml +4 -0
- package/assets/skills/superflow-pipeline/references/api-design-template.md +431 -0
- package/assets/skills/superflow-pipeline/references/architecture-design-template.md +119 -0
- package/assets/skills/superflow-pipeline/references/batch-prompt-template.md +536 -0
- package/assets/skills/superflow-pipeline/references/batch-split-guide.md +140 -0
- package/assets/skills/superflow-pipeline/references/decision-point.md +30 -0
- package/assets/skills/superflow-pipeline/references/dirty-worktree.md +35 -0
- package/assets/skills/superflow-pipeline/references/document-templates.md +123 -0
- package/assets/skills/superflow-pipeline/references/feature-gated-workflow.md +124 -0
- package/assets/skills/superflow-pipeline/references/implementation-prompt-template.md +1056 -0
- package/assets/skills/superflow-pipeline/references/mock-strategy-guide.md +86 -0
- package/assets/skills/superflow-pipeline/references/openspec-format.md +57 -0
- package/assets/skills/superflow-pipeline/references/orchestration.md +639 -0
- package/assets/skills/superflow-pipeline/references/p0-baseline-template.md +174 -0
- package/assets/skills/superflow-pipeline/references/project-config.md +40 -0
- package/assets/skills/superflow-pipeline/references/prompt-usage-template.md +152 -0
- package/assets/skills/superflow-pipeline/references/quality-gate.md +299 -0
- package/assets/skills/superflow-pipeline/references/quality-standards.md +190 -0
- package/assets/skills/superflow-pipeline/references/reviewer-checklist.md +154 -0
- package/assets/skills/superflow-pipeline/references/sql-risk-review-checklist.md +323 -0
- package/assets/skills/superflow-pipeline/references/subagent-progress.md +90 -0
- package/assets/skills/superflow-pipeline/references/superpower-technical-design-template.md +125 -0
- package/assets/skills/superflow-pipeline/references/test-execution-template.md +220 -0
- package/assets/skills/superflow-pipeline/references/test-guide.md +30 -0
- package/assets/skills/superflow-pipeline/references/traceability-matrix.md +106 -0
- package/assets/skills/superflow-pipeline/references/validation-integrity.md +134 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-archive.sh +178 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-env.sh +118 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-guard.sh +428 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-handoff.sh +296 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-state.sh +574 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-status.sh +172 -0
- package/assets/skills/superflow-pipeline/scripts/superflow-yaml-validate.sh +138 -0
- package/assets/skills/superflow-table-impact-analysis/SKILL.md +77 -0
- package/assets/skills/superflow-tweak/SKILL.md +46 -0
- package/assets/skills/superflow-verify/SKILL.md +112 -0
- package/assets/skills-en/api-doc-changelog/SKILL.md +193 -0
- package/assets/skills-en/openspec-apply-change/SKILL.md +156 -0
- package/assets/skills-en/openspec-archive-change/SKILL.md +114 -0
- package/assets/skills-en/openspec-explore/SKILL.md +288 -0
- package/assets/skills-en/openspec-propose/SKILL.md +110 -0
- package/assets/skills-en/superflow-archive/SKILL.md +61 -0
- package/assets/skills-en/superflow-clarify/SKILL.md +146 -0
- package/assets/skills-en/superflow-clarify/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-design/SKILL.md +83 -0
- package/assets/skills-en/superflow-design/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-docs/SKILL.md +316 -0
- package/assets/skills-en/superflow-docs/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-hotfix/SKILL.md +48 -0
- package/assets/skills-en/superflow-implement/SKILL.md +461 -0
- package/assets/skills-en/superflow-implement/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-pipeline/SKILL.md +844 -0
- package/assets/skills-en/superflow-pipeline/agents/openai.yaml +4 -0
- package/assets/skills-en/superflow-pipeline/references/api-design-template.md +431 -0
- package/assets/skills-en/superflow-pipeline/references/architecture-design-template.md +119 -0
- package/assets/skills-en/superflow-pipeline/references/batch-prompt-template.md +536 -0
- package/assets/skills-en/superflow-pipeline/references/batch-split-guide.md +140 -0
- package/assets/skills-en/superflow-pipeline/references/decision-point.md +30 -0
- package/assets/skills-en/superflow-pipeline/references/dirty-worktree.md +35 -0
- package/assets/skills-en/superflow-pipeline/references/document-templates.md +123 -0
- package/assets/skills-en/superflow-pipeline/references/feature-gated-workflow.md +124 -0
- package/assets/skills-en/superflow-pipeline/references/implementation-prompt-template.md +1056 -0
- package/assets/skills-en/superflow-pipeline/references/mock-strategy-guide.md +86 -0
- package/assets/skills-en/superflow-pipeline/references/openspec-format.md +57 -0
- package/assets/skills-en/superflow-pipeline/references/orchestration.md +639 -0
- package/assets/skills-en/superflow-pipeline/references/p0-baseline-template.md +174 -0
- package/assets/skills-en/superflow-pipeline/references/project-config.md +40 -0
- package/assets/skills-en/superflow-pipeline/references/prompt-usage-template.md +152 -0
- package/assets/skills-en/superflow-pipeline/references/quality-gate.md +299 -0
- package/assets/skills-en/superflow-pipeline/references/quality-standards.md +190 -0
- package/assets/skills-en/superflow-pipeline/references/reviewer-checklist.md +154 -0
- package/assets/skills-en/superflow-pipeline/references/sql-risk-review-checklist.md +323 -0
- package/assets/skills-en/superflow-pipeline/references/subagent-progress.md +90 -0
- package/assets/skills-en/superflow-pipeline/references/superpower-technical-design-template.md +125 -0
- package/assets/skills-en/superflow-pipeline/references/test-execution-template.md +220 -0
- package/assets/skills-en/superflow-pipeline/references/test-guide.md +30 -0
- package/assets/skills-en/superflow-pipeline/references/traceability-matrix.md +106 -0
- package/assets/skills-en/superflow-pipeline/references/validation-integrity.md +134 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-archive.sh +178 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-env.sh +118 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-guard.sh +428 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-handoff.sh +296 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-state.sh +574 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-status.sh +172 -0
- package/assets/skills-en/superflow-pipeline/scripts/superflow-yaml-validate.sh +138 -0
- package/assets/skills-en/superflow-table-impact-analysis/SKILL.md +77 -0
- package/assets/skills-en/superflow-tweak/SKILL.md +46 -0
- package/assets/skills-en/superflow-verify/SKILL.md +112 -0
- package/dist/cli/index.js +186 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/commands/archive.js +6 -0
- package/dist/commands/archive.js.map +1 -0
- package/dist/commands/clarify.js +6 -0
- package/dist/commands/clarify.js.map +1 -0
- package/dist/commands/design.js +6 -0
- package/dist/commands/design.js.map +1 -0
- package/dist/commands/docs.js +6 -0
- package/dist/commands/docs.js.map +1 -0
- package/dist/commands/doctor.js +473 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/implement.js +6 -0
- package/dist/commands/implement.js.map +1 -0
- package/dist/commands/init.js +471 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/pipeline.js +6 -0
- package/dist/commands/pipeline.js.map +1 -0
- package/dist/commands/scan.js +59 -0
- package/dist/commands/scan.js.map +1 -0
- package/dist/commands/status.js +173 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/uninstall.js +213 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/commands/update.js +187 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/commands/verify.js +6 -0
- package/dist/commands/verify.js.map +1 -0
- package/dist/core/assets.js +27 -0
- package/dist/core/assets.js.map +1 -0
- package/dist/core/context.js +100 -0
- package/dist/core/context.js.map +1 -0
- package/dist/core/dependencies.js +146 -0
- package/dist/core/dependencies.js.map +1 -0
- package/dist/core/detect.js +71 -0
- package/dist/core/detect.js.map +1 -0
- package/dist/core/i18n.js +103 -0
- package/dist/core/i18n.js.map +1 -0
- package/dist/core/integrity.js +46 -0
- package/dist/core/integrity.js.map +1 -0
- package/dist/core/manifest.js +18 -0
- package/dist/core/manifest.js.map +1 -0
- package/dist/core/prompts.js +20 -0
- package/dist/core/prompts.js.map +1 -0
- package/dist/core/registry.js +134 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/rules.js +17 -0
- package/dist/core/rules.js.map +1 -0
- package/dist/core/scripts.js +40 -0
- package/dist/core/scripts.js.map +1 -0
- package/dist/core/skill-check.js +31 -0
- package/dist/core/skill-check.js.map +1 -0
- package/dist/core/skills.js +56 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/state.js +43 -0
- package/dist/core/state.js.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/path.js +11 -0
- package/dist/utils/path.js.map +1 -0
- package/dist/utils/shell.js +29 -0
- package/dist/utils/shell.js.map +1 -0
- package/package.json +60 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SuperBridge Flow delivery closeout verifier.
|
|
3
|
+
# Blocks implementation commits that forget to update the matching PXX
|
|
4
|
+
# delivery documents and test evidence.
|
|
5
|
+
|
|
6
|
+
set -u
|
|
7
|
+
|
|
8
|
+
MODE="${1:-}"
|
|
9
|
+
|
|
10
|
+
read_json_field() {
|
|
11
|
+
local field="$1"
|
|
12
|
+
python3 -c "
|
|
13
|
+
import json, os, sys
|
|
14
|
+
try:
|
|
15
|
+
data = json.load(sys.stdin)
|
|
16
|
+
except Exception:
|
|
17
|
+
print('')
|
|
18
|
+
raise SystemExit
|
|
19
|
+
tool_input = data.get('tool_input', {})
|
|
20
|
+
print(tool_input.get('$field') or tool_input.get('cmd') or '')
|
|
21
|
+
" 2>/dev/null
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if [ "$MODE" = "--check-staged" ]; then
|
|
25
|
+
CWD="${2:-$(pwd)}"
|
|
26
|
+
else
|
|
27
|
+
INPUT=$(cat)
|
|
28
|
+
COMMAND=$(printf '%s' "$INPUT" | read_json_field command)
|
|
29
|
+
case "$COMMAND" in
|
|
30
|
+
*"git commit"*) ;;
|
|
31
|
+
*) exit 0 ;;
|
|
32
|
+
esac
|
|
33
|
+
CWD=$(printf '%s' "$INPUT" | python3 -c "
|
|
34
|
+
import json, os, sys
|
|
35
|
+
try:
|
|
36
|
+
data = json.load(sys.stdin)
|
|
37
|
+
except Exception:
|
|
38
|
+
print(os.getcwd())
|
|
39
|
+
raise SystemExit
|
|
40
|
+
tool_input = data.get('tool_input', {})
|
|
41
|
+
print(tool_input.get('cwd') or os.getcwd())
|
|
42
|
+
" 2>/dev/null)
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
REPO_ROOT=$(git -C "$CWD" rev-parse --show-toplevel 2>/dev/null)
|
|
46
|
+
if [ $? -ne 0 ]; then
|
|
47
|
+
exit 0
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
CHANGED=$(git -C "$REPO_ROOT" diff --cached --name-only)
|
|
51
|
+
if [ -z "$CHANGED" ]; then
|
|
52
|
+
exit 0
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
SDD_ACTIVE=0
|
|
56
|
+
if [ -f "$REPO_ROOT/.sdd-enforced" ]; then
|
|
57
|
+
SDD_ACTIVE=1
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
RUNTIME_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
|
|
61
|
+
'(^|/)src/main/.*\.(java|xml|yml|yaml|properties)$|(^|/)mapper/.*\.xml$|(^|/)sql/.*\.sql$' || true)
|
|
62
|
+
|
|
63
|
+
CODE_RUNTIME_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
|
|
64
|
+
'(^|/)src/main/.*\.(java|xml|yml|yaml|properties)$|(^|/)mapper/.*\.xml$' || true)
|
|
65
|
+
|
|
66
|
+
P_DIRS=$(printf '%s\n' "$CHANGED" | sed -nE \
|
|
67
|
+
's#^(.*embedded-changes/(p[0-9]+[^/]*))/.*$#\1#p' | sort -u)
|
|
68
|
+
|
|
69
|
+
REPORTS=$(printf '%s\n' "$CHANGED" | grep -E \
|
|
70
|
+
'(^|/)embedded-changes/p[0-9][^/]*/test-report\.md$|(^|/)test-report\.md$' || true)
|
|
71
|
+
|
|
72
|
+
AGGREGATE_DOCS=$(printf '%s\n' "$CHANGED" | grep -E \
|
|
73
|
+
'(^|/)openspec/changes/[^/]+/(tasks|test-report|traceability-matrix|sdd-quality-gate|tests)\.md$|(^|/)doc/openspec/changes/[^/]+/(tasks|test-report|traceability-matrix|sdd-quality-gate|tests)\.md$' || true)
|
|
74
|
+
|
|
75
|
+
SDD_DOCS_CHANGED=$(printf '%s\n' "$CHANGED" | grep -E \
|
|
76
|
+
'(^|/)(doc/)?openspec/changes/|(^|/)embedded-changes/p[0-9]' || true)
|
|
77
|
+
|
|
78
|
+
AGGREGATE_ALLOWED=0
|
|
79
|
+
if [ -f "$REPO_ROOT/.sdd-aggregate-closeout" ]; then
|
|
80
|
+
AGGREGATE_ALLOWED=1
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
if [ "$SDD_ACTIVE" -eq 0 ] && [ -z "$SDD_DOCS_CHANGED" ]; then
|
|
84
|
+
exit 0
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
FAILED=0
|
|
88
|
+
|
|
89
|
+
fail() {
|
|
90
|
+
echo "FAIL $1"
|
|
91
|
+
FAILED=1
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
ok() {
|
|
95
|
+
echo "OK $1"
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
has_staged_file() {
|
|
99
|
+
local path="$1"
|
|
100
|
+
printf '%s\n' "$CHANGED" | grep -Fxq "$path"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
check_no_open_placeholders() {
|
|
104
|
+
local file="$1"
|
|
105
|
+
if [ ! -f "$file" ]; then
|
|
106
|
+
fail "[$file] 文件不存在"
|
|
107
|
+
return
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if grep -Eiq '待执行|待补充|后续补测|后续测试|TODO|测试报告待更新|验证待回填' "$file"; then
|
|
111
|
+
fail "[$file] 仍包含待执行/待补充/TODO 类交付占位"
|
|
112
|
+
else
|
|
113
|
+
ok "[$file] 未发现明显交付占位"
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
check_has_closeout_status() {
|
|
118
|
+
local file="$1"
|
|
119
|
+
if grep -Eiq 'Real integration passed|Partially verified|Partial real entry|Blocked|真实入口|真实接口|验证闭环|阻塞|部分验证' "$file"; then
|
|
120
|
+
ok "[$file] 包含交付结论"
|
|
121
|
+
else
|
|
122
|
+
fail "[$file] 缺少 Real integration passed / Partially verified / Blocked 等交付结论"
|
|
123
|
+
fi
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
check_context_drift_guard() {
|
|
127
|
+
local rel_dir="$1"
|
|
128
|
+
local abs_dir="$REPO_ROOT/$rel_dir"
|
|
129
|
+
local state_file="$abs_dir/.sdd/state.yaml"
|
|
130
|
+
local handoff_dir="$abs_dir/.sdd/handoff"
|
|
131
|
+
local hash_file="$handoff_dir/sdd-context.sha256"
|
|
132
|
+
local marker=""
|
|
133
|
+
|
|
134
|
+
if [ ! -d "$abs_dir" ]; then
|
|
135
|
+
return
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
marker=$(grep -REi 'handoff_hash|sdd-context|上下文防漂移|防漂移' \
|
|
139
|
+
"$abs_dir/sdd-quality-gate.md" "$abs_dir/test-report.md" "$abs_dir/design.md" \
|
|
140
|
+
"$abs_dir/prompt" 2>/dev/null || true)
|
|
141
|
+
|
|
142
|
+
if [ -z "$marker" ] && [ ! -f "$hash_file" ]; then
|
|
143
|
+
return
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
if [ ! -f "$state_file" ]; then
|
|
147
|
+
fail "[$rel_dir] 缺少 .sdd/state.yaml,上下文防漂移状态未初始化"
|
|
148
|
+
return
|
|
149
|
+
fi
|
|
150
|
+
|
|
151
|
+
if [ ! -f "$handoff_dir/sdd-context.md" ] \
|
|
152
|
+
|| [ ! -f "$handoff_dir/sdd-context.json" ] \
|
|
153
|
+
|| [ ! -f "$hash_file" ]; then
|
|
154
|
+
fail "[$rel_dir] 缺少 .sdd/handoff/sdd-context.{md,json,sha256}"
|
|
155
|
+
return
|
|
156
|
+
fi
|
|
157
|
+
|
|
158
|
+
local hash_value
|
|
159
|
+
hash_value=$(cat "$hash_file" 2>/dev/null | tr -d '[:space:]')
|
|
160
|
+
if [ -z "$hash_value" ]; then
|
|
161
|
+
fail "[$rel_dir] sdd-context.sha256 为空"
|
|
162
|
+
return
|
|
163
|
+
fi
|
|
164
|
+
|
|
165
|
+
if grep -Riq "$hash_value" \
|
|
166
|
+
"$abs_dir/sdd-quality-gate.md" "$abs_dir/test-report.md" "$abs_dir/design.md" \
|
|
167
|
+
"$abs_dir/prompt" 2>/dev/null; then
|
|
168
|
+
ok "[$rel_dir] handoff hash 已被质量门/报告/prompt 继承"
|
|
169
|
+
else
|
|
170
|
+
fail "[$rel_dir] handoff hash 未写入 design、quality gate、prompt 或 test-report"
|
|
171
|
+
fi
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
is_blocked_doc_freeze_report() {
|
|
175
|
+
local file="$1"
|
|
176
|
+
if [ ! -f "$file" ]; then
|
|
177
|
+
return 1
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
grep -Eiq 'Blocked|阻塞' "$file" \
|
|
181
|
+
&& grep -Eiq 'SDD 文档|文档阶段|范围收口|范围冻结|任务冻结|不包含代码实现|没有 Java 实现变更|未进入实现|当前报告记录可交付边界' "$file"
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
DOC_FREEZE_ALLOWED=0
|
|
185
|
+
for report in $REPORTS; do
|
|
186
|
+
if is_blocked_doc_freeze_report "$REPO_ROOT/$report"; then
|
|
187
|
+
DOC_FREEZE_ALLOWED=1
|
|
188
|
+
fi
|
|
189
|
+
done
|
|
190
|
+
|
|
191
|
+
if [ -n "$RUNTIME_CHANGED" ] && [ -z "$REPORTS" ]; then
|
|
192
|
+
fail "检测到运行时代码/SQL 变更,但本次提交没有 staged 当前任务的 test-report.md"
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
if [ -n "$AGGREGATE_DOCS" ] && [ "$AGGREGATE_ALLOWED" -eq 0 ] && [ "$DOC_FREEZE_ALLOWED" -eq 0 ]; then
|
|
196
|
+
fail "检测到根级汇总 SDD 文档变更,但当前不是汇总收口 worktree"
|
|
197
|
+
printf '%s\n' "$AGGREGATE_DOCS" | sed 's/^/ - /'
|
|
198
|
+
cat <<'AGGREGATE_MSG'
|
|
199
|
+
|
|
200
|
+
并行研发 agent 只允许更新自己的 embedded-changes/pXX-* 目录,避免多个
|
|
201
|
+
worktree 合并时反复冲突根级 test-report/tasks/traceability 文档。
|
|
202
|
+
|
|
203
|
+
如果你是 Leader/集成收口 agent,需要统一汇总各 P 任务状态,请先执行:
|
|
204
|
+
touch .sdd-aggregate-closeout
|
|
205
|
+
|
|
206
|
+
然后在单独的汇总提交中更新根级 SDD 文档。
|
|
207
|
+
AGGREGATE_MSG
|
|
208
|
+
fi
|
|
209
|
+
|
|
210
|
+
if [ -n "$P_DIRS" ]; then
|
|
211
|
+
for p_dir in $P_DIRS; do
|
|
212
|
+
rel_tasks="$p_dir/tasks.md"
|
|
213
|
+
rel_report="$p_dir/test-report.md"
|
|
214
|
+
rel_gate="$p_dir/sdd-quality-gate.md"
|
|
215
|
+
|
|
216
|
+
if [ -f "$REPO_ROOT/$rel_tasks" ]; then
|
|
217
|
+
if has_staged_file "$rel_tasks"; then
|
|
218
|
+
ok "[$rel_tasks] 已 staged"
|
|
219
|
+
else
|
|
220
|
+
fail "[$rel_tasks] 未 staged,当前 P 任务交付状态可能未同步"
|
|
221
|
+
fi
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
if [ -f "$REPO_ROOT/$rel_report" ]; then
|
|
225
|
+
if has_staged_file "$rel_report"; then
|
|
226
|
+
ok "[$rel_report] 已 staged"
|
|
227
|
+
check_no_open_placeholders "$REPO_ROOT/$rel_report"
|
|
228
|
+
check_has_closeout_status "$REPO_ROOT/$rel_report"
|
|
229
|
+
if [ -x "$HOME/.codex/hooks/superflow-test-report-lint.py" ]; then
|
|
230
|
+
lint_args=(--repo-root "$REPO_ROOT")
|
|
231
|
+
if [ -f "$REPO_ROOT/$p_dir/tests.md" ]; then
|
|
232
|
+
lint_args+=(--tests "$REPO_ROOT/$p_dir/tests.md")
|
|
233
|
+
fi
|
|
234
|
+
"$HOME/.codex/hooks/superflow-test-report-lint.py" \
|
|
235
|
+
"${lint_args[@]}" "$REPO_ROOT/$rel_report"
|
|
236
|
+
if [ $? -ne 0 ]; then
|
|
237
|
+
FAILED=1
|
|
238
|
+
fi
|
|
239
|
+
fi
|
|
240
|
+
else
|
|
241
|
+
fail "[$rel_report] 未 staged,当前 P 任务测试报告未同步"
|
|
242
|
+
fi
|
|
243
|
+
else
|
|
244
|
+
fail "[$rel_report] 不存在,当前 P 任务缺少独立测试报告"
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
if [ -f "$REPO_ROOT/$rel_gate" ]; then
|
|
248
|
+
if has_staged_file "$rel_gate"; then
|
|
249
|
+
ok "[$rel_gate] 已 staged"
|
|
250
|
+
check_no_open_placeholders "$REPO_ROOT/$rel_gate"
|
|
251
|
+
else
|
|
252
|
+
fail "[$rel_gate] 未 staged,当前 P 任务质量门禁可能未同步"
|
|
253
|
+
fi
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
check_context_drift_guard "$p_dir"
|
|
257
|
+
done
|
|
258
|
+
fi
|
|
259
|
+
|
|
260
|
+
VERIFY_REPORTS=$(printf '%s\n' "$REPORTS" | grep -E \
|
|
261
|
+
'(^|/)embedded-changes/p[0-9][^/]*/test-report\.md$' || true)
|
|
262
|
+
|
|
263
|
+
if [ -n "$VERIFY_REPORTS" ] && [ -x "$HOME/.codex/hooks/superflow-verify-integration.sh" ]; then
|
|
264
|
+
VERIFY_ARGS=""
|
|
265
|
+
for report in $VERIFY_REPORTS; do
|
|
266
|
+
abs_report="$REPO_ROOT/$report"
|
|
267
|
+
if is_blocked_doc_freeze_report "$abs_report" && [ -z "$CODE_RUNTIME_CHANGED" ]; then
|
|
268
|
+
ok "[$report] 为 SDD 文档冻结 Blocked 报告,跳过真实集成验收脚本"
|
|
269
|
+
continue
|
|
270
|
+
fi
|
|
271
|
+
VERIFY_ARGS="$VERIFY_ARGS $abs_report"
|
|
272
|
+
done
|
|
273
|
+
if [ -n "$VERIFY_ARGS" ]; then
|
|
274
|
+
"$HOME/.codex/hooks/superflow-verify-integration.sh" $VERIFY_ARGS
|
|
275
|
+
if [ $? -ne 0 ]; then
|
|
276
|
+
FAILED=1
|
|
277
|
+
fi
|
|
278
|
+
fi
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
ROOT_REPORTS=$(printf '%s\n' "$REPORTS" | grep -E \
|
|
282
|
+
'(^|/)openspec/changes/[^/]+/test-report\.md$|(^|/)doc/openspec/changes/[^/]+/test-report\.md$' || true)
|
|
283
|
+
|
|
284
|
+
if [ -n "$ROOT_REPORTS" ] && [ -x "$HOME/.codex/hooks/superflow-test-report-lint.py" ]; then
|
|
285
|
+
for report in $ROOT_REPORTS; do
|
|
286
|
+
root_lint_args=(--warn-only --repo-root "$REPO_ROOT")
|
|
287
|
+
root_tests="${report%/test-report.md}/tests.md"
|
|
288
|
+
if [ -f "$REPO_ROOT/$root_tests" ]; then
|
|
289
|
+
root_lint_args+=(--tests "$REPO_ROOT/$root_tests")
|
|
290
|
+
fi
|
|
291
|
+
"$HOME/.codex/hooks/superflow-test-report-lint.py" \
|
|
292
|
+
"${root_lint_args[@]}" "$REPO_ROOT/$report"
|
|
293
|
+
if [ $? -ne 0 ]; then
|
|
294
|
+
FAILED=1
|
|
295
|
+
fi
|
|
296
|
+
done
|
|
297
|
+
fi
|
|
298
|
+
|
|
299
|
+
if [ "$FAILED" -ne 0 ]; then
|
|
300
|
+
cat <<'BLOCK_MSG'
|
|
301
|
+
|
|
302
|
+
[SDD 交付完整性拦截]
|
|
303
|
+
当前提交还没有形成可交付闭环。请补齐当前 P 任务的:
|
|
304
|
+
1. tasks.md 状态
|
|
305
|
+
2. test-report.md 真实验证证据
|
|
306
|
+
3. sdd-quality-gate.md 质量门禁状态(如果存在)
|
|
307
|
+
|
|
308
|
+
如果确实无法验证,请在 test-report.md 中写明 Blocked 或 Partially verified
|
|
309
|
+
以及具体阻塞原因,不能用“后续补测/待补充”作为完成态。
|
|
310
|
+
BLOCK_MSG
|
|
311
|
+
exit 2
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
echo "SDD 交付完整性检查通过"
|
|
315
|
+
exit 0
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# Session-scoped Superflow dependency update checker.
|
|
3
|
+
# Default mode checks and reports updates. Set SUPERFLOW_AUTO_UPDATE=apply to install.
|
|
4
|
+
|
|
5
|
+
set -u
|
|
6
|
+
|
|
7
|
+
INPUT=""
|
|
8
|
+
if [ ! -t 0 ]; then
|
|
9
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
MODE="${SUPERFLOW_AUTO_UPDATE:-check}"
|
|
13
|
+
case "$MODE" in
|
|
14
|
+
0|off|false|none) exit 0 ;;
|
|
15
|
+
1|true|yes|apply) MODE="apply" ;;
|
|
16
|
+
check|"") MODE="check" ;;
|
|
17
|
+
*) MODE="check" ;;
|
|
18
|
+
esac
|
|
19
|
+
|
|
20
|
+
if [ "$MODE" = "0" ]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
STATE_DIR="${XDG_STATE_HOME:-$HOME/.local/state}/superflow"
|
|
25
|
+
mkdir -p "$STATE_DIR" 2>/dev/null || exit 0
|
|
26
|
+
|
|
27
|
+
SESSION_KEY=$(printf '%s' "$INPUT" | python3 -c '
|
|
28
|
+
import hashlib, json, os, sys
|
|
29
|
+
raw = sys.stdin.read()
|
|
30
|
+
try:
|
|
31
|
+
data = json.loads(raw) if raw.strip() else {}
|
|
32
|
+
except Exception:
|
|
33
|
+
data = {}
|
|
34
|
+
for key in ("session_id", "conversation_id", "thread_id", "transcript_path"):
|
|
35
|
+
value = data.get(key)
|
|
36
|
+
if value:
|
|
37
|
+
print(str(value))
|
|
38
|
+
raise SystemExit
|
|
39
|
+
cwd = data.get("cwd") or os.getcwd()
|
|
40
|
+
print("fallback:" + hashlib.sha256(cwd.encode()).hexdigest()[:16])
|
|
41
|
+
' 2>/dev/null)
|
|
42
|
+
|
|
43
|
+
[ -n "$SESSION_KEY" ] || SESSION_KEY="unknown"
|
|
44
|
+
SESSION_HASH=$(printf '%s' "$SESSION_KEY" | shasum -a 256 2>/dev/null | awk '{print $1}')
|
|
45
|
+
if [ -z "$SESSION_HASH" ]; then
|
|
46
|
+
SESSION_HASH=$(printf '%s' "$SESSION_KEY" | sha256sum 2>/dev/null | awk '{print $1}')
|
|
47
|
+
fi
|
|
48
|
+
[ -n "$SESSION_HASH" ] || SESSION_HASH="unknown"
|
|
49
|
+
|
|
50
|
+
STAMP="$STATE_DIR/dependency-update-$SESSION_HASH.stamp"
|
|
51
|
+
GLOBAL_STAMP="$STATE_DIR/dependency-update-last.stamp"
|
|
52
|
+
LOG_FILE="$STATE_DIR/dependency-update.log"
|
|
53
|
+
LOCK_DIR="$STATE_DIR/dependency-update.lock"
|
|
54
|
+
|
|
55
|
+
now_epoch() {
|
|
56
|
+
date +%s
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
MIN_INTERVAL="${SUPERFLOW_UPDATE_MIN_INTERVAL_SECONDS:-21600}"
|
|
60
|
+
NOW=$(now_epoch)
|
|
61
|
+
|
|
62
|
+
if [ -f "$STAMP" ]; then
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
if [ -f "$GLOBAL_STAMP" ]; then
|
|
67
|
+
LAST=$(cat "$GLOBAL_STAMP" 2>/dev/null || printf '0')
|
|
68
|
+
case "$LAST" in
|
|
69
|
+
''|*[!0-9]*) LAST=0 ;;
|
|
70
|
+
esac
|
|
71
|
+
if [ "$((NOW - LAST))" -lt "$MIN_INTERVAL" ]; then
|
|
72
|
+
printf '%s\n' "$NOW" > "$STAMP" 2>/dev/null || true
|
|
73
|
+
exit 0
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
|
78
|
+
exit 0
|
|
79
|
+
fi
|
|
80
|
+
trap 'rmdir "$LOCK_DIR" 2>/dev/null || true' EXIT
|
|
81
|
+
|
|
82
|
+
printf '%s\n' "$NOW" > "$STAMP" 2>/dev/null || true
|
|
83
|
+
printf '%s\n' "$NOW" > "$GLOBAL_STAMP" 2>/dev/null || true
|
|
84
|
+
|
|
85
|
+
run_logged() {
|
|
86
|
+
printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >> "$LOG_FILE" 2>/dev/null || true
|
|
87
|
+
"$@" >> "$LOG_FILE" 2>&1
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
latest_npm_version() {
|
|
91
|
+
npm view "$1" version 2>/dev/null | tail -n 1
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
installed_npm_version() {
|
|
95
|
+
local json
|
|
96
|
+
json="$(npm list -g "$1" --depth=0 --json 2>/dev/null || true)"
|
|
97
|
+
printf '%s' "$json" | python3 -c '
|
|
98
|
+
import json, sys
|
|
99
|
+
package_name = sys.argv[1]
|
|
100
|
+
try:
|
|
101
|
+
data = json.load(sys.stdin)
|
|
102
|
+
except Exception:
|
|
103
|
+
raise SystemExit
|
|
104
|
+
dep = (data.get("dependencies") or {}).get(package_name) or {}
|
|
105
|
+
version = dep.get("version")
|
|
106
|
+
if version:
|
|
107
|
+
print(version)
|
|
108
|
+
' "$1"
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
update_npm_if_needed() {
|
|
112
|
+
local package_name="$1"
|
|
113
|
+
local current latest
|
|
114
|
+
command -v npm >/dev/null 2>&1 || return 0
|
|
115
|
+
latest="$(latest_npm_version "$package_name")"
|
|
116
|
+
[ -n "$latest" ] || return 0
|
|
117
|
+
current="$(installed_npm_version "$package_name")"
|
|
118
|
+
if [ "$current" != "$latest" ]; then
|
|
119
|
+
run_logged npm install -g "$package_name@latest" || true
|
|
120
|
+
fi
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
check_npm_package() {
|
|
124
|
+
local package_name="$1"
|
|
125
|
+
local current latest
|
|
126
|
+
command -v npm >/dev/null 2>&1 || return 0
|
|
127
|
+
latest="$(latest_npm_version "$package_name")"
|
|
128
|
+
[ -n "$latest" ] || return 0
|
|
129
|
+
current="$(installed_npm_version "$package_name")"
|
|
130
|
+
if [ -z "$current" ]; then
|
|
131
|
+
printf '%s latest=%s installed=missing\n' "$package_name" "$latest"
|
|
132
|
+
elif [ "$current" != "$latest" ]; then
|
|
133
|
+
printf '%s latest=%s installed=%s\n' "$package_name" "$latest" "$current"
|
|
134
|
+
fi
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if [ "$MODE" = "apply" ]; then
|
|
138
|
+
update_npm_if_needed "@chenmk/superflow"
|
|
139
|
+
update_npm_if_needed "@fission-ai/openspec"
|
|
140
|
+
|
|
141
|
+
if command -v claude >/dev/null 2>&1; then
|
|
142
|
+
run_logged claude plugin install superpowers@superpowers-marketplace || true
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
if command -v codex >/dev/null 2>&1; then
|
|
146
|
+
run_logged codex plugin add superpowers@openai-curated || true
|
|
147
|
+
fi
|
|
148
|
+
else
|
|
149
|
+
STATUS_FILE="$STATE_DIR/dependency-update-status.txt"
|
|
150
|
+
{
|
|
151
|
+
printf 'checked_at=%s\n' "$(date '+%Y-%m-%d %H:%M:%S')"
|
|
152
|
+
check_npm_package "@chenmk/superflow"
|
|
153
|
+
check_npm_package "@fission-ai/openspec"
|
|
154
|
+
printf 'superpowers=run superflow update --with-package to refresh selected agent plugins\n'
|
|
155
|
+
} > "$STATUS_FILE" 2>/dev/null || true
|
|
156
|
+
if grep -Eq '^@' "$STATUS_FILE" 2>/dev/null; then
|
|
157
|
+
printf '[Superflow] 有核心依赖更新,执行 superflow update --with-package 统一更新;详情:%s\n' "$STATUS_FILE" >&2
|
|
158
|
+
fi
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
exit 0
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SDD 强制门禁 Hook
|
|
3
|
+
# 拦截 Edit/Write 工具调用,确保:
|
|
4
|
+
# 1. 不在主工作树直接编辑(必须用 worktree)
|
|
5
|
+
# 2. 数据库结构已核查(必须有 .db-verified 标记)
|
|
6
|
+
#
|
|
7
|
+
# 触发条件:项目根目录存在 .sdd-enforced 文件
|
|
8
|
+
# 标记说明:
|
|
9
|
+
# .sdd-enforced - 由 SDD prompt 创建,表示当前有活跃的 SDD 任务
|
|
10
|
+
# .db-verified - 由研发 agent 数据库核查后创建
|
|
11
|
+
#
|
|
12
|
+
# 退出码:0=允许 2=拦截
|
|
13
|
+
|
|
14
|
+
INPUT=$(cat)
|
|
15
|
+
|
|
16
|
+
FILE_PATH=$(echo "$INPUT" | python3 -c "
|
|
17
|
+
import sys, json
|
|
18
|
+
d = json.load(sys.stdin)
|
|
19
|
+
print(d.get('tool_input', {}).get('file_path', ''))
|
|
20
|
+
" 2>/dev/null)
|
|
21
|
+
|
|
22
|
+
if [ -z "$FILE_PATH" ]; then
|
|
23
|
+
exit 0
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
FILE_DIR=$(dirname "$FILE_PATH")
|
|
27
|
+
|
|
28
|
+
REPO_ROOT=$(git -C "$FILE_DIR" rev-parse --show-toplevel 2>/dev/null)
|
|
29
|
+
if [ $? -ne 0 ]; then
|
|
30
|
+
exit 0
|
|
31
|
+
fi
|
|
32
|
+
|
|
33
|
+
if [ ! -f "$REPO_ROOT/.sdd-enforced" ]; then
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
if [ -d "$REPO_ROOT/.git" ]; then
|
|
38
|
+
cat <<'BLOCK_MSG'
|
|
39
|
+
[SDD 门禁拦截] 检测到在主工作树直接编辑,已阻止。
|
|
40
|
+
|
|
41
|
+
原因:SDD 任务要求在独立 worktree 分支中开发,禁止污染主工作树。
|
|
42
|
+
|
|
43
|
+
请立即执行:
|
|
44
|
+
git worktree add -b feature/{任务分支名} ../{项目}-worktree HEAD
|
|
45
|
+
cd ../{项目}-worktree
|
|
46
|
+
# 在 worktree 内重新创建门禁标记:
|
|
47
|
+
touch .sdd-enforced
|
|
48
|
+
|
|
49
|
+
完成后即可继续编辑。如需取消门禁,删除主仓库的 .sdd-enforced 文件。
|
|
50
|
+
BLOCK_MSG
|
|
51
|
+
exit 2
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [ ! -f "$REPO_ROOT/.db-verified" ]; then
|
|
55
|
+
cat <<'BLOCK_MSG'
|
|
56
|
+
[SDD 门禁拦截] 数据库结构尚未核查,编辑已阻止。
|
|
57
|
+
|
|
58
|
+
原因:写业务代码前必须先确认开发环境数据库结构与设计一致。
|
|
59
|
+
|
|
60
|
+
请立即执行数据库前置门禁:
|
|
61
|
+
1. 连接开发库执行 SHOW CREATE TABLE 确认表结构
|
|
62
|
+
2. 如有缺失,从汇总 SQL 文件取脚本执行
|
|
63
|
+
3. 确认后创建标记:touch .db-verified
|
|
64
|
+
|
|
65
|
+
完成后即可继续编辑。如需跳过(不推荐),删除 .sdd-enforced 文件。
|
|
66
|
+
BLOCK_MSG
|
|
67
|
+
exit 2
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
exit 0
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# SDD phase-aware hook guard.
|
|
3
|
+
# Blocks writes that conflict with .sdd/state.yaml when .sdd-enforced is active.
|
|
4
|
+
|
|
5
|
+
set -u
|
|
6
|
+
|
|
7
|
+
INPUT=""
|
|
8
|
+
if [ ! -t 0 ]; then
|
|
9
|
+
INPUT=$(cat 2>/dev/null || true)
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
FILE_PATH="${FILE_PATH:-}"
|
|
13
|
+
if [ -z "$FILE_PATH" ] && [ -n "$INPUT" ]; then
|
|
14
|
+
FILE_PATH=$(printf '%s' "$INPUT" | python3 -c "
|
|
15
|
+
import json, sys
|
|
16
|
+
try:
|
|
17
|
+
data = json.load(sys.stdin)
|
|
18
|
+
except Exception:
|
|
19
|
+
print('')
|
|
20
|
+
raise SystemExit
|
|
21
|
+
tool_input = data.get('tool_input', {})
|
|
22
|
+
print(tool_input.get('file_path') or '')
|
|
23
|
+
" 2>/dev/null)
|
|
24
|
+
fi
|
|
25
|
+
|
|
26
|
+
if [ -z "$FILE_PATH" ]; then
|
|
27
|
+
exit 0
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
FILE_PATH=$(printf '%s' "$FILE_PATH" | sed 's|\\|/|g')
|
|
31
|
+
FILE_DIR=$(dirname "$FILE_PATH")
|
|
32
|
+
while [ ! -d "$FILE_DIR" ] && [ "$FILE_DIR" != "." ] && [ "$FILE_DIR" != "/" ]; do
|
|
33
|
+
FILE_DIR=$(dirname "$FILE_DIR")
|
|
34
|
+
done
|
|
35
|
+
REPO_ROOT=$(git -C "$FILE_DIR" rev-parse --show-toplevel 2>/dev/null)
|
|
36
|
+
if [ $? -ne 0 ]; then
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
if [ ! -f "$REPO_ROOT/.sdd-enforced" ]; then
|
|
41
|
+
exit 0
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
REL=$(python3 - "$REPO_ROOT" "$FILE_PATH" <<'PY'
|
|
45
|
+
import os, sys
|
|
46
|
+
root = os.path.abspath(sys.argv[1])
|
|
47
|
+
path = os.path.abspath(sys.argv[2])
|
|
48
|
+
try:
|
|
49
|
+
print(os.path.relpath(path, root).replace(os.sep, '/'))
|
|
50
|
+
except Exception:
|
|
51
|
+
print(path.replace(os.sep, '/'))
|
|
52
|
+
PY
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
STATE_FILE=""
|
|
56
|
+
case "$REL" in
|
|
57
|
+
*embedded-changes/*)
|
|
58
|
+
prefix=$(printf '%s\n' "$REL" | sed -nE 's#^(.*embedded-changes/[^/]+).*#\1#p')
|
|
59
|
+
[ -n "$prefix" ] && [ -f "$REPO_ROOT/$prefix/.sdd/state.yaml" ] && STATE_FILE="$REPO_ROOT/$prefix/.sdd/state.yaml"
|
|
60
|
+
;;
|
|
61
|
+
*openspec/changes/*)
|
|
62
|
+
prefix=$(printf '%s\n' "$REL" | sed -nE 's#^(.*openspec/changes/[^/]+).*#\1#p')
|
|
63
|
+
[ -n "$prefix" ] && [ -f "$REPO_ROOT/$prefix/.sdd/state.yaml" ] && STATE_FILE="$REPO_ROOT/$prefix/.sdd/state.yaml"
|
|
64
|
+
;;
|
|
65
|
+
esac
|
|
66
|
+
|
|
67
|
+
if [ -z "$STATE_FILE" ]; then
|
|
68
|
+
STATE_FILE=$(find "$REPO_ROOT" -path '*/.sdd/state.yaml' -type f 2>/dev/null | head -n 1)
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
[ -n "$STATE_FILE" ] || exit 0
|
|
72
|
+
|
|
73
|
+
PHASE=$(awk -F':' '$1=="phase"{gsub(/^[ \t]+|[ \t]+$/, "", $2); print $2}' "$STATE_FILE" 2>/dev/null)
|
|
74
|
+
[ -n "$PHASE" ] || exit 0
|
|
75
|
+
|
|
76
|
+
is_sdd_doc_path() {
|
|
77
|
+
printf '%s\n' "$REL" | grep -Eq '(^|/)(openspec/changes|embedded-changes)/|(^|/)\.sdd/|(^|/)prompt/|(^|/)docs/superpowers/'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
is_root_markdown_or_config() {
|
|
81
|
+
case "$REL" in
|
|
82
|
+
*/*) return 1 ;;
|
|
83
|
+
*.md|.sdd-enforced|.db-verified) return 0 ;;
|
|
84
|
+
*) return 1 ;;
|
|
85
|
+
esac
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
case "$REL" in
|
|
89
|
+
*.java|*.xml|*.sql|*.yml|*.yaml|*.properties)
|
|
90
|
+
RUNTIME=1
|
|
91
|
+
;;
|
|
92
|
+
*)
|
|
93
|
+
RUNTIME=0
|
|
94
|
+
;;
|
|
95
|
+
esac
|
|
96
|
+
|
|
97
|
+
case "$PHASE" in
|
|
98
|
+
docs)
|
|
99
|
+
if is_sdd_doc_path || is_root_markdown_or_config; then
|
|
100
|
+
exit 0
|
|
101
|
+
fi
|
|
102
|
+
if [ "$RUNTIME" -eq 1 ]; then
|
|
103
|
+
cat <<'BLOCK_MSG' >&2
|
|
104
|
+
[SDD phase guard] 当前 phase=docs,禁止直接修改运行时代码。
|
|
105
|
+
请先完成 SDD docs、handoff、state 和 docs guard,再进入 implement 阶段。
|
|
106
|
+
BLOCK_MSG
|
|
107
|
+
exit 2
|
|
108
|
+
fi
|
|
109
|
+
;;
|
|
110
|
+
implement|verify)
|
|
111
|
+
exit 0
|
|
112
|
+
;;
|
|
113
|
+
archive)
|
|
114
|
+
if printf '%s\n' "$REL" | grep -Eq '(^|/)\.sdd/state\.yaml$|(^|/)\.openspec\.yaml$'; then
|
|
115
|
+
exit 0
|
|
116
|
+
fi
|
|
117
|
+
cat <<'BLOCK_MSG' >&2
|
|
118
|
+
[SDD phase guard] 当前 SDD 已进入 archive,禁止继续修改运行时代码或交付文档。
|
|
119
|
+
如需调整,请先执行 superflow-state.sh transition <change-dir> archive-reopen。
|
|
120
|
+
BLOCK_MSG
|
|
121
|
+
exit 2
|
|
122
|
+
;;
|
|
123
|
+
done)
|
|
124
|
+
cat <<'BLOCK_MSG' >&2
|
|
125
|
+
[SDD phase guard] 当前 SDD 已进入 archive/done,禁止继续修改交付文件或代码。
|
|
126
|
+
如需调整,请先执行 superflow-state.sh transition <change-dir> archive-reopen。
|
|
127
|
+
BLOCK_MSG
|
|
128
|
+
exit 2
|
|
129
|
+
;;
|
|
130
|
+
esac
|
|
131
|
+
|
|
132
|
+
exit 0
|