@open-mercato/cli 0.6.8-develop.6969.1.7a32706312 → 0.6.8-develop.6971.1.20c09ca9ea
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.
- package/dist/agentic/claude-code/hooks/gate-evidence.ts +269 -0
- package/dist/agentic/claude-code/settings.json +21 -0
- package/dist/agentic/codex/hooks/gate-evidence.mjs +199 -0
- package/dist/agentic/codex/hooks.json +29 -0
- package/dist/agentic/cursor/hooks/gate-evidence.mjs +181 -0
- package/dist/agentic/cursor/hooks.json +10 -0
- package/dist/agentic/guides/module-facts.json +112 -112
- package/dist/agentic/guides/module-facts.v2.json +112 -112
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
- package/dist/agentic/guides/modules/customers/index.md +1 -1
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/index.md +1 -1
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/index.md +1 -1
- package/dist/agentic/guides/reference-module-facts.json +1 -1
- package/dist/agentic/guides/upstream/manifest.json +1 -1
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/api-and-domain.md +13 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/module-surfaces.md +22 -0
- package/dist/agentic/shared/ai/skills/om-module-scaffold/references/verification.md +20 -0
- package/dist/lib/agentic-setup.js +8 -2
- package/dist/lib/agentic-setup.js.map +2 -2
- package/dist/lib/generators/module-di.js +25 -1
- package/dist/lib/generators/module-di.js.map +2 -2
- package/dist/lib/generators/module-registry.js +61 -15
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/openapi.js +3 -1
- package/dist/lib/generators/openapi.js.map +2 -2
- package/package.json +6 -6
- package/src/lib/agentic-setup.ts +17 -2
- package/src/lib/generators/__tests__/silent-convention-warnings.test.ts +154 -0
- package/src/lib/generators/module-di.ts +35 -0
- package/src/lib/generators/module-registry.ts +93 -7
- 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
|
}
|