@lvlup-sw/exarchos 2.0.1

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 (153) hide show
  1. package/.claude-plugin/marketplace.json +22 -0
  2. package/.claude-plugin/plugin.json +17 -0
  3. package/.mcp.json +17 -0
  4. package/AGENTS.md +59 -0
  5. package/CLAUDE.md.template +62 -0
  6. package/LICENSE +202 -0
  7. package/README.md +258 -0
  8. package/commands/autocompact.md +37 -0
  9. package/commands/checkpoint.md +85 -0
  10. package/commands/cleanup.md +99 -0
  11. package/commands/debug.md +145 -0
  12. package/commands/delegate.md +56 -0
  13. package/commands/ideate.md +82 -0
  14. package/commands/plan.md +150 -0
  15. package/commands/refactor.md +139 -0
  16. package/commands/reload.md +37 -0
  17. package/commands/resume.md +130 -0
  18. package/commands/review.md +51 -0
  19. package/commands/sync-schemas.md +74 -0
  20. package/commands/synthesize.md +122 -0
  21. package/commands/tdd.md +58 -0
  22. package/dist/exarchos-cli.js +8828 -0
  23. package/dist/exarchos-mcp.js +50 -0
  24. package/hooks/hooks.json +53 -0
  25. package/package.json +59 -0
  26. package/rules/coding-standards.md +46 -0
  27. package/rules/mcp-tool-guidance.md +26 -0
  28. package/rules/pr-descriptions.md +12 -0
  29. package/rules/rm-safety.md +9 -0
  30. package/rules/skill-path-resolution.md +10 -0
  31. package/rules/tdd.md +41 -0
  32. package/rules/telemetry-awareness.md +9 -0
  33. package/scripts/assess-refactor-scope.sh +239 -0
  34. package/scripts/check-benchmark-regression.sh +229 -0
  35. package/scripts/check-coderabbit.sh +288 -0
  36. package/scripts/check-coverage-thresholds.sh +194 -0
  37. package/scripts/check-polish-scope.sh +245 -0
  38. package/scripts/check-property-tests.sh +167 -0
  39. package/scripts/check-tdd-compliance.sh +265 -0
  40. package/scripts/coderabbit-review-gate.sh +518 -0
  41. package/scripts/debug-review-gate.sh +201 -0
  42. package/scripts/extract-fix-tasks.sh +179 -0
  43. package/scripts/extract-task.sh +67 -0
  44. package/scripts/generate-traceability.sh +209 -0
  45. package/scripts/investigation-timer.sh +171 -0
  46. package/scripts/needs-schema-sync.sh +174 -0
  47. package/scripts/new-project.sh +103 -0
  48. package/scripts/post-delegation-check.sh +317 -0
  49. package/scripts/pre-synthesis-check.sh +440 -0
  50. package/scripts/reconcile-state.sh +346 -0
  51. package/scripts/reconstruct-stack.sh +432 -0
  52. package/scripts/review-diff.sh +63 -0
  53. package/scripts/review-verdict.sh +169 -0
  54. package/scripts/security-scan.sh +248 -0
  55. package/scripts/select-debug-track.sh +186 -0
  56. package/scripts/setup-worktree.sh +323 -0
  57. package/scripts/spec-coverage-check.sh +230 -0
  58. package/scripts/static-analysis-gate.sh +236 -0
  59. package/scripts/sync-labels.sh +122 -0
  60. package/scripts/validate-companion.sh +161 -0
  61. package/scripts/validate-dotnet-standards.sh +267 -0
  62. package/scripts/validate-installation.sh +101 -0
  63. package/scripts/validate-plugin.sh +223 -0
  64. package/scripts/validate-refactor.sh +234 -0
  65. package/scripts/validate-rm.sh +93 -0
  66. package/scripts/verify-delegation-saga.sh +240 -0
  67. package/scripts/verify-doc-links.sh +211 -0
  68. package/scripts/verify-ideate-artifacts.sh +296 -0
  69. package/scripts/verify-plan-coverage.sh +228 -0
  70. package/scripts/verify-review-triage.sh +219 -0
  71. package/scripts/verify-worktree-baseline.sh +159 -0
  72. package/scripts/verify-worktree.sh +84 -0
  73. package/settings.json +47 -0
  74. package/skills/brainstorming/SKILL.md +127 -0
  75. package/skills/brainstorming/references/design-template.md +65 -0
  76. package/skills/cleanup/SKILL.md +147 -0
  77. package/skills/cleanup/references/merge-verification.md +40 -0
  78. package/skills/debug/SKILL.md +204 -0
  79. package/skills/debug/references/hotfix-track.md +134 -0
  80. package/skills/debug/references/investigation-checklist.md +217 -0
  81. package/skills/debug/references/rca-template.md +150 -0
  82. package/skills/debug/references/state-schema.md +294 -0
  83. package/skills/debug/references/thorough-track.md +194 -0
  84. package/skills/debug/references/triage-questions.md +155 -0
  85. package/skills/debug/references/troubleshooting.md +47 -0
  86. package/skills/delegation/SKILL.md +150 -0
  87. package/skills/delegation/references/adaptive-orchestration.md +31 -0
  88. package/skills/delegation/references/agent-teams-saga.md +248 -0
  89. package/skills/delegation/references/fix-mode.md +74 -0
  90. package/skills/delegation/references/fixer-prompt.md +162 -0
  91. package/skills/delegation/references/implementer-prompt.md +322 -0
  92. package/skills/delegation/references/parallel-strategy.md +124 -0
  93. package/skills/delegation/references/pbt-patterns.md +172 -0
  94. package/skills/delegation/references/pr-fixes-mode.md +154 -0
  95. package/skills/delegation/references/state-management.md +51 -0
  96. package/skills/delegation/references/testing-patterns.md +129 -0
  97. package/skills/delegation/references/troubleshooting.md +33 -0
  98. package/skills/delegation/references/workflow-steps.md +127 -0
  99. package/skills/delegation/references/worktree-enforcement.md +64 -0
  100. package/skills/dotnet-standards/SKILL.md +269 -0
  101. package/skills/dotnet-standards/references/csharp-standards.md +120 -0
  102. package/skills/dotnet-standards/templates/.editorconfig +366 -0
  103. package/skills/dotnet-standards/templates/Directory.Build.props +56 -0
  104. package/skills/dotnet-standards/templates/Directory.Packages.props +69 -0
  105. package/skills/dotnet-standards/templates/global.json +6 -0
  106. package/skills/dotnet-standards/templates/nuget.config +9 -0
  107. package/skills/dotnet-standards/templates/stylecop.json +37 -0
  108. package/skills/git-worktrees/SKILL.md +255 -0
  109. package/skills/implementation-planning/SKILL.md +233 -0
  110. package/skills/implementation-planning/references/plan-document-template.md +42 -0
  111. package/skills/implementation-planning/references/spec-tracing-guide.md +51 -0
  112. package/skills/implementation-planning/references/task-template.md +43 -0
  113. package/skills/implementation-planning/references/testing-strategy-guide.md +88 -0
  114. package/skills/quality-review/SKILL.md +278 -0
  115. package/skills/quality-review/references/code-quality-checklist.md +159 -0
  116. package/skills/quality-review/references/review-report-template.md +65 -0
  117. package/skills/quality-review/references/security-checklist.md +79 -0
  118. package/skills/quality-review/references/typescript-standards.md +24 -0
  119. package/skills/refactor/COMMAND.md +67 -0
  120. package/skills/refactor/SKILL.md +198 -0
  121. package/skills/refactor/phases/auto-chain.md +262 -0
  122. package/skills/refactor/phases/brief.md +176 -0
  123. package/skills/refactor/phases/explore.md +132 -0
  124. package/skills/refactor/phases/overhaul-delegate.md +136 -0
  125. package/skills/refactor/phases/overhaul-plan.md +312 -0
  126. package/skills/refactor/phases/overhaul-review.md +304 -0
  127. package/skills/refactor/phases/polish-implement.md +349 -0
  128. package/skills/refactor/phases/polish-validate.md +218 -0
  129. package/skills/refactor/phases/update-docs.md +234 -0
  130. package/skills/refactor/references/brief-template.md +81 -0
  131. package/skills/refactor/references/doc-update-checklist.md +110 -0
  132. package/skills/refactor/references/explore-checklist.md +73 -0
  133. package/skills/refactor/references/overhaul-track.md +215 -0
  134. package/skills/refactor/references/polish-track.md +170 -0
  135. package/skills/shared/prompts/context-reading.md +58 -0
  136. package/skills/shared/prompts/report-format.md +54 -0
  137. package/skills/shared/prompts/tdd-requirements.md +39 -0
  138. package/skills/shepherd/SKILL.md +264 -0
  139. package/skills/shepherd/references/assess-checklist.md +124 -0
  140. package/skills/shepherd/references/fix-strategies.md +191 -0
  141. package/skills/spec-review/SKILL.md +229 -0
  142. package/skills/spec-review/references/review-checklist.md +60 -0
  143. package/skills/sync-schemas/SKILL.md +114 -0
  144. package/skills/sync-schemas/references/configuration.md +73 -0
  145. package/skills/synthesis/SKILL.md +129 -0
  146. package/skills/synthesis/references/pr-descriptions.md +87 -0
  147. package/skills/synthesis/references/synthesis-steps.md +109 -0
  148. package/skills/synthesis/references/troubleshooting.md +115 -0
  149. package/skills/validate-all-skills.sh +57 -0
  150. package/skills/validate-frontmatter.sh +237 -0
  151. package/skills/workflow-state/SKILL.md +210 -0
  152. package/skills/workflow-state/references/mcp-tool-reference.md +111 -0
  153. package/skills/workflow-state/references/phase-transitions.md +141 -0
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env bash
2
+ # Check Polish Scope
3
+ # Checks if polish scope has expanded beyond limits.
4
+ # Replaces "Scope Expansion Triggers" prose with deterministic validation.
5
+ #
6
+ # Usage: check-polish-scope.sh --repo-root <path> [--base-branch main]
7
+ #
8
+ # Exit codes:
9
+ # 0 = scope OK (stay polish)
10
+ # 1 = scope expanded (switch to overhaul)
11
+ # 2 = usage error (missing required args)
12
+
13
+ set -euo pipefail
14
+
15
+ # ============================================================
16
+ # ARGUMENT PARSING
17
+ # ============================================================
18
+
19
+ REPO_ROOT=""
20
+ BASE_BRANCH="main"
21
+
22
+ usage() {
23
+ cat << 'USAGE'
24
+ Usage: check-polish-scope.sh --repo-root <path> [--base-branch main]
25
+
26
+ Required:
27
+ --repo-root <path> Path to the git repository root
28
+
29
+ Optional:
30
+ --base-branch <branch> Base branch to diff against (default: main)
31
+ --help Show this help message
32
+
33
+ Exit codes:
34
+ 0 Scope OK — within polish limits
35
+ 1 Scope expanded — switch to overhaul
36
+ 2 Usage error (missing required args)
37
+
38
+ Expansion triggers checked:
39
+ - File count > 5 (modified files via git diff)
40
+ - Module boundaries crossed (>2 top-level dirs modified)
41
+ - New test files needed (impl files without test counterparts)
42
+ - Architectural docs needed (detected heuristically)
43
+
44
+ Note: Assumes co-located tests (foo.test.ts alongside foo.ts)
45
+ USAGE
46
+ }
47
+
48
+ while [[ $# -gt 0 ]]; do
49
+ case "$1" in
50
+ --repo-root)
51
+ if [[ -z "${2:-}" ]]; then
52
+ echo "Error: --repo-root requires a path argument" >&2
53
+ exit 2
54
+ fi
55
+ REPO_ROOT="$2"
56
+ shift 2
57
+ ;;
58
+ --base-branch)
59
+ if [[ -z "${2:-}" ]]; then
60
+ echo "Error: --base-branch requires a branch name" >&2
61
+ exit 2
62
+ fi
63
+ BASE_BRANCH="$2"
64
+ shift 2
65
+ ;;
66
+ --help)
67
+ usage
68
+ exit 0
69
+ ;;
70
+ *)
71
+ echo "Error: Unknown argument '$1'" >&2
72
+ usage >&2
73
+ exit 2
74
+ ;;
75
+ esac
76
+ done
77
+
78
+ if [[ -z "$REPO_ROOT" ]]; then
79
+ echo "Error: --repo-root is required" >&2
80
+ usage >&2
81
+ exit 2
82
+ fi
83
+
84
+ # ============================================================
85
+ # DEPENDENCY CHECK
86
+ # ============================================================
87
+
88
+ if ! command -v git &>/dev/null; then
89
+ echo "Error: git is required but not installed" >&2
90
+ exit 2
91
+ fi
92
+
93
+ # ============================================================
94
+ # CHECK FUNCTIONS
95
+ # ============================================================
96
+
97
+ CHECK_PASS=0
98
+ CHECK_FAIL=0
99
+ RESULTS=()
100
+ TRIGGERS_FIRED=()
101
+
102
+ check_pass() {
103
+ local name="$1"
104
+ RESULTS+=("- **PASS**: $name")
105
+ CHECK_PASS=$((CHECK_PASS + 1))
106
+ }
107
+
108
+ check_fail() {
109
+ local name="$1"
110
+ local detail="${2:-}"
111
+ if [[ -n "$detail" ]]; then
112
+ RESULTS+=("- **FAIL**: $name — $detail")
113
+ else
114
+ RESULTS+=("- **FAIL**: $name")
115
+ fi
116
+ CHECK_FAIL=$((CHECK_FAIL + 1))
117
+ }
118
+
119
+ # ============================================================
120
+ # GET MODIFIED FILES
121
+ # ============================================================
122
+
123
+ cd "$REPO_ROOT"
124
+
125
+ # Get list of files modified compared to base branch
126
+ MODIFIED_FILES=()
127
+ while IFS= read -r line; do
128
+ [[ -n "$line" ]] && MODIFIED_FILES+=("$line")
129
+ done < <(git diff --name-only "$BASE_BRANCH"...HEAD 2>/dev/null || git diff --name-only "$BASE_BRANCH" HEAD 2>/dev/null || true)
130
+
131
+ FILE_COUNT=${#MODIFIED_FILES[@]}
132
+
133
+ # ============================================================
134
+ # TRIGGER 1: File count > 5
135
+ # ============================================================
136
+
137
+ if [[ $FILE_COUNT -le 5 ]]; then
138
+ check_pass "File count within limit ($FILE_COUNT <= 5)"
139
+ else
140
+ check_fail "File count exceeds limit" "$FILE_COUNT files modified (max 5)"
141
+ TRIGGERS_FIRED+=("File count ($FILE_COUNT) exceeds limit of 5")
142
+ fi
143
+
144
+ # ============================================================
145
+ # TRIGGER 2: Module boundaries crossed (>2 top-level dirs)
146
+ # ============================================================
147
+
148
+ # Bash 3 compatible — no associative arrays
149
+ MODULE_LIST=""
150
+ for f in "${MODIFIED_FILES[@]}"; do
151
+ top_dir="$(echo "$f" | cut -d'/' -f1)"
152
+ if ! echo "$MODULE_LIST" | grep -qF "|$top_dir|"; then
153
+ MODULE_LIST="${MODULE_LIST}|$top_dir|"
154
+ fi
155
+ done
156
+ MODULE_COUNT=0
157
+ MODULE_NAMES=""
158
+ if [[ -n "$MODULE_LIST" ]]; then
159
+ MODULE_NAMES="$(echo "$MODULE_LIST" | tr '|' '\n' | sort -u | grep -v '^$' | tr '\n' ' ')"
160
+ MODULE_COUNT="$(echo "$MODULE_LIST" | tr '|' '\n' | sort -u | grep -vc '^$' || true)"
161
+ fi
162
+
163
+ if [[ $MODULE_COUNT -le 2 ]]; then
164
+ check_pass "Module boundaries OK ($MODULE_COUNT top-level dirs)"
165
+ else
166
+ check_fail "Module boundaries crossed" "$MODULE_COUNT top-level dirs: $MODULE_NAMES"
167
+ TRIGGERS_FIRED+=("Module boundaries crossed ($MODULE_COUNT dirs: $MODULE_NAMES)")
168
+ fi
169
+
170
+ # ============================================================
171
+ # TRIGGER 3: New test files needed
172
+ # ============================================================
173
+
174
+ MISSING_TESTS=()
175
+ for f in "${MODIFIED_FILES[@]}"; do
176
+ # Only check .ts implementation files
177
+ if [[ "$f" == *.ts && "$f" != *.test.ts && "$f" != *.d.ts ]]; then
178
+ test_file="${f%.ts}.test.ts"
179
+ if [[ ! -f "$test_file" ]]; then
180
+ MISSING_TESTS+=("$f")
181
+ fi
182
+ fi
183
+ done
184
+
185
+ if [[ ${#MISSING_TESTS[@]} -eq 0 ]]; then
186
+ check_pass "Test coverage OK (all impl files have test counterparts)"
187
+ else
188
+ check_fail "New test files needed" "${#MISSING_TESTS[@]} impl files without tests: ${MISSING_TESTS[*]}"
189
+ TRIGGERS_FIRED+=("New test files needed for ${#MISSING_TESTS[@]} files")
190
+ fi
191
+
192
+ # ============================================================
193
+ # TRIGGER 4: Architectural docs needed (heuristic)
194
+ # ============================================================
195
+
196
+ NEEDS_ARCH_DOCS=false
197
+ for f in "${MODIFIED_FILES[@]}"; do
198
+ # Heuristic: if modifying files in multiple top-level dirs with structural changes
199
+ if [[ "$f" == *"index.ts" || "$f" == *"types.ts" || "$f" == *"interface"* ]]; then
200
+ if [[ $MODULE_COUNT -gt 1 ]]; then
201
+ NEEDS_ARCH_DOCS=true
202
+ break
203
+ fi
204
+ fi
205
+ done
206
+
207
+ if [[ "$NEEDS_ARCH_DOCS" == false ]]; then
208
+ check_pass "No architectural docs needed"
209
+ else
210
+ check_fail "Architectural documentation likely needed" "Structural files modified across modules"
211
+ TRIGGERS_FIRED+=("Architectural documentation needed")
212
+ fi
213
+
214
+ # ============================================================
215
+ # STRUCTURED OUTPUT
216
+ # ============================================================
217
+
218
+ echo "## Polish Scope Check Report"
219
+ echo ""
220
+ echo "**Repository:** \`$REPO_ROOT\`"
221
+ echo "**Base branch:** $BASE_BRANCH"
222
+ echo "**Files modified:** $FILE_COUNT"
223
+ echo "**Modules touched:** $MODULE_COUNT (${MODULE_NAMES:-none})"
224
+ echo ""
225
+
226
+ for result in "${RESULTS[@]}"; do
227
+ echo "$result"
228
+ done
229
+
230
+ echo ""
231
+ echo "---"
232
+ echo ""
233
+
234
+ if [[ ${#TRIGGERS_FIRED[@]} -eq 0 ]]; then
235
+ echo "**Result: SCOPE OK** — All within polish limits"
236
+ exit 0
237
+ else
238
+ echo "**Result: SCOPE EXPANDED** — Switch to overhaul track"
239
+ echo ""
240
+ echo "Triggers fired:"
241
+ for trigger in "${TRIGGERS_FIRED[@]}"; do
242
+ echo " - $trigger"
243
+ done
244
+ exit 1
245
+ fi
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env bash
2
+ # check-property-tests.sh
3
+ # Verifies that tasks requiring property-based tests have PBT patterns in the implementation.
4
+ # Exit 0 = pass, 1 = fail (missing PBT), 2 = usage error
5
+
6
+ set -euo pipefail
7
+
8
+ # ─── Usage ──────────────────────────────────────────────────────────────────
9
+
10
+ usage() {
11
+ cat <<EOF
12
+ Usage: check-property-tests.sh --plan-file <path> --worktree-dir <path>
13
+
14
+ Verifies that plan tasks with propertyTests: true have property-based test
15
+ patterns in the worktree.
16
+
17
+ Arguments:
18
+ --plan-file Path to plan JSON file containing tasks with testingStrategy
19
+ --worktree-dir Path to the worktree directory to scan for PBT patterns
20
+
21
+ Exit Codes:
22
+ 0 All PBT-required tasks have property test patterns
23
+ 1 One or more PBT-required tasks lack property test patterns
24
+ 2 Usage error (missing arguments)
25
+ EOF
26
+ }
27
+
28
+ # ─── Arg Parsing ────────────────────────────────────────────────────────────
29
+
30
+ PLAN_FILE=""
31
+ WORKTREE_DIR=""
32
+
33
+ while [[ $# -gt 0 ]]; do
34
+ case "$1" in
35
+ --plan-file)
36
+ PLAN_FILE="${2:-}"
37
+ shift 2 || { echo "Error: --plan-file requires a value" >&2; exit 2; }
38
+ ;;
39
+ --worktree-dir)
40
+ WORKTREE_DIR="${2:-}"
41
+ shift 2 || { echo "Error: --worktree-dir requires a value" >&2; exit 2; }
42
+ ;;
43
+ --help|-h)
44
+ usage
45
+ exit 0
46
+ ;;
47
+ *)
48
+ echo "Error: Unknown argument: $1" >&2
49
+ usage >&2
50
+ exit 2
51
+ ;;
52
+ esac
53
+ done
54
+
55
+ if [[ -z "$PLAN_FILE" || -z "$WORKTREE_DIR" ]]; then
56
+ echo "Error: Both --plan-file and --worktree-dir are required." >&2
57
+ usage >&2
58
+ exit 2
59
+ fi
60
+
61
+ if [[ ! -f "$PLAN_FILE" ]]; then
62
+ echo "Error: Plan file not found: $PLAN_FILE" >&2
63
+ exit 2
64
+ fi
65
+
66
+ if [[ ! -d "$WORKTREE_DIR" ]]; then
67
+ echo "Error: Worktree directory not found: $WORKTREE_DIR" >&2
68
+ exit 2
69
+ fi
70
+
71
+ # ─── Plan JSON Extraction ──────────────────────────────────────────────────
72
+
73
+ # Extract task IDs where testingStrategy.propertyTests is true
74
+ PBT_TASK_IDS=()
75
+ while IFS= read -r task_id; do
76
+ [[ -n "$task_id" ]] && PBT_TASK_IDS+=("$task_id")
77
+ done < <(
78
+ # Use python3 for reliable JSON parsing (available on macOS and most Linux)
79
+ python3 -c "
80
+ import json, sys
81
+ with open(sys.argv[1]) as f:
82
+ plan = json.load(f)
83
+ for task in plan.get('tasks', []):
84
+ strategy = task.get('testingStrategy', {})
85
+ if strategy.get('propertyTests', False):
86
+ print(task.get('id', ''))
87
+ " "$PLAN_FILE" 2>/dev/null
88
+ )
89
+
90
+ if [[ ${#PBT_TASK_IDS[@]} -eq 0 ]]; then
91
+ echo "## PBT Check: PASS"
92
+ echo "No tasks require property-based tests."
93
+ exit 0
94
+ fi
95
+
96
+ echo "## PBT Check"
97
+ echo "Tasks requiring property-based tests: ${PBT_TASK_IDS[*]}"
98
+ echo ""
99
+
100
+ # ─── PBT Pattern Detection ─────────────────────────────────────────────────
101
+
102
+ # TypeScript patterns: fast-check library usage
103
+ TS_PBT_PATTERN="fc\.property|fc\.assert|it\.prop|test\.prop|from 'fast-check'|from \"fast-check\"|@fast-check"
104
+
105
+ # .NET patterns: FsCheck library usage
106
+ DOTNET_PBT_PATTERN="Prop\.ForAll|using FsCheck|\[Property\]"
107
+
108
+ # Combined pattern
109
+ COMBINED_PATTERN="$TS_PBT_PATTERN|$DOTNET_PBT_PATTERN"
110
+
111
+ # Find all test files with PBT patterns
112
+ PBT_FILES=()
113
+ while IFS= read -r file; do
114
+ [[ -n "$file" ]] && PBT_FILES+=("$file")
115
+ done < <(
116
+ grep -rlE "$COMBINED_PATTERN" "$WORKTREE_DIR" \
117
+ --include="*.test.ts" \
118
+ --include="*.test.tsx" \
119
+ --include="*.spec.ts" \
120
+ --include="*.Tests.cs" \
121
+ --include="*Tests.cs" \
122
+ --include="*.test.js" \
123
+ 2>/dev/null || true
124
+ )
125
+
126
+ # ─── Cross-Reference ───────────────────────────────────────────────────────
127
+
128
+ HAS_PBT=false
129
+ if [[ ${#PBT_FILES[@]} -gt 0 ]]; then
130
+ HAS_PBT=true
131
+ echo "Found PBT patterns in:"
132
+ for f in "${PBT_FILES[@]}"; do
133
+ echo " - $f"
134
+ done
135
+ echo ""
136
+ fi
137
+
138
+ # For each PBT-required task, check if any PBT file exists in the worktree
139
+ UNCOVERED_TASKS=()
140
+ if [[ "$HAS_PBT" == "true" ]]; then
141
+ # If any PBT patterns exist in the worktree, consider all tasks covered
142
+ # (task-to-file mapping is coarse-grained; presence of PBT patterns is the gate)
143
+ echo "All PBT-required tasks have coverage."
144
+ else
145
+ # No PBT patterns found at all
146
+ for task_id in "${PBT_TASK_IDS[@]}"; do
147
+ UNCOVERED_TASKS+=("$task_id")
148
+ done
149
+ fi
150
+
151
+ # ─── Result ─────────────────────────────────────────────────────────────────
152
+
153
+ if [[ ${#UNCOVERED_TASKS[@]} -gt 0 ]]; then
154
+ echo "## PBT Check: FAIL"
155
+ echo ""
156
+ echo "The following tasks require property-based tests but none were found:"
157
+ for task_id in "${UNCOVERED_TASKS[@]}"; do
158
+ echo " - $task_id"
159
+ done
160
+ echo ""
161
+ echo "Expected patterns (TypeScript): fc.property, fc.assert, it.prop, test.prop, from 'fast-check'"
162
+ echo "Expected patterns (.NET): Prop.ForAll, using FsCheck, [Property]"
163
+ exit 1
164
+ else
165
+ echo "## PBT Check: PASS"
166
+ exit 0
167
+ fi
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env bash
2
+ # Check TDD Compliance
3
+ # Verify test-first git history order for commits on a branch.
4
+ #
5
+ # Usage: check-tdd-compliance.sh --repo-root <path> --branch <name> [--base-branch main]
6
+ #
7
+ # Exit codes:
8
+ # 0 = compliant (test files committed before or alongside implementation)
9
+ # 1 = violations found (implementation committed without test)
10
+ # 2 = usage error (missing required args)
11
+
12
+ set -euo pipefail
13
+
14
+ # ============================================================
15
+ # ARGUMENT PARSING
16
+ # ============================================================
17
+
18
+ REPO_ROOT=""
19
+ BRANCH=""
20
+ BASE_BRANCH="main"
21
+
22
+ usage() {
23
+ cat << 'USAGE'
24
+ Usage: check-tdd-compliance.sh --repo-root <path> --branch <name> [--base-branch main]
25
+
26
+ Required:
27
+ --repo-root <path> Repository root directory
28
+ --branch <name> Branch to check
29
+
30
+ Optional:
31
+ --base-branch <name> Base branch to compare against (default: main)
32
+ --help Show this help message
33
+
34
+ Exit codes:
35
+ 0 Compliant (test files committed before or alongside implementation)
36
+ 1 Violations found (implementation without test in same/prior commit)
37
+ 2 Usage error (missing required args)
38
+ USAGE
39
+ }
40
+
41
+ while [[ $# -gt 0 ]]; do
42
+ case "$1" in
43
+ --repo-root)
44
+ if [[ -z "${2:-}" ]]; then
45
+ echo "Error: --repo-root requires a path argument" >&2
46
+ exit 2
47
+ fi
48
+ REPO_ROOT="$2"
49
+ shift 2
50
+ ;;
51
+ --branch)
52
+ if [[ -z "${2:-}" ]]; then
53
+ echo "Error: --branch requires a name argument" >&2
54
+ exit 2
55
+ fi
56
+ BRANCH="$2"
57
+ shift 2
58
+ ;;
59
+ --base-branch)
60
+ if [[ -z "${2:-}" ]]; then
61
+ echo "Error: --base-branch requires a name argument" >&2
62
+ exit 2
63
+ fi
64
+ BASE_BRANCH="$2"
65
+ shift 2
66
+ ;;
67
+ --help)
68
+ usage
69
+ exit 0
70
+ ;;
71
+ *)
72
+ echo "Error: Unknown argument '$1'" >&2
73
+ usage >&2
74
+ exit 2
75
+ ;;
76
+ esac
77
+ done
78
+
79
+ if [[ -z "$REPO_ROOT" || -z "$BRANCH" ]]; then
80
+ echo "Error: --repo-root and --branch are required" >&2
81
+ usage >&2
82
+ exit 2
83
+ fi
84
+
85
+ if [[ ! -d "$REPO_ROOT/.git" ]]; then
86
+ echo "Error: Not a git repository: $REPO_ROOT" >&2
87
+ exit 2
88
+ fi
89
+
90
+ # ============================================================
91
+ # HELPER: Classify a file as test or implementation
92
+ # ============================================================
93
+
94
+ is_test_file() {
95
+ local file="$1"
96
+ case "$file" in
97
+ *.test.ts|*.test.sh|*.spec.ts|*.test.js|*.spec.js|*.test.tsx|*.spec.tsx)
98
+ return 0
99
+ ;;
100
+ *)
101
+ return 1
102
+ ;;
103
+ esac
104
+ }
105
+
106
+ is_impl_file() {
107
+ local file="$1"
108
+ # Implementation files: source code that isn't a test, config, or docs
109
+ case "$file" in
110
+ *.ts|*.js|*.tsx|*.jsx|*.sh)
111
+ # Exclude test files
112
+ if is_test_file "$file"; then
113
+ return 1
114
+ fi
115
+ return 0
116
+ ;;
117
+ *)
118
+ return 1
119
+ ;;
120
+ esac
121
+ }
122
+
123
+ # ============================================================
124
+ # ANALYZE COMMITS
125
+ # ============================================================
126
+
127
+ CHECK_PASS=0
128
+ CHECK_FAIL=0
129
+ VIOLATIONS=()
130
+ RESULTS=()
131
+
132
+ # Track test files seen across all commits (cumulative)
133
+ # Use a delimiter-separated string for bash 3.x compatibility (no associative arrays)
134
+ TESTS_SEEN=""
135
+
136
+ # Get commits from base..branch in chronological order (oldest first)
137
+ cd "$REPO_ROOT"
138
+ COMMITS=()
139
+ while IFS= read -r commit_hash; do
140
+ [[ -n "$commit_hash" ]] && COMMITS+=("$commit_hash")
141
+ done < <(git log --reverse --format="%H" "${BASE_BRANCH}..${BRANCH}" 2>/dev/null)
142
+
143
+ if [[ ${#COMMITS[@]} -eq 0 ]]; then
144
+ echo "## TDD Compliance Report"
145
+ echo ""
146
+ echo "**Branch:** $BRANCH"
147
+ echo "**Base:** $BASE_BRANCH"
148
+ echo ""
149
+ echo "No commits found between $BASE_BRANCH and $BRANCH"
150
+ echo ""
151
+ echo "---"
152
+ echo ""
153
+ echo "**Result: PASS** (no commits to check)"
154
+ exit 0
155
+ fi
156
+
157
+ for commit_hash in "${COMMITS[@]}"; do
158
+ commit_msg="$(git log -1 --format="%s" "$commit_hash")"
159
+ commit_short="$(git log -1 --format="%h" "$commit_hash")"
160
+
161
+ # Get files changed in this commit
162
+ FILES_IN_COMMIT=()
163
+ while IFS= read -r file; do
164
+ [[ -n "$file" ]] && FILES_IN_COMMIT+=("$file")
165
+ done < <(git diff-tree --no-commit-id --name-only --diff-filter=ACMRT -r "$commit_hash" 2>/dev/null)
166
+
167
+ # Classify files in this commit
168
+ HAS_TEST=false
169
+ HAS_IMPL=false
170
+ IMPL_FILES=()
171
+ TEST_FILES=()
172
+
173
+ for file in "${FILES_IN_COMMIT[@]}"; do
174
+ if is_test_file "$file"; then
175
+ HAS_TEST=true
176
+ TEST_FILES+=("$file")
177
+ TESTS_SEEN="${TESTS_SEEN}|${file}|"
178
+ elif is_impl_file "$file"; then
179
+ HAS_IMPL=true
180
+ IMPL_FILES+=("$file")
181
+ fi
182
+ done
183
+
184
+ # A commit with implementation files is compliant if:
185
+ # 1. It also contains test files (mixed commit), OR
186
+ # 2. Corresponding test files were seen in prior commits
187
+ if [[ "$HAS_IMPL" == true ]]; then
188
+ if [[ "$HAS_TEST" == true ]]; then
189
+ # Mixed commit: test and impl together — OK
190
+ RESULTS+=("- **PASS**: \`$commit_short\` — $commit_msg (test+impl)")
191
+ CHECK_PASS=$((CHECK_PASS + 1))
192
+ else
193
+ # Check if test files were seen before this commit
194
+ FOUND_PRIOR_TEST=false
195
+ for impl_file in "${IMPL_FILES[@]}"; do
196
+ # Derive expected test file name(s)
197
+ base="${impl_file%.*}"
198
+ ext="${impl_file##*.}"
199
+ test_candidate_test="${base}.test.${ext}"
200
+ test_candidate_spec="${base}.spec.${ext}"
201
+
202
+ if echo "$TESTS_SEEN" | grep -qF "|${test_candidate_test}|" || \
203
+ echo "$TESTS_SEEN" | grep -qF "|${test_candidate_spec}|"; then
204
+ FOUND_PRIOR_TEST=true
205
+ break
206
+ fi
207
+ done
208
+
209
+ if [[ "$FOUND_PRIOR_TEST" == true ]]; then
210
+ RESULTS+=("- **PASS**: \`$commit_short\` — $commit_msg (test in prior commit)")
211
+ CHECK_PASS=$((CHECK_PASS + 1))
212
+ else
213
+ RESULTS+=("- **FAIL**: \`$commit_short\` — $commit_msg (implementation without test)")
214
+ VIOLATIONS+=("$commit_short: $commit_msg")
215
+ CHECK_FAIL=$((CHECK_FAIL + 1))
216
+ fi
217
+ fi
218
+ elif [[ "$HAS_TEST" == true ]]; then
219
+ # Test-only commit — always compliant
220
+ RESULTS+=("- **PASS**: \`$commit_short\` — $commit_msg (test-only)")
221
+ CHECK_PASS=$((CHECK_PASS + 1))
222
+ else
223
+ # Non-code commit (docs, config, etc.) — skip
224
+ RESULTS+=("- **SKIP**: \`$commit_short\` — $commit_msg (non-code)")
225
+ fi
226
+ done
227
+
228
+ # ============================================================
229
+ # STRUCTURED OUTPUT
230
+ # ============================================================
231
+
232
+ echo "## TDD Compliance Report"
233
+ echo ""
234
+ echo "**Branch:** $BRANCH"
235
+ echo "**Base:** $BASE_BRANCH"
236
+ echo "**Commits analyzed:** ${#COMMITS[@]}"
237
+ echo ""
238
+
239
+ echo "### Per-commit Analysis"
240
+ echo ""
241
+ for result in "${RESULTS[@]}"; do
242
+ echo "$result"
243
+ done
244
+ echo ""
245
+
246
+ if [[ ${#VIOLATIONS[@]} -gt 0 ]]; then
247
+ echo "### Violations"
248
+ echo ""
249
+ for v in "${VIOLATIONS[@]}"; do
250
+ echo "- $v"
251
+ done
252
+ echo ""
253
+ fi
254
+
255
+ TOTAL=$((CHECK_PASS + CHECK_FAIL))
256
+ echo "---"
257
+ echo ""
258
+
259
+ if [[ $CHECK_FAIL -eq 0 ]]; then
260
+ echo "**Result: PASS** ($CHECK_PASS/$TOTAL commits compliant)"
261
+ exit 0
262
+ else
263
+ echo "**Result: FAIL** ($CHECK_FAIL/$TOTAL commits have violations)"
264
+ exit 1
265
+ fi