forgecraft-mcp 1.3.2 → 1.6.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 (254) hide show
  1. package/README.md +2 -2
  2. package/dist/analyzers/anchors/anchor-loader.d.ts +47 -0
  3. package/dist/analyzers/anchors/anchor-loader.d.ts.map +1 -0
  4. package/dist/analyzers/anchors/anchor-loader.js +113 -0
  5. package/dist/analyzers/anchors/anchor-loader.js.map +1 -0
  6. package/dist/analyzers/anti-pattern.d.ts.map +1 -1
  7. package/dist/analyzers/anti-pattern.js +38 -26
  8. package/dist/analyzers/anti-pattern.js.map +1 -1
  9. package/dist/analyzers/completeness-helpers.d.ts +5 -0
  10. package/dist/analyzers/completeness-helpers.d.ts.map +1 -1
  11. package/dist/analyzers/completeness-helpers.js +17 -0
  12. package/dist/analyzers/completeness-helpers.js.map +1 -1
  13. package/dist/analyzers/completeness.d.ts.map +1 -1
  14. package/dist/analyzers/completeness.js +4 -4
  15. package/dist/analyzers/completeness.js.map +1 -1
  16. package/dist/analyzers/gs-scorer.d.ts +3 -1
  17. package/dist/analyzers/gs-scorer.d.ts.map +1 -1
  18. package/dist/analyzers/gs-scorer.js +5 -2
  19. package/dist/analyzers/gs-scorer.js.map +1 -1
  20. package/dist/analyzers/scorers/composable-scorer.d.ts +4 -2
  21. package/dist/analyzers/scorers/composable-scorer.d.ts.map +1 -1
  22. package/dist/analyzers/scorers/composable-scorer.js +50 -2
  23. package/dist/analyzers/scorers/composable-scorer.js.map +1 -1
  24. package/dist/analyzers/scorers/executable-scorer.d.ts +3 -2
  25. package/dist/analyzers/scorers/executable-scorer.d.ts.map +1 -1
  26. package/dist/analyzers/scorers/executable-scorer.js +64 -4
  27. package/dist/analyzers/scorers/executable-scorer.js.map +1 -1
  28. package/dist/analyzers/scorers/scorer-utils.d.ts +5 -3
  29. package/dist/analyzers/scorers/scorer-utils.d.ts.map +1 -1
  30. package/dist/analyzers/scorers/scorer-utils.js +34 -9
  31. package/dist/analyzers/scorers/scorer-utils.js.map +1 -1
  32. package/dist/analyzers/scorers/self-describing-scorer.d.ts +7 -4
  33. package/dist/analyzers/scorers/self-describing-scorer.d.ts.map +1 -1
  34. package/dist/analyzers/scorers/self-describing-scorer.js +17 -18
  35. package/dist/analyzers/scorers/self-describing-scorer.js.map +1 -1
  36. package/dist/artifacts/commit-hooks.d.ts +1 -1
  37. package/dist/artifacts/commit-hooks.d.ts.map +1 -1
  38. package/dist/artifacts/commit-hooks.js +2 -0
  39. package/dist/artifacts/commit-hooks.js.map +1 -1
  40. package/dist/cli/commands.d.ts +35 -1
  41. package/dist/cli/commands.d.ts.map +1 -1
  42. package/dist/cli/commands.js +109 -2
  43. package/dist/cli/commands.js.map +1 -1
  44. package/dist/cli/help.d.ts.map +1 -1
  45. package/dist/cli/help.js +7 -0
  46. package/dist/cli/help.js.map +1 -1
  47. package/dist/cli.d.ts.map +1 -1
  48. package/dist/cli.js +10 -1
  49. package/dist/cli.js.map +1 -1
  50. package/dist/disciplines/catalog.d.ts +16 -0
  51. package/dist/disciplines/catalog.d.ts.map +1 -0
  52. package/dist/disciplines/catalog.js +196 -0
  53. package/dist/disciplines/catalog.js.map +1 -0
  54. package/dist/disciplines/runner.d.ts +13 -0
  55. package/dist/disciplines/runner.d.ts.map +1 -0
  56. package/dist/disciplines/runner.js +35 -0
  57. package/dist/disciplines/runner.js.map +1 -0
  58. package/dist/registry/remote-gates.js +1 -1
  59. package/dist/registry/remote-gates.js.map +1 -1
  60. package/dist/sentinel/detect.d.ts +41 -0
  61. package/dist/sentinel/detect.d.ts.map +1 -0
  62. package/dist/sentinel/detect.js +122 -0
  63. package/dist/sentinel/detect.js.map +1 -0
  64. package/dist/sentinel/write.d.ts +54 -0
  65. package/dist/sentinel/write.d.ts.map +1 -0
  66. package/dist/sentinel/write.js +75 -0
  67. package/dist/sentinel/write.js.map +1 -0
  68. package/dist/shared/cnt-health.d.ts.map +1 -1
  69. package/dist/shared/cnt-health.js +12 -4
  70. package/dist/shared/cnt-health.js.map +1 -1
  71. package/dist/shared/config.d.ts +8 -0
  72. package/dist/shared/config.d.ts.map +1 -1
  73. package/dist/shared/config.js +23 -0
  74. package/dist/shared/config.js.map +1 -1
  75. package/dist/shared/project-gates-helpers.d.ts +9 -0
  76. package/dist/shared/project-gates-helpers.d.ts.map +1 -1
  77. package/dist/shared/project-gates-helpers.js +35 -0
  78. package/dist/shared/project-gates-helpers.js.map +1 -1
  79. package/dist/shared/result-utils.d.ts +27 -0
  80. package/dist/shared/result-utils.d.ts.map +1 -0
  81. package/dist/shared/result-utils.js +41 -0
  82. package/dist/shared/result-utils.js.map +1 -0
  83. package/dist/shared/types/config.d.ts +7 -1
  84. package/dist/shared/types/config.d.ts.map +1 -1
  85. package/dist/shared/types/gates.d.ts +28 -0
  86. package/dist/shared/types/gates.d.ts.map +1 -1
  87. package/dist/shared/types/project.d.ts +66 -0
  88. package/dist/shared/types/project.d.ts.map +1 -1
  89. package/dist/shared/types/project.js.map +1 -1
  90. package/dist/shared/types/verify.d.ts +51 -1
  91. package/dist/shared/types/verify.d.ts.map +1 -1
  92. package/dist/shared/types/verify.js +37 -1
  93. package/dist/shared/types/verify.js.map +1 -1
  94. package/dist/tools/advise-session-advisor.d.ts +16 -0
  95. package/dist/tools/advise-session-advisor.d.ts.map +1 -0
  96. package/dist/tools/advise-session-advisor.js +89 -0
  97. package/dist/tools/advise-session-advisor.js.map +1 -0
  98. package/dist/tools/advise-session-signals.d.ts +21 -0
  99. package/dist/tools/advise-session-signals.d.ts.map +1 -0
  100. package/dist/tools/advise-session-signals.js +113 -0
  101. package/dist/tools/advise-session-signals.js.map +1 -0
  102. package/dist/tools/advise-session.d.ts +22 -0
  103. package/dist/tools/advise-session.d.ts.map +1 -0
  104. package/dist/tools/advise-session.js +31 -0
  105. package/dist/tools/advise-session.js.map +1 -0
  106. package/dist/tools/change-request.d.ts +53 -0
  107. package/dist/tools/change-request.d.ts.map +1 -0
  108. package/dist/tools/change-request.js +375 -0
  109. package/dist/tools/change-request.js.map +1 -0
  110. package/dist/tools/check-cascade-contracts.d.ts +13 -0
  111. package/dist/tools/check-cascade-contracts.d.ts.map +1 -1
  112. package/dist/tools/check-cascade-contracts.js +73 -2
  113. package/dist/tools/check-cascade-contracts.js.map +1 -1
  114. package/dist/tools/check-cascade.d.ts +4 -3
  115. package/dist/tools/check-cascade.d.ts.map +1 -1
  116. package/dist/tools/check-cascade.js +30 -12
  117. package/dist/tools/check-cascade.js.map +1 -1
  118. package/dist/tools/check-spec-consistency.d.ts +25 -0
  119. package/dist/tools/check-spec-consistency.d.ts.map +1 -0
  120. package/dist/tools/check-spec-consistency.js +339 -0
  121. package/dist/tools/check-spec-consistency.js.map +1 -0
  122. package/dist/tools/check-t4.d.ts +54 -0
  123. package/dist/tools/check-t4.d.ts.map +1 -0
  124. package/dist/tools/check-t4.js +305 -0
  125. package/dist/tools/check-t4.js.map +1 -0
  126. package/dist/tools/close-cycle-helpers.d.ts +21 -2
  127. package/dist/tools/close-cycle-helpers.d.ts.map +1 -1
  128. package/dist/tools/close-cycle-helpers.js +66 -10
  129. package/dist/tools/close-cycle-helpers.js.map +1 -1
  130. package/dist/tools/close-cycle.d.ts +2 -2
  131. package/dist/tools/close-cycle.d.ts.map +1 -1
  132. package/dist/tools/close-cycle.js +342 -4
  133. package/dist/tools/close-cycle.js.map +1 -1
  134. package/dist/tools/consolidate-status.d.ts +112 -0
  135. package/dist/tools/consolidate-status.d.ts.map +1 -0
  136. package/dist/tools/consolidate-status.js +356 -0
  137. package/dist/tools/consolidate-status.js.map +1 -0
  138. package/dist/tools/executable-gates.d.ts +52 -0
  139. package/dist/tools/executable-gates.d.ts.map +1 -0
  140. package/dist/tools/executable-gates.js +332 -0
  141. package/dist/tools/executable-gates.js.map +1 -0
  142. package/dist/tools/forgecraft-dispatch-extended.d.ts.map +1 -1
  143. package/dist/tools/forgecraft-dispatch-extended.js +75 -0
  144. package/dist/tools/forgecraft-dispatch-extended.js.map +1 -1
  145. package/dist/tools/forgecraft-dispatch.d.ts.map +1 -1
  146. package/dist/tools/forgecraft-dispatch.js +21 -0
  147. package/dist/tools/forgecraft-dispatch.js.map +1 -1
  148. package/dist/tools/forgecraft-router.d.ts +8 -0
  149. package/dist/tools/forgecraft-router.d.ts.map +1 -1
  150. package/dist/tools/forgecraft-router.js +21 -1
  151. package/dist/tools/forgecraft-router.js.map +1 -1
  152. package/dist/tools/forgecraft-schema-params.d.ts +61 -4
  153. package/dist/tools/forgecraft-schema-params.d.ts.map +1 -1
  154. package/dist/tools/forgecraft-schema-params.js +95 -0
  155. package/dist/tools/forgecraft-schema-params.js.map +1 -1
  156. package/dist/tools/forgecraft-schema.d.ts +62 -5
  157. package/dist/tools/forgecraft-schema.d.ts.map +1 -1
  158. package/dist/tools/forgecraft-schema.js +24 -0
  159. package/dist/tools/forgecraft-schema.js.map +1 -1
  160. package/dist/tools/gate-violations.d.ts +59 -0
  161. package/dist/tools/gate-violations.d.ts.map +1 -0
  162. package/dist/tools/gate-violations.js +152 -0
  163. package/dist/tools/gate-violations.js.map +1 -0
  164. package/dist/tools/generate-adr.js +6 -6
  165. package/dist/tools/generate-adr.js.map +1 -1
  166. package/dist/tools/generate-env-probe.d.ts +49 -0
  167. package/dist/tools/generate-env-probe.d.ts.map +1 -0
  168. package/dist/tools/generate-env-probe.js +365 -0
  169. package/dist/tools/generate-env-probe.js.map +1 -0
  170. package/dist/tools/generate-harness.d.ts +52 -0
  171. package/dist/tools/generate-harness.d.ts.map +1 -0
  172. package/dist/tools/generate-harness.js +333 -0
  173. package/dist/tools/generate-harness.js.map +1 -0
  174. package/dist/tools/generate-roadmap.d.ts +1 -1
  175. package/dist/tools/generate-roadmap.d.ts.map +1 -1
  176. package/dist/tools/generate-roadmap.js +38 -4
  177. package/dist/tools/generate-roadmap.js.map +1 -1
  178. package/dist/tools/generate-session-prompt.d.ts +4 -4
  179. package/dist/tools/generate-session-prompt.d.ts.map +1 -1
  180. package/dist/tools/generate-session-prompt.js +66 -16
  181. package/dist/tools/generate-session-prompt.js.map +1 -1
  182. package/dist/tools/generate-slo-probe.d.ts +53 -0
  183. package/dist/tools/generate-slo-probe.d.ts.map +1 -0
  184. package/dist/tools/generate-slo-probe.js +366 -0
  185. package/dist/tools/generate-slo-probe.js.map +1 -0
  186. package/dist/tools/layer-status-gates.d.ts +24 -0
  187. package/dist/tools/layer-status-gates.d.ts.map +1 -0
  188. package/dist/tools/layer-status-gates.js +151 -0
  189. package/dist/tools/layer-status-gates.js.map +1 -0
  190. package/dist/tools/layer-status.d.ts +133 -0
  191. package/dist/tools/layer-status.d.ts.map +1 -0
  192. package/dist/tools/layer-status.js +593 -0
  193. package/dist/tools/layer-status.js.map +1 -0
  194. package/dist/tools/postcondition-coverage.d.ts +57 -0
  195. package/dist/tools/postcondition-coverage.d.ts.map +1 -0
  196. package/dist/tools/postcondition-coverage.js +256 -0
  197. package/dist/tools/postcondition-coverage.js.map +1 -0
  198. package/dist/tools/probe-runners.d.ts +21 -0
  199. package/dist/tools/probe-runners.d.ts.map +1 -0
  200. package/dist/tools/probe-runners.js +246 -0
  201. package/dist/tools/probe-runners.js.map +1 -0
  202. package/dist/tools/probe-templates.d.ts +27 -0
  203. package/dist/tools/probe-templates.d.ts.map +1 -0
  204. package/dist/tools/probe-templates.js +279 -0
  205. package/dist/tools/probe-templates.js.map +1 -0
  206. package/dist/tools/propose-session.d.ts +28 -0
  207. package/dist/tools/propose-session.d.ts.map +1 -0
  208. package/dist/tools/propose-session.js +333 -0
  209. package/dist/tools/propose-session.js.map +1 -0
  210. package/dist/tools/roadmap-builder.d.ts +34 -1
  211. package/dist/tools/roadmap-builder.d.ts.map +1 -1
  212. package/dist/tools/roadmap-builder.js +153 -11
  213. package/dist/tools/roadmap-builder.js.map +1 -1
  214. package/dist/tools/run-env-probe.d.ts +57 -0
  215. package/dist/tools/run-env-probe.d.ts.map +1 -0
  216. package/dist/tools/run-env-probe.js +270 -0
  217. package/dist/tools/run-env-probe.js.map +1 -0
  218. package/dist/tools/run-harness.d.ts +52 -0
  219. package/dist/tools/run-harness.d.ts.map +1 -0
  220. package/dist/tools/run-harness.js +279 -0
  221. package/dist/tools/run-harness.js.map +1 -0
  222. package/dist/tools/run-slo-probe.d.ts +50 -0
  223. package/dist/tools/run-slo-probe.d.ts.map +1 -0
  224. package/dist/tools/run-slo-probe.js +281 -0
  225. package/dist/tools/run-slo-probe.js.map +1 -0
  226. package/dist/tools/scaffold-writer.d.ts.map +1 -1
  227. package/dist/tools/scaffold-writer.js +4 -0
  228. package/dist/tools/scaffold-writer.js.map +1 -1
  229. package/dist/tools/session-prompt-builders.d.ts +20 -0
  230. package/dist/tools/session-prompt-builders.d.ts.map +1 -1
  231. package/dist/tools/session-prompt-builders.js +111 -14
  232. package/dist/tools/session-prompt-builders.js.map +1 -1
  233. package/dist/tools/session-prompt-sections.d.ts +4 -2
  234. package/dist/tools/session-prompt-sections.d.ts.map +1 -1
  235. package/dist/tools/session-prompt-sections.js +22 -10
  236. package/dist/tools/session-prompt-sections.js.map +1 -1
  237. package/dist/tools/setup-monitoring.d.ts +41 -0
  238. package/dist/tools/setup-monitoring.d.ts.map +1 -0
  239. package/dist/tools/setup-monitoring.js +364 -0
  240. package/dist/tools/setup-monitoring.js.map +1 -0
  241. package/dist/tools/verify-formatter.d.ts.map +1 -1
  242. package/dist/tools/verify-formatter.js +15 -1
  243. package/dist/tools/verify-formatter.js.map +1 -1
  244. package/dist/tools/verify.d.ts.map +1 -1
  245. package/dist/tools/verify.js +3 -0
  246. package/dist/tools/verify.js.map +1 -1
  247. package/package.json +11 -2
  248. package/templates/api/harness/uc-template.hurl +20 -0
  249. package/templates/docs-manifest.yaml +224 -0
  250. package/templates/game/harness/uc-template.sim.ts +29 -0
  251. package/templates/universal/claude-md-blocks/layer-navigation.md +20 -0
  252. package/templates/universal/claude-md-blocks/nfr-contracts.md +22 -0
  253. package/templates/universal/hooks.yaml +212 -20
  254. package/templates/web-react/harness/uc-template.spec.ts +35 -0
