@boyingliu01/xp-gate 0.18.5 → 0.19.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.
Files changed (254) hide show
  1. package/adapter-common.sh +27 -0
  2. package/adapters/gate-3.sh +0 -0
  3. package/adapters/gate-4.sh +0 -0
  4. package/adapters/gate-7.sh +0 -0
  5. package/adapters/gate-8.sh +0 -0
  6. package/adapters/gate-9.sh +0 -0
  7. package/adapters/powershell.sh +85 -33
  8. package/adapters/typescript.sh +8 -8
  9. package/build-integrity/__tests__/import-resolver.test.ts +22 -0
  10. package/build-integrity/__tests__/orchestrator.test.ts +1 -2
  11. package/build-integrity/gate-10.ts +7 -0
  12. package/gate-12-file-hygiene.sh +230 -0
  13. package/hooks/adapter-common.sh +27 -0
  14. package/hooks/gate-10.sh +105 -0
  15. package/hooks/gate-12-file-hygiene.sh +0 -0
  16. package/hooks/lib/validate-code-walkthrough.cjs +125 -0
  17. package/hooks/pre-commit +130 -33
  18. package/hooks/pre-push +15 -91
  19. package/lib/__tests__/check-version.test.js +67 -46
  20. package/lib/__tests__/doctor.test.js +7 -6
  21. package/lib/__tests__/gate-runner.test.js +267 -0
  22. package/lib/__tests__/init.test.js +32 -0
  23. package/lib/__tests__/npm-package.test.js +13 -0
  24. package/lib/__tests__/phase-transition.test.js +604 -12
  25. package/lib/__tests__/sprint-discovery.test.ts +0 -1
  26. package/lib/__tests__/sprint-status-rework.test.js +100 -1
  27. package/lib/__tests__/update-hooks.test.js +143 -10
  28. package/lib/__tests__/upgrade-exec.test.js +73 -45
  29. package/lib/__tests__/upgrade-skill-sync.test.js +1 -1
  30. package/lib/__tests__/upgrade.test.js +12 -11
  31. package/lib/doctor.js +2 -1
  32. package/lib/gate-runner.js +68 -23
  33. package/lib/init.js +34 -5
  34. package/lib/install-skill.js +1 -0
  35. package/lib/mattpocock-license-verdict.json +1 -1
  36. package/lib/phase-transition.js +203 -23
  37. package/lib/shared-paths.js +2 -0
  38. package/lib/sprint-status.js +15 -4
  39. package/lib/test-alignment.ts +3 -13
  40. package/lib/update-hooks.js +129 -15
  41. package/mock-policy/AGENTS.md +5 -5
  42. package/mock-policy/__tests__/gate-m3.test.ts +0 -5
  43. package/mock-policy/__tests__/scope-scanner.test.ts +8 -2
  44. package/mutation/AGENTS.md +4 -4
  45. package/mutation/__tests__/mutmut-runner.test.ts +443 -2
  46. package/mutation/__tests__/update-baseline.test.ts +0 -2
  47. package/mutation/runners/mutmut-runner.ts +67 -17
  48. package/package.json +9 -3
  49. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  50. package/plugins/claude-code/skills/clipboard-vision/SKILL.md +47 -0
  51. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  52. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  53. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  54. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  55. package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  56. package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.sh +161 -0
  57. package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  58. package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  59. package/plugins/claude-code/skills/delphi-review/.delphi-config.json.example +24 -27
  60. package/plugins/claude-code/skills/delphi-review/AGENTS.md +13 -12
  61. package/plugins/claude-code/skills/delphi-review/INSTALL.md +15 -17
  62. package/plugins/claude-code/skills/delphi-review/SKILL.md +30 -29
  63. package/plugins/claude-code/skills/delphi-review/opencode.json.delphi.example +6 -10
  64. package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +33 -49
  65. package/plugins/claude-code/skills/delphi-review/references/requirements.md +66 -54
  66. package/plugins/claude-code/skills/delphi-review/references/round-templates.md +7 -7
  67. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +5 -5
  68. package/plugins/claude-code/skills/sprint-flow/SKILL.md +4 -4
  69. package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +2 -2
  70. package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +37 -35
  71. package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +1 -1
  72. package/plugins/claude-code/skills/sprint-flow/references/phase-1-prep.md +1 -1
  73. package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +24 -14
  74. package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +6 -6
  75. package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +2 -2
  76. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
  77. package/plugins/dsh/README.md +25 -0
  78. package/plugins/dsh/cordis.patch.yml +7 -0
  79. package/plugins/dsh/package.json +70 -0
  80. package/plugins/dsh/scripts/prepack.cjs +82 -0
  81. package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +113 -0
  82. package/plugins/dsh/skills/admin-template-guidelines/references/rule-1.md +91 -0
  83. package/plugins/dsh/skills/admin-template-guidelines/references/rule-2.md +155 -0
  84. package/plugins/dsh/skills/admin-template-guidelines/references/rule-3.md +70 -0
  85. package/plugins/dsh/skills/admin-template-guidelines/references/rule-4.md +154 -0
  86. package/plugins/dsh/skills/admin-template-guidelines/references/rule-5.md +126 -0
  87. package/plugins/dsh/skills/admin-template-guidelines/references/rule-6.md +180 -0
  88. package/plugins/dsh/skills/batch-grill-me/SKILL.md +15 -0
  89. package/plugins/dsh/skills/clipboard-vision/SKILL.md +47 -0
  90. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  91. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  92. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  93. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  94. package/plugins/dsh/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  95. package/plugins/dsh/skills/clipboard-vision/describe-clipboard.sh +161 -0
  96. package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  97. package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  98. package/plugins/dsh/skills/delphi-review/.delphi-config.json.example +70 -0
  99. package/plugins/dsh/skills/delphi-review/AGENTS.md +67 -0
  100. package/plugins/dsh/skills/delphi-review/INSTALL.md +150 -0
  101. package/plugins/dsh/skills/delphi-review/SKILL.md +559 -0
  102. package/plugins/dsh/skills/delphi-review/hooks.md +19 -0
  103. package/plugins/dsh/skills/delphi-review/opencode.json.delphi.example +52 -0
  104. package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +470 -0
  105. package/plugins/dsh/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
  106. package/plugins/dsh/skills/delphi-review/references/requirements.md +402 -0
  107. package/plugins/dsh/skills/delphi-review/references/round-templates.md +134 -0
  108. package/plugins/dsh/skills/delphi-review/scripts/verify-consensus.sh +43 -0
  109. package/plugins/dsh/skills/delphi-review/tools.md +29 -0
  110. package/plugins/dsh/skills/domain-modeling/ADR-FORMAT.md +64 -0
  111. package/plugins/dsh/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  112. package/plugins/dsh/skills/domain-modeling/SKILL.md +74 -0
  113. package/plugins/dsh/skills/grill-with-docs/SKILL.md +7 -0
  114. package/plugins/dsh/skills/grilling/SKILL.md +12 -0
  115. package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +156 -0
  116. package/plugins/dsh/skills/ralph-loop/SKILL.md +198 -0
  117. package/plugins/dsh/skills/ralph-loop/references/components.md +113 -0
  118. package/plugins/dsh/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
  119. package/plugins/dsh/skills/ralph-loop/references/workflow-details.md +99 -0
  120. package/plugins/dsh/skills/ralph-loop/templates/progress-log.md +36 -0
  121. package/plugins/dsh/skills/sprint-flow/AGENTS.md +103 -0
  122. package/plugins/dsh/skills/sprint-flow/SKILL.md +411 -0
  123. package/plugins/dsh/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
  124. package/plugins/dsh/skills/sprint-flow/hooks.md +18 -0
  125. package/plugins/dsh/skills/sprint-flow/references/components/memory.md +87 -0
  126. package/plugins/dsh/skills/sprint-flow/references/components/middleware.md +75 -0
  127. package/plugins/dsh/skills/sprint-flow/references/components/skill-invocations.md +105 -0
  128. package/plugins/dsh/skills/sprint-flow/references/components/system-prompt.md +27 -0
  129. package/plugins/dsh/skills/sprint-flow/references/components/tool-descriptions.md +95 -0
  130. package/plugins/dsh/skills/sprint-flow/references/force-levels.md +204 -0
  131. package/plugins/dsh/skills/sprint-flow/references/orchestration-rules.md +236 -0
  132. package/plugins/dsh/skills/sprint-flow/references/phase-1-prep.md +294 -0
  133. package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +273 -0
  134. package/plugins/dsh/skills/sprint-flow/references/phase-3-build.md +348 -0
  135. package/plugins/dsh/skills/sprint-flow/references/phase-4-verify.md +225 -0
  136. package/plugins/dsh/skills/sprint-flow/references/phase-5-ship.md +222 -0
  137. package/plugins/dsh/skills/sprint-flow/references/phase-6-close.md +266 -0
  138. package/plugins/dsh/skills/sprint-flow/references/phase-overview.md +486 -0
  139. package/plugins/dsh/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
  140. package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-learning-log.md +136 -0
  141. package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-output-template.md +110 -0
  142. package/plugins/dsh/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
  143. package/plugins/dsh/skills/sprint-flow/templates/pain-document-template.md +115 -0
  144. package/plugins/dsh/skills/sprint-flow/templates/sprint-progress-template.md +140 -0
  145. package/plugins/dsh/skills/sprint-flow/templates/sprint-summary-template.md +121 -0
  146. package/plugins/dsh/skills/sprint-flow/tools.md +29 -0
  147. package/plugins/dsh/skills/test-driven-development/SKILL.md +371 -0
  148. package/plugins/dsh/skills/test-driven-development/testing-anti-patterns.md +299 -0
  149. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +56 -0
  150. package/plugins/dsh/skills/test-specification-alignment/SKILL.md +205 -0
  151. package/plugins/dsh/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
  152. package/plugins/dsh/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
  153. package/plugins/dsh/skills/test-specification-alignment/references/specification-format.md +348 -0
  154. package/plugins/dsh/skills/to-issues/SKILL.md +277 -0
  155. package/plugins/dsh/src/command.test.ts +102 -0
  156. package/plugins/dsh/src/command.ts +75 -0
  157. package/plugins/dsh/src/contract.test.ts +62 -0
  158. package/plugins/dsh/src/gate-runner.test.ts +100 -0
  159. package/plugins/dsh/src/gate-runner.ts +58 -0
  160. package/plugins/dsh/src/index.test.ts +97 -0
  161. package/plugins/dsh/src/index.ts +94 -0
  162. package/plugins/dsh/src/skills.test.ts +58 -0
  163. package/plugins/dsh/tsconfig.build.json +13 -0
  164. package/plugins/dsh/tsconfig.json +19 -0
  165. package/plugins/dsh/vitest.config.ts +8 -0
  166. package/plugins/opencode/package.json +1 -1
  167. package/plugins/opencode/scripts/prepack.cjs +1 -0
  168. package/plugins/opencode/skills/clipboard-vision/SKILL.md +47 -0
  169. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  170. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  171. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  172. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  173. package/plugins/opencode/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  174. package/plugins/opencode/skills/clipboard-vision/describe-clipboard.sh +161 -0
  175. package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  176. package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  177. package/plugins/opencode/skills/delphi-review/.delphi-config.json.example +24 -27
  178. package/plugins/opencode/skills/delphi-review/AGENTS.md +13 -12
  179. package/plugins/opencode/skills/delphi-review/INSTALL.md +15 -17
  180. package/plugins/opencode/skills/delphi-review/SKILL.md +30 -29
  181. package/plugins/opencode/skills/delphi-review/opencode.json.delphi.example +6 -10
  182. package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +33 -49
  183. package/plugins/opencode/skills/delphi-review/references/requirements.md +66 -54
  184. package/plugins/opencode/skills/delphi-review/references/round-templates.md +7 -7
  185. package/plugins/opencode/skills/sprint-flow/AGENTS.md +5 -5
  186. package/plugins/opencode/skills/sprint-flow/SKILL.md +4 -4
  187. package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +2 -2
  188. package/plugins/opencode/skills/sprint-flow/references/force-levels.md +37 -35
  189. package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +1 -1
  190. package/plugins/opencode/skills/sprint-flow/references/phase-1-prep.md +1 -1
  191. package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +24 -14
  192. package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +6 -6
  193. package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +2 -2
  194. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
  195. package/plugins/qoder/plugin.json +1 -1
  196. package/plugins/qoder/skills/clipboard-vision/SKILL.md +47 -0
  197. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  198. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  199. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  200. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  201. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +47 -0
  202. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  203. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  204. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  205. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  206. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  207. package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  208. package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  209. package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  210. package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +3 -11
  211. package/plugins/qoder/skills/delphi-review/AGENTS.md +15 -17
  212. package/plugins/qoder/skills/delphi-review/INSTALL.md +12 -12
  213. package/plugins/qoder/skills/delphi-review/SKILL.md +58 -25
  214. package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +1 -1
  215. package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +33 -49
  216. package/plugins/qoder/skills/sprint-flow/AGENTS.md +5 -5
  217. package/plugins/qoder/skills/sprint-flow/SKILL.md +2 -2
  218. package/plugins/qoder/skills/sprint-flow/references/force-levels.md +38 -35
  219. package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
  220. package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +48 -6
  221. package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +5 -6
  222. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
  223. package/principles/AGENTS.md +4 -4
  224. package/scripts/__tests__/sync-package-content.test.js +30 -0
  225. package/scripts/delphi-external-review.cjs +194 -63
  226. package/scripts/sync-package-content.js +9 -2
  227. package/skills/clipboard-vision/SKILL.md +47 -0
  228. package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
  229. package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
  230. package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
  231. package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
  232. package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  233. package/skills/clipboard-vision/describe-clipboard.sh +161 -0
  234. package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
  235. package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
  236. package/skills/delphi-review/.delphi-config.json.example +24 -27
  237. package/skills/delphi-review/AGENTS.md +13 -12
  238. package/skills/delphi-review/INSTALL.md +15 -17
  239. package/skills/delphi-review/SKILL.md +30 -29
  240. package/skills/delphi-review/opencode.json.delphi.example +6 -10
  241. package/skills/delphi-review/references/code-walkthrough.md +33 -49
  242. package/skills/delphi-review/references/requirements.md +66 -54
  243. package/skills/delphi-review/references/round-templates.md +7 -7
  244. package/skills/sprint-flow/AGENTS.md +5 -5
  245. package/skills/sprint-flow/SKILL.md +4 -4
  246. package/skills/sprint-flow/references/components/skill-invocations.md +2 -2
  247. package/skills/sprint-flow/references/force-levels.md +37 -35
  248. package/skills/sprint-flow/references/orchestration-rules.md +1 -1
  249. package/skills/sprint-flow/references/phase-1-prep.md +1 -1
  250. package/skills/sprint-flow/references/phase-2-design.md +24 -14
  251. package/skills/sprint-flow/references/phase-4-verify.md +6 -6
  252. package/skills/sprint-flow/references/phase-overview.md +2 -2
  253. package/skills/test-specification-alignment/AGENTS.md +4 -4
  254. package/sprint-gate.sh +291 -0
