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
@@ -0,0 +1,279 @@
1
+ /**
2
+ * Probe content template generators for generate_harness.
3
+ * Each function returns the file content for a given probe type.
4
+ */
5
+ // ── Template generators ───────────────────────────────────────────────
6
+ export function generatePlaywrightProbe(ucId, title, precondition, postcondition, steps, scenario = "happy") {
7
+ const stepComments = steps.length > 0
8
+ ? steps.map((s, i) => ` // Step ${i + 1}: ${s}`).join("\n")
9
+ : ` // TODO: implement ${ucId} main flow steps`;
10
+ return `import { test, expect } from '@playwright/test';
11
+
12
+ /**
13
+ * L2 Harness: ${ucId} — ${title} [${scenario}]
14
+ * Precondition: ${precondition}
15
+ * Postcondition: ${postcondition}
16
+ */
17
+ test.describe('${ucId}: ${title}', () => {
18
+ test('postcondition: ${postcondition}', async ({ page }) => {
19
+ ${stepComments}
20
+ throw new Error('Probe not yet implemented — fill in the ${ucId} flow');
21
+ });
22
+ });
23
+ `;
24
+ }
25
+ export function generateHurlProbe(ucId, title, precondition, postcondition, steps, scenario = "happy") {
26
+ const stepComments = steps.length > 0
27
+ ? steps.map((s, i) => `# Step ${i + 1}: ${s}`).join("\n")
28
+ : `# TODO: implement ${ucId} main flow steps`;
29
+ return `# L2 Harness: ${ucId} — ${title} [${scenario}]
30
+ # Precondition: ${precondition}
31
+ # Postcondition: ${postcondition}
32
+ ${stepComments}
33
+ POST http://{{host}}/api/endpoint
34
+ Content-Type: application/json
35
+ { "field": "value" }
36
+ HTTP 200
37
+ `;
38
+ }
39
+ export function generateGraphqlHurlProbe(ucId, title, postcondition, scenario = "happy") {
40
+ return `# L2 Harness: ${ucId} — ${title} [${scenario}]
41
+ # GraphQL postcondition: ${postcondition}
42
+ POST http://{{host}}/graphql
43
+ Content-Type: application/json
44
+ { "query": "query { __typename }", "variables": {} }
45
+ HTTP 200
46
+ [Asserts]
47
+ jsonpath "$.data" exists
48
+ `;
49
+ }
50
+ export function generateShProbe(ucId, title, postcondition, probeType, scenario = "happy") {
51
+ if (probeType === "mcp_call") {
52
+ return `#!/usr/bin/env bash
53
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
54
+ # Verifies MCP action postconditions
55
+ set -euo pipefail
56
+ echo "TODO: implement ${ucId} MCP probe"
57
+ exit 1
58
+ `;
59
+ }
60
+ return `#!/usr/bin/env bash
61
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
62
+ # Postcondition: ${postcondition}
63
+ set -euo pipefail
64
+ echo "PASS: ${ucId} postcondition verified"
65
+ `;
66
+ }
67
+ export function generateDbShProbe(ucId, title, postcondition, scenario = "happy") {
68
+ return `#!/usr/bin/env bash
69
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
70
+ # DB postcondition: ${postcondition}
71
+ set -euo pipefail
72
+ DB_URL="\${DATABASE_URL:-}"
73
+ if [ -z "$DB_URL" ]; then
74
+ echo "SKIP: DATABASE_URL not set — cannot verify DB postcondition"
75
+ exit 0
76
+ fi
77
+ echo "TODO: implement DB postcondition check for ${ucId} [${scenario}]"
78
+ exit 1
79
+ `;
80
+ }
81
+ export function generateMqShProbe(ucId, title, postcondition, scenario = "happy") {
82
+ return `#!/usr/bin/env bash
83
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
84
+ # Queue postcondition: ${postcondition}
85
+ set -euo pipefail
86
+ BROKER="\${KAFKA_BROKER:?KAFKA_BROKER is required}"
87
+ TOPIC="\${QUEUE_TOPIC:-}"
88
+ if command -v kcat &>/dev/null; then
89
+ echo "TODO: assert event published to $TOPIC via kcat"
90
+ exit 1
91
+ elif command -v rabbitmqadmin &>/dev/null; then
92
+ echo "TODO: rabbitmqadmin get queue={{queue_name}}"
93
+ exit 1
94
+ else
95
+ echo "SKIP: no queue inspection tool found (kcat, rabbitmqadmin)"
96
+ exit 0
97
+ fi
98
+ `;
99
+ }
100
+ export function generateK6Probe(ucId, title, postcondition, scenario = "happy") {
101
+ return `// L2 Harness: ${ucId} — ${title} [${scenario}]
102
+ // NFR contract: ${postcondition}
103
+ import http from 'k6/http';
104
+ import { check, sleep } from 'k6';
105
+ export const options = {
106
+ vus: 10,
107
+ duration: '30s',
108
+ thresholds: {
109
+ http_req_duration: ['p(99)<500'],
110
+ http_req_failed: ['rate<0.01'],
111
+ },
112
+ };
113
+ export default function () {
114
+ const res = http.get(__ENV.API_URL + '/api/health');
115
+ check(res, { 'status is 200': (r) => r.status === 200 });
116
+ sleep(1);
117
+ }
118
+ `;
119
+ }
120
+ export function generateWsShProbe(ucId, title, postcondition, scenario = "happy") {
121
+ return `#!/usr/bin/env bash
122
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
123
+ # WebSocket postcondition: ${postcondition}
124
+ set -euo pipefail
125
+ WS_URL="\${WS_URL:?WS_URL is required}"
126
+ if command -v wscat &>/dev/null; then
127
+ echo "TODO: assert WebSocket message for ${ucId} [${scenario}]"
128
+ exit 1
129
+ else
130
+ echo "SKIP: wscat not found (npm install -g wscat)"
131
+ exit 0
132
+ fi
133
+ `;
134
+ }
135
+ export function generateLogShProbe(ucId, title, _postcondition, scenario = "happy") {
136
+ return `#!/usr/bin/env bash
137
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
138
+ # Log postcondition: structured log entry must appear after use case execution
139
+ set -euo pipefail
140
+ LOG_FILE="\${LOG_FILE:-/tmp/app.log}"
141
+ EXPECTED_PATTERN="\${LOG_PATTERN:-${ucId}}"
142
+ echo "TODO: implement log assertion for ${ucId} [${scenario}]"
143
+ exit 1
144
+ `;
145
+ }
146
+ export function generateA11yProbe(ucId, title) {
147
+ return `import { test, expect } from '@playwright/test';
148
+ import AxeBuilder from '@axe-core/playwright';
149
+
150
+ /**
151
+ * L2 Harness: ${ucId} — ${title} [Accessibility]
152
+ * Contract: Zero WCAG 2.1 AA violations on the primary use case page.
153
+ */
154
+ test.describe('${ucId} Accessibility: ${title}', () => {
155
+ test('WCAG 2.1 AA — zero violations', async ({ page }) => {
156
+ await page.goto(process.env['BASE_URL'] ?? '');
157
+ const results = await new AxeBuilder({ page })
158
+ .withTags(['wcag2a', 'wcag2aa', 'wcag21aa'])
159
+ .analyze();
160
+ expect(results.violations).toEqual([]);
161
+ });
162
+ });
163
+ `;
164
+ }
165
+ export function generateConsumerContractProbe(ucId, title) {
166
+ return `import { PactV3 } from '@pact-foundation/pact';
167
+ import { join } from 'path';
168
+
169
+ /**
170
+ * L2 Harness: ${ucId} — ${title} [Consumer Contract]
171
+ */
172
+ const provider = new PactV3({
173
+ consumer: '{{consumer_name}}',
174
+ provider: '{{provider_name}}',
175
+ dir: join(__dirname, '../../pacts'),
176
+ });
177
+ describe('${ucId} Consumer Contract', () => {
178
+ it('has a valid interaction for ${title}', async () => {
179
+ throw new Error('Not implemented: define the Pact interaction for ${ucId}');
180
+ });
181
+ });
182
+ `;
183
+ }
184
+ export function generateProviderContractProbe(ucId, title) {
185
+ return `import { Verifier } from '@pact-foundation/pact';
186
+ import { join } from 'path';
187
+
188
+ /**
189
+ * L2 Harness: ${ucId} — ${title} [Provider Verification]
190
+ */
191
+ describe('${ucId} Provider Verification', () => {
192
+ it('satisfies all consumer contracts', async () => {
193
+ return new Verifier({
194
+ provider: '{{provider_name}}',
195
+ providerBaseUrl: process.env.PROVIDER_URL ?? 'http://localhost:3000',
196
+ pactUrls: [join(__dirname, '../../pacts')],
197
+ }).verifyProvider();
198
+ });
199
+ });
200
+ `;
201
+ }
202
+ export function generateGrpcShProbe(ucId, title, postcondition, scenario = "happy") {
203
+ return `#!/usr/bin/env bash
204
+ # L2 Harness: ${ucId} — ${title} [${scenario}]
205
+ # gRPC postcondition: ${postcondition}
206
+ set -euo pipefail
207
+ GRPC_HOST="\${GRPC_HOST:?GRPC_HOST is required}"
208
+ if ! command -v grpcurl &>/dev/null; then
209
+ echo "SKIP: grpcurl not found (brew install grpcurl)"
210
+ exit 0
211
+ fi
212
+ echo "TODO: implement gRPC probe for ${ucId} [${scenario}]"
213
+ exit 1
214
+ `;
215
+ }
216
+ export function generateZapShProbe(ucId, title) {
217
+ return `#!/usr/bin/env bash
218
+ # L2 Harness: ${ucId} — ${title} [Security Scan]
219
+ # Contract: zero HIGH/CRITICAL vulnerabilities on the use case endpoint.
220
+ set -euo pipefail
221
+ TARGET_URL="\${TARGET_URL:?TARGET_URL is required}"
222
+ if ! command -v docker &>/dev/null; then
223
+ echo "SKIP: docker not found — cannot run OWASP ZAP scan"
224
+ exit 0
225
+ fi
226
+ docker run --rm -t owasp/zap2docker-stable \\
227
+ zap-baseline.py -t "$TARGET_URL" -l WARN --exit-code true 2>&1 || {
228
+ echo "FAIL: ZAP found vulnerabilities on $TARGET_URL"
229
+ exit 1
230
+ }
231
+ `;
232
+ }
233
+ export function generateSimProbe(ucId, title) {
234
+ return `/**
235
+ * L2 Harness: ${ucId} — ${title}
236
+ * Headless simulation probe — verifies behavioral invariants without rendering.
237
+ */
238
+ throw new Error('Probe not yet implemented — implement the ${ucId} simulation scenario');
239
+ `;
240
+ }
241
+ export function generateProbeContent(ucId, title, probeType, details, scenario = "happy") {
242
+ switch (probeType) {
243
+ case "playwright":
244
+ return generatePlaywrightProbe(ucId, title, details.precondition, details.postcondition, details.steps, scenario);
245
+ case "api_call":
246
+ case "hurl":
247
+ return generateHurlProbe(ucId, title, details.precondition, details.postcondition, details.steps, scenario);
248
+ case "graphql":
249
+ return generateGraphqlHurlProbe(ucId, title, details.postcondition, scenario);
250
+ case "headless_sim":
251
+ return generateSimProbe(ucId, title);
252
+ case "db_query":
253
+ return generateDbShProbe(ucId, title, details.postcondition, scenario);
254
+ case "message_queue":
255
+ return generateMqShProbe(ucId, title, details.postcondition, scenario);
256
+ case "performance":
257
+ return generateK6Probe(ucId, title, details.postcondition, scenario);
258
+ case "websocket":
259
+ return generateWsShProbe(ucId, title, details.postcondition, scenario);
260
+ case "log_assertion":
261
+ return generateLogShProbe(ucId, title, details.postcondition, scenario);
262
+ case "a11y":
263
+ return generateA11yProbe(ucId, title);
264
+ case "contract_consumer":
265
+ return generateConsumerContractProbe(ucId, title);
266
+ case "contract_provider":
267
+ return generateProviderContractProbe(ucId, title);
268
+ case "grpc":
269
+ return generateGrpcShProbe(ucId, title, details.postcondition, scenario);
270
+ case "security_scan":
271
+ return generateZapShProbe(ucId, title);
272
+ case "mcp_call":
273
+ return generateShProbe(ucId, title, details.postcondition, "mcp_call", scenario);
274
+ case "file_system":
275
+ default:
276
+ return generateShProbe(ucId, title, details.postcondition, "file_system", scenario);
277
+ }
278
+ }
279
+ //# sourceMappingURL=probe-templates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"probe-templates.js","sourceRoot":"","sources":["../../src/tools/probe-templates.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * propose_session tool handler.
3
+ *
4
+ * Produces a pre-implementation impact assessment before generate_session_prompt runs.
5
+ * Adapted from OpenSpec's Propose phase, extended with forgecraft's layer-awareness.
6
+ *
7
+ * Output: which specs will change (ADDED/MODIFIED), which layers are affected,
8
+ * which gates must pass before close_cycle, and an implementation checklist.
9
+ * Writes .forgecraft/proposal.md as a persistent artifact.
10
+ */
11
+ import { z } from "zod";
12
+ import type { ToolResult } from "../shared/types.js";
13
+ export declare const proposeSessionSchema: z.ZodObject<{
14
+ project_dir: z.ZodString;
15
+ item_description: z.ZodOptional<z.ZodString>;
16
+ roadmap_item_id: z.ZodOptional<z.ZodString>;
17
+ }, "strip", z.ZodTypeAny, {
18
+ project_dir: string;
19
+ item_description?: string | undefined;
20
+ roadmap_item_id?: string | undefined;
21
+ }, {
22
+ project_dir: string;
23
+ item_description?: string | undefined;
24
+ roadmap_item_id?: string | undefined;
25
+ }>;
26
+ export type ProposeSessionInput = z.infer<typeof proposeSessionSchema>;
27
+ export declare function proposeSessionHandler(args: ProposeSessionInput): Promise<ToolResult>;
28
+ //# sourceMappingURL=propose-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"propose-session.d.ts","sourceRoot":"","sources":["../../src/tools/propose-session.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAMrD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;EAe/B,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAqBvE,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,UAAU,CAAC,CAqDrB"}
@@ -0,0 +1,333 @@
1
+ /**
2
+ * propose_session tool handler.
3
+ *
4
+ * Produces a pre-implementation impact assessment before generate_session_prompt runs.
5
+ * Adapted from OpenSpec's Propose phase, extended with forgecraft's layer-awareness.
6
+ *
7
+ * Output: which specs will change (ADDED/MODIFIED), which layers are affected,
8
+ * which gates must pass before close_cycle, and an implementation checklist.
9
+ * Writes .forgecraft/proposal.md as a persistent artifact.
10
+ */
11
+ import { z } from "zod";
12
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, } from "node:fs";
13
+ import { join, resolve } from "node:path";
14
+ import { parseUseCases } from "./layer-status.js";
15
+ import { detectClarificationMarkers } from "./session-prompt-builders.js";
16
+ // ── Schema ────────────────────────────────────────────────────────────
17
+ export const proposeSessionSchema = z.object({
18
+ project_dir: z.string().describe("Absolute path to the project root."),
19
+ item_description: z
20
+ .string()
21
+ .min(10)
22
+ .optional()
23
+ .describe("What this session should build or fix. One sentence with actor, behavior, and postcondition."),
24
+ roadmap_item_id: z
25
+ .string()
26
+ .optional()
27
+ .describe("Roadmap item ID to propose for (e.g. 'RM-001'). Reads title from docs/roadmap.md."),
28
+ });
29
+ // ── Handler ──────────────────────────────────────────────────────────
30
+ export async function proposeSessionHandler(args) {
31
+ const projectDir = resolve(args.project_dir);
32
+ let itemDescription = args.item_description;
33
+ if (!itemDescription && args.roadmap_item_id) {
34
+ itemDescription =
35
+ resolveRoadmapTitle(projectDir, args.roadmap_item_id) ?? undefined;
36
+ }
37
+ if (!itemDescription) {
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: "## Proposal Blocked\n\nProvide `item_description` or a valid `roadmap_item_id`.",
43
+ },
44
+ ],
45
+ };
46
+ }
47
+ const useCases = loadUseCases(projectDir);
48
+ const relatedUcIds = matchRelatedUcs(itemDescription, useCases);
49
+ const specDelta = buildSpecDelta(projectDir, itemDescription, relatedUcIds, useCases);
50
+ const gateRequirements = loadRequiredGates(projectDir);
51
+ const clarifications = detectClarificationMarkers(projectDir);
52
+ const layerSummary = buildLayerSummary(projectDir, relatedUcIds, useCases);
53
+ const checklist = buildChecklist(projectDir, itemDescription, specDelta, relatedUcIds);
54
+ const proposal = renderProposal({
55
+ projectDir,
56
+ itemDescription,
57
+ specDelta,
58
+ layerSummary,
59
+ gateRequirements,
60
+ clarifications,
61
+ checklist,
62
+ });
63
+ const forgecraftDir = join(projectDir, ".forgecraft");
64
+ mkdirSync(forgecraftDir, { recursive: true });
65
+ writeFileSync(join(forgecraftDir, "proposal.md"), proposal, "utf-8");
66
+ return { content: [{ type: "text", text: proposal }] };
67
+ }
68
+ // ── Use Case Matching ─────────────────────────────────────────────────
69
+ function loadUseCases(projectDir) {
70
+ const path = join(projectDir, "docs", "use-cases.md");
71
+ if (!existsSync(path))
72
+ return [];
73
+ try {
74
+ return parseUseCases(readFileSync(path, "utf-8"));
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ }
80
+ function matchRelatedUcs(description, ucs) {
81
+ if (ucs.length === 0)
82
+ return [];
83
+ const lower = description.toLowerCase();
84
+ const words = lower.split(/\s+/).filter((w) => w.length > 3);
85
+ return ucs
86
+ .filter((uc) => {
87
+ const title = uc.title.toLowerCase();
88
+ return words.some((w) => title.includes(w));
89
+ })
90
+ .map((uc) => uc.id);
91
+ }
92
+ // ── Spec Delta ────────────────────────────────────────────────────────
93
+ function buildSpecDelta(projectDir, description, relatedUcIds, ucs) {
94
+ const deltas = [];
95
+ // Use cases
96
+ if (existsSync(join(projectDir, "docs", "use-cases.md"))) {
97
+ if (relatedUcIds.length > 0) {
98
+ deltas.push({
99
+ artifact: "docs/use-cases.md",
100
+ change: "MODIFIED",
101
+ reason: `Affects use case(s): ${relatedUcIds.join(", ")}`,
102
+ });
103
+ }
104
+ else if (ucs.length > 0) {
105
+ deltas.push({
106
+ artifact: "docs/use-cases.md",
107
+ change: "MODIFIED",
108
+ reason: "May require a new use case entry for this session",
109
+ });
110
+ }
111
+ }
112
+ else {
113
+ deltas.push({
114
+ artifact: "docs/use-cases.md",
115
+ change: "ADDED",
116
+ reason: "Use cases file does not exist — create before implementing",
117
+ });
118
+ }
119
+ // PRD
120
+ if (!existsSync(join(projectDir, "docs", "PRD.md"))) {
121
+ deltas.push({
122
+ artifact: "docs/PRD.md",
123
+ change: "ADDED",
124
+ reason: "Product requirements not found — run setup_project",
125
+ });
126
+ }
127
+ else {
128
+ deltas.push({
129
+ artifact: "docs/PRD.md",
130
+ change: "MODIFIED",
131
+ reason: "Requirement coverage may need updating after session",
132
+ });
133
+ }
134
+ // ADR — check if a related ADR likely exists
135
+ const adrHint = needsAdr(description);
136
+ if (adrHint) {
137
+ deltas.push({
138
+ artifact: "docs/adrs/NNNN-<decision-slug>.md",
139
+ change: "ADDED",
140
+ reason: adrHint,
141
+ });
142
+ }
143
+ // Session prompt
144
+ deltas.push({
145
+ artifact: ".forgecraft/proposal.md",
146
+ change: "ADDED",
147
+ reason: "This proposal (written now)",
148
+ });
149
+ return deltas;
150
+ }
151
+ function needsAdr(description) {
152
+ const triggers = [
153
+ ["database", "DB", "storage", "schema", "migration"],
154
+ ["auth", "authentication", "authorization", "JWT", "token"],
155
+ ["api", "endpoint", "REST", "GraphQL", "gRPC"],
156
+ ["cache", "Redis", "CDN"],
157
+ ["queue", "event", "message", "Kafka", "SQS"],
158
+ ["deploy", "infrastructure", "IaC", "container", "Docker", "Kubernetes"],
159
+ ];
160
+ const lower = description.toLowerCase();
161
+ for (const group of triggers) {
162
+ if (group.some((kw) => lower.includes(kw.toLowerCase()))) {
163
+ return `Decision involves ${group[0]} — likely needs an ADR before committing`;
164
+ }
165
+ }
166
+ return null;
167
+ }
168
+ // ── Gate Loading ──────────────────────────────────────────────────────
169
+ function loadRequiredGates(projectDir) {
170
+ const dirs = [
171
+ join(projectDir, ".forgecraft", "gates", "active"),
172
+ join(projectDir, ".forgecraft", "gates", "project", "active"),
173
+ ];
174
+ const gates = [];
175
+ for (const dir of dirs) {
176
+ if (!existsSync(dir))
177
+ continue;
178
+ try {
179
+ for (const f of readdirSync(dir).filter((n) => n.endsWith(".yaml"))) {
180
+ const gate = parseGateYaml(join(dir, f));
181
+ if (gate)
182
+ gates.push(gate);
183
+ }
184
+ }
185
+ catch {
186
+ /* skip */
187
+ }
188
+ }
189
+ return gates;
190
+ }
191
+ function parseGateYaml(filePath) {
192
+ try {
193
+ const text = readFileSync(filePath, "utf-8");
194
+ const id = /^id:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "";
195
+ const description = /^description:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "";
196
+ const priority = /^priority:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "P2";
197
+ const layer = /^layer:\s*(.+)$/m.exec(text)?.[1]?.trim() ?? "L1";
198
+ if (!id)
199
+ return null;
200
+ return { gateId: id, description, priority, layer };
201
+ }
202
+ catch {
203
+ return null;
204
+ }
205
+ }
206
+ function buildLayerSummary(projectDir, relatedUcIds, ucs) {
207
+ const harnessDir = join(projectDir, "tests", "harness");
208
+ const envDir = join(projectDir, "tests", "env");
209
+ const sloDir = join(projectDir, "tests", "slo");
210
+ const candidates = relatedUcIds.length > 0
211
+ ? ucs.filter((uc) => relatedUcIds.includes(uc.id))
212
+ : ucs.slice(0, 5); // show first 5 if no match
213
+ return candidates.map((uc) => {
214
+ const lower = uc.id.toLowerCase().replace(/_/g, "-");
215
+ const l2 = existsSync(harnessDir) &&
216
+ readdirSync(harnessDir).some((f) => f.toLowerCase().startsWith(lower))
217
+ ? "✅ probes found"
218
+ : "❌ no probes";
219
+ const l3 = existsSync(envDir) &&
220
+ readdirSync(envDir).some((f) => f.toLowerCase().startsWith(lower))
221
+ ? "✅ env probes"
222
+ : "—";
223
+ const l4 = existsSync(sloDir) &&
224
+ readdirSync(sloDir).some((f) => f.toLowerCase().startsWith(lower))
225
+ ? "✅ slo probes"
226
+ : "—";
227
+ const l1 = existsSync(join(projectDir, "docs", "use-cases.md"))
228
+ ? "✅ use case"
229
+ : "❌ missing";
230
+ return { ucId: uc.id, title: uc.title, l1, l2, l3, l4 };
231
+ });
232
+ }
233
+ // ── Checklist ─────────────────────────────────────────────────────────
234
+ function buildChecklist(projectDir, _description, specDelta, relatedUcIds) {
235
+ const items = [];
236
+ // Cascade check
237
+ items.push("Run `check_cascade` — all 5 steps must be complete before starting");
238
+ // Clarifications
239
+ items.push("Resolve all `[NEEDS CLARIFICATION]` markers in spec artifacts");
240
+ // Spec updates
241
+ const added = specDelta.filter((d) => d.change === "ADDED");
242
+ if (added.length > 0) {
243
+ for (const d of added) {
244
+ items.push(`Create missing artifact: \`${d.artifact}\` — ${d.reason}`);
245
+ }
246
+ }
247
+ // Harness
248
+ if (relatedUcIds.length > 0) {
249
+ items.push(`Run \`generate_harness\` for affected use cases: ${relatedUcIds.join(", ")}`);
250
+ items.push("Implement harness probe TODO sections before closing");
251
+ }
252
+ // TDD loop
253
+ items.push("Write failing probes first, then implement until green");
254
+ items.push("Run `run_harness` — all L2 probes must pass before `close_cycle`");
255
+ // Env / infra check
256
+ if (existsSync(join(projectDir, "tests", "env"))) {
257
+ items.push("Run `run_env_probe` — L3 env contracts must hold after changes");
258
+ }
259
+ // Close
260
+ items.push("Run `close_cycle` — evaluates all gates and writes `.claude/state.md`");
261
+ items.push("Update `Status.md` with completed changes and next steps");
262
+ return items;
263
+ }
264
+ // ── Roadmap Resolution ────────────────────────────────────────────────
265
+ function resolveRoadmapTitle(projectDir, itemId) {
266
+ const path = join(projectDir, "docs", "roadmap.md");
267
+ if (!existsSync(path))
268
+ return null;
269
+ const text = readFileSync(path, "utf-8");
270
+ const re = new RegExp(`\\|\\s*${itemId}\\s*\\|[^|]+\\|([^|]+)\\|`);
271
+ const m = re.exec(text);
272
+ return m ? (m[1]?.trim() ?? null) : null;
273
+ }
274
+ function renderProposal(input) {
275
+ const { itemDescription, specDelta, layerSummary, gateRequirements, clarifications, checklist, } = input;
276
+ const date = new Date().toISOString().slice(0, 10);
277
+ const lines = [];
278
+ lines.push(`# Session Proposal`, ``, `**Date:** ${date}`, `**Scope:** ${itemDescription}`, ``);
279
+ lines.push(`> This proposal is a pre-implementation impact assessment.`);
280
+ lines.push(`> Resolve all blockers, then run \`generate_session_prompt\` to commit to implementation.`, ``);
281
+ // Spec delta
282
+ lines.push(`## Spec Delta`, ``);
283
+ lines.push(`| Artifact | Change | Reason |`, `|---|---|---|`);
284
+ for (const d of specDelta) {
285
+ const badge = d.change === "ADDED"
286
+ ? "🆕 ADDED"
287
+ : d.change === "MODIFIED"
288
+ ? "✏️ MODIFIED"
289
+ : "🗑 REMOVED";
290
+ lines.push(`| \`${d.artifact}\` | ${badge} | ${d.reason} |`);
291
+ }
292
+ lines.push(``);
293
+ // Layer readiness
294
+ if (layerSummary.length > 0) {
295
+ lines.push(`## Layer Readiness — Affected Use Cases`, ``);
296
+ lines.push(`| UC | Title | L1 Spec | L2 Probes | L3 Env | L4 SLO |`, `|---|---|---|---|---|---|`);
297
+ for (const r of layerSummary) {
298
+ lines.push(`| ${r.ucId} | ${r.title} | ${r.l1} | ${r.l2} | ${r.l3} | ${r.l4} |`);
299
+ }
300
+ lines.push(``);
301
+ }
302
+ else {
303
+ lines.push(`## Layer Readiness`, ``, `_No use cases found. Run \`setup_project\` to create the spec foundation._`, ``);
304
+ }
305
+ // Clarifications
306
+ if (clarifications.length > 0) {
307
+ lines.push(`## ⚠️ Unresolved Clarifications — Must Resolve Before Session`, ``);
308
+ lines.push(`| File | Marker |`, `|---|---|`);
309
+ for (const c of clarifications) {
310
+ lines.push(`| \`${c.file}\` | ${c.marker} |`);
311
+ }
312
+ lines.push(``, `Resolve these before running \`generate_session_prompt\`.`, ``);
313
+ }
314
+ // Gates
315
+ if (gateRequirements.length > 0) {
316
+ lines.push(`## Gates That Must Pass Before \`close_cycle\``, ``);
317
+ lines.push(`| Gate ID | Layer | Priority | Description |`, `|---|---|---|---|`);
318
+ for (const g of gateRequirements) {
319
+ lines.push(`| ${g.gateId} | ${g.layer} | ${g.priority} | ${g.description} |`);
320
+ }
321
+ lines.push(``);
322
+ }
323
+ // Checklist
324
+ lines.push(`## Pre-Implementation Checklist`, ``);
325
+ for (const item of checklist) {
326
+ lines.push(`- [ ] ${item}`);
327
+ }
328
+ lines.push(``);
329
+ lines.push(`---`);
330
+ lines.push(`_Proposal written to \`.forgecraft/proposal.md\`. Run \`generate_session_prompt\` when ready to commit._`);
331
+ return lines.join("\n");
332
+ }
333
+ //# sourceMappingURL=propose-session.js.map