@@ -90,6 +90,10 @@ hooks:
90
90
  echo "$STAGED" | grep '\.py$' | xargs -r isort --quiet 2>/dev/null
91
91
  # TypeScript/JavaScript
92
92
  echo "$STAGED" | grep '\.\(ts\|tsx\|js\|jsx\)$' | xargs -r npx prettier --write 2>/dev/null
93
+ # Rust
94
+ if echo "$STAGED" | grep -q '\.rs$' && [ -f "Cargo.toml" ]; then
95
+ cargo fmt 2>/dev/null
96
+ fi
93
97
  # Re-stage formatted files
94
98
  echo "$STAGED" | xargs -r git add
95
99
 
@@ -99,6 +103,20 @@ hooks:
99
103
  filename: pre-commit-secrets.sh
100
104
  script: |
101
105
  #!/bin/bash
106
+ _fc_write_violation() {
107
+ local hook_name="$1" severity="${2:-error}" message="$3"
108
+ local repo_root
109
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0
110
+ local dir="$repo_root/.forgecraft"
111
+ mkdir -p "$dir" 2>/dev/null || return 0
112
+ local ts
113
+ ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || printf "unknown")"
114
+ local esc_msg
115
+ esc_msg="$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')"
116
+ printf '{"hook":"%s","severity":"%s","message":"%s","timestamp":"%s"}\n' \
117
+ "$hook_name" "$severity" "$esc_msg" "$ts" \
118
+ >> "$dir/gate-violations.jsonl" 2>/dev/null || true
119
+ }
102
120
  PATTERNS=(
103
121
  'AKIA[0-9A-Z]{16}'
104
122
  'password\s*=\s*["\x27][^"\x27]+'
@@ -111,6 +129,7 @@ hooks:
111
129
  for pattern in "${PATTERNS[@]}"; do
112
130
  if grep -qE "$pattern" "$file" 2>/dev/null; then
113
131
  echo "❌ Potential secret found in $file matching pattern"
132
+ _fc_write_violation "pre-commit-secrets" "error" "Potential secrets detected in staged files — review output above"
114
133
  exit 1
115
134
  fi
116
135
  done
@@ -122,6 +141,20 @@ hooks:
122
141
  filename: pre-commit-compile.sh
123
142
  script: |
124
143
  #!/bin/bash
144
+ _fc_write_violation() {
145
+ local hook_name="$1" severity="${2:-error}" message="$3"
146
+ local repo_root
147
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0
148
+ local dir="$repo_root/.forgecraft"
149
+ mkdir -p "$dir" 2>/dev/null || return 0
150
+ local ts
151
+ ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || printf "unknown")"
152
+ local esc_msg
153
+ esc_msg="$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')"
154
+ printf '{"hook":"%s","severity":"%s","message":"%s","timestamp":"%s"}\n' \
155
+ "$hook_name" "$severity" "$esc_msg" "$ts" \
156
+ >> "$dir/gate-violations.jsonl" 2>/dev/null || true
157
+ }
125
158
  echo "🔨 Running build check..."