package/adapter-common.sh CHANGED
@@ -27,6 +27,17 @@ detect_os_env() {
27
27
  esac
28
28
  }
29
29
 
30
+ # Git exports repository-local variables to hooks. Child test processes must
31
+ # not inherit them, or nested git repositories modify the caller's repository.
32
+ run_without_git_context() (
33
+ unset GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_CONFIG GIT_CONFIG_PARAMETERS
34
+ unset GIT_CONFIG_COUNT GIT_OBJECT_DIRECTORY GIT_DIR GIT_WORK_TREE
35
+ unset GIT_IMPLICIT_WORK_TREE GIT_GRAFT_FILE GIT_INDEX_FILE
36
+ unset GIT_NO_REPLACE_OBJECTS GIT_REPLACE_REF_BASE GIT_PREFIX
37
+ unset GIT_SHALLOW_FILE GIT_COMMON_DIR
38
+ "$@"
39
+ )
40
+
30
41
  detect_project_lang() {
31
42
  if [ -f "tsconfig.json" ]; then
32
43
  echo "typescript"
@@ -241,6 +252,22 @@ detect_go_mutation_testable() {
241
252
  fi
242
253
  return 1
243
254
  }
255
+
256
+ # Check if any changed file matches any of the given regex patterns.
257
+ # Usage: any_changed_files_match "\.ts$" "\.py$"
258
+ # Returns 0 (true) if at least one changed file matches any pattern, 1 (false) otherwise.
259
+ any_changed_files_match() {
260
+ if [ -z "${CHANGED_FILES:-}" ]; then
261
+ return 1
262
+ fi
263
+ for pattern in "$@"; do
264
+ if echo "$CHANGED_FILES" | grep -qE "$pattern" 2>/dev/null; then
265
+ return 0
266
+ fi
267
+ done
268
+ return 1
269
+ }
270
+
244
271
  # Detect if a Java/Kotlin project has PITest (pitest-maven or pitest-gradle) configured
245
272
  detect_pitest_testable() {
246
273
  # Check for Maven + pitest-maven plugin
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -29,14 +29,57 @@ _detect_pwsh() {
29
29
  fi
30
30
  }
31
31
 
32
+ _find_powershell_files() {
33
+ local name_pattern="$1"
34
+ find . \
35
+ \( -type d \( \
36
+ -name .git -o \
37
+ -name node_modules -o \
38
+ -name dist -o \
39
+ -name coverage -o \
40
+ -path './plugins/claude-code/skills' -o \
41
+ -path './plugins/opencode/skills' -o \
42
+ -path './plugins/qoder/skills' -o \
43
+ -path './src/npm-package/skills' -o \
44
+ -path './src/npm-package/plugins' \
45
+ \) -prune \) -o \
46
+ \( -type f -name "$name_pattern" -print \) 2>/dev/null
47
+ }
48
+
49
+ _powershell_invocation() {
50
+ local executable="$1"
51
+ POWERSHELL_ARGS=(-NoProfile)
52
+ case "${executable##*/}" in
53
+ powershell.exe|powershell)
54
+ POWERSHELL_ARGS[${#POWERSHELL_ARGS[@]}]=-ExecutionPolicy
55
+ POWERSHELL_ARGS[${#POWERSHELL_ARGS[@]}]=Bypass
56
+ ;;
57
+ esac
58
+ POWERSHELL_ARGS[${#POWERSHELL_ARGS[@]}]=-Command
59
+ }
60
+
61
+ _powershell_path_array() {
62
+ local path
63
+ local separator=""
64
+ printf '@('
65
+ for path in "$@"; do
66
+ path=${path//\'/\'\'}
67
+ printf "%s'%s'" "$separator" "$path"
68
+ separator=","
69
+ done
70
+ printf ')'
71
+ }
72
+
32
73
  run_static_analysis() {
33
74
  local PWSH
34
75
  PWSH=$(_detect_pwsh)
35
76
  if [ -n "$PWSH" ]; then
77
+ local POWERSHELL_ARGS
78
+ _powershell_invocation "$PWSH"
36
79
  echo "Running PSScriptAnalyzer static analysis on PowerShell scripts..."
37
80
  # Recursively analyze all .ps1 files from repo root
38
81
  # Exit with non-zero if Error or Warning severity issues found
39
- "$PWSH" -NoProfile -Command "
82
+ "$PWSH" "${POWERSHELL_ARGS[@]}" "
40
83
  \$results = Invoke-ScriptAnalyzer -Path . -Recurse -Severity Error,Warning
41
84
  if (\$results) {
42
85
  \$results | Format-Table -AutoSize
@@ -64,30 +107,24 @@ run_tests() {
64
107
  return 0
65
108
  fi
66
109
 
67
- # Check if Pester tests exist in common locations
68
- local test_paths=""
69
- if [ -d "tests" ]; then
70
- test_paths="tests/"
71
- elif [ -d "test" ]; then
72
- test_paths="test/"
73
- fi
110
+ local POWERSHELL_ARGS
111
+ _powershell_invocation "$PWSH"
74
112
 
75
- if [ -n "$test_paths" ] && find "$test_paths" -name "*.Tests.ps1" -type f 2>/dev/null | sed -n '1p; 1q' | grep -q "."; then
113
+ local test_files=()
114
+ local discovery_file
115
+ local path
116
+ local test_paths
117
+ discovery_file=$(mktemp)
118
+ _find_powershell_files "*.Tests.ps1" > "$discovery_file"
119
+ while IFS= read -r path; do
120
+ test_files[${#test_files[@]}]="$path"
121
+ done < "$discovery_file"
122
+ rm -f "$discovery_file"
123
+ if [ "${#test_files[@]}" -gt 0 ]; then
124
+ test_paths=$(_powershell_path_array "${test_files[@]}")
76
125
  echo "Running Pester tests..."
77
- "$PWSH" -NoProfile -Command "
78
- \$results = Invoke-Pester -Path '$test_paths' -PassThru
79
- if (\$results.FailedCount -gt 0) {
80
- Write-Host \"FAILED: \$(\$results.FailedCount) test(s)\"
81
- exit 1
82
- }
83
- Write-Host \"PASSED: \$(\$results.PassedCount) test(s)\"
84
- exit 0
85
- "
86
- return $?
87
- elif find . -maxdepth 2 -name "*.Tests.ps1" -type f 2>/dev/null | sed -n '1p; 1q' | grep -q "."; then
88
- echo "Running Pester tests in current directory..."
89
- "$PWSH" -NoProfile -Command "
90
- \$results = Invoke-Pester -CI -PassThru
126
+ "$PWSH" "${POWERSHELL_ARGS[@]}" "
127
+ \$results = Invoke-Pester -Path $test_paths -PassThru
91
128
  if (\$results.FailedCount -gt 0) {
92
129
  Write-Host \"FAILED: \$(\$results.FailedCount) test(s)\"
93
130
  exit 1
@@ -110,18 +147,32 @@ run_coverage() {
110
147
  return 0
111
148
  fi
112
149
 
113
- local test_paths=""
114
- if [ -d "tests" ]; then
115
- test_paths="tests/"
116
- elif [ -d "test" ]; then
117
- test_paths="test/"
118
- fi
150
+ local POWERSHELL_ARGS
151
+ _powershell_invocation "$PWSH"
119
152
 
120
- if [ -n "$test_paths" ] || find . -maxdepth 2 -name "*.Tests.ps1" -type f 2>/dev/null | sed -n '1p; 1q' | grep -q "."; then
153
+ local test_files=()
154
+ local source_files=()
155
+ local discovery_file
156
+ local path
157
+ local test_paths
158
+ local coverage_paths
159
+ discovery_file=$(mktemp)
160
+ _find_powershell_files "*.Tests.ps1" > "$discovery_file"
161
+ # Discovery is line-delimited; filenames containing newlines are unsupported.
162
+ while IFS= read -r path; do
163
+ test_files[${#test_files[@]}]="$path"
164
+ done < "$discovery_file"
165
+ if [ "${#test_files[@]}" -gt 0 ]; then
166
+ _find_powershell_files "*.ps1" | grep -v '\.Tests\.ps1$' > "$discovery_file"
167
+ while IFS= read -r path; do
168
+ source_files[${#source_files[@]}]="$path"
169
+ done < "$discovery_file"
170
+ rm -f "$discovery_file"
171
+ test_paths=$(_powershell_path_array "${test_files[@]}")
172
+ coverage_paths=$(_powershell_path_array "${source_files[@]}")
121
173
  echo "Running Pester with code coverage..."
122
- local path_arg="${test_paths:-.}"
123
- "$PWSH" -NoProfile -Command "
124
- \$results = Invoke-Pester -Path '$path_arg' -CodeCoverage @(Get-ChildItem -Path . -Filter *.ps1 -Recurse -Exclude *.Tests.ps1) -PassThru
174
+ "$PWSH" "${POWERSHELL_ARGS[@]}" "
175
+ \$results = Invoke-Pester -Path $test_paths -CodeCoverage $coverage_paths -PassThru
125
176
  \$pct = [math]::Round(\$results.CodeCoverage.CoveragePercent, 1)
126
177
  Write-Host \"Coverage: \$pct%\"
127
178
  if (\$pct -lt 80) {
@@ -132,6 +183,7 @@ run_coverage() {
132
183
  " 2>&1
133
184
  return $?
134
185
  else
186
+ rm -f "$discovery_file"
135
187
  echo "No Pester tests found for coverage measurement"
136
188
  return 0
137
189
  fi
@@ -107,10 +107,10 @@ run_lint() {
107
107
  run_tests() {
108
108
  if command -v npx >/dev/null 2>&1; then
109
109
  echo "Running TypeScript tests..."
110
- if npx vitest --version >/dev/null 2>&1; then
111
- npx vitest run
112
- elif npx jest --version >/dev/null 2>&1; then
113
- npx jest --passWithNoTests
110
+ if run_without_git_context npx vitest --version >/dev/null 2>&1; then
111
+ run_without_git_context npx vitest run
112
+ elif run_without_git_context npx jest --version >/dev/null 2>&1; then
113
+ run_without_git_context npx jest --passWithNoTests
114
114
  else
115
115
  echo "No test runner available (vitest or jest required)"
116
116
  return 1
@@ -125,10 +125,10 @@ run_tests() {
125
125
  run_coverage() {
126
126
  if command -v npx >/dev/null 2>&1; then
127
127
  echo "Running TypeScript coverage..."
128
- if npx vitest --version >/dev/null 2>&1; then
129
- npx vitest run --coverage
130
- elif npx jest --version >/dev/null 2>&1; then
131
- npx jest --coverage
128
+ if run_without_git_context npx vitest --version >/dev/null 2>&1; then
129
+ run_without_git_context npx vitest run --coverage
130
+ elif run_without_git_context npx jest --version >/dev/null 2>&1; then
131
+ run_without_git_context npx jest --coverage
132
132
  else
133
133
  echo "No test runner available for coverage"
134
134
  return 1
@@ -321,6 +321,28 @@ describe('runImportCheck', () => {
321
321
  expect(result.violations[0].reason).toMatch(/not found|does not exist|missing/i);
322
322
  });
323
323
 
324
+ it('skips YAML files containing JavaScript-like require text', async () => {
325
+ const workflowDir = path.join(tmpDir, '.github', 'workflows');
326
+ await fs.mkdir(workflowDir, { recursive: true });
327
+
328
+ const workflowFile = path.join(workflowDir, 'cross-platform-ci.yml');
329
+ await fs.writeFile(workflowFile, `run: node -e "require('../test')"\n`);
330
+
331
+ const result = await runImportCheck([workflowFile], tmpDir, 30000);
332
+ expect(result.status).toBe('pass');
333
+ expect(result.violations).toEqual([]);
334
+ });
335
+
336
+ it('still reports missing relative imports in supported JavaScript modules', async () => {
337
+ const sourceFile = path.join(tmpDir, 'source.mjs');
338
+ await fs.writeFile(sourceFile, `import './missing.js';\n`);
339
+
340
+ const result = await runImportCheck([sourceFile], tmpDir, 30000);
341
+ expect(result.status).toBe('fail');
342
+ expect(result.violations).toHaveLength(1);
343
+ expect(result.violations[0].importPath).toBe('./missing.js');
344
+ });
345
+
324
346
  it('returns pass when only bare npm imports are used', async () => {
325
347
  const srcDir = path.join(tmpDir, 'src');
326
348
  await fs.mkdir(srcDir);
@@ -12,7 +12,7 @@ import os from 'os';
12
12
 
13
13
  import { runGate10, main } from '../gate-10';
14
14
 
15
- vi.setConfig({ testTimeout: 30000 });
15
+ vi.setConfig({ testTimeout: 90000 });
16
16
 
17
17
  describe('runGate10', () => {
18
18
  let tmpDir: string;
@@ -215,7 +215,6 @@ describe('runGate10', () => {
215
215
  */
216
216
  describe('main', () => {
217
217
  let tmpDir: string;
218
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
219
218
  let consoleSpy: any;
220
219
 
221
220
  beforeEach(async () => {
@@ -38,6 +38,11 @@ import type {
38
38
  */
39
39
 
40
40
  const execFileAsync = promisify(execFile);
41
+ const SUPPORTED_MODULE_EXTENSIONS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']);
42
+
43
+ function isSupportedModuleFile(file: string): boolean {
44
+ return SUPPORTED_MODULE_EXTENSIONS.has(path.extname(file));
45
+ }
41
46
 
42
47
  /**
43
48
  * Gate 10: Build Integrity Check — TypeScript compilation verification.
@@ -303,6 +308,8 @@ export async function runImportCheck(
303
308
  const fsSync = fsNative;
304
309
 
305
310
  for (const file of changedFiles) {
311
+ if (!isSupportedModuleFile(file)) continue;
312
+
306
313
  // Skip test files — they contain intentionally broken imports for unit testing.
307
314
  // The published npm package does not include __tests__ directories.
308
315
  if (file.includes('__tests__')) continue;
@@ -0,0 +1,230 @@
1
+ # ============================================================================
2
+ # GATE 12: File Hygiene Check
3
+ # Detects common file quality issues in staged files:
4
+ # - Trailing whitespace
5
+ # - Missing EOF newline
6
+ # - Merge conflict markers (<<<<<<< , =======, >>>>>>>)
7
+ # - Oversized files (>1MB default, configurable)
8
+ # ============================================================================
9
+
10
+ 2>&1 echo ""
11
+ 2>&1 echo "→ Gate 12: File hygiene check..."
12
+ GATE_12_START=$(gate_start_ms)
13
+
14
+ # Configuration (can be overridden via environment)
15
+ MAX_FILE_SIZE_BYTES="${XP_GATE_MAX_FILE_SIZE:-1048576}" # 1MB default
16
+
17
+ # Get list of staged files (excluding deleted files)
18
+ STAGED_FILES=$(git diff --cached --name-only --diff-filter=d 2>/dev/null)
19
+
20
+ if [ -z "$STAGED_FILES" ]; then
21
+ echo " ℹ️ No staged files — skipping"
22
+ echo " ✅ PASSED - File Hygiene (no files to check)"
23
+ GATE_12_STATUS="SKIP"
24
+ record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "0" "$GATE_12_START"
25
+ return 0 2>/dev/null || true
26
+ fi
27
+
28
+ HYGIENE_ISSUES=0
29
+ HYGIENE_FILES=""
30
+
31
+ # --- Check 1: Trailing whitespace ---
32
+ TRAILING_WS_FILES=""
33
+ while IFS= read -r file; do
34
+ [ -z "$file" ] && continue
35
+ # Skip binary files by checking if file contains null bytes
36
+ if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
37
+ continue
38
+ fi
39
+ # Check staged content for trailing whitespace (spaces/tabs at end of lines)
40
+ if git show ":$file" 2>/dev/null | grep -qE '[[:space:]]$'; then
41
+ TRAILING_WS_FILES="$TRAILING_WS_FILES $file"
42
+ fi
43
+ done <<EOF
44
+ $STAGED_FILES
45
+ EOF
46
+
47
+ if [ -n "$TRAILING_WS_FILES" ]; then
48
+ echo " ⚠️ Trailing whitespace detected in:"
49
+ for f in $TRAILING_WS_FILES; do
50
+ echo " - $f"
51
+ done
52
+ HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
53
+ HYGIENE_FILES="$HYGIENE_FILES trailing-whitespace"
54
+ fi
55
+
56
+ # --- Check 2: Missing EOF newline ---
57
+ NO_EOF_NEWLINE_FILES=""
58
+ while IFS= read -r file; do
59
+ [ -z "$file" ] && continue
60
+ # Skip binary files
61
+ if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
62
+ continue
63
+ fi
64
+ # Check if file content ends with newline
65
+ FILE_CONTENT=$(git show ":$file" 2>/dev/null)
66
+ if [ -n "$FILE_CONTENT" ]; then
67
+ LAST_CHAR=$(printf '%s' "$FILE_CONTENT" | tail -c 1)
68
+ if [ "$LAST_CHAR" != "" ]; then
69
+ NO_EOF_NEWLINE_FILES="$NO_EOF_NEWLINE_FILES $file"
70
+ fi
71
+ fi
72
+ done <<EOF
73
+ $STAGED_FILES
74
+ EOF
75
+
76
+ if [ -n "$NO_EOF_NEWLINE_FILES" ]; then
77
+ echo " ⚠️ Missing EOF newline in:"
78
+ for f in $NO_EOF_NEWLINE_FILES; do
79
+ echo " - $f"
80
+ done
81
+ HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
82
+ HYGIENE_FILES="$HYGIENE_FILES no-eof-newline"
83
+ fi
84
+
85
+ # --- Check 3: Merge conflict markers ---
86
+ CONFLICT_MARKER_FILES=""
87
+ while IFS= read -r file; do
88
+ [ -z "$file" ] && continue
89
+ # Skip binary files
90
+ if git diff --cached --no-color -- "$file" 2>/dev/null | grep -q $'\x00'; then
91
+ continue
92
+ fi
93
+ # Check for conflict markers in staged content
94
+ if git show ":$file" 2>/dev/null | grep -qE '^(<<<<<<<|=======|>>>>>>>)'; then
95
+ CONFLICT_MARKER_FILES="$CONFLICT_MARKER_FILES $file"
96
+ fi
97
+ done <<EOF
98
+ $STAGED_FILES
99
+ EOF
100
+
101
+ if [ -n "$CONFLICT_MARKER_FILES" ]; then
102
+ echo " ❌ Merge conflict markers detected in:"
103
+ for f in $CONFLICT_MARKER_FILES; do
104
+ echo " - $f"
105
+ done
106
+ HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
107
+ HYGIENE_FILES="$HYGIENE_FILES conflict-markers"
108
+ fi
109
+
110
+ # --- Check 4: Oversized files ---
111
+ OVERSIZED_FILES=""
112
+ while IFS= read -r file; do
113
+ [ -z "$file" ] && continue
114
+ [ ! -f "$file" ] && continue
115
+ FILE_SIZE=$(wc -c < "$file" 2>/dev/null | tr -d ' ')
116
+ if [ -n "$FILE_SIZE" ] && [ "$FILE_SIZE" -gt "$MAX_FILE_SIZE_BYTES" ]; then
117
+ SIZE_MB=$((FILE_SIZE / 1048576))
118
+ OVERSIZED_FILES="$OVERSIZED_FILES $file(${SIZE_MB}MB)"
119
+ fi
120
+ done <<EOF
121
+ $STAGED_FILES
122
+ EOF
123
+
124
+ if [ -n "$OVERSIZED_FILES" ]; then
125
+ echo " ⚠️ Oversized files (>${MAX_FILE_SIZE_BYTES} bytes):"
126
+ for f in $OVERSIZED_FILES; do
127
+ echo " - $f"
128
+ done
129
+ HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
130
+ HYGIENE_FILES="$HYGIENE_FILES oversized-files"
131
+ fi
132
+
133
+ # --- Check 5: YAML/JSON syntax validation (Issue #351) ---
134
+ # Only check staged YAML/JSON files that have changes
135
+ YAML_JSON_ERRORS=""
136
+ YAML_JSON_CHECK_COUNT=0
137
+
138
+ # Detect available validators
139
+ YAML_CMD=""
140
+ if command -v python3 >/dev/null 2>&1; then
141
+ YAML_CMD="python3"
142
+ elif command -v python >/dev/null 2>&1; then
143
+ YAML_CMD="python"
144
+ fi
145
+
146
+ JSON_CMD=""
147
+ if command -v node >/dev/null 2>&1; then
148
+ JSON_CMD="node"
149
+ elif command -v jq >/dev/null 2>&1; then
150
+ JSON_CMD="jq"
151
+ fi
152
+
153
+ while IFS= read -r file; do
154
+ [ -z "$file" ] && continue
155
+ [ ! -f "$file" ] && continue
156
+
157
+ case "$file" in
158
+ *.yml|*.yaml)
159
+ if [ -n "$YAML_CMD" ]; then
160
+ YAML_JSON_CHECK_COUNT=$((YAML_JSON_CHECK_COUNT + 1))
161
+ # Validate YAML syntax using Python's yaml module
162
+ YAML_ERROR=$(git show ":$file" 2>/dev/null | $YAML_CMD -c "
163
+ import sys, yaml
164
+ try:
165
+ yaml.safe_load(sys.stdin.read())
166
+ except yaml.YAMLError as e:
167
+ print(str(e).split(chr(10))[0])
168
+ sys.exit(1)
169
+ except Exception:
170
+ pass
171
+ " 2>&1)
172
+ if [ $? -ne 0 ] && [ -n "$YAML_ERROR" ]; then
173
+ YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(yaml:$YAML_ERROR)"
174
+ fi
175
+ fi
176
+ ;;
177
+ *.json)
178
+ YAML_JSON_CHECK_COUNT=$((YAML_JSON_CHECK_COUNT + 1))
179
+ if [ "$JSON_CMD" = "node" ]; then
180
+ JSON_ERROR=$(git show ":$file" 2>/dev/null | node -e "
181
+ let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{try{JSON.parse(d)}catch(e){console.log(e.message.split('\\n')[0]);process.exit(1)}})
182
+ " 2>&1)
183
+ if [ $? -ne 0 ] && [ -n "$JSON_ERROR" ]; then
184
+ YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(json:$JSON_ERROR)"
185
+ fi
186
+ elif [ "$JSON_CMD" = "jq" ]; then
187
+ JSON_ERROR=$(git show ":$file" 2>/dev/null | jq empty 2>&1)
188
+ if [ $? -ne 0 ] && [ -n "$JSON_ERROR" ]; then
189
+ YAML_JSON_ERRORS="$YAML_JSON_ERRORS $file(json:syntax error)"
190
+ fi
191
+ fi
192
+ ;;
193
+ esac
194
+ done <<EOF
195
+ $STAGED_FILES
196
+ EOF
197
+
198
+ if [ -n "$YAML_JSON_ERRORS" ]; then
199
+ echo " ❌ YAML/JSON syntax errors:"
200
+ for f in $YAML_JSON_ERRORS; do
201
+ echo " - $f"
202
+ done
203
+ HYGIENE_ISSUES=$((HYGIENE_ISSUES + 1))
204
+ HYGIENE_FILES="$HYGIENE_FILES yaml-json-syntax"
205
+ elif [ "$YAML_JSON_CHECK_COUNT" -gt 0 ]; then
206
+ echo " ✅ YAML/JSON syntax OK ($YAML_JSON_CHECK_COUNT file(s) checked)"
207
+ fi
208
+
209
+ # --- Result ---
210
+ if [ "$HYGIENE_ISSUES" -eq 0 ]; then
211
+ echo " ✅ PASSED - No file hygiene issues detected."
212
+ GATE_12_STATUS="PASS"
213
+ else
214
+ # Hard blocks: conflict markers + YAML/JSON syntax errors
215
+ if [ -n "$CONFLICT_MARKER_FILES" ] || [ -n "$YAML_JSON_ERRORS" ]; then
216
+ echo ""
217
+ echo "❌ BLOCKED - File hygiene violations must be fixed before commit."
218
+ GATE_12_STATUS="BLOCK"
219
+ record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "$HYGIENE_ISSUES" "$GATE_12_START"
220
+ exit 1
221
+ else
222
+ echo ""
223
+ echo " ⚠️ WARNINGS - $HYGIENE_ISSUES file hygiene issue(s) found."
224
+ echo " Fix recommended but not blocking (no conflict markers or syntax errors)."
225
+ echo " Issues:$HYGIENE_FILES"
226
+ GATE_12_STATUS="WARN"
227
+ fi
228
+ fi
229
+
230
+ record_gate_audit "gate-12" "file-hygiene" "$GATE_12_STATUS" "$HYGIENE_ISSUES" "$GATE_12_START"
@@ -27,6 +27,17 @@ detect_os_env() {
27
27
  esac
28
28
  }
29
29
 
30
+ # Git exports repository-local variables to hooks. Child test processes must
31
+ # not inherit them, or nested git repositories modify the caller's repository.
32
+ run_without_git_context() (
33
+ unset GIT_ALTERNATE_OBJECT_DIRECTORIES GIT_CONFIG GIT_CONFIG_PARAMETERS
34
+ unset GIT_CONFIG_COUNT GIT_OBJECT_DIRECTORY GIT_DIR GIT_WORK_TREE
35
+ unset GIT_IMPLICIT_WORK_TREE GIT_GRAFT_FILE GIT_INDEX_FILE
36
+ unset GIT_NO_REPLACE_OBJECTS GIT_REPLACE_REF_BASE GIT_PREFIX
37
+ unset GIT_SHALLOW_FILE GIT_COMMON_DIR
38
+ "$@"
39
+ )
40
+
30
41
  detect_project_lang() {
31
42
  if [ -f "tsconfig.json" ]; then
32
43
  echo "typescript"
@@ -241,6 +252,22 @@ detect_go_mutation_testable() {
241
252
  fi
242
253
  return 1
243
254
  }
255
+
256
+ # Check if any changed file matches any of the given regex patterns.
257
+ # Usage: any_changed_files_match "\.ts$" "\.py$"
258
+ # Returns 0 (true) if at least one changed file matches any pattern, 1 (false) otherwise.
259
+ any_changed_files_match() {
260
+ if [ -z "${CHANGED_FILES:-}" ]; then
261
+ return 1
262
+ fi
263
+ for pattern in "$@"; do
264
+ if echo "$CHANGED_FILES" | grep -qE "$pattern" 2>/dev/null; then
265
+ return 0
266
+ fi
267
+ done
268
+ return 1
269
+ }
270
+
244
271
  # Detect if a Java/Kotlin project has PITest (pitest-maven or pitest-gradle) configured
245
272
  detect_pitest_testable() {
246
273
  # Check for Maven + pitest-maven plugin