@open-mercato/cli 0.6.8-develop.6969.1.7a32706312 → 0.6.8-develop.6972.1.275e435508

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 (84) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/agentic/claude-code/hooks/gate-evidence.ts +269 -0
  3. package/dist/agentic/claude-code/settings.json +21 -0
  4. package/dist/agentic/codex/hooks/gate-evidence.mjs +199 -0
  5. package/dist/agentic/codex/hooks.json +29 -0
  6. package/dist/agentic/cursor/hooks/gate-evidence.mjs +181 -0
  7. package/dist/agentic/cursor/hooks.json +10 -0
  8. package/dist/agentic/guides/module-facts.json +112 -112
  9. package/dist/agentic/guides/module-facts.v2.json +112 -112
  10. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  11. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  12. package/dist/agentic/guides/modules/api_keys/index.md +1 -1
  13. package/dist/agentic/guides/modules/attachments/index.md +1 -1
  14. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  15. package/dist/agentic/guides/modules/auth/index.md +1 -1
  16. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  17. package/dist/agentic/guides/modules/catalog/index.md +1 -1
  18. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  19. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  20. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  21. package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
  22. package/dist/agentic/guides/modules/configs/index.md +1 -1
  23. package/dist/agentic/guides/modules/content/index.md +1 -1
  24. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  25. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  26. package/dist/agentic/guides/modules/customers/index.md +1 -1
  27. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  28. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  29. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  30. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  31. package/dist/agentic/guides/modules/directory/index.md +1 -1
  32. package/dist/agentic/guides/modules/entities/index.md +1 -1
  33. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  34. package/dist/agentic/guides/modules/events/index.md +1 -1
  35. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  36. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  37. package/dist/agentic/guides/modules/generators/index.md +1 -1
  38. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  39. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  40. package/dist/agentic/guides/modules/messages/index.md +1 -1
  41. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  42. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  43. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  44. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  45. package/dist/agentic/guides/modules/planner/index.md +1 -1
  46. package/dist/agentic/guides/modules/portal/index.md +1 -1
  47. package/dist/agentic/guides/modules/progress/index.md +1 -1
  48. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  49. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  50. package/dist/agentic/guides/modules/resources/index.md +1 -1
  51. package/dist/agentic/guides/modules/sales/index.md +1 -1
  52. package/dist/agentic/guides/modules/scheduler/index.md +1 -1
  53. package/dist/agentic/guides/modules/search/index.md +1 -1
  54. package/dist/agentic/guides/modules/security/index.md +1 -1
  55. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  56. package/dist/agentic/guides/modules/sso/index.md +1 -1
  57. package/dist/agentic/guides/modules/staff/index.md +1 -1
  58. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  59. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  60. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  61. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  62. package/dist/agentic/guides/modules/translations/index.md +1 -1
  63. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  64. package/dist/agentic/guides/modules/wms/index.md +1 -1
  65. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  66. package/dist/agentic/guides/reference-module-facts.json +1 -1
  67. package/dist/agentic/guides/upstream/manifest.json +1 -1
  68. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +13 -0
  69. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +22 -0
  70. package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +20 -0
  71. package/dist/lib/agentic-setup.js +8 -2
  72. package/dist/lib/agentic-setup.js.map +2 -2
  73. package/dist/lib/generators/module-di.js +25 -1
  74. package/dist/lib/generators/module-di.js.map +2 -2
  75. package/dist/lib/generators/module-registry.js +61 -15
  76. package/dist/lib/generators/module-registry.js.map +2 -2
  77. package/dist/lib/generators/openapi.js +3 -1
  78. package/dist/lib/generators/openapi.js.map +2 -2
  79. package/package.json +6 -6
  80. package/src/lib/agentic-setup.ts +17 -2
  81. package/src/lib/generators/__tests__/silent-convention-warnings.test.ts +154 -0
  82. package/src/lib/generators/module-di.ts +35 -0
  83. package/src/lib/generators/module-registry.ts +93 -7
  84. package/src/lib/generators/openapi.ts +5 -1