126
159
  if [ -f "pyproject.toml" ] || [ -f "setup.py" ] || [ -f "requirements.txt" ]; then
127
160
  STAGED_PY=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$')
@@ -130,6 +163,7 @@ hooks:
130
163
  python -m py_compile "$file" 2>&1
131
164
  if [ $? -ne 0 ]; then
132
165
  echo "❌ Syntax error in $file"
166
+ _fc_write_violation "pre-commit-compile" "error" "TypeScript compilation failed — run tsc --noEmit to see errors"
133
167
  exit 1
134
168
  fi
135
169
  done
@@ -140,12 +174,62 @@ hooks:
140
174
  npx tsc --noEmit 2>&1
141
175
  if [ $? -ne 0 ]; then
142
176
  echo "❌ TypeScript compilation failed."
177
+ _fc_write_violation "pre-commit-compile" "error" "TypeScript compilation failed — run tsc --noEmit to see errors"
143
178
  exit 1
144
179
  fi
145
180
  echo " ✅ TypeScript compilation OK"
146
181
  fi
182
+ if [ -f "Cargo.toml" ]; then
183
+ STAGED_RS=$(git diff --cached --name-only --diff-filter=ACM | grep '\.rs$')
184
+ if [ -n "$STAGED_RS" ]; then
185
+ cargo check --quiet 2>&1
186
+ if [ $? -ne 0 ]; then
187
+ echo "❌ Rust cargo check failed."
188
+ _fc_write_violation "pre-commit-compile" "error" "Rust cargo check failed — run cargo check to see errors"
189
+ exit 1
190
+ fi
191
+ echo " ✅ Rust cargo check OK"
192
+ fi
193
+ fi
147
194
  echo "🔨 Build check passed"
