spec-runner 1.0.7 → 1.0.8

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 (39) hide show
  1. package/README.md +16 -9
  2. package/bin/spec-runner.js +112 -144
  3. package/package.json +1 -6
  4. package/templates/.spec-runner/hooks/pre-commit +25 -11
  5. package/templates/.spec-runner/project.json.example +10 -8
  6. package/templates/.spec-runner/scripts/branch/uc-next-start.sh +100 -9
  7. package/templates/.spec-runner/scripts/check.sh +396 -13
  8. package/templates/.spec-runner/scripts/spec-runner-core.sh +286 -157
  9. package/templates/.spec-runner/scripts/test/require-tests-green.sh +7 -63
  10. package/templates/.spec-runner/steps/steps.json +171 -0
  11. package/templates/.spec-runner/steps//343/201/235/343/201/256/344/273/226/344/275/234/346/245/255.md +25 -13
  12. package/templates/.spec-runner/steps//343/202/277/343/202/271/343/202/257/344/270/200/350/246/247.md +67 -104
  13. package/templates/.spec-runner/steps//343/203/201/343/202/247/343/203/203/343/202/257/343/203/252/343/202/271/343/203/210.md +52 -78
  14. package/templates/.spec-runner/steps//343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +41 -34
  15. package/templates/.spec-runner/steps//343/203/211/343/203/241/343/202/244/343/203/263/350/250/255/350/250/210.md +34 -14
  16. package/templates/.spec-runner/steps//344/273/225/346/247/230/347/255/226/345/256/232.md +161 -207
  17. package/templates/.spec-runner/steps//345/210/206/346/236/220.md +64 -127
  18. package/templates/.spec-runner/steps//345/256/237/350/243/205.md +67 -79
  19. package/templates/.spec-runner/steps//345/256/237/350/243/205/350/250/210/347/224/273.md +56 -56
  20. package/templates/.spec-runner/steps//346/206/262/347/253/240.md +67 -46
  21. package/templates/.spec-runner/steps//346/233/226/346/230/247/343/201/225/350/247/243/346/266/210.md +88 -148
  22. package/templates/.spec-runner/templates/UC-N-MMDD-/345/210/244/346/226/255/350/250/230/351/214/262/343/203/206/343/203/263/343/203/227/343/203/254.md +33 -0
  23. package/templates/.spec-runner/templates/{UC-NNN- → UC-N-}/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/345/220/215.md +1 -3
  24. package/templates/.spec-runner/templates/grade-history.json +5 -0
  25. package/templates/.spec-runner/templates/phase-locks.json +29 -0
  26. package/templates/.spec-runner/templates//343/203/211/343/203/241/343/202/244/343/203/263/343/203/242/343/203/207/343/203/253.md +21 -0
  27. package/templates/.spec-runner/templates//343/203/246/343/203/223/343/202/255/343/202/277/343/202/271/350/250/200/350/252/236/350/276/236/346/233/270.md +16 -0
  28. package/templates/.spec-runner/templates//346/206/262/347/253/240.md +51 -0
  29. package/templates/.spec-runner/templates//351/233/206/347/264/204.md +46 -0
  30. package/templates/.spec-runner/scripts/branch/create-uc-branch.sh +0 -105
  31. package/templates/.spec-runner/scripts/branch/uc-next-id.sh +0 -17
  32. package/templates/.spec-runner/scripts/check/drift.sh +0 -66
  33. package/templates/.spec-runner/scripts/check/health.sh +0 -103
  34. package/templates/.spec-runner/scripts/check/naming.sh +0 -51
  35. package/templates/.spec-runner/scripts/check/schema-drift.sh +0 -74
  36. package/templates/.spec-runner/scripts/check/schema-sync.sh +0 -153
  37. package/templates/.spec-runner/scripts/lib/uc-context.sh +0 -75
  38. package/templates/.spec-runner/scripts/openapi/openapi-generate.sh +0 -207
  39. package/templates/.spec-runner/scripts/setup/init-project.sh +0 -152
@@ -1,105 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 新規 UC 用ブランチ作成(Section 8-1)
3
- # 使用例: ./create-uc-branch.sh UC-001 order-placement [カテゴリ] [--json]
4
- # ブランチ名: feature/UC-001-order-placement。UC 仕様書: docs/05_ユースケース仕様/<カテゴリ>/UC-001-order-placement.md
5
-
6
- set -e
7
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
8
- cd "$REPO_ROOT"
9
-
10
- JSON_MODE=false
11
- ARGS=()
12
- for a in "$@"; do
13
- case "$a" in
14
- --json) JSON_MODE=true ;;
15
- *) ARGS+=("$a") ;;
16
- esac
17
- done
18
-
19
- # 引数が 1 つ(説明のみ)のときは uc-next-id で UC 番号を取得
20
- if [[ ${#ARGS[@]} -eq 1 ]]; then
21
- UC_ID=$("$(dirname "$0")/uc-next-id.sh" 2>/dev/null || true)
22
- [[ -z "$UC_ID" ]] && { echo "Error: uc-next-id の取得に失敗しました" >&2; exit 1; }
23
- ARGS=("$UC_ID" "${ARGS[0]}")
24
- fi
25
-
26
- UC_ID="${ARGS[0]:-}"
27
- DESC="${ARGS[1]:-}"
28
- if [[ -z "$UC_ID" || -z "$DESC" ]]; then
29
- echo "Usage: $0 <UC-ID> <kebab-description> [カテゴリ] [--json]" >&2
30
- echo " $0 <kebab-description> # UC 番号は自動採番" >&2
31
- echo "Example: $0 UC-001 order-placement" >&2
32
- echo "Example: $0 order-placement" >&2
33
- exit 1
34
- fi
35
- # 第3引数: カテゴリ(省略時はデフォルト)。--json の場合はカテゴリ未指定
36
- CATEGORY="${ARGS[2]:-}"
37
- [[ "${CATEGORY}" == "--json" ]] && CATEGORY=""
38
- # カテゴリ未指定時はデフォルト(英数字・ハイフンのみでサニタイズ)
39
- CATEGORY="${CATEGORY:-ユースケース}"
40
- CATEGORY=$(echo "$CATEGORY" | sed 's/[^a-zA-Z0-9_ーぁ-んァ-ン一-龥\-]//g')
41
- [[ -z "$CATEGORY" ]] && CATEGORY="ユースケース"
42
-
43
- # UC-ID 形式: UC-NNN
44
- if ! echo "$UC_ID" | grep -qE '^UC-[0-9]{3}$'; then
45
- echo "Error: UC-ID must be UC-NNN (e.g. UC-001)" >&2
46
- exit 1
47
- fi
48
-
49
- # 説明は英小文字・ハイフン
50
- DESC=$(echo "$DESC" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-//' | sed 's/-$//')
51
- # ブランチ接頭辞は project.json の naming.branch_prefix(初期化で設定)。無ければ feature
52
- BRANCH_PREFIX="feature"
53
- if [[ -f ".spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
54
- p=$(jq -r '.naming.branch_prefix // empty' .spec-runner/project.json 2>/dev/null)
55
- [[ -n "$p" ]] && BRANCH_PREFIX="$p"
56
- fi
57
- BRANCH_NAME="${BRANCH_PREFIX}/${UC_ID}-${DESC}"
58
-
59
- # 命名規則チェック(project.json の branch_prefix + UC 形式)。AI 実行前に検証。
60
- valid_uc_pattern="^${BRANCH_PREFIX}/UC-[0-9]{3}-[a-z0-9-]+\$"
61
- if ! echo "$BRANCH_NAME" | grep -qE "$valid_uc_pattern"; then
62
- echo "Error: ブランチ名が命名規則に合いません: $BRANCH_NAME(期待: ${BRANCH_PREFIX}/UC-NNN-kebab-description)" >&2
63
- exit 1
64
- fi
65
-
66
- if git rev-parse --verify "$BRANCH_NAME" >/dev/null 2>&1; then
67
- echo "Error: Branch '$BRANCH_NAME' already exists." >&2
68
- exit 1
69
- fi
70
-
71
- git checkout -b "$BRANCH_NAME"
72
- echo "Created branch: $BRANCH_NAME"
73
-
74
- # UC 仕様書を docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md に作成(1 UC = 1 ファイル)
75
- FEATURE_DIR="docs/05_ユースケース仕様/${CATEGORY}"
76
- UC_DOC="${FEATURE_DIR}/${UC_ID}-${DESC}.md"
77
- mkdir -p "$FEATURE_DIR"
78
- TEMPLATE="$REPO_ROOT/.spec-runner/templates/UC-NNN-ユースケース名.md"
79
- if [[ -f "$TEMPLATE" ]]; then
80
- sed "s/UC-NNN/${UC_ID}/g; s/{ユースケース名}/${DESC}/g" "$TEMPLATE" > "$UC_DOC"
81
- echo "Created: $UC_DOC"
82
- else
83
- touch "$UC_DOC"
84
- echo "# ${UC_ID}: ${DESC}" >> "$UC_DOC"
85
- echo "status: draft" >> "$UC_DOC"
86
- echo "Created: $UC_DOC"
87
- fi
88
-
89
- if [[ "$JSON_MODE" == true ]]; then
90
- SPEC_ABS="$REPO_ROOT/$UC_DOC"
91
- DIR_ABS="$REPO_ROOT/$FEATURE_DIR"
92
- if command -v jq >/dev/null 2>&1; then
93
- jq -cn \
94
- --arg branch "$BRANCH_NAME" \
95
- --arg spec_file "$SPEC_ABS" \
96
- --arg feature_dir "$DIR_ABS" \
97
- '{BRANCH_NAME:$branch,SPEC_FILE:$spec_file,FEATURE_SPEC:$spec_file,FEATURE_DIR:$feature_dir}'
98
- else
99
- printf '{"BRANCH_NAME":"%s","SPEC_FILE":"%s","FEATURE_SPEC":"%s","FEATURE_DIR":"%s"}\n' \
100
- "$(printf '%s' "$BRANCH_NAME" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
101
- "$(printf '%s' "$SPEC_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
102
- "$(printf '%s' "$SPEC_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')" \
103
- "$(printf '%s' "$DIR_ABS" | sed 's/\\/\\\\/g; s/"/\\"/g')"
104
- fi
105
- fi
@@ -1,17 +0,0 @@
1
- #!/usr/bin/env bash
2
- # docs/05_ユースケース仕様/<カテゴリ>/UC-*.md から次に使う UC-NNN を表示する(例: UC-001)
3
-
4
- set -e
5
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
6
- DIR="$REPO_ROOT/docs/05_ユースケース仕様"
7
- mkdir -p "$DIR"
8
- MAX=0
9
- for f in "$DIR"/*/UC-*.md; do
10
- [[ -e "$f" ]] || continue
11
- base=$(basename "$f" .md)
12
- if [[ "$base" =~ ^UC-([0-9]{3})- ]]; then
13
- n=$((10#${BASH_REMATCH[1]}))
14
- [[ $n -gt $MAX ]] && MAX=$n
15
- fi
16
- done
17
- printf "UC-%03d\n" $((MAX + 1))
@@ -1,66 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ドリフト確認(Section 10: 仕様とコードの乖離検出)
3
- # オプション: --用語(禁止語), --スキーマ(集約↔DBML), --命名(命名規則), --レポート(レポート出力)
4
-
5
- set -e
6
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
7
- cd "$REPO_ROOT"
8
-
9
- MODE=""
10
- for a in "$@"; do
11
- case "$a" in
12
- --用語) MODE="terms" ;;
13
- --スキーマ) MODE="schema" ;;
14
- --命名) MODE="naming" ;;
15
- --レポート) MODE="report" ;;
16
- esac
17
- done
18
-
19
- if [[ "$MODE" == "naming" ]]; then
20
- exec "$(dirname "$0")/naming.sh"
21
- fi
22
-
23
- if [[ "$MODE" == "terms" ]]; then
24
- # 禁止語チェック: ユビキタス言語辞書の forbidden を src/ で検索(命名規則.md の SOURCE_EXTENSIONS を参照)
25
- DICT="docs/02_ドメイン設計/ユビキタス言語辞書.md"
26
- NAMING="docs/03_アーキテクチャ/命名規則.md"
27
- if [[ ! -f "$DICT" ]]; then
28
- echo "ドリフト確認(用語): ユビキタス言語辞書がありません。スキップします。"
29
- exit 0
30
- fi
31
- source_exts="ts,php,py,go,java"
32
- if [[ -f "$NAMING" ]]; then
33
- line=$(grep -E '^SOURCE_EXTENSIONS:' "$NAMING" 2>/dev/null | head -1)
34
- [[ -n "$line" ]] && source_exts=$(echo "$line" | sed 's/SOURCE_EXTENSIONS:[[:space:]]*//;s/[[:space:]]//g')
35
- fi
36
- found=0
37
- if command -v yq >/dev/null 2>&1; then
38
- while IFS= read -r -d '' forbidden; do
39
- [[ -z "$forbidden" ]] && continue
40
- c=0
41
- if [[ -d "src" ]]; then
42
- for ext in $(echo "$source_exts" | tr ',' ' '); do
43
- n=$(find src -type f -name "*.$ext" 2>/dev/null | xargs grep -l "$forbidden" 2>/dev/null | wc -l)
44
- c=$((c + n))
45
- done
46
- fi
47
- if [[ "$c" -gt 0 ]]; then
48
- echo "禁止語「$forbidden」が src/ に ${c} 件あります。"
49
- found=$((found+1))
50
- fi
51
- done < <(yq '.terms[]? | select(.forbidden != null) | .forbidden[]?' "$DICT" -r -0 2>/dev/null || true)
52
- fi
53
- if [[ $found -gt 0 ]]; then
54
- exit 1
55
- fi
56
- echo "✅ 禁止語チェック: 問題なし"
57
- exit 0
58
- fi
59
-
60
- if [[ "$MODE" == "schema" ]]; then
61
- exec "$(dirname "$0")/schema-drift.sh"
62
- fi
63
-
64
- # デフォルト: オプション未指定時はメッセージのみ
65
- echo "✅ ドリフト確認: 完了(--用語 / --スキーマ / --命名 で個別チェック、健全性確認で一括チェック)"
66
- exit 0
@@ -1,103 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 健全性確認(spec.md セクション 12)。--形式=json で JSON を標準出力に出す。
3
-
4
- set -e
5
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
6
- cd "$REPO_ROOT"
7
-
8
- drifts=()
9
-
10
- # 設計書品質
11
- for f in docs/05_ユースケース仕様/*/UC-*.md; do
12
- [[ -f "$f" ]] || continue
13
- base=$(basename "$f" .md)
14
- if ! grep -qE '受入条件|成功基準|Given|When|Then|EARS' "$f" 2>/dev/null; then
15
- drifts+=("UC ${base}: 受入条件または成功基準がありません")
16
- fi
17
- count=$(grep -c '\[要確認:' "$f" 2>/dev/null || echo 0)
18
- count=$(echo "$count" | head -1 | tr -cd '0-9'); count=${count:-0}
19
- if [[ "$count" -gt 3 ]]; then
20
- drifts+=("UC ${base}: [要確認: が ${count} 個(3個以下にすること)")
21
- fi
22
- # 実装方針・タスクは UC の .md の一番下に記載する。
23
- if ! grep -qE '^## 実装方針' "$f" 2>/dev/null; then
24
- drifts+=("UC ${base}: 「## 実装方針」の見出しがありません(UC 仕様書の一番下に記載すること)")
25
- fi
26
- if ! grep -qE '^## タスク一覧|^## タスク\b' "$f" 2>/dev/null; then
27
- drifts+=("UC ${base}: 「## タスク」または「## タスク一覧」の見出しがありません(UC 仕様書の一番下に記載すること)")
28
- fi
29
- done
30
-
31
- adr_count=$(find docs/03_アーキテクチャ/設計判断記録 -name "*.md" 2>/dev/null | wc -l)
32
- [[ "${adr_count:-0}" -lt 1 ]] && drifts+=("ADR が 1 件もありません(設計判断記録)")
33
-
34
- if [[ ! -f "docs/02_ドメイン設計/ユビキタス言語辞書.md" ]]; then
35
- drifts+=("ユビキタス言語辞書.md が存在しません")
36
- else
37
- if ! grep -qE '禁止語|forbidden' "docs/02_ドメイン設計/ユビキタス言語辞書.md" 2>/dev/null; then
38
- drifts+=("ユビキタス言語辞書に禁止語欄が定義されていません")
39
- fi
40
- fi
41
-
42
- if [[ -f "docs/02_ドメイン設計/集約.md" ]]; then
43
- if ! grep -q '対応テーブル' "docs/02_ドメイン設計/集約.md" 2>/dev/null; then
44
- drifts+=("集約.md に「対応テーブル」欄がありません")
45
- fi
46
- fi
47
-
48
- if [[ -f ".spec-runner/grade-history.json" ]] && command -v jq >/dev/null 2>&1; then
49
- grade=$(jq -r '.current_grade // ""' .spec-runner/grade-history.json 2>/dev/null)
50
- if [[ "$grade" == "A" ]]; then
51
- [[ ! -f "docs/04_インフラ設計/schema.dbml" ]] && drifts+=("Grade A 必須: schema.dbml が存在しません")
52
- # ドキュメントと Prisma / マイグレーション結果の一致
53
- if [[ -f "$(dirname "$0")/schema-sync.sh" ]]; then
54
- "$(dirname "$0")/schema-sync.sh" >/dev/null 2>&1 || drifts+=("Prisma と schema.dbml のテーブルが一致していません(spec-runner 健全性確認 または .spec-runner/scripts/check/schema-sync.sh で詳細確認)")
55
- fi
56
- # schema-drift(禁止語・集約参照・必須カラム)は任意。必要なら spec-runner ドリフト確認 --スキーマ を実行
57
- fi
58
- fi
59
-
60
- [[ ! -f "docs/06_API仕様/openapi.yaml" ]] && drifts+=("openapi.yaml が存在しません")
61
-
62
- # プロセス品質
63
- branch=$(git branch --show-current 2>/dev/null || echo "")
64
- bp="feature"
65
- other_work="work/.+|infra/.+|cicd/.+"
66
- if [[ -f "$REPO_ROOT/.spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
67
- p=$(jq -r '.naming.branch_prefix // empty' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null); [[ -n "$p" ]] && bp="$p"
68
- ow=$(jq -r '.naming.other_work_prefixes[]? | . + "/.+"' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null | tr '\n' '|' | sed 's/|$//'); [[ -n "$ow" ]] && other_work="$ow"
69
- fi
70
- valid_branch="^(main|develop|${bp}/(UC-[0-9]{3}-.+|${other_work})|fix/UC-[0-9]{3}-.+|release/[0-9]+\.[0-9]+\.[0-9]+.*|hotfix/[0-9]+\.[0-9]+\.[0-9]+-.+)\$"
71
- if [[ -n "$branch" ]] && ! echo "$branch" | grep -qE "$valid_branch"; then
72
- drifts+=("ブランチ名が規則違反: $branch")
73
- fi
74
-
75
- # JSON 出力(jq がなければ手組み)
76
- build_json() {
77
- local i
78
- echo -n '{"drifts":['
79
- for i in "${!drifts[@]}"; do
80
- [[ $i -gt 0 ]] && echo -n ','
81
- # 簡易エスケープ
82
- s="${drifts[$i]}"
83
- s="${s//\\/\\\\}"
84
- s="${s//\"/\\\"}"
85
- echo -n "\"$s\""
86
- done
87
- echo -n '],"phase":"ok","grade":"ok"}'
88
- }
89
-
90
- if [[ "$*" == *"--形式=json"* ]] || [[ "$*" == *"--形式"="json"* ]]; then
91
- build_json
92
- [[ ${#drifts[@]} -eq 0 ]] && exit 0 || exit 1
93
- fi
94
-
95
- # テキスト出力
96
- if [[ ${#drifts[@]} -eq 0 ]]; then
97
- echo "健全性確認: 問題なし"
98
- exit 0
99
- else
100
- echo "健全性確認: ${#drifts[@]} 件の指摘" >&2
101
- for d in "${drifts[@]}"; do echo " - $d" >&2; done
102
- exit 1
103
- fi
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env bash
2
- # 命名規則チェック(Section 8-3 簡易版)
3
- # ブランチ名・フォルダ名・禁止パターンを検証する。
4
-
5
- set -e
6
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
7
- cd "$REPO_ROOT"
8
- errors=0
9
-
10
- # ① ブランチ名チェック
11
- check_branch_name() {
12
- local branch
13
- branch=$(git branch --show-current 2>/dev/null || echo "nobranch")
14
- local bp="feature"
15
- local other_work="work/.+|infra/.+|cicd/.+"
16
- if [[ -f "$REPO_ROOT/.spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
17
- p=$(jq -r '.naming.branch_prefix // empty' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null); [[ -n "$p" ]] && bp="$p"
18
- ow=$(jq -r '.naming.other_work_prefixes[]? | . + "/.+"' "$REPO_ROOT/.spec-runner/project.json" 2>/dev/null | tr '\n' '|' | sed 's/|$//'); [[ -n "$ow" ]] && other_work="$ow"
19
- fi
20
- local valid="^(main|develop|${bp}/(UC-[0-9]{3}-.+|${other_work})|fix/UC-[0-9]{3}-.+|release/[0-9]+\.[0-9]+\.[0-9]+.*|hotfix/[0-9]+\.[0-9]+\.[0-9]+-.+)$"
21
- if [[ "$branch" != "nobranch" ]] && ! echo "$branch" | grep -qE "$valid"; then
22
- echo "NAMING: ブランチ名「$branch」が規則違反" >&2
23
- return 1
24
- fi
25
- return 0
26
- }
27
-
28
- # ② フォルダ名チェック(src が存在する場合のみ・kebab-case)
29
- check_folder_names() {
30
- local n=0
31
- [[ -d "src" ]] || return 0
32
- while IFS= read -r dir; do
33
- [[ -z "$dir" ]] && continue
34
- base=$(basename "$dir")
35
- if ! echo "$base" | grep -qE '^[a-z][a-z0-9-]*$'; then
36
- echo "NAMING: フォルダ名「$dir」はkebab-caseで命名してください" >&2
37
- n=$((n+1))
38
- fi
39
- done < <(find src/ -type d 2>/dev/null || true)
40
- return $n
41
- }
42
-
43
- check_branch_name || errors=$((errors+1))
44
- check_folder_names || errors=$((errors+1))
45
-
46
- if [[ $errors -eq 0 ]]; then
47
- echo "✅ 命名規則チェック: 問題なし"
48
- exit 0
49
- else
50
- exit 1
51
- fi
@@ -1,74 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ドリフト確認 --スキーマ(spec.md セクション 11)
3
- # ① DBML テーブル名の禁止語 ② テーブル note の集約参照 ③ 集約.md 対応テーブル↔DBML ④ 必須カラム
4
-
5
- set -e
6
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
7
- cd "$REPO_ROOT"
8
-
9
- DBML="docs/04_インフラ設計/schema.dbml"
10
- AGGREGATES="docs/02_ドメイン設計/集約.md"
11
- DICT="docs/02_ドメイン設計/ユビキタス言語辞書.md"
12
- errors=0
13
-
14
- [[ -f "$DBML" ]] || { echo "SCHEMA_DRIFT: $DBML が存在しません" >&2; exit 1; }
15
-
16
- # DBML 内のテーブル名一覧を取得(Table name { の形式)
17
- get_dbml_tables() {
18
- grep -E '^Table [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Table \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' || true
19
- }
20
-
21
- # ① DBML テーブル名に禁止語が含まれていないか
22
- if [[ -f "$DICT" ]] && command -v yq >/dev/null 2>&1; then
23
- while IFS= read -r -d '' forbidden; do
24
- [[ -z "$forbidden" ]] && continue
25
- while IFS= read -r table; do
26
- [[ -z "$table" ]] && continue
27
- if echo "$table" | grep -qi "$forbidden"; then
28
- echo "SCHEMA_TERM_DRIFT: テーブル「$table」に禁止語「$forbidden」が含まれています" >&2
29
- errors=$((errors+1))
30
- fi
31
- done < <(get_dbml_tables)
32
- done < <(yq '.terms[]? | select(.forbidden != null) | .forbidden[]?' "$DICT" -r -0 2>/dev/null || true)
33
- fi
34
-
35
- # ② 各テーブルの Note に集約参照(docs/02_ドメイン設計 または 集約)が含まれるか
36
- while IFS= read -r table; do
37
- [[ -z "$table" ]] && continue
38
- block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null || true)
39
- if ! echo "$block" | grep -qE 'docs/02_ドメイン設計|集約\.md'; then
40
- echo "SCHEMA_DRIFT: テーブル「$table」の Note に集約参照がありません(docs/02_ドメイン設計 または 集約.md)" >&2
41
- errors=$((errors+1))
42
- fi
43
- done < <(get_dbml_tables)
44
-
45
- # ③ 集約.md の「対応テーブル」欄のテーブルが DBML に存在するか
46
- if [[ -f "$AGGREGATES" ]]; then
47
- # 対応テーブルセクション内の `tablename` を抽出(| `name` | の形式)
48
- mapping_tables=$(sed -n '/### 対応テーブル/,/^###/p' "$AGGREGATES" 2>/dev/null | grep -oE '\`[a-z_][a-z0-9_]*\`' | tr -d '`' || true)
49
- for mapping_table in $mapping_tables; do
50
- if ! get_dbml_tables | grep -qx "$mapping_table"; then
51
- echo "SCHEMA_DRIFT: 集約.md の対応テーブル「$mapping_table」が DBML に存在しません" >&2
52
- errors=$((errors+1))
53
- fi
54
- done
55
- fi
56
-
57
- # ④ 各テーブルに必須カラム id, created_at, updated_at があるか
58
- while IFS= read -r table; do
59
- [[ -z "$table" ]] && continue
60
- block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null || true)
61
- for col in id created_at updated_at; do
62
- if ! echo "$block" | grep -qE "^\s+${col}\s"; then
63
- echo "SCHEMA_DRIFT: テーブル「$table」に必須カラム「${col}」がありません" >&2
64
- errors=$((errors+1))
65
- fi
66
- done
67
- done < <(get_dbml_tables)
68
-
69
- if [[ $errors -eq 0 ]]; then
70
- echo "✅ スキーマ整合性チェック: 問題なし"
71
- exit 0
72
- else
73
- exit 1
74
- fi
@@ -1,153 +0,0 @@
1
- #!/usr/bin/env bash
2
- # ドキュメントとマイグレーション結果の一致を検証する。
3
- # ① Prisma スキーマ ↔ schema.dbml のテーブル・ENUM 一致
4
- # ② 集約.md の「対応テーブル」に登場するテーブルが schema.dbml に存在するか
5
- # ③ 設計書の「テーブル.カラム」の使い方・型が schema.dbml の定義と一致するか(集約.md の型列は本スクリプトの正規化ルールで比較)
6
-
7
- set -e
8
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
9
- cd "$REPO_ROOT"
10
-
11
- PRISMA="prisma/schema.prisma"
12
- DBML="docs/04_インフラ設計/schema.dbml"
13
- AGGREGATES="docs/02_ドメイン設計/集約.md"
14
- errors=0
15
-
16
- [[ -f "$PRISMA" ]] || { echo "SCHEMA_SYNC: $PRISMA が存在しません" >&2; exit 1; }
17
- [[ -f "$DBML" ]] || { echo "SCHEMA_SYNC: $DBML が存在しません" >&2; exit 1; }
18
-
19
- # Prisma の @@map("table_name") からテーブル名一覧を取得
20
- get_prisma_tables() {
21
- grep -E '@@map\(' "$PRISMA" 2>/dev/null | sed -n 's/.*@@map("\([^"]*\)").*/\1/p' | sort -u
22
- }
23
-
24
- # Prisma の enum 名を取得(DBML では status_enum のように _enum 付きのことが多い)
25
- get_prisma_enums() {
26
- grep -E '^enum ' "$PRISMA" 2>/dev/null | sed 's/^enum \([A-Za-z0-9_]*\).*/\1/' | sort -u
27
- }
28
-
29
- # DBML の Table 名一覧を取得
30
- get_dbml_tables() {
31
- grep -E '^Table [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Table \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' | sort -u
32
- }
33
-
34
- # DBML の Enum 名一覧を取得
35
- get_dbml_enums() {
36
- grep -E '^Enum [a-zA-Z_][a-zA-Z0-9_]*' "$DBML" 2>/dev/null | sed 's/^Enum \([a-zA-Z_][a-zA-Z0-9_]*\).*/\1/' | sort -u
37
- }
38
-
39
- # テーブル一致チェック
40
- prisma_tables=$(get_prisma_tables)
41
- dbml_tables=$(get_dbml_tables)
42
-
43
- for t in $prisma_tables; do
44
- if ! echo "$dbml_tables" | grep -qx "$t"; then
45
- echo "SCHEMA_SYNC: Prisma のテーブル「$t」が schema.dbml にありません" >&2
46
- errors=$((errors+1))
47
- fi
48
- done
49
- for t in $dbml_tables; do
50
- if ! echo "$prisma_tables" | grep -qx "$t"; then
51
- echo "SCHEMA_SYNC: schema.dbml のテーブル「$t」が Prisma にありません" >&2
52
- errors=$((errors+1))
53
- fi
54
- done
55
-
56
- # ENUM は Prisma が PascalCase・DBML が snake_case_enum のため、DBML に Enum が存在するかだけ確認
57
- # (値の一致までは見ない。テーブル一致で運用のずれは検知できる)
58
- dbml_enum_count=$(get_dbml_enums | wc -l | tr -d ' ')
59
- prisma_enum_count=$(get_prisma_enums | wc -l | tr -d ' ')
60
- if [[ "$prisma_enum_count" -gt 0 ]] && [[ "$dbml_enum_count" -eq 0 ]]; then
61
- echo "SCHEMA_SYNC: Prisma に enum がありますが schema.dbml に Enum がありません" >&2
62
- errors=$((errors+1))
63
- fi
64
-
65
- # DBML 内で指定テーブルが指定カラムを持つか(Table ブロック内の行で「 col type」を検出)
66
- table_has_column() {
67
- local table="$1"
68
- local col="$2"
69
- local block
70
- block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null)
71
- echo "$block" | grep -qE "^\s+${col}\s+"
72
- }
73
-
74
- # DBML 内の指定テーブル.カラムの型を取得(2番目のトークン。例: uuid, varchar(200), status_enum)
75
- get_dbml_column_type() {
76
- local table="$1"
77
- local col="$2"
78
- local block
79
- block=$(awk "/^Table ${table}[^a-zA-Z0-9_]/,/^}/" "$DBML" 2>/dev/null)
80
- echo "$block" | grep -E "^\s+${col}\s+" | head -1 | awk '{ printf "%s", $2 }'
81
- }
82
-
83
- # 集約.md の対応テーブル表で table.column の行から「型」列(| 区切り4番目)を取得
84
- get_aggregate_doc_type() {
85
- local ref="$1"
86
- grep "| *${ref} *|" "$AGGREGATES" 2>/dev/null | head -1 | awk -F'|' 'NF>=4 {
87
- gsub(/^[ \t]+|[ \t]+$/, "", $4); print $4
88
- }'
89
- }
90
-
91
- # 型を正規化して比較用に統一(uuid/varchar/text/timestamptz/date/enum)
92
- # 正規化後: uuid | varchar | text | timestamptz | date | enum
93
- normalize_type() {
94
- local raw
95
- raw=$(printf '%s' "$1" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//; s/ .*//; s/(.*//')
96
- [[ -z "$raw" ]] && return
97
- raw=$(echo "$raw" | awk '{ print tolower($0) }')
98
- if [[ "$raw" == varchar* ]]; then echo -n "varchar"; return; fi
99
- if [[ "$raw" == *enum* ]] || [[ "$raw" == *_enum ]]; then echo -n "enum"; return; fi
100
- case "$raw" in
101
- uuid|text|timestamptz|date) echo -n "$raw" ;;
102
- *) echo -n "$raw" ;;
103
- esac
104
- }
105
-
106
- # 集約.md の「対応テーブル」に登場するテーブルが DBML に存在するか(schema-drift と重複するがここでも確認)
107
- if [[ -f "$AGGREGATES" ]]; then
108
- mapping_tables=$(sed -n '/### 対応テーブル/,/^###/p' "$AGGREGATES" 2>/dev/null | grep -oE '\`[a-z_][a-z0-9_]*\`' | tr -d '`' | sort -u)
109
- for t in $mapping_tables; do
110
- if ! echo "$dbml_tables" | grep -qx "$t"; then
111
- echo "SCHEMA_SYNC: 集約.md の対応テーブル「$t」が schema.dbml にありません" >&2
112
- errors=$((errors+1))
113
- fi
114
- done
115
-
116
- # 設計書の「テーブル.カラム」の使い方が schema.dbml の定義と一致するか
117
- # 集約.md の「DBテーブル.カラム」列から table.column を抽出("schema.dbml" は見出し由来のため除外)
118
- table_col_refs=$(grep -oE '[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*' "$AGGREGATES" 2>/dev/null | grep -v '^schema\.dbml$' | sort -u)
119
- while IFS= read -r ref; do
120
- ref=$(printf '%s' "$ref" | tr -d '\r\n')
121
- [[ -z "$ref" ]] && continue
122
- t="${ref%%.*}"
123
- c="${ref#*.}"
124
- [[ -z "$t" || -z "$c" ]] && continue
125
- if ! echo "$dbml_tables" | grep -qx "$t"; then
126
- echo "SCHEMA_SYNC: 集約.md で参照されているテーブル「$t」が schema.dbml にありません" >&2
127
- errors=$((errors+1))
128
- elif ! table_has_column "$t" "$c"; then
129
- echo "SCHEMA_SYNC: 集約.md の「$ref」— schema.dbml のテーブル「$t」にカラム「$c」がありません" >&2
130
- errors=$((errors+1))
131
- else
132
- # 型の一致(集約.md の「型」列と schema.dbml を正規化して比較)
133
- doc_type=$(get_aggregate_doc_type "$ref" | tr -d '\r\n')
134
- dbml_type=$(get_dbml_column_type "$t" "$c" | tr -d '\r\n')
135
- if [[ -n "$doc_type" ]] && [[ -n "$dbml_type" ]]; then
136
- norm_doc=$(normalize_type "$doc_type" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//')
137
- norm_dbml=$(normalize_type "$dbml_type" | tr -d '\r\n' | sed 's/^[ \t]*//; s/[ \t]*$//')
138
- if [[ -n "$norm_doc" ]] && [[ -n "$norm_dbml" ]] && [[ "x${norm_doc}" != "x${norm_dbml}" ]]; then
139
- echo "SCHEMA_SYNC: 集約.md の「${ref}」の型が schema.dbml と不一致(集約: ${doc_type} → ${norm_doc}, DBML: ${dbml_type} → ${norm_dbml})" >&2
140
- errors=$((errors+1))
141
- fi
142
- fi
143
- fi
144
- done <<< "$table_col_refs"
145
- fi
146
-
147
- if [[ $errors -eq 0 ]]; then
148
- echo "✅ スキーマ同期チェック: ドキュメントと Prisma は一致しています"
149
- exit 0
150
- else
151
- echo "SCHEMA_SYNC: $errors 件の不整合" >&2
152
- exit 1
153
- fi
@@ -1,75 +0,0 @@
1
- #!/usr/bin/env bash
2
- # UC コンテキスト: 現在ブランチ(feature/UC-NNN-xxx)に対応する「関連ファイルのパス」を返す。
3
- # ステップ(分析・仕様策定・実装 等)で AI が「どのファイルを読むか」を 1 回の実行で取得するために使う。
4
- # 構成: docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md(1 UC = 1 ファイル。実装方針・タスクはこの .md の一番下に記載)
5
- # 出力: FEATURE_SPEC, FEATURE_DIR を JSON またはテキスト
6
- # 使用: uc-context.sh [--json] [--paths-only]
7
-
8
- set -e
9
- REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
10
- cd "$REPO_ROOT"
11
-
12
- PATHS_ONLY=false
13
- JSON_MODE=false
14
- for a in "$@"; do
15
- case "$a" in
16
- --paths-only) PATHS_ONLY=true ;;
17
- --json) JSON_MODE=true ;;
18
- esac
19
- done
20
-
21
- BRANCH=$(git branch --show-current 2>/dev/null || echo "")
22
- # ブランチ接頭辞は project.json の naming.branch_prefix。無ければ feature
23
- BRANCH_PREFIX="feature"
24
- if [[ -f ".spec-runner/project.json" ]] && command -v jq >/dev/null 2>&1; then
25
- p=$(jq -r '.naming.branch_prefix // empty' .spec-runner/project.json 2>/dev/null)
26
- [[ -n "$p" ]] && BRANCH_PREFIX="$p"
27
- fi
28
- # 正規表現は接頭辞/UC-NNN-xxx 形式(接頭辞は英数字とハイフンのみ想定)
29
- if [[ ! "$BRANCH" =~ ^${BRANCH_PREFIX}/(UC-[0-9]{3}-[a-z0-9-]+)$ ]]; then
30
- echo "ERROR: UC 用ブランチではありません。${BRANCH_PREFIX}/UC-NNN-xxx 形式で作成してください(例: ブランチ作成 UC-001 order-placement)。接頭辞は project.json の naming.branch_prefix で変更可。" >&2
31
- exit 1
32
- fi
33
-
34
- # feature/UC-001-order-placement → UC-001-order-placement
35
- UC_BASE="${BASH_REMATCH[1]}"
36
- UC_DIR="$REPO_ROOT/docs/05_ユースケース仕様"
37
- # カテゴリフォルダ内の UC-NNN-xxx.md を探す(docs/05_ユースケース仕様/<カテゴリ>/UC-NNN-xxx.md)
38
- FEATURE_SPEC=""
39
- for f in "$UC_DIR"/*/"${UC_BASE}.md"; do
40
- [[ -f "$f" ]] && FEATURE_SPEC="$f" && break
41
- done
42
- if [[ -n "$FEATURE_SPEC" ]]; then
43
- FEATURE_DIR=$(dirname "$FEATURE_SPEC")
44
- else
45
- FEATURE_DIR="$UC_DIR"
46
- fi
47
-
48
- if [[ "$PATHS_ONLY" != true ]]; then
49
- if [[ -z "$FEATURE_SPEC" || ! -f "$FEATURE_SPEC" ]]; then
50
- echo "ERROR: UC 仕様書がありません。docs/05_ユースケース仕様/<カテゴリ>/${UC_BASE}.md のいずれかに配置してください。" >&2
51
- exit 1
52
- fi
53
- fi
54
-
55
- json_escape() {
56
- printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g; s/\r/\\r/g'
57
- }
58
- if [[ "$JSON_MODE" == true ]]; then
59
- if command -v jq >/dev/null 2>&1; then
60
- jq -cn \
61
- --arg repo_root "$REPO_ROOT" \
62
- --arg branch "$BRANCH" \
63
- --arg feature_spec "$FEATURE_SPEC" \
64
- --arg feature_dir "$FEATURE_DIR" \
65
- '{REPO_ROOT:$repo_root,BRANCH:$branch,FEATURE_SPEC:$feature_spec,FEATURE_DIR:$feature_dir}'
66
- else
67
- printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_SPEC":"%s","FEATURE_DIR":"%s"}\n' \
68
- "$(json_escape "$REPO_ROOT")" "$(json_escape "$BRANCH")" "$(json_escape "$FEATURE_SPEC")" "$(json_escape "$FEATURE_DIR")"
69
- fi
70
- else
71
- echo "REPO_ROOT: $REPO_ROOT"
72
- echo "BRANCH: $BRANCH"
73
- echo "FEATURE_SPEC: $FEATURE_SPEC"
74
- echo "FEATURE_DIR: $FEATURE_DIR"
75
- fi