@@ -0,0 +1,181 @@
1
+ /**
2
+ * Record validation-gate outcomes, and refuse to finish on unverified source changes (Cursor).
3
+ *
4
+ * Mirrors the claude-code hook: `record` on `afterShellExecution`, `check` on `stop`.
5
+ *
6
+ * Cursor's `afterShellExecution` payload documents `command`, `output`, `duration`, and
7
+ * `sandbox` — no exit code — so the outcome is inferred from output text rather than read.
8
+ * Blocking contract: exit 2 blocks, exit 0 proceeds, other codes fail open.
9
+ */
10
+ import { mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
11
+ import { join, resolve } from 'node:path'
12
+
13
+ /**
14
+ * Extracts every gate a shell command ran.
15
+ *
16
+ * Quoted spans are blanked first: `git commit -m "run tsc --noEmit"` names a gate without
17
+ * running one, and its zero exit status would otherwise be recorded as a green typecheck.
18
+ */
19
+ export function matchGates(command) {
20
+ const executable = command.replace(/'[^']*'|"[^"]*"/g, ' ')
21
+ const found = new Set()
22
+ const named = [
23
+ ['typecheck', /\b(?:yarn|npm run|pnpm)\s+typecheck\b|\btsc\b[^&|;]*--noEmit/],
24
+ ['lint', /\b(?:yarn|npm run|pnpm)\s+lint\b|\beslint\b/],
25
+ ['test', /\b(?:yarn|npm run|pnpm)\s+test\b|\bjest\b/],
26
+ ['build', /\b(?:yarn|npm run|pnpm)\s+build\b|\bnext build\b/],
27
+ ['generate', /\b(?:yarn|npm run|pnpm)\s+generate\b|\bmercato\s+generate\b/],
28
+ ]
29
+ for (const [gate, pattern] of named) {
30
+ if (pattern.test(executable)) found.add(gate)
31
+ }
32
+ return [...found]
33
+ }
34
+
35
+ /**
36
+ * Whether a command's outcome can be attributed to the gates it names.
37
+ *
38
+ * A pipeline reports its LAST stage's status, and `;` / `||` break the link the same way.
39
+ * `&&` does not: it short-circuits, so a failure still belongs to a gate that ran.
40
+ */
41
+ export function isAttributableGateCommand(command) {
42
+ return !/[|;\n]/.test(command)
43
+ }
44
+
45
+ /** See the claude-code hook. */
46
+ export function nextSessionState(previous, sessionId, startedAt) {
47
+ if (!sessionId) {
48
+ return previous.sessionStartedAt ? previous : { ...previous, sessionStartedAt: startedAt }
49
+ }
50
+ if (previous.sessionId === sessionId && previous.sessionStartedAt) return previous
51
+ return { sessionId, sessionStartedAt: startedAt }
52
+ }
53
+
54
+ /** See the claude-code hook: an absent typecheck record does not block on its own. */
55
+ export function shouldBlock({ newestSrcMtimeMs, sessionStartedAtMs, lastGreenTypecheckMs }) {
56
+ if (newestSrcMtimeMs === null) return false
57
+ if (newestSrcMtimeMs < sessionStartedAtMs) return false
58
+ if (lastGreenTypecheckMs === null) return true
59
+ return newestSrcMtimeMs > lastGreenTypecheckMs
60
+ }
61
+
62
+ /**
63
+ * Infers a gate outcome from its output, because this host reports no exit code.
64
+ *
65
+ * Returns 1 (failure) for empty output and for any failure signature. The bias is
66
+ * deliberate: over-reporting failure costs one re-run, under-reporting it records a pass
67
+ * that never happened — the property this hook exists to remove. `No tests found` is a
68
+ * failure here for the same reason it is not a pass in the harness rules.
69
+ */
70
+ export function inferExitCode(output) {
71
+ if (typeof output !== 'string' || output.trim() === '') return 1
72
+ const failureSignatures = [
73
+ /\berror\s+TS\d+/i, /FATAL ERROR/i, /heap out of memory/i,
74
+ /\bTests?:\s+\d+\s+failed/i, /\bfail(ed|ing)\b/i, /\bERROR\b/,
75
+ /exited \(\d+\)/, /command not found/i, /No tests found/i,
76
+ ]
77
+ return failureSignatures.some((pattern) => pattern.test(output)) ? 1 : 0
78
+ }
79
+
80
+ const STATE_RELATIVE_PATH = '.ai/.gate-state.json'
81
+ const WATCHED_ROOT = 'src'
82
+
83
+ function projectDir() {
84
+ return process.env.CURSOR_PROJECT_DIR || resolve('.')
85
+ }
86
+
87
+ function statePath() { return join(projectDir(), STATE_RELATIVE_PATH) }
88
+
89
+ function readState() {
90
+ try { return JSON.parse(readFileSync(statePath(), 'utf8')) } catch { return {} }
91
+ }
92
+
93
+ function writeState(state) {
94
+ try {
95
+ mkdirSync(join(projectDir(), '.ai'), { recursive: true })
96
+ writeFileSync(statePath(), `${JSON.stringify(state, null, 2)}\n`, 'utf8')
97
+ } catch {
98
+ // A hook must never fail the turn over its own bookkeeping.
99
+ }
100
+ }
101
+
102
+ function newestMtimeMs(dir) {
103
+ let newest = null
104
+ const walk = (current) => {
105
+ let entries
106
+ try { entries = readdirSync(current) } catch { return }
107
+ for (const entry of entries) {
108
+ if (entry === 'node_modules' || entry.startsWith('.')) continue
109
+ const full = join(current, entry)
110
+ let stats
111
+ try { stats = statSync(full) } catch { continue }
112
+ if (stats.isDirectory()) walk(full)
113
+ else if (newest === null || stats.mtimeMs > newest) newest = stats.mtimeMs
114
+ }
115
+ }
116
+ walk(dir)
117
+ return newest
118
+ }
119
+
120
+ function readStdin() {
121
+ return new Promise((done) => {
122
+ let raw = ''
123
+ if (process.stdin.isTTY) { done(''); return }
124
+ process.stdin.setEncoding('utf8')
125
+ process.stdin.on('data', (chunk) => { raw += chunk })
126
+ process.stdin.on('end', () => done(raw))
127
+ process.stdin.on('error', () => done(''))
128
+ })
129
+ }
130
+
131
+ async function main() {
132
+ const mode = process.argv[2] === 'check' ? 'check' : 'record'
133
+ const raw = await readStdin()
134
+
135
+ let data = {}
136
+ if (raw.trim()) {
137
+ try { data = JSON.parse(raw) } catch { process.exit(0) }
138
+ }
139
+
140
+ const previous = readState()
141
+ const now = new Date()
142
+ const state = nextSessionState(previous, data.session_id ?? null, now.toISOString())
143
+ if (state !== previous) writeState(state)
144
+
145
+ if (mode === 'record') {
146
+ const command = data.command ?? data.tool_input?.command
147
+ if (!command) process.exit(0)
148
+ const gates = matchGates(command)
149
+ if (!gates.length) process.exit(0)
150
+ if (!isAttributableGateCommand(command)) process.exit(0)
151
+ const exitCode = inferExitCode(data.output ?? '')
152
+ state.gates = state.gates ?? {}
153
+ for (const gate of gates) {
154
+ state.gates[gate] = { exitCode, finishedAt: now.toISOString() }
155
+ }
156
+ writeState(state)
157
+ process.exit(0)
158
+ }
159
+
160
+ if (data.stop_hook_active) process.exit(0)
161
+
162
+ const typecheck = state.gates?.typecheck
163
+ const blocked = shouldBlock({
164
+ newestSrcMtimeMs: newestMtimeMs(join(projectDir(), WATCHED_ROOT)),
165
+ sessionStartedAtMs: Date.parse(state.sessionStartedAt ?? now.toISOString()),
166
+ lastGreenTypecheckMs: typecheck && typecheck.exitCode === 0 ? Date.parse(typecheck.finishedAt) : null,
167
+ })
168
+ if (!blocked) process.exit(0)
169
+
170
+ process.stderr.write([
171
+ `Source under ${WATCHED_ROOT}/ changed this session and has not passed a typecheck since.`,
172
+ '',
173
+ 'Run `yarn typecheck` and report its exit status before finishing.',
174
+ 'If it genuinely fails and you cannot fix it, report the failure to the user.',
175
+ '',
176
+ ].join('\n'))
177
+ process.exit(2)
178
+ }
179
+
180
+ const invokedDirectly = process.argv[1] && process.argv[1].endsWith('gate-evidence.mjs')
181
+ if (invokedDirectly) await main()
@@ -5,6 +5,16 @@
5
5
  "command": "node",
6
6
  "args": [".cursor/hooks/entity-migration-check.mjs"],
7
7
  "description": "Check if entity edit requires a database migration"
8
+ },
9
+ "afterShellExecution": {
10
+ "command": "node",
11
+ "args": [".cursor/hooks/gate-evidence.mjs", "record"],
12
+ "description": "Record validation-gate outcomes so a gate cannot be claimed without running"
13
+ },
14
+ "stop": {
15
+ "command": "node",
16
+ "args": [".cursor/hooks/gate-evidence.mjs", "check"],
17
+ "description": "Block finishing when src/ changed without a passing typecheck since"
8
18
  }
9
19
  }
10
20
  }