148
195
 
196
+ - name: cargo-clippy
197
+ trigger: pre-commit
198
+ description: "Run cargo clippy on Rust projects — catches bugs, style issues, and common mistakes beyond cargo check"
199
+ filename: pre-commit-clippy.sh
200
+ script: |
201
+ #!/bin/bash
202
+ _fc_write_violation() {
203
+ local hook_name="$1" severity="${2:-error}" message="$3"
204
+ local repo_root
205
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0
206
+ local dir="$repo_root/.forgecraft"
207
+ mkdir -p "$dir" 2>/dev/null || return 0
208
+ local ts
209
+ ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || printf "unknown")"
210
+ local esc_msg
211
+ esc_msg="$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')"
212
+ printf '{"hook":"%s","severity":"%s","message":"%s","timestamp":"%s"}\n' \
213
+ "$hook_name" "$severity" "$esc_msg" "$ts" \
214
+ >> "$dir/gate-violations.jsonl" 2>/dev/null || true
215
+ }
216
+ if [ ! -f "Cargo.toml" ]; then
217
+ exit 0
218
+ fi
219
+ STAGED_RS=$(git diff --cached --name-only --diff-filter=ACM | grep '\.rs$')
220
+ if [ -z "$STAGED_RS" ]; then
221
+ exit 0
222
+ fi
223
+ echo "🦀 Running cargo clippy..."
224
+ cargo clippy --all-targets --all-features 2>&1
225
+ if [ $? -ne 0 ]; then
226
+ echo "❌ cargo clippy failed — fix lint errors before committing."
227
+ echo " Run: cargo clippy --all-targets --all-features"
228
+ _fc_write_violation "pre-commit-clippy" "error" "Cargo clippy violations found — run cargo clippy to see details"
229
+ exit 1
230
+ fi
231
+ echo " ✅ cargo clippy passed"
232
+
149
233
  - name: tdd-phase-gate
150
234
  trigger: pre-commit
151
235
  description: "RED gate: test-only commits must have failing tests; warn on implementation without tests"
@@ -180,6 +264,8 @@ hooks:
180
264
  elif grep -q '"jest"' package.json 2>/dev/null; then
181
265
  RUN_CMD="npx jest --passWithNoTests"
182
266
  fi
267
+ elif [ -f "Cargo.toml" ]; then
268
+ RUN_CMD="cargo test"
183
269
  elif [ -f "pytest.ini" ] || [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
184
270
  RUN_CMD="python -m pytest"
185
271
  fi
@@ -270,6 +356,14 @@ hooks:
270
356
  echo " ✅ Python tests passed"
271
357
  fi
272
358
  fi
359
+ if [ -f "Cargo.toml" ]; then
360
+ cargo test --quiet 2>&1
361
+ if [ $? -ne 0 ]; then
362
+ echo "❌ Rust tests failed."
363
+ exit 1
364
+ fi
365
+ echo " ✅ Rust tests passed"
366
+ fi
273
367
  echo "🧪 All tests passed"
274
368
 
275
369
  - name: coverage-gate
@@ -278,6 +372,20 @@ hooks:
278
372
  filename: pre-commit-coverage.sh
279
373
  script: |
280
374
  #!/bin/bash
375
+ _fc_write_violation() {
376
+ local hook_name="$1" severity="${2:-error}" message="$3"
377
+ local repo_root
378
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0
379
+ local dir="$repo_root/.forgecraft"
380
+ mkdir -p "$dir" 2>/dev/null || return 0
381
+ local ts
382
+ ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || printf "unknown")"
383
+ local esc_msg
384
+ esc_msg="$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')"
385
+ printf '{"hook":"%s","severity":"%s","message":"%s","timestamp":"%s"}\n' \
386
+ "$hook_name" "$severity" "$esc_msg" "$ts" \
387
+ >> "$dir/gate-violations.jsonl" 2>/dev/null || true
388
+ }
281
389
  # ──────────────────────────────────────────────────────────────────────
282
390
  # Pre-Commit Hook: Coverage Gate
283
391
  #
@@ -317,8 +425,30 @@ hooks:
317
425
  # ── Run coverage ───────────────────────────────────────────────────────
318
426
  echo "📊 Running coverage gate (src/ files staged)..."
319
427
 
320
- if [ ! -f "package.json" ]; then
321
- echo " ⚠️ No package.json found — skipping coverage check."
428
+ if [ ! -f "package.json" ] && [ ! -f "pyproject.toml" ] && [ ! -f "setup.py" ]; then
429
+ if [ -f "Cargo.toml" ]; then
430
+ # Rust: run tests; use cargo-tarpaulin for coverage if available
431
+ if command -v cargo-tarpaulin &> /dev/null; then
432
+ cargo tarpaulin --out Stdout --fail-under {{coverage_minimum | default: 80}} 2>&1
433
+ if [ $? -ne 0 ]; then
434
+ echo "❌ Coverage gate failed — below {{coverage_minimum | default: 80}}%."
435
+ echo " Run: cargo tarpaulin --out Html"
436
+ _fc_write_violation "pre-commit-coverage" "error" "Coverage gate failed — run npm test --coverage to see report"
437
+ exit 1
438
+ fi
439
+ echo " ✅ Rust coverage gate passed"
440
+ else
441
+ cargo test --quiet 2>&1
442
+ if [ $? -ne 0 ]; then
443
+ echo "❌ Rust tests failed."
444
+ _fc_write_violation "pre-commit-coverage" "error" "Coverage gate failed — run npm test --coverage to see report"
445
+ exit 1
446
+ fi
447
+ echo " ✅ Rust tests passed (install cargo-tarpaulin for coverage enforcement)"
448
+ fi
449
+ else
450
+ echo " ⚠️ No supported build system found — skipping coverage check."
451
+ fi
322
452
  exit 0
323
453
  fi
324
454
 
@@ -332,6 +462,7 @@ hooks:
332
462
  echo "❌ Coverage gate failed — thresholds not met."
333
463
  echo " Run 'npx vitest run --coverage' locally to see the full report."
334
464
  echo " Add tests until coverage meets the configured minimums."
465
+ _fc_write_violation "pre-commit-coverage" "error" "Coverage gate failed — run npm test --coverage to see report"
335
466
  exit 1
336
467
  fi
337
468
  echo " ✅ Coverage gate passed"
@@ -345,17 +476,19 @@ hooks:
345
476
  --silent 2>&1
346
477
  if [ $? -ne 0 ]; then
347
478
  echo "❌ Coverage gate failed — thresholds not met."
479
+ _fc_write_violation "pre-commit-coverage" "error" "Coverage gate failed — run npm test --coverage to see report"
348
480
  exit 1
349
481
  fi
350
482
  echo " ✅ Coverage gate passed"
351
483
  exit 0
352
484
  fi
353
485
 
354
- if [ -f "pyproject.toml" ] || [ -f "setup.py" ]; then
486
+ if [ -f "pyproject.toml" ]|| [ -f "setup.py" ]; then
355
487
  if command -v pytest &> /dev/null; then
356
488
  pytest --tb=no --quiet --cov=src --cov-fail-under={{coverage_minimum | default: 80}} 2>&1
357
489
  if [ $? -ne 0 ]; then
358
490
  echo "❌ Coverage gate failed — below {{coverage_minimum | default: 80}}%."
491
+ _fc_write_violation "pre-commit-coverage" "error" "Coverage gate failed — run npm test --coverage to see report"
359
492
  exit 1
360
493
  fi
361
494
  echo " ✅ Coverage gate passed"
@@ -376,8 +509,22 @@ hooks:
376
509
  - .env vs .env.example drift (warns on missing variables)
377
510
  script: |
378
511
  #!/bin/bash
512
+ _fc_write_violation() {
513
+ local hook_name="$1" severity="${2:-error}" message="$3"
514
+ local repo_root
515
+ repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || return 0
516
+ local dir="$repo_root/.forgecraft"
517
+ mkdir -p "$dir" 2>/dev/null || return 0
518
+ local ts
519
+ ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || printf "unknown")"
520
+ local esc_msg
521
+ esc_msg="$(printf '%s' "$message" | sed 's/\\/\\\\/g; s/"/\\"/g')"
522
+ printf '{"hook":"%s","severity":"%s","message":"%s","timestamp":"%s"}\n' \
523
+ "$hook_name" "$severity" "$esc_msg" "$ts" \
524
+ >> "$dir/gate-violations.jsonl" 2>/dev/null || true
525
+ }
379
526
  STAGED=$(git diff --cached --name-only --diff-filter=ACM)
380
- SOURCE_FILES=$(echo "$STAGED" | grep -E '\.(py|ts|tsx|js|jsx)$' | grep -vE '(test_|\.test\.|\.spec\.|__tests__|tests/|fixtures/|mock|conftest)')
527
+ SOURCE_FILES=$(echo "$STAGED" | grep -E '\.(py|ts|tsx|js|jsx|rs)$' | grep -vE '(test_|\.test\.|\.spec\.|__tests__|tests/|fixtures/|mock|conftest|_test\.rs)')
381
528
  if [ -z "$SOURCE_FILES" ]; then exit 0; fi
382
529
  VIOLATIONS=0
383
530
  WARNINGS=0
@@ -443,10 +590,39 @@ hooks:
443
590
  echo " ⚠️ $file — $LINE_COUNT lines (max {{max_file_length | default: 300}})"
444
591
  WARNINGS=$((WARNINGS + 1))
445
592
  fi
446
- done
593
+ # Rust-specific anti-patterns
594
+ if echo "$file" | grep -q '\.rs$'; then
595
+ if ! is_excepted "rust/unwrap" "$file"; then
596
+ if grep -nE '\.unwrap\(\)' "$file" > /tmp/violations 2>/dev/null; then
597
+ if [ -s /tmp/violations ]; then
598
+ echo " ⚠️ $file — .unwrap() in production code — use ? or explicit error handling"
599
+ WARNINGS=$((WARNINGS + 1))
600
+ fi
601
+ fi
602
+ fi
603
+ if grep -nE '\btodo!\(|\bunimplemented!\(' "$file" > /tmp/violations 2>/dev/null; then
604
+ if [ -s /tmp/violations ]; then
605
+ echo " ❌ $file — todo!/unimplemented! in production code"
606
+ VIOLATIONS=$((VIOLATIONS + 1))
607
+ fi
608
+ fi
609
+ if grep -nE '^[[:space:]]*#\[allow\(dead_code\)\]' "$file" > /tmp/violations 2>/dev/null; then
610
+ if [ -s /tmp/violations ]; then
611
+ echo " ⚠️ $file — #[allow(dead_code)] suppression — delete orphaned code instead"
612
+ WARNINGS=$((WARNINGS + 1))
613
+ fi
614
+ fi
615
+ if grep -nE '^[[:space:]]*unsafe[[:space:]]*\{' "$file" > /tmp/violations 2>/dev/null; then
616
+ if [ -s /tmp/violations ]; then
617
+ echo " ⚠️ $file — unsafe block present — requires explicit justification comment"
618
+ WARNINGS=$((WARNINGS + 1))
619
+ fi
620
+ fi
621
+ fi
447
622
  rm -f /tmp/violations
448
623
  if [ $VIOLATIONS -gt 0 ]; then
449
624
  echo "❌ $VIOLATIONS violation(s) found — commit blocked."
625
+ _fc_write_violation "pre-commit-anti-patterns" "error" "Anti-patterns detected in staged files — see output above"
450
626
  exit 1
451
627
  fi
452
628
  if [ $WARNINGS -gt 0 ]; then
@@ -462,26 +638,42 @@ hooks:
462
638
  #!/bin/bash
463
639
  MAX_LENGTH={{max_function_length | default: 50}}
464
640
  STAGED=$(git diff --cached --name-only --diff-filter=ACM)
465
- SOURCE_FILES=$(echo "$STAGED" | grep -E '\.(ts|tsx|js|jsx)$' | grep -vE '(\.test\.|\.spec\.|__tests__|tests/)')
641
+ SOURCE_FILES=$(echo "$STAGED" | grep -E '\.(ts|tsx|js|jsx|rs)$' | grep -vE '(\.test\.|\.spec\.|__tests__|tests/|_test\.rs)')
466
642
  if [ -z "$SOURCE_FILES" ]; then exit 0; fi
467
643
  WARNINGS=0
468
644
  for file in $SOURCE_FILES; do
469
- # Heuristic: find function/method declarations and count lines to next declaration or closing brace
470
- awk -v max="$MAX_LENGTH" -v fname="$file" '
471
- /^[[:space:]]*(export )?(async )?(function |const [a-zA-Z]+ = (async )?\(|[a-zA-Z]+\(.*\) \{|[a-zA-Z]+\(.*\): )/ {
472
- if (start > 0 && NR - start > max) {
473
- printf " ⚠️ %s:%d — function starting here is %d lines (max %d)\n", fname, start, NR - start, max
474
- warnings++
645
+ # Heuristic: find function/method declarations and count lines to next declaration
646
+ if echo "$file" | grep -q '\.rs$'; then
647
+ awk -v max="$MAX_LENGTH" -v fname="$file" '
648
+ /^[[:space:]]*(pub )?(async )?fn [a-zA-Z_]/ {
649
+ if (start > 0 && NR - start > max) {
650
+ printf " ⚠️ %s:%d — fn starting here is %d lines (max %d)\n", fname, start, NR - start, max
651
+ }
652
+ start = NR
475
653
  }
476
- start = NR
477
- }
478
- END {
479
- if (start > 0 && NR - start > max) {
480
- printf " ⚠️ %s:%d — function starting here is %d lines (max %d)\n", fname, start, NR - start, max
481
- warnings++
654
+ END {
655
+ if (start > 0 && NR - start > max) {
656
+ printf " ⚠️ %s:%d — fn starting here is %d lines (max %d)\n", fname, start, NR - start, max
657
+ }
482
658
  }
483
- }
484
- ' "$file"
659
+ ' "$file"
660
+ else
661
+ awk -v max="$MAX_LENGTH" -v fname="$file" '
662
+ /^[[:space:]]*(export )?(async )?(function |const [a-zA-Z]+ = (async )?\(|[a-zA-Z]+\(.*\) \{|[a-zA-Z]+\(.*\): )/ {
663
+ if (start > 0 && NR - start > max) {
664
+ printf " ⚠️ %s:%d — function starting here is %d lines (max %d)\n", fname, start, NR - start, max
665
+ warnings++
666
+ }
667
+ start = NR
668
+ }
669
+ END {
670
+ if (start > 0 && NR - start > max) {
671
+ printf " ⚠️ %s:%d — function starting here is %d lines (max %d)\n", fname, start, NR - start, max
672
+ warnings++
673
+ }
674
+ }
675
+ ' "$file"
676
+ fi
485
677
  WARNINGS=$((WARNINGS + $?))
486
678
  done
487
679
  # Warning only — does not block commit since bash heuristics aren't perfect
@@ -0,0 +1,35 @@
1
+ import { test, expect } from "@playwright/test";
2
+
3
+ /**
4
+ * L2 Harness: {{uc_id}} — {{uc_title}}
5
+ *
6
+ * Actor: {{actor}}
7
+ * Precondition: {{precondition}}
8
+ * Postcondition: {{postcondition}}
9
+ *
10
+ * Behavioral harness probe — verifies use case postconditions in the running UI.
11
+ * Failure = specification violation. Regenerate from spec; don't patch the code.
12
+ */
13
+ test.describe("{{uc_id}}: {{uc_title}}", () => {
14
+ test.beforeEach(async ({ page }) => {
15
+ // Set up precondition: {{precondition}}
16
+ await page.goto("http://localhost:3000");
17
+ });
18
+
19
+ test("postcondition: {{postcondition}}", async ({ page }) => {
20
+ // TODO: implement UC main flow
21
+ // {{step_1}}
22
+ // {{step_2}}
23
+
24
+ // Assert postcondition
25
+ // await expect(page.locator('...')).toBeVisible();
26
+ throw new Error(
27
+ "Not implemented: add UI interactions following {{uc_id}} main flow",
28
+ );
29
+ });
30
+
31
+ test("error case: [fill in error case]", async ({ page }) => {
32
+ // TODO: test error paths from use case
33
+ throw new Error("Not implemented: add error path for {{uc_id}}");
34
+ });
35
+ });