spec-runner 1.0.6 → 1.0.7

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 (95) hide show
  1. package/README.md +33 -188
  2. package/bin/spec-runner.js +192 -706
  3. package/install.sh +6 -12
  4. package/package.json +3 -2
  5. package/templates/.spec-runner/hooks/pre-commit +33 -0
  6. package/templates/.spec-runner/hooks/pre-push +9 -0
  7. package/templates/.spec-runner/project.json.example +25 -0
  8. package/templates/.spec-runner/scripts/branch/create-uc-branch.sh +105 -0
  9. package/templates/.spec-runner/scripts/branch/uc-next-id.sh +17 -0
  10. package/templates/.spec-runner/scripts/branch/uc-next-start.sh +81 -0
  11. package/templates/.spec-runner/scripts/check/drift.sh +66 -0
  12. package/templates/.spec-runner/scripts/check/health.sh +103 -0
  13. package/templates/.spec-runner/scripts/check/naming.sh +51 -0
  14. package/templates/.spec-runner/scripts/check/schema-drift.sh +74 -0
  15. package/templates/.spec-runner/scripts/check/schema-sync.sh +153 -0
  16. package/templates/.spec-runner/scripts/check.sh +20 -0
  17. package/templates/.spec-runner/scripts/lib/uc-context.sh +75 -0
  18. package/templates/.spec-runner/scripts/openapi/openapi-generate.sh +207 -0
  19. package/templates/.spec-runner/scripts/setup/init-project.sh +152 -0
  20. package/templates/.spec-runner/scripts/spec-runner-core.sh +282 -0
  21. package/templates/.spec-runner/scripts/test/require-tests-green.sh +83 -0
  22. package/templates/.spec-runner/spec-runner.sh +30 -0
  23. package/templates/.spec-runner/steps//343/201/235/343/201/256/344/273/226/344/275/234/346/245/255.md +22 -0
  24. package/templates/.spec-runner/steps//343/202/277/343/202/271/343/202/257/344/270/200/350/246/247.md +132 -0
  25. 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 +106 -0
  26. package/templates/.spec-runner/steps//343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +50 -0
  27. package/templates/.spec-runner/steps//343/203/211/343/203/241/343/202/244/343/203/263/350/250/255/350/250/210.md +32 -0
  28. package/templates/.spec-runner/steps//344/273/225/346/247/230/347/255/226/345/256/232.md +219 -0
  29. package/templates/.spec-runner/steps//345/210/206/346/236/220.md +167 -0
  30. package/templates/.spec-runner/steps//345/256/237/350/243/205.md +91 -0
  31. package/templates/.spec-runner/steps//345/256/237/350/243/205/350/250/210/347/224/273.md +96 -0
  32. package/templates/.spec-runner/steps//346/206/262/347/253/240.md +74 -0
  33. package/templates/.spec-runner/steps//346/233/226/346/230/247/343/201/225/350/247/243/346/266/210.md +159 -0
  34. package/templates/.spec-runner/templates/UC-NNN-/343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271/345/220/215.md +22 -0
  35. package/templates/spec-runner-command.md +42 -0
  36. package/templates/base/.github/PULL_REQUEST_TEMPLATE.md +0 -43
  37. package/templates/base/.github/workflows/phase-gate-check.yml +0 -216
  38. package/templates/base/scripts/spec-runner.sh +0 -1144
  39. package/templates/base/templates/01_/350/246/201/344/273/266/345/256/232/347/276/251//343/201/262/343/201/252/345/275/242.md +0 -40
  40. package/templates/base/templates/03_/350/251/263/347/264/260/350/250/255/350/250/210//343/202/244/343/203/263/343/203/225/343/203/251.md +0 -14
  41. package/templates/base/templates/03_/350/251/263/347/264/260/350/250/255/350/250/210//343/203/206/343/203/274/343/203/226/343/203/253.md +0 -17
  42. package/templates/base/templates/03_/350/251/263/347/264/260/350/250/255/350/250/210//343/203/211/343/203/241/343/202/244/343/203/263.md +0 -18
  43. package/templates/base/templates/03_/350/251/263/347/264/260/350/250/255/350/250/210//343/203/246/343/203/274/343/202/271/343/202/261/343/203/274/343/202/271.md +0 -12
  44. package/templates/base/templates/99_/350/250/255/350/250/210/345/210/244/346/226/255/350/250/230/351/214/262//343/201/262/343/201/252/345/275/242.md +0 -46
  45. package/templates/base/templates/README.md +0 -32
  46. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210/01_/346/206/262/347/253/240.md +0 -48
  47. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210/02_/344/273/225/346/247/230.md +0 -39
  48. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210/03_/347/224/250/350/252/236/351/233/206.md +0 -51
  49. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210/99_/350/250/255/350/250/210/345/210/244/346/226/255/350/250/230/351/214/262/.gitkeep +0 -0
  50. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210//343/203/206/343/203/263/343/203/227/343/203/254/343/203/274/343/203/210/344/270/200/350/246/247.md +0 -14
  51. package/templates/base/templates//345/210/235/346/234/237/343/203/211/343/202/255/343/203/245/343/203/241/343/203/263/343/203/210//346/214/257/343/202/212/350/277/224/343/202/212//350/262/240/345/202/265.md +0 -8
  52. package/templates/claude/.claude/commands/sr-/343/202/262/343/203/274/343/203/210/350/250/255/345/256/232.md +0 -9
  53. package/templates/claude/.claude/commands/sr-/343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +0 -9
  54. package/templates/claude/.claude/commands/sr-/343/203/254/343/203/223/343/203/245/343/203/274.md +0 -9
  55. package/templates/claude/.claude/commands/sr-/344/273/225/346/247/230.md +0 -9
  56. package/templates/claude/.claude/commands/sr-/344/277/256/346/255/243.md +0 -9
  57. package/templates/claude/.claude/commands/sr-/345/210/235/346/234/237/345/214/226.md +0 -10
  58. package/templates/claude/.claude/commands/sr-/345/256/237/350/243/205.md +0 -9
  59. package/templates/claude/.claude/commands/sr-/346/206/262/347/253/240.md +0 -9
  60. package/templates/claude/.claude/commands/sr-/346/246/202/350/246/201/350/250/255/350/250/210.md +0 -9
  61. package/templates/claude/.claude/commands/sr-/347/212/266/346/205/213.md +0 -9
  62. package/templates/claude/.claude/commands/sr-/347/267/212/346/200/245/344/277/256/346/255/243.md +0 -9
  63. package/templates/claude/.claude/commands/sr-/350/250/255/345/256/232.md +0 -11
  64. package/templates/claude/.claude/commands/sr-/350/251/263/347/264/260/350/250/255/350/250/210.md +0 -9
  65. package/templates/claude/.claude/hooks/pre-tool-use.sh +0 -79
  66. package/templates/claude/.claude/settings.json +0 -29
  67. package/templates/claude/CLAUDE.md +0 -141
  68. package/templates/copilot/.github/copilot-instructions.md +0 -25
  69. package/templates/copilot/.github/prompts/sr-/343/202/262/343/203/274/343/203/210/350/250/255/345/256/232.prompt.md +0 -14
  70. package/templates/copilot/.github/prompts/sr-/343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.prompt.md +0 -13
  71. package/templates/copilot/.github/prompts/sr-/343/203/254/343/203/223/343/203/245/343/203/274.prompt.md +0 -14
  72. package/templates/copilot/.github/prompts/sr-/344/273/225/346/247/230.prompt.md +0 -13
  73. package/templates/copilot/.github/prompts/sr-/344/277/256/346/255/243.prompt.md +0 -14
  74. package/templates/copilot/.github/prompts/sr-/345/210/235/346/234/237/345/214/226.prompt.md +0 -15
  75. package/templates/copilot/.github/prompts/sr-/345/256/237/350/243/205.prompt.md +0 -13
  76. package/templates/copilot/.github/prompts/sr-/346/206/262/347/253/240.prompt.md +0 -13
  77. package/templates/copilot/.github/prompts/sr-/346/246/202/350/246/201/350/250/255/350/250/210.prompt.md +0 -13
  78. package/templates/copilot/.github/prompts/sr-/347/212/266/346/205/213.prompt.md +0 -13
  79. package/templates/copilot/.github/prompts/sr-/347/267/212/346/200/245/344/277/256/346/255/243.prompt.md +0 -14
  80. package/templates/copilot/.github/prompts/sr-/350/250/255/345/256/232.prompt.md +0 -13
  81. package/templates/copilot/.github/prompts/sr-/350/251/263/347/264/260/350/250/255/350/250/210.prompt.md +0 -14
  82. package/templates/cursor/.cursor/commands/sr-/343/202/262/343/203/274/343/203/210/350/250/255/345/256/232.md +0 -11
  83. package/templates/cursor/.cursor/commands/sr-/343/203/206/343/202/271/343/203/210/350/250/255/350/250/210.md +0 -9
  84. package/templates/cursor/.cursor/commands/sr-/343/203/254/343/203/223/343/203/245/343/203/274.md +0 -9
  85. package/templates/cursor/.cursor/commands/sr-/344/273/225/346/247/230.md +0 -9
  86. package/templates/cursor/.cursor/commands/sr-/344/277/256/346/255/243.md +0 -9
  87. package/templates/cursor/.cursor/commands/sr-/345/210/235/346/234/237/345/214/226.md +0 -35
  88. package/templates/cursor/.cursor/commands/sr-/345/256/237/350/243/205.md +0 -9
  89. package/templates/cursor/.cursor/commands/sr-/346/206/262/347/253/240.md +0 -9
  90. package/templates/cursor/.cursor/commands/sr-/346/246/202/350/246/201/350/250/255/350/250/210.md +0 -9
  91. package/templates/cursor/.cursor/commands/sr-/347/212/266/346/205/213.md +0 -9
  92. package/templates/cursor/.cursor/commands/sr-/347/267/212/346/200/245/344/277/256/346/255/243.md +0 -9
  93. package/templates/cursor/.cursor/commands/sr-/350/250/255/345/256/232.md +0 -20
  94. package/templates/cursor/.cursor/commands/sr-/350/251/263/347/264/260/350/250/255/350/250/210.md +0 -9
  95. package/templates/cursor/.cursorrules +0 -26
@@ -0,0 +1,282 @@
1
+ #!/usr/bin/env bash
2
+ # spec-runner 判定の単一入口。振る舞い仕様に沿って「フェーズ・ゲート・状態・グレード」を扱う。
3
+ # 使用: spec-runner-core.sh [--phase] [--json] | --gate [GRADE] | --status | --grade
4
+ # cmd-dispatch.sh(次のステップ・ゲート確認・ブランチ作成)から呼ばれる。
5
+
6
+ set -e
7
+ REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
8
+ cd "$REPO_ROOT"
9
+ STEPS_DIR="${STEPS_DIR:-$REPO_ROOT/.spec-runner/steps}"
10
+ LOCK_FILE=".spec-runner/phase-locks.json"
11
+ PROJECT_JSON=".spec-runner/project.json"
12
+
13
+ MODE="phase"
14
+ JSON_MODE=false
15
+ GATE_GRADE=""
16
+ while [[ $# -gt 0 ]]; do
17
+ case "$1" in
18
+ --phase) MODE="phase" ;;
19
+ --json) JSON_MODE=true ;;
20
+ --gate) MODE="gate"; GATE_GRADE="${2:-}"; shift ;;
21
+ --status) MODE="status" ;;
22
+ --grade) MODE="grade" ;;
23
+ *) [[ "$MODE" == "gate" && -z "$GATE_GRADE" ]] && GATE_GRADE="$1" ;;
24
+ esac
25
+ shift
26
+ done
27
+
28
+ # === 共通: lock / grade / branch を一度だけ読む ===
29
+ has_charter_lock=0
30
+ has_domain_lock=0
31
+ has_arch_lock=0
32
+ has_infra_lock=0
33
+ if [[ -f "$LOCK_FILE" ]] && command -v jq >/dev/null 2>&1; then
34
+ [[ "$(jq -r '.charter.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] && has_charter_lock=1
35
+ [[ "$(jq -r '.domain.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] && has_domain_lock=1
36
+ [[ "$(jq -r '.architecture.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] && has_arch_lock=1
37
+ [[ "$(jq -r '.infra.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] && has_infra_lock=1
38
+ fi
39
+
40
+ grade="LOOP1"
41
+ if [[ -f ".spec-runner/grade-history.json" ]] && command -v jq >/dev/null 2>&1; then
42
+ grade=$(jq -r '.current_grade // "LOOP1"' .spec-runner/grade-history.json 2>/dev/null || echo "LOOP1")
43
+ fi
44
+
45
+ branch=$(git branch --show-current 2>/dev/null || echo "")
46
+ branch_prefix="feature"
47
+ test_dir="tests"
48
+ test_pattern="*.spec.*"
49
+ if [[ -f "$PROJECT_JSON" ]] && command -v jq >/dev/null 2>&1; then
50
+ p=$(jq -r '.naming.branch_prefix // empty' "$PROJECT_JSON" 2>/dev/null)
51
+ [[ -n "$p" ]] && branch_prefix="$p"
52
+ d=$(jq -r '.test_design.dir // empty' "$PROJECT_JSON" 2>/dev/null)
53
+ [[ -n "$d" ]] && test_dir="$d"
54
+ pat=$(jq -r '.test_design.pattern // empty' "$PROJECT_JSON" 2>/dev/null)
55
+ [[ -n "$pat" ]] && test_pattern="$pat"
56
+ fi
57
+ on_uc_branch=0
58
+ on_other_work_branch=0
59
+ current_uc_id=""
60
+ other_work_pattern="work|infra|cicd"
61
+ if [[ -f "$PROJECT_JSON" ]] && command -v jq >/dev/null 2>&1; then
62
+ ow=$(jq -r '.naming.other_work_prefixes[]? // empty' "$PROJECT_JSON" 2>/dev/null | tr '\n' '|' | sed 's/|$//')
63
+ [[ -n "$ow" ]] && other_work_pattern="$ow"
64
+ fi
65
+ if [[ "$branch" =~ ^${branch_prefix}/UC-[0-9]{3}- ]]; then
66
+ on_uc_branch=1
67
+ current_uc_id="${branch#*/}"
68
+ elif [[ "$branch" =~ ^${branch_prefix}/(${other_work_pattern})/ ]]; then
69
+ on_other_work_branch=1
70
+ fi
71
+
72
+ # === ゲート確認モード ===
73
+ run_gate() {
74
+ exit_error() { echo "GATE: $1" >&2; exit 1; }
75
+ GRD="${GATE_GRADE:-$grade}"
76
+ [[ -f "$LOCK_FILE" ]] || exit_error "phase-locks.json が存在しません"
77
+ command -v jq >/dev/null 2>&1 || exit_error "jq がインストールされていません(例: brew install jq)"
78
+
79
+ check_required_paths() {
80
+ local key="$1"
81
+ local list
82
+ if [[ -f "$PROJECT_JSON" ]]; then
83
+ list=$(jq -r --arg k "$key" '.required_docs[$k][]? // empty' "$PROJECT_JSON" 2>/dev/null)
84
+ fi
85
+ if [[ -z "$list" ]]; then return 1; fi
86
+ while IFS= read -r path; do
87
+ [[ -z "$path" ]] && continue
88
+ if [[ -f "$path" ]]; then
89
+ :
90
+ elif [[ -d "$path" ]]; then
91
+ count=$(find "$path" -maxdepth 1 -name "*.md" 2>/dev/null | wc -l | tr -d ' ')
92
+ [[ "${count:-0}" -ge 1 ]] || exit_error "必須: $path に 1 件以上の .md がありません"
93
+ else
94
+ exit_error "必須: $path が存在しません"
95
+ fi
96
+ done <<< "$list"
97
+ return 0
98
+ }
99
+
100
+ if check_required_paths "charter"; then :; else
101
+ [[ -f "docs/01_憲章/憲章.md" ]] || exit_error "憲章.md が存在しません"
102
+ fi
103
+ if [[ "$GRD" == "LOOP1" ]]; then
104
+ [[ "$(jq -r '.charter.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] || exit_error "Phase 0 未完了(phase-locks.json の charter.completed)"
105
+ jq -e '.charter.reviewed_by' "$LOCK_FILE" >/dev/null 2>&1 || exit_error "憲章に署名がありません(charter.reviewed_by)"
106
+ echo "Phase 0: OK"
107
+ fi
108
+ if [[ "$GRD" != "LOOP1" ]]; then
109
+ [[ "$(jq -r '.domain.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] || exit_error "Phase 1 未完了"
110
+ if check_required_paths "domain"; then :; else
111
+ for doc in ユビキタス言語辞書 ドメインモデル 集約; do
112
+ [[ -f "docs/02_ドメイン設計/${doc}.md" ]] || exit_error "${doc}.md が存在しません"
113
+ done
114
+ fi
115
+ echo "Phase 1: OK"
116
+ [[ "$(jq -r '.architecture.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] || exit_error "Phase 2 未完了"
117
+ if check_required_paths "architecture"; then :; else
118
+ [[ -f "docs/03_アーキテクチャ/パターン選定.md" ]] || exit_error "パターン選定.md が存在しません"
119
+ [[ -f "docs/03_アーキテクチャ/インフラ方針.md" ]] || exit_error "インフラ方針.md が存在しません"
120
+ [[ $(find "docs/03_アーキテクチャ/設計判断記録/" -name "*.md" 2>/dev/null | wc -l) -gt 0 ]] || exit_error "ADR が 1 件もありません"
121
+ fi
122
+ echo "Phase 2: OK"
123
+ fi
124
+ if [[ "$GRD" == "A" ]]; then
125
+ [[ "$(jq -r '.infra.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] || exit_error "Grade A: インフラ設計未完了"
126
+ if check_required_paths "grade_a"; then :; else
127
+ [[ -f "docs/04_インフラ設計/schema.dbml" ]] || exit_error "Grade A 必須: docs/04_インフラ設計/schema.dbml が存在しません"
128
+ fi
129
+ echo "Phase 4 (Grade A): OK"
130
+ fi
131
+ if [[ "$GRD" == "A" || "$GRD" == "B" ]]; then
132
+ uc_count=$(find docs/05_ユースケース仕様 -mindepth 2 -maxdepth 2 -name "UC-*.md" 2>/dev/null | wc -l)
133
+ if [[ "${uc_count:-0}" -gt 0 ]]; then
134
+ uc_reviewed_count=$(jq -r '.uc_reviewed // [] | length' "$LOCK_FILE" 2>/dev/null || echo 0)
135
+ [[ "${uc_reviewed_count:-0}" -gt 0 ]] || exit_error "Gate 3: uc_reviewed に少なくとも1件の UC 識別子を登録してください"
136
+ if check_required_paths "gate3_openapi"; then :; else
137
+ [[ -f "docs/06_API仕様/openapi.yaml" ]] || exit_error "Gate 3: docs/06_API仕様/openapi.yaml が存在しません"
138
+ fi
139
+ echo "Gate 3 (UC+OpenAPI): OK"
140
+ fi
141
+ test_design_ok=0
142
+ [[ "$(jq -r '.test_design.completed // false' "$LOCK_FILE" 2>/dev/null)" == "true" ]] && test_design_ok=1
143
+ if [[ $test_design_ok -eq 1 ]] || [[ -d "$test_dir" && -n "$(find "$test_dir" -type f -name "$test_pattern" 2>/dev/null | head -1)" ]]; then
144
+ echo "Gate 5 (テスト設計): OK"
145
+ fi
146
+ fi
147
+ if [[ "$GRD" == "A" || "$GRD" == "B" || "$GRD" == "C" ]]; then
148
+ if [[ -f ".spec-runner/scripts/test/require-tests-green.sh" ]]; then
149
+ .spec-runner/scripts/test/require-tests-green.sh 2>/dev/null && echo "Gate 6 (テスト通過): OK" || true
150
+ elif [[ -f "docker-compose.yml" ]] || [[ -f "compose.yml" ]]; then
151
+ docker compose run --rm app npm test 2>/dev/null && echo "Gate 6 (テスト通過): OK" || true
152
+ elif [[ -f "package.json" ]]; then
153
+ npm test 2>/dev/null && echo "Gate 6 (テスト通過): OK" || true
154
+ fi
155
+ fi
156
+ echo "ゲート確認: 通過"
157
+ }
158
+
159
+ # === フェーズ判定モード ===
160
+ run_phase() {
161
+ phase=0
162
+ phase_name_ja=""
163
+ command=""
164
+ command_file=""
165
+
166
+ if [[ $has_charter_lock -eq 0 ]]; then
167
+ phase=0; phase_name_ja="憲章策定"; command="憲章"; command_file="$STEPS_DIR/憲章.md"
168
+ elif [[ $has_domain_lock -eq 0 ]]; then
169
+ phase=1; phase_name_ja="ドメイン設計"; command="ドメイン設計"; command_file="$STEPS_DIR/ドメイン設計.md"
170
+ elif [[ $has_arch_lock -eq 0 ]]; then
171
+ phase=2; phase_name_ja="アーキテクチャ選択"; command="実装計画"; command_file="$STEPS_DIR/実装計画.md"
172
+ elif [[ $on_uc_branch -eq 1 ]]; then
173
+ uc_spec=""
174
+ if [[ -n "$current_uc_id" ]]; then
175
+ for f in docs/05_ユースケース仕様/*/"${current_uc_id}.md"; do [[ -f "$f" ]] && uc_spec="$f" && break; done
176
+ fi
177
+ if [[ -z "$uc_spec" ]]; then
178
+ phase=3; phase_name_ja="UC 仕様書"; command="仕様策定"; command_file="$STEPS_DIR/仕様策定.md"
179
+ else
180
+ uc_dir=$(basename "$uc_spec" .md)
181
+ reviewed=0
182
+ [[ -f "$LOCK_FILE" ]] && command -v jq >/dev/null 2>&1 && jq -e --arg u "$uc_dir" '.uc_reviewed[]? == $u' "$LOCK_FILE" 2>/dev/null | grep -q true && reviewed=1
183
+ if [[ $reviewed -eq 0 ]]; then
184
+ phase=3; phase_name_ja="UC 仕様書(レビュー通過まで)"; command="曖昧さ解消"; command_file="$STEPS_DIR/曖昧さ解消.md"
185
+ else
186
+ if [[ "$grade" == "A" ]] && [[ $has_infra_lock -eq 0 ]]; then
187
+ phase=4; phase_name_ja="インフラ詳細設計"; command="実装計画"; command_file="$STEPS_DIR/実装計画.md"
188
+ else
189
+ has_tests=0
190
+ [[ -d "$test_dir" ]] && [[ -n "$(find "$test_dir" -type f -name "$test_pattern" 2>/dev/null | head -1)" ]] && has_tests=1
191
+ if [[ $has_tests -eq 0 ]]; then
192
+ phase=5; phase_name_ja="テスト設計"; command="テスト設計"; command_file="$STEPS_DIR/テスト設計.md"
193
+ else
194
+ phase=6; phase_name_ja="実装"; command="実装"; command_file="$STEPS_DIR/実装.md"
195
+ fi
196
+ fi
197
+ fi
198
+ fi
199
+ else
200
+ if [[ $on_other_work_branch -eq 1 ]]; then
201
+ phase=3; phase_name_ja="その他作業(CI/CD・インフラ等)"; command="その他作業"; command_file="$STEPS_DIR/その他作業.md"
202
+ elif [[ $has_arch_lock -eq 0 ]]; then
203
+ phase=2; phase_name_ja="アーキテクチャ選択"; command="実装計画"; command_file="$STEPS_DIR/実装計画.md"
204
+ else
205
+ phase=3; phase_name_ja="UC 開始(仕様策定)"; command="仕様策定"; command_file="$STEPS_DIR/仕様策定.md"
206
+ fi
207
+ fi
208
+
209
+ if [[ "$JSON_MODE" == true ]]; then
210
+ if command -v jq >/dev/null 2>&1; then
211
+ jq -cn --argjson phase $phase --arg name "$phase_name_ja" --arg cmd "$command" --arg file "$command_file" --arg grade "$grade" \
212
+ '{phase:$phase, phase_name_ja:$name, command:$cmd, command_file:$file, grade:$grade}'
213
+ else
214
+ echo "{\"phase\":$phase,\"phase_name_ja\":\"$phase_name_ja\",\"command\":\"$command\",\"command_file\":\"$command_file\",\"grade\":\"$grade\"}"
215
+ fi
216
+ else
217
+ echo "現在フェーズ: Phase $phase($phase_name_ja)"
218
+ echo "推奨コマンド: $command"
219
+ echo "コマンドファイル: $command_file"
220
+ echo "グレード: $grade"
221
+ if [[ $on_uc_branch -eq 0 ]] && [[ -n "$branch" ]]; then
222
+ echo ""
223
+ echo "注意: main 等のままの修正は危険です。UC 用ブランチを作成してから作業してください(仕様策定ステップでブランチ作成)。"
224
+ fi
225
+ fi
226
+ }
227
+
228
+ # === 状態表示(lock 一覧)===
229
+ run_status() {
230
+ echo "=== spec-runner フェーズ状況 ==="
231
+ echo "グレード: $grade"
232
+ echo ""
233
+ echo "Lock(.spec-runner/phase-locks.json):"
234
+ if [[ -f "$LOCK_FILE" ]] && command -v jq >/dev/null 2>&1; then
235
+ for sec in charter domain architecture infra test_design; do
236
+ val=$(jq -r --arg s "$sec" '.[$s].completed // false' "$LOCK_FILE" 2>/dev/null)
237
+ [[ "$val" == "true" ]] && echo " ✓ $sec" || echo " - $sec"
238
+ done
239
+ else
240
+ echo " phase-locks.json が存在しないか jq がインストールされていません"
241
+ fi
242
+ }
243
+
244
+ # === グレード判定チェックリスト ===
245
+ run_grade() {
246
+ echo "=== spec-runner グレード判定 ==="
247
+ echo ""
248
+ echo "STEP 1: 作業の種別を判定する"
249
+ echo " → 既存UCの修正・バグfix: Grade C 確定(以降の判定不要)"
250
+ echo " → 新規UC: STEP 2 へ"
251
+ echo ""
252
+ echo "STEP 2: Grade B 仮置きで UC 草稿を作成する"
253
+ echo " → AI と対話して UC 仕様書の草稿(status: draft)を作成する"
254
+ echo " → この時点ではまだブランチ作成・コミット不要"
255
+ echo ""
256
+ echo "STEP 3: UC 草稿を見て Grade を確定する"
257
+ echo " 以下のいずれかが草稿に含まれていれば Grade A:"
258
+ echo " □ 新しいDB・テーブル・コレクションの追加が必要"
259
+ echo " □ 外部API・SaaS との新規連携が必要"
260
+ echo " □ 新規クラウドサービスの追加が必要(S3バケット・Redisクラスタ等)"
261
+ echo " □ ネットワーク構成の変更が必要"
262
+ echo " □ CI/CDパイプラインの変更が必要"
263
+ echo " → 一つでも該当: Grade A 確定 → ブランチ作成 → Phase 3(UC仕様書)へ"
264
+ echo " → 全て非該当: Grade B 確定 → ブランチ作成 → Phase 3(UC仕様書)へ(草稿を流用)"
265
+ echo ""
266
+ echo "判定結果は .spec-runner/grade-history.json に記録する(ブランチ名には含めない)。"
267
+ echo "迷ったら上位グレードを選ぶ。"
268
+ echo ""
269
+ echo "現在の記録: current_grade = $grade"
270
+ }
271
+
272
+ # === 実行 ===
273
+ if [[ "$MODE" == "gate" ]]; then
274
+ echo "=== ゲート確認(グレード: ${GATE_GRADE:-$grade}) ==="
275
+ run_gate
276
+ elif [[ "$MODE" == "status" ]]; then
277
+ run_status
278
+ elif [[ "$MODE" == "grade" ]]; then
279
+ run_grade
280
+ else
281
+ run_phase
282
+ fi
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env bash
2
+ # テストコマンドを実行し、すべてグリーンでないと終了コード 1 を返す。
3
+ # 実装ステップの「完了検証」で必須。コマンドは .spec-runner/project.json の test_command.run で指定。
4
+ # 未設定時はプロジェクトを検出して候補を実行し、設定を促す。
5
+
6
+ set -e
7
+ REPO_ROOT="${REPO_ROOT:-$(git rev-parse --show-toplevel 2>/dev/null || echo ".")}"
8
+ cd "$REPO_ROOT"
9
+ SPEC_RUNNER="${REPO_ROOT}/.spec-runner"
10
+ PROJECT_JSON="${SPEC_RUNNER}/project.json"
11
+ LEGACY_TEST_JSON="${SPEC_RUNNER}/test-command.json"
12
+
13
+ # 設定を読む: project.json の test_command.run → 後方互換で test-command.json の run
14
+ get_run_command() {
15
+ local run=""
16
+ if [[ -f "$PROJECT_JSON" ]]; then
17
+ if command -v jq >/dev/null 2>&1; then
18
+ run=$(jq -r '.test_command.run // empty' "$PROJECT_JSON" 2>/dev/null)
19
+ else
20
+ run=$(grep -o '"run"[[:space:]]*:[[:space:]]*"[^"]*"' "$PROJECT_JSON" 2>/dev/null | head -1 | sed 's/.*"\([^"]*\)" *$/\1/')
21
+ fi
22
+ fi
23
+ if [[ -z "$run" ]] && [[ -f "$LEGACY_TEST_JSON" ]]; then
24
+ if command -v jq >/dev/null 2>&1; then
25
+ run=$(jq -r '.run // empty' "$LEGACY_TEST_JSON" 2>/dev/null)
26
+ else
27
+ run=$(grep -o '"run"[[:space:]]*:[[:space:]]*"[^"]*"' "$LEGACY_TEST_JSON" 2>/dev/null | sed 's/.*"\([^"]*\)" *$/\1/')
28
+ fi
29
+ fi
30
+ echo -n "$run"
31
+ }
32
+
33
+ # 未設定時に検出して実行するコマンドを決める
34
+ detect_and_run() {
35
+ if [[ -f "package.json" ]] && grep -q '"test"' package.json 2>/dev/null; then
36
+ echo "npm test"
37
+ return
38
+ fi
39
+ if [[ -f "pyproject.toml" ]]; then
40
+ if command -v poetry >/dev/null 2>&1; then
41
+ echo "poetry run pytest"
42
+ else
43
+ echo "pytest"
44
+ fi
45
+ return
46
+ fi
47
+ if [[ -f "go.mod" ]]; then
48
+ echo "go test ./..."
49
+ return
50
+ fi
51
+ if [[ -f "docker-compose.yml" ]] || [[ -f "docker-compose.yaml" ]]; then
52
+ # サービス名は app を仮定(未設定時は検出のみで実行しない)
53
+ echo ""
54
+ return
55
+ fi
56
+ echo ""
57
+ }
58
+
59
+ RUN_CMD=$(get_run_command)
60
+
61
+ # 未設定なら検出
62
+ if [[ -z "$RUN_CMD" ]]; then
63
+ RUN_CMD=$(detect_and_run)
64
+ if [[ -z "$RUN_CMD" ]]; then
65
+ echo "require-tests-green: テストコマンドが未設定です。.spec-runner/project.json の test_command.run を設定するか、初期化(init-project.sh)を実行してください。" >&2
66
+ echo " 例: project.json に \"test_command\": {\"run\": \"npm test\"} を追加" >&2
67
+ exit 1
68
+ fi
69
+ echo "require-tests-green: project.json に test_command がありません。検出したコマンドで実行します: $RUN_CMD" >&2
70
+ echo " 恒久設定: 初期化で設定するか、project.json に test_command.run を追加してください。" >&2
71
+ fi
72
+
73
+ echo "require-tests-green: テストを実行しています($RUN_CMD)..."
74
+ if eval "$RUN_CMD"; then
75
+ echo "require-tests-green: すべてグリーンです。実装完了の条件(テスト)を満たしています。"
76
+ exit 0
77
+ else
78
+ echo "" >&2
79
+ echo "require-tests-green: テストが失敗しました。実装完了とみなせません。" >&2
80
+ echo " テストを修正し、再度 .spec-runner/scripts/test/require-tests-green.sh を実行してください。" >&2
81
+ echo " コマンドを変更する場合は .spec-runner/project.json の test_command.run を編集するか、初期化(init-project.sh)を実行してください。" >&2
82
+ exit 1
83
+ fi
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env bash
2
+ # spec-runner エントリポイント。次のステップ(現在フェーズ・やるべきステップ .md)を返す。
3
+ # 使用: .spec-runner/spec-runner.sh [次のステップ] [--json|--lock|--グレード]
4
+
5
+ set -e
6
+ REPO_ROOT="${REPO_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}"
7
+ cd "$REPO_ROOT"
8
+ SR="$REPO_ROOT/.spec-runner/scripts"
9
+
10
+ cmd="${1:-}"
11
+ shift || true
12
+
13
+ case "$cmd" in
14
+ 次のステップ|"")
15
+ if [[ "$cmd" == "" ]]; then
16
+ # 引数なしは「次のステップ」として扱う(/spec-runner スラッシュコマンド用)
17
+ :
18
+ fi
19
+ case "${1:-}" in
20
+ --lock) exec "$SR/spec-runner-core.sh" --status ;;
21
+ --グレード) exec "$SR/spec-runner-core.sh" --grade ;;
22
+ *) exec "$SR/spec-runner-core.sh" --phase "$@" ;;
23
+ esac
24
+ ;;
25
+ *)
26
+ echo "spec-runner: 引数なし、または「次のステップ」[--json|--lock|--グレード] のみ対応しています。" >&2
27
+ echo " 使用例: .spec-runner/spec-runner.sh 次のステップ --json" >&2
28
+ exit 1
29
+ ;;
30
+ esac
@@ -0,0 +1,22 @@
1
+ # その他作業(CI/CD・インフラ・共通設定など)
2
+
3
+ **このブランチは UC 用ではありません。** `feature/<接頭辞>/xxx` 形式(接頭辞は project.json の **naming.other_work_prefixes** で指定)のため、spec-runner は「CI/CD 構築」「インフラ」「共通設定」など UC 以外の作業用とみなします。
4
+
5
+ ## ブランチの例
6
+
7
+ - `feature/cicd/github-actions` … CI/CD 構築
8
+ - `feature/infra/docker` … インフラ・コンテナ
9
+ - `feature/work/setup-tooling` … 共通ツール導入
10
+
11
+ 接頭辞は **project.json の naming.other_work_prefixes** に配列で書く(既定: work, infra, cicd)。プロジェクトに合わせて追加・変更してよい。
12
+
13
+ ## やること
14
+
15
+ - **spec-runner の UC ステップ(仕様策定〜実装)は対象外**。このステップを出したまま、通常の開発フローで進めてよい。
16
+ - 必要に応じて **実装計画.md**(`.spec-runner/steps/実装計画.md`)を参照し、設計書(docs/03, docs/04, docs/06 等)に沿って作業する。
17
+ - 作業が終わったら main にマージするか、UC 用ブランチ(`feature/UC-NNN-xxx`)に切り替えて spec-runner を再実行する。
18
+
19
+ ## 注意
20
+
21
+ - phase-locks の `uc_reviewed` や UC 用の判定には影響しない。このブランチ用の lock は不要。
22
+ - 再度 spec-runner で「次のステップ」を見たい場合は、UC 用ブランチにチェックアウトするか、main に戻る。
@@ -0,0 +1,132 @@
1
+ > **実装タスクの整理**: UC 仕様書(UC-NNN-xxx.md)の**一番下**に「## タスク」または「## タスク一覧」セクションを追加・更新する。タスクは UC の .md 内に記載する。テスト設計は別ステップ「テスト設計」で行う。
2
+
3
+ ## ユーザー入力
4
+
5
+ ```text
6
+ $ARGUMENTS
7
+ ```
8
+
9
+ (空でない場合)処理を進める前にユーザー入力を**必ず**考慮すること。
10
+
11
+ ## 実行前チェック
12
+
13
+ **拡張フックの確認(タスク生成前)**:
14
+ - プロジェクトルートに `.spec-runner/extensions.yml` が存在するか確認する(任意。なければスキップ)
15
+ - あれば `hooks.before_tasks` のエントリを読む
16
+ - YAML がパースできない・不正な場合はフック確認を静かにスキップして続行
17
+ - `enabled: true` のフックだけを対象にする
18
+ - 各フックの `condition` 式の解釈・評価は行わない(条件が空でなければフックをスキップし、評価は HookExecutor に任せる)
19
+ - 実行可能なフックごとに `optional` に応じて出力する:
20
+ - **任意フック**(`optional: true`): 拡張名・コマンド・説明・プロンプト・実行方法を表示
21
+ - **必須フック**(`optional: false`): 拡張名・実行するコマンドを表示し、結果を待ってから概要に進む
22
+ - 登録フックがなければ、または `.spec-runner/extensions.yml` がなければ静かにスキップする
23
+
24
+ ## 概要
25
+
26
+ 1. **セットアップ**: リポジトリルートから `.spec-runner/scripts/lib/uc-context.sh --json` を実行する。現在ブランチは feature/UC-NNN-xxx。JSON から FEATURE_SPEC(UC 仕様書)、FEATURE_DIR をパースする。パスは絶対パス。引数にシングルクォートを含む場合はエスケープ(例: 'I'\''m Groot')またはダブルクォートを使う。
27
+
28
+ 2. **設計ドキュメントの読み込み**: FEATURE_SPEC(UC 仕様書)を必須で読む。UC の「## 実装方針」から技術スタック・構造を読む。無ければ UC 仕様書とプロジェクト既存ファイルから推測する。
29
+
30
+ 3. **タスク整理ワークフローの実行**:
31
+ - UC の「## 実装方針」から技術スタック・プロジェクト構造を抽出する。無ければ UC 仕様書とプロジェクト既存ファイルから推測する。
32
+ - spec.md を読み、優先度(P1, P2, P3 等)付きユーザーストーリーを抽出する
33
+ - data-model.md があればエンティティを抽出しユーザーストーリーにマッピングする
34
+ - contracts/ があればインターフェース契約をユーザーストーリーにマッピングする
35
+ - research.md があれば決定をセットアップタスクに反映する
36
+ - ユーザーストーリーごとにタスクを整理して生成する(下記タスク生成ルール参照)
37
+ - ユーザーストーリー完了順の依存グラフを作成する
38
+ - ストーリーごとに並列実行例を作成する
39
+ - タスクの完全性を検証する(各ユーザーストーリーに必要なタスクが揃い、独立してテスト可能であること)
40
+
41
+ 4. **UC 仕様書へのタスクセクション追加・更新**: **FEATURE_SPEC(UC の .md)の一番下**に「## タスク」または「## タスク一覧」見出しを置き、以下で埋める。既にセクションがあれば内容を更新する。
42
+ - UC の実装方針から正しい機能名
43
+ - Phase 1: セットアップタスク(プロジェクト初期化)
44
+ - Phase 2: 基盤タスク(全ユーザーストーリーの前提)
45
+ - Phase 3 以降: 優先度順でユーザーストーリーごとに 1 フェーズ
46
+ - 各フェーズにストーリー目標・独立したテスト基準・(依頼があれば)テスト・実装タスクを含める
47
+ - 最終フェーズ: 仕上げと横断的関心事
48
+ - 全タスクは下記チェックリスト形式に厳守する
49
+ - 各タスクに明確なファイルパスを付ける
50
+ - ストーリー完了順の依存セクション
51
+ - ストーリーごとの並列実行例
52
+ - 実装戦略セクション(MVP 優先・段階的リリース)
53
+
54
+ 5. **報告**: 更新した UC 仕様書(FEATURE_SPEC)のパスとタスクセクションの概要を出力する:
55
+ - タスク総数
56
+ - ユーザーストーリーごとのタスク数
57
+ - 並列実行の機会
58
+ - ストーリーごとの独立したテスト基準
59
+ - 推奨 MVP スコープ(多くはユーザーストーリー 1 のみ)
60
+ - 形式検証: 全タスクがチェックリスト形式(チェックボックス・ID・ラベル・ファイルパス)に従っていることを確認する
61
+
62
+ 6. **拡張フックの確認(タスク追記後)**: UC へのタスクセクション追記後に `.spec-runner/extensions.yml` の `hooks.after_tasks` があれば同様のルールで表示または実行する(なければスキップ)。
63
+
64
+ タスク整理の文脈: $ARGUMENTS
65
+
66
+ UC 内のタスクセクションは即実行可能であること。各タスクは LLM が追加文脈なしで完了できる程度に具体的であること。**確定事項の完了は健全性確認(check.sh)で検証される。**
67
+
68
+ ## タスク生成ルール
69
+
70
+ **重要**: 独立した実装・テストを可能にするため、タスクは**ユーザーストーリーごと**に整理すること。
71
+
72
+ **テストは任意**: 機能仕様またはユーザーが TDD を依頼した場合にのみ、テストタスクを生成する。
73
+
74
+ ### チェックリスト形式(必須)
75
+
76
+ 各タスクは次の形式に厳守する:
77
+
78
+ ```text
79
+ - [ ] [TaskID] [P?] [Story?] 説明(ファイルパス付き)
80
+ ```
81
+
82
+ **構成要素**:
83
+
84
+ 1. **チェックボックス**: 常に `- [ ]` で始める
85
+ 2. **タスク ID**: 実行順の連番(T001, T002, T003 ...)
86
+ 3. **[P] マーカー**: 並列実行可能なタスクのみ付ける(別ファイル・未完了タスクに依存しない)
87
+ 4. **[Story] ラベル**: ユーザーストーリーフェーズのタスクのみ必須。形式: [US1], [US2], [US3] 等(spec.md のユーザーストーリーに対応)。セットアップ・基盤・仕上げフェーズにはストーリーラベルを付けない
88
+ 5. **説明**: 明確なアクションと正確なファイルパス
89
+
90
+ **例**:
91
+
92
+ - ✅ 正: `- [ ] T001 実装計画に従いプロジェクト構造を作成する`
93
+ - ✅ 正: `- [ ] T005 [P] src/middleware/auth.py に認証ミドルウェアを実装する`
94
+ - ✅ 正: `- [ ] T012 [P] [US1] src/models/user.py に User モデルを作成する`
95
+ - ✅ 正: `- [ ] T014 [US1] src/services/user_service.py に UserService を実装する`
96
+ - ❌ 誤: `- [ ] User モデルを作成する`(ID とストーリーラベルがない)
97
+ - ❌ 誤: `T001 [US1] モデルを作成する`(チェックボックスがない)
98
+ - ❌ 誤: `- [ ] [US1] User モデルを作成する`(タスク ID がない)
99
+ - ❌ 誤: `- [ ] T001 [US1] モデルを作成する`(ファイルパスがない)
100
+
101
+ ### タスクの整理
102
+
103
+ 1. **ユーザーストーリー(spec.md)を主軸とする**:
104
+ - 各ユーザーストーリー(P1, P2, P3 ...)に 1 フェーズ
105
+ - ストーリーに関連するコンポーネントをすべてマッピング: モデル・サービス・インターフェース/UI・(依頼があれば)テスト
106
+ - ストーリー間の依存を記載する(多くのストーリーは独立)
107
+
108
+ 2. **契約から**:
109
+ - 各インターフェース契約を、それを提供するユーザーストーリーに対応づける
110
+ - テストを依頼されている場合: 各契約に対して、そのストーリーフェーズの実装前に契約テストタスク [P] を置く
111
+
112
+ 3. **データモデルから**:
113
+ - 各エンティティを、それを必要とするユーザーストーリーに対応づける
114
+ - 複数ストーリーで使うエンティティは、最初のストーリーまたはセットアップフェーズに含める
115
+ - リレーションは適切なストーリーフェーズのサービス層タスクで扱う
116
+
117
+ 4. **セットアップ/インフラから**:
118
+ - 共通インフラ → Phase 1(セットアップ)
119
+ - 基盤/ブロッキングタスク → Phase 2(基盤)
120
+ - ストーリー固有のセットアップ → そのストーリーのフェーズ内
121
+
122
+ ### フェーズ構成
123
+
124
+ - **Phase 1**: セットアップ(プロジェクト初期化)
125
+ - **Phase 2**: 基盤(全ユーザーストーリーの前提。ユーザーストーリーより前に完了必須)
126
+ - **Phase 3 以降**: 優先度順のユーザーストーリー(P1, P2, P3 ...)
127
+ - 各ストーリー内: (依頼があれば)テスト → モデル → サービス → エンドポイント → 統合
128
+ - 各フェーズは独立してテスト可能な増分として完了させる
129
+ - **最終フェーズ**: 仕上げと横断的関心事
130
+
131
+ ---
132
+ 完了したら次のステップに進む。