@beechcms/cms 0.8.1 → 0.9.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 (4) hide show
  1. package/bin/cli.mjs +418 -303
  2. package/bin/create.mjs +396 -391
  3. package/package.json +10 -4
  4. package/README.md +0 -1
package/bin/cli.mjs CHANGED
@@ -1,303 +1,418 @@
1
- #!/usr/bin/env node
2
- // @ts-check
3
-
4
- import pc from 'picocolors'
5
-
6
- let [,, command, ...args] = process.argv
7
-
8
- if (command && args[0] && ['db', 'seed', 'schema', 'dev', 'generate', 'mailpit'].includes(command)) {
9
- command = `${command}:${args.shift()}`
10
- }
11
-
12
- if (command === 'gen' && args[0] === 'types') {
13
- command = 'gen-types'
14
- args.shift()
15
- if (args[0] === 'typescript') {
16
- args.shift()
17
- }
18
- }
19
-
20
- const COMMANDS = {
21
- build: cmdBuild,
22
- 'seed:load': cmdSeedLoad,
23
- 'seed:create': cmdSeedCreate,
24
- 'schema:diff': cmdSchemaDiff,
25
- 'init': cmdInit,
26
- 'validate': cmdValidate,
27
- 'deploy': cmdDeploy,
28
- 'update': cmdUpdate,
29
- 'onboard': cmdOnboard,
30
- 'reset': cmdReset,
31
- 'forms': cmdForms,
32
- 'form': cmdForms,
33
- 'forms:add': cmdForms,
34
- 'gen-types': cmdGenerateTypes,
35
- 'gen:types': cmdGenerateTypes,
36
- 'generate:types': cmdGenerateTypes,
37
- // New unified command mappings:
38
- 'db:migrate': cmdDbMigrate,
39
- 'db:reset': cmdDbReset,
40
- 'dev': cmdDev,
41
- 'start': cmdDev,
42
- 'dev:stop': cmdDevStop,
43
- 'dev:reset': cmdDevReset,
44
- 'dev:tunnel': cmdDevTunnel,
45
- 'mailpit:clear': cmdMailpitClear,
46
- 'logs': cmdLogs,
47
- 'test': cmdTest,
48
- 'lint': cmdLint,
49
- 'doctor': cmdDoctor,
50
- 'setup:cloudflare': cmdSetupCloudflare,
51
- 'setup:cf': cmdSetupCloudflare,
52
- }
53
-
54
- function help() {
55
- console.log(`
56
- ${pc.cyan('beech')} <command> [options]
57
-
58
- ${pc.bold('1. Local Management & Onboarding')}
59
- ${pc.cyan('init')} Check project files and optionally initialise the database
60
- --db Also initialise the D1 database (system tables)
61
- --remote Target remote D1 instead of local (default: local)
62
- --db-name <n> Override D1 database name
63
- --yes, -y Run in non-interactive mode
64
- ${pc.cyan('onboard')} One-command local provisioning (init --db)
65
- --remote Target remote D1 instead of local (default: local)
66
- --yes, -y Skip all interactive prompts (non-interactive mode)
67
- --db <name> Override D1 database name
68
- ${pc.cyan('update')} Update internals to latest, then apply system D1 migrations
69
-
70
- ${pc.bold('2. Database & Migrations')}
71
- ${pc.cyan('db:migrate')} Apply all pending local migrations
72
- ${pc.cyan('db:reset')} Remove local Wrangler state and re-bootstrap database
73
-
74
- ${pc.bold('3. Database & Types Management')}
75
- ${pc.cyan('gen types typescript')} (alias: ${pc.cyan('gen-types')})
76
- Generate TypeScript interfaces from active D1 database
77
- --local Target local D1 SQLite state (default)
78
- --remote Target remote Cloudflare D1
79
- --db <name> Override D1 database name
80
- -o, --output Output file path (default: standard output)
81
- ${pc.cyan('validate')} Validate runtime schema status
82
-
83
- ${pc.bold('4. Forms & Frontend Generation')}
84
- ${pc.cyan('forms / form')} Interactive wizard to generate React, Vue, Svelte, or Web Component forms
85
- --framework <f> Framework: react, vue, svelte, vanilla
86
- --seed <slug> Seed slug to bind to (e.g. clienti)
87
- --mode <mode> styled (Tailwind) or headless
88
- --yes, -y Skip interactive prompts
89
-
90
- ${pc.bold('5. Local Stack & Docker')}
91
- ${pc.cyan('dev / start')} Start the local dev environment (Docker + API + Dashboard)
92
- --plain Avoid Ink visual TUI and run clean log streaming
93
- ${pc.cyan('dev:stop')} Stop Docker containers without wiping data
94
- ${pc.cyan('dev:reset')} Stop Docker containers and remove all persistent volumes
95
- ${pc.cyan('dev:tunnel')} Display Cloudflare tunnel public testing URL
96
- ${pc.cyan('mailpit:clear')} Clear local test inbox in Mailpit
97
-
98
- ${pc.bold('6. Logs Streaming')}
99
- ${pc.cyan('logs <service>')} Show streaming logs for docker service: mailpit, db, tunnel, storage
100
-
101
- ${pc.bold('7. Quality & Deployment')}
102
- ${pc.cyan('test')} Run the test suite via Turborepo / Vitest
103
- --coverage Generate coverage reports
104
- --diff Run test coverage only for files modified on the branch
105
- ${pc.cyan('lint')} Run ESLint quality checks
106
- ${pc.cyan('setup:cloudflare')} (alias: ${pc.cyan('setup:cf')})
107
- Interactive 1-step Cloudflare provisioning (D1, R2, Presigned S3 secrets)
108
- --name <n> Project name override
109
- --yes, -y Non-interactive mode
110
- ${pc.cyan('deploy')} Compile, test, deploy to Cloudflare environment
111
- --skip-check Skip /admin reachability check
112
- ${pc.cyan('doctor')} Execute React diagnostics check on Dashboard
113
- `)
114
- }
115
-
116
- function cmdBuild() {
117
- console.log(
118
- '\nNo build step needed for BeechCMS projects.\n' +
119
- 'Manage content types dynamically in the BeechCMS Dashboard at /admin.\n'
120
- )
121
- }
122
-
123
- async function cmdInit(args) {
124
- const initDb = args.includes('--db')
125
- const remote = args.includes('--remote')
126
- const dbIdx = args.indexOf('--db-name')
127
- const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
128
- const yes = args.includes('--yes') || args.includes('-y')
129
-
130
- const { init } = await import('@beechcms/cli')
131
- await init({ initDb, local: !remote, db, nonInteractive: yes })
132
- }
133
-
134
- async function cmdSeedLoad(_args) {
135
- const { seedLoad } = await import('@beechcms/cli')
136
- await seedLoad({})
137
- }
138
-
139
- async function cmdValidate(_args) {
140
- const { validate } = await import('@beechcms/cli')
141
- await validate({})
142
- }
143
-
144
- async function cmdSeedCreate(_args) {
145
- const { seedCreate } = await import('@beechcms/cli')
146
- await seedCreate({})
147
- }
148
-
149
- async function cmdDeploy(args) {
150
- const skipSeed = args.includes('--skip-seed')
151
- const skipCheck = args.includes('--skip-check')
152
- const { deploy } = await import('@beechcms/cli')
153
- await deploy({ skipSeed, skipCheck })
154
- }
155
-
156
- async function cmdUpdate(_args) {
157
- const { update } = await import('@beechcms/cli')
158
- await update({})
159
- }
160
-
161
- async function cmdOnboard(args) {
162
- const local = !args.includes('--remote')
163
- const yes = args.includes('--yes') || args.includes('-y')
164
- const dbIdx = args.indexOf('--db')
165
- const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
166
- const { onboard } = await import('@beechcms/cli')
167
- await onboard({ local, yes, db })
168
- }
169
-
170
- async function cmdReset(args) {
171
- const db = args.includes('--db')
172
- const docker = args.includes('--docker')
173
- const all = args.includes('--all')
174
- const yes = args.includes('--yes') || args.includes('-y')
175
-
176
- const { reset } = await import('@beechcms/cli')
177
- await reset({ db, docker, all, yes })
178
- }
179
-
180
- async function cmdSchemaDiff(args) {
181
- const remote = args.includes('--remote')
182
- const write = args.includes('--write')
183
- const nameIdx = args.indexOf('--name')
184
- const name = nameIdx !== -1 ? args[nameIdx + 1] : undefined
185
- const dbIdx = args.indexOf('--db')
186
- const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
187
- const { schemaDiff } = await import('@beechcms/cli')
188
- await schemaDiff({ local: !remote, write, name, db })
189
- }
190
-
191
- async function cmdGenerateTypes(args) {
192
- const remote = args.includes('--remote')
193
- const local = !remote
194
-
195
- let out = null
196
- const outIdx = args.indexOf('--out')
197
- const outputIdx = args.indexOf('--output')
198
- const oIdx = args.indexOf('-o')
199
-
200
- if (outIdx !== -1 && args[outIdx + 1]) out = args[outIdx + 1]
201
- else if (outputIdx !== -1 && args[outputIdx + 1]) out = args[outputIdx + 1]
202
- else if (oIdx !== -1 && args[oIdx + 1]) out = args[oIdx + 1]
203
-
204
- const dbIdx = args.indexOf('--db')
205
- const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
206
-
207
- const { generateTypes } = await import('@beechcms/cli')
208
- await generateTypes({ out, local, db })
209
- }
210
-
211
- async function cmdForms(args) {
212
- const yes = args.includes('--yes') || args.includes('-y')
213
- const json = args.includes('--json')
214
- const frameworkIdx = args.indexOf('--framework')
215
- const framework = frameworkIdx !== -1 ? args[frameworkIdx + 1] : undefined
216
- const seedIdx = args.indexOf('--seed')
217
- const seed = seedIdx !== -1 ? args[seedIdx + 1] : undefined
218
- const modeIdx = args.indexOf('--mode')
219
- const mode = modeIdx !== -1 ? args[modeIdx + 1] : undefined
220
- const outIdx = args.indexOf('--out')
221
- const out = outIdx !== -1 ? args[outIdx + 1] : undefined
222
-
223
- const { forms } = await import('@beechcms/cli')
224
- await forms({ framework, seed, mode, out, yes, json })
225
- }
226
-
227
- // New unified command wrappers:
228
- async function cmdDbMigrate(args) {
229
- const { dbMigrate } = await import('@beechcms/cli')
230
- await dbMigrate({})
231
- }
232
-
233
- async function cmdDbReset(args) {
234
- const { dbReset } = await import('@beechcms/cli')
235
- await dbReset({})
236
- }
237
-
238
- async function cmdDev(args) {
239
- const plain = args.includes('--plain')
240
- const { dev } = await import('@beechcms/cli')
241
- await dev({ plain })
242
- }
243
-
244
- async function cmdDevStop(args) {
245
- const { devStop } = await import('@beechcms/cli')
246
- await devStop()
247
- }
248
-
249
- async function cmdDevReset(args) {
250
- const { devReset } = await import('@beechcms/cli')
251
- await devReset()
252
- }
253
-
254
- async function cmdDevTunnel(args) {
255
- const { devTunnel } = await import('@beechcms/cli')
256
- await devTunnel()
257
- }
258
-
259
- async function cmdMailpitClear(args) {
260
- const { mailpitClear } = await import('@beechcms/cli')
261
- await mailpitClear()
262
- }
263
-
264
- async function cmdLogs(args) {
265
- const service = args[0]
266
- const { logs } = await import('@beechcms/cli')
267
- await logs({ service })
268
- }
269
-
270
- async function cmdTest(args) {
271
- const coverage = args.includes('--coverage')
272
- const diff = args.includes('--diff')
273
- const { test } = await import('@beechcms/cli')
274
- await test({ coverage, diff })
275
- }
276
-
277
- async function cmdLint(args) {
278
- const { lint } = await import('@beechcms/cli')
279
- await lint()
280
- }
281
-
282
- async function cmdDoctor(args) {
283
- const { doctor } = await import('@beechcms/cli')
284
- await doctor()
285
- }
286
-
287
- async function cmdSetupCloudflare(args) {
288
- const yes = args.includes('--yes') || args.includes('-y')
289
- const nameIdx = args.indexOf('--name')
290
- const projectName = nameIdx !== -1 ? args[nameIdx + 1] : undefined
291
- const { setupCloudflare } = await import('@beechcms/cli')
292
- await setupCloudflare({ projectName, nonInteractive: yes })
293
- }
294
-
295
- const handler = COMMANDS[command]
296
- if (!handler) {
297
- help()
298
- if (command && command !== '--help' && command !== '-h' && command !== 'help') process.exit(1)
299
- } else if (args.includes('--help') || args.includes('-h')) {
300
- help()
301
- } else {
302
- await handler(args)
303
- }
1
+ #!/usr/bin/env node
2
+ // @ts-check
3
+
4
+ import pc from 'picocolors'
5
+
6
+ let [,, command, ...args] = process.argv
7
+
8
+ if (command && args[0] && !args[0].startsWith('-') && ['db', 'seed', 'schema', 'types', 'dev', 'generate', 'mailpit'].includes(command)) {
9
+ command = `${command}:${args.shift()}`
10
+ }
11
+
12
+ if (command === 'gen' && args[0] === 'types') {
13
+ command = 'gen-types'
14
+ args.shift()
15
+ if (args[0] === 'typescript') {
16
+ args.shift()
17
+ }
18
+ }
19
+
20
+ const COMMANDS = {
21
+ build: cmdBuild,
22
+ 'seed:load': cmdSeedLoad,
23
+ 'seed:create': cmdSeedCreate,
24
+ 'schema:diff': cmdSchemaDiff,
25
+ 'schema:audit': cmdSchemaAudit,
26
+ 'schema:migrate': cmdSchemaMigrate,
27
+ 'schema:export': cmdSchemaExport,
28
+ 'schema:plan': cmdSchemaPlan,
29
+ 'schema:apply': cmdSchemaApply,
30
+ 'types:generate': cmdGenerateTypes,
31
+ 'types:check': cmdTypesCheck,
32
+ 'init': cmdInit,
33
+ 'validate': cmdValidate,
34
+ 'deploy': cmdDeploy,
35
+ 'update': cmdUpdate,
36
+ 'onboard': cmdOnboard,
37
+ 'reset': cmdReset,
38
+ 'forms': cmdForms,
39
+ 'form': cmdForms,
40
+ 'forms:add': cmdForms,
41
+ 'gen-types': cmdGenerateTypes,
42
+ 'gen:types': cmdGenerateTypes,
43
+ 'generate:types': cmdGenerateTypes,
44
+ // New unified command mappings:
45
+ 'db:migrate': cmdDbMigrate,
46
+ 'db:reset': cmdDbReset,
47
+ 'dev': cmdDev,
48
+ 'start': cmdDev,
49
+ 'dev:stop': cmdDevStop,
50
+ 'dev:reset': cmdDevReset,
51
+ 'dev:tunnel': cmdDevTunnel,
52
+ 'mailpit:clear': cmdMailpitClear,
53
+ 'logs': cmdLogs,
54
+ 'test': cmdTest,
55
+ 'lint': cmdLint,
56
+ 'doctor': cmdDoctor,
57
+ 'setup:cloudflare': cmdSetupCloudflare,
58
+ 'setup:cf': cmdSetupCloudflare,
59
+ }
60
+
61
+ function help() {
62
+ console.log(`
63
+ ${pc.cyan('beech')} <command> [options]
64
+
65
+ ${pc.bold('1. Local Management & Onboarding')}
66
+ ${pc.cyan('init')} Check project files and optionally initialise the database
67
+ --db Also initialise the D1 database (system tables)
68
+ --remote Target remote D1 instead of local (default: local)
69
+ --db-name <n> Override D1 database name
70
+ --yes, -y Run in non-interactive mode
71
+ ${pc.cyan('onboard')} One-command local provisioning (init --db)
72
+ --remote Target remote D1 instead of local (default: local)
73
+ --yes, -y Skip all interactive prompts (non-interactive mode)
74
+ --db <name> Override D1 database name
75
+ ${pc.cyan('update')} Update internals to latest, then apply system D1 migrations
76
+
77
+ ${pc.bold('2. Database & Migrations')}
78
+ ${pc.cyan('db:migrate')} Apply all pending local migrations
79
+ ${pc.cyan('db:reset')} Remove local Wrangler state and re-bootstrap database
80
+
81
+ ${pc.bold('3. Database & Types Management')}
82
+ ${pc.cyan('types generate')} (aliases: ${pc.cyan('gen-types')}, ${pc.cyan('gen types typescript')})
83
+ Generate TypeScript interfaces + schema fingerprint from live D1
84
+ --local / --remote Target local SQLite state (default) or remote D1
85
+ --db <name> Override D1 database name
86
+ -o, --output <file> Output path (default: beech.generated.ts; aliases print to stdout)
87
+ ${pc.cyan('types check')} Diff committed generated types against live D1 (writes nothing)
88
+ --local / --remote Target local SQLite state (default) or remote D1
89
+ --db <name> Override D1 database name
90
+ -o, --output <file> File to check (default: beech.generated.ts)
91
+ Exits 1 when the file is missing or stale.
92
+ ${pc.cyan('schema export')} Write beech.schema.ts from live D1 state
93
+ --out <file> Destination (default: beech.schema.ts)
94
+ --stdout Print instead of writing
95
+ --remote Target remote D1
96
+ ${pc.cyan('schema diff')} Compare beech.schema.ts against the deployed schema
97
+ --manifest <f> Manifest path (default: beech.schema.ts)
98
+ Exits 1 when drift is detected.
99
+ ${pc.cyan('schema audit')} Report relation storage and legacy tag definitions that need repair (read-only)
100
+ --remote Target remote D1 instead of local (default: local)
101
+ --db <name> Override D1 database name
102
+ ${pc.cyan('schema migrate')} Migrate pre-0.9 tag/choice seeds to the closed-vocabulary tags contract
103
+ --apply Write the changes (default: dry run, exits 1 while work is pending)
104
+ --remote Target remote D1 instead of local (default: local)
105
+ --db <name> Override D1 database name
106
+ ${pc.cyan('schema plan')} Show what applying beech.schema.ts would change (writes nothing)
107
+ --manifest <f> Manifest path (default: beech.schema.ts)
108
+ --api-url <url> API origin (default: BEECH_API_URL or http://localhost:8789)
109
+ Exits 1 when a seed cannot be applied.
110
+ ${pc.cyan('schema apply')} Apply beech.schema.ts through the control plane
111
+ --manifest <f> Manifest path (default: beech.schema.ts)
112
+ --api-url <url> API origin
113
+ -y, --yes Skip the confirmation prompt
114
+ Additive only. Authorizes in the browser on first use.
115
+ ${pc.cyan('validate')} Validate runtime schema status
116
+
117
+ ${pc.bold('4. Forms & Frontend Generation')}
118
+ ${pc.cyan('forms / form')} Interactive wizard to generate React, Vue, Svelte, or Web Component forms
119
+ --framework <f> Framework: react, vue, svelte, vanilla
120
+ --seed <slug> Seed slug to bind to (e.g. clienti)
121
+ --mode <mode> styled (Tailwind) or headless
122
+ --yes, -y Skip interactive prompts
123
+
124
+ ${pc.bold('5. Local Stack & Docker')}
125
+ ${pc.cyan('dev / start')} Start the local dev environment (Docker + API + Dashboard)
126
+ --plain Avoid Ink visual TUI and run clean log streaming
127
+ ${pc.cyan('dev:stop')} Stop Docker containers without wiping data
128
+ ${pc.cyan('dev:reset')} Stop Docker containers and remove all persistent volumes
129
+ ${pc.cyan('dev:tunnel')} Display Cloudflare tunnel public testing URL
130
+ ${pc.cyan('mailpit:clear')} Clear local test inbox in Mailpit
131
+
132
+ ${pc.bold('6. Logs Streaming')}
133
+ ${pc.cyan('logs <service>')} Show streaming logs for docker service: mailpit, db, tunnel, storage
134
+
135
+ ${pc.bold('7. Quality & Deployment')}
136
+ ${pc.cyan('test')} Run the test suite via Turborepo / Vitest
137
+ --coverage Generate coverage reports
138
+ --diff Run test coverage only for files modified on the branch
139
+ --tier <list> Run one or more tiers: unit, flow, integration, e2e (comma-separated)
140
+ e2e is Playwright-driven and is never selected by --diff
141
+ ${pc.cyan('lint')} Run ESLint quality checks
142
+ ${pc.cyan('setup:cloudflare')} (alias: ${pc.cyan('setup:cf')})
143
+ Interactive 1-step Cloudflare provisioning (D1, R2, Presigned S3 secrets)
144
+ --name <n> Project name override
145
+ --yes, -y Non-interactive mode
146
+ ${pc.cyan('deploy')} Compile, test, deploy to Cloudflare environment
147
+ --skip-check Skip /admin reachability check
148
+ ${pc.cyan('doctor')} Execute React diagnostics check on Dashboard
149
+ `)
150
+ }
151
+
152
+ function cmdBuild() {
153
+ console.log(
154
+ '\nNo build step needed for BeechCMS projects.\n' +
155
+ 'Manage content types dynamically in the BeechCMS Dashboard at /admin.\n'
156
+ )
157
+ }
158
+
159
+ async function cmdInit(args) {
160
+ const initDb = args.includes('--db')
161
+ const remote = args.includes('--remote')
162
+ const dbIdx = args.indexOf('--db-name')
163
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
164
+ const yes = args.includes('--yes') || args.includes('-y')
165
+
166
+ const { init } = await import('@beechcms/cli')
167
+ await init({ initDb, local: !remote, db, nonInteractive: yes })
168
+ }
169
+
170
+ async function cmdSeedLoad() {
171
+ const { seedLoad } = await import('@beechcms/cli')
172
+ await seedLoad({})
173
+ }
174
+
175
+ async function cmdValidate() {
176
+ const { validate } = await import('@beechcms/cli')
177
+ await validate({})
178
+ }
179
+
180
+ async function cmdSeedCreate() {
181
+ const { seedCreate } = await import('@beechcms/cli')
182
+ await seedCreate({})
183
+ }
184
+
185
+ async function cmdDeploy(args) {
186
+ const skipSeed = args.includes('--skip-seed')
187
+ const skipCheck = args.includes('--skip-check')
188
+ const { deploy } = await import('@beechcms/cli')
189
+ await deploy({ skipSeed, skipCheck })
190
+ }
191
+
192
+ async function cmdUpdate() {
193
+ const { update } = await import('@beechcms/cli')
194
+ await update({})
195
+ }
196
+
197
+ async function cmdOnboard(args) {
198
+ const local = !args.includes('--remote')
199
+ const yes = args.includes('--yes') || args.includes('-y')
200
+ const dbIdx = args.indexOf('--db')
201
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
202
+ const { onboard } = await import('@beechcms/cli')
203
+ await onboard({ local, yes, db })
204
+ }
205
+
206
+ async function cmdReset(args) {
207
+ const db = args.includes('--db')
208
+ const docker = args.includes('--docker')
209
+ const all = args.includes('--all')
210
+ const yes = args.includes('--yes') || args.includes('-y')
211
+
212
+ const { reset } = await import('@beechcms/cli')
213
+ await reset({ db, docker, all, yes })
214
+ }
215
+
216
+ async function cmdSchemaDiff(args) {
217
+ const remote = args.includes('--remote')
218
+ const manifestIdx = args.indexOf('--manifest')
219
+ const manifest = manifestIdx !== -1 ? args[manifestIdx + 1] : undefined
220
+ const dbIdx = args.indexOf('--db')
221
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
222
+ const { schemaDiff } = await import('@beechcms/cli')
223
+ await schemaDiff({ local: !remote, manifest, db })
224
+ }
225
+
226
+ async function cmdSchemaAudit(args) {
227
+ const remote = args.includes('--remote')
228
+ const dbIdx = args.indexOf('--db')
229
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
230
+ const { schemaAudit } = await import('@beechcms/cli')
231
+ await schemaAudit({ local: !remote, db })
232
+ }
233
+
234
+ async function cmdSchemaMigrate(args) {
235
+ const remote = args.includes('--remote')
236
+ const apply = args.includes('--apply')
237
+ const dbIdx = args.indexOf('--db')
238
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
239
+ const { schemaMigrate } = await import('@beechcms/cli')
240
+ await schemaMigrate({ apply, local: !remote, db })
241
+ }
242
+
243
+ async function cmdSchemaExport(args) {
244
+ const remote = args.includes('--remote')
245
+ const stdout = args.includes('--stdout')
246
+ const outIdx = args.indexOf('--out')
247
+ const oIdx = args.indexOf('-o')
248
+ const out = stdout ? null
249
+ : outIdx !== -1 ? args[outIdx + 1]
250
+ : oIdx !== -1 ? args[oIdx + 1]
251
+ : undefined
252
+ const dbIdx = args.indexOf('--db')
253
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
254
+ const { schemaExport } = await import('@beechcms/cli')
255
+ await schemaExport({ out, local: !remote, db })
256
+ }
257
+
258
+ async function cmdSchemaPlan(args) {
259
+ const manifestIdx = args.indexOf('--manifest')
260
+ const manifest = manifestIdx !== -1 ? args[manifestIdx + 1] : undefined
261
+ const apiIdx = args.indexOf('--api-url')
262
+ const apiUrl = apiIdx !== -1 ? args[apiIdx + 1] : undefined
263
+ const { schemaPlan } = await import('@beechcms/cli')
264
+ await schemaPlan({ manifest, apiUrl })
265
+ }
266
+
267
+ async function cmdSchemaApply(args) {
268
+ const manifestIdx = args.indexOf('--manifest')
269
+ const manifest = manifestIdx !== -1 ? args[manifestIdx + 1] : undefined
270
+ const apiIdx = args.indexOf('--api-url')
271
+ const apiUrl = apiIdx !== -1 ? args[apiIdx + 1] : undefined
272
+ const yes = args.includes('--yes') || args.includes('-y')
273
+ const { schemaApply } = await import('@beechcms/cli')
274
+ await schemaApply({ manifest, apiUrl, yes })
275
+ }
276
+
277
+ async function cmdGenerateTypes(args) {
278
+ const remote = args.includes('--remote')
279
+ const local = !remote
280
+
281
+ let out = null
282
+ const outIdx = args.indexOf('--out')
283
+ const outputIdx = args.indexOf('--output')
284
+ const oIdx = args.indexOf('-o')
285
+
286
+ if (outIdx !== -1 && args[outIdx + 1]) out = args[outIdx + 1]
287
+ else if (outputIdx !== -1 && args[outputIdx + 1]) out = args[outputIdx + 1]
288
+ else if (oIdx !== -1 && args[oIdx + 1]) out = args[oIdx + 1]
289
+
290
+ const dbIdx = args.indexOf('--db')
291
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
292
+
293
+ // `beech types generate` writes beech.generated.ts; the historical `gen-types` aliases keep
294
+ // their stdout default, so no existing script changes behaviour.
295
+ const isTypesGenerate = command === 'types:generate'
296
+ const destination = out ?? (isTypesGenerate ? undefined : null)
297
+
298
+ const { generateTypes } = await import('@beechcms/cli')
299
+ await generateTypes({ out: destination, local, db })
300
+ }
301
+
302
+ async function cmdTypesCheck(args) {
303
+ const remote = args.includes('--remote')
304
+ const local = !remote
305
+
306
+ let out
307
+ const outIdx = args.indexOf('--out')
308
+ const outputIdx = args.indexOf('--output')
309
+ const oIdx = args.indexOf('-o')
310
+
311
+ if (outIdx !== -1 && args[outIdx + 1]) out = args[outIdx + 1]
312
+ else if (outputIdx !== -1 && args[outputIdx + 1]) out = args[outputIdx + 1]
313
+ else if (oIdx !== -1 && args[oIdx + 1]) out = args[oIdx + 1]
314
+
315
+ const dbIdx = args.indexOf('--db')
316
+ const db = dbIdx !== -1 ? args[dbIdx + 1] : undefined
317
+
318
+ const { typesCheck } = await import('@beechcms/cli')
319
+ await typesCheck({ out, local, db })
320
+ }
321
+
322
+ async function cmdForms(args) {
323
+ const yes = args.includes('--yes') || args.includes('-y')
324
+ const json = args.includes('--json')
325
+ const frameworkIdx = args.indexOf('--framework')
326
+ const framework = frameworkIdx !== -1 ? args[frameworkIdx + 1] : undefined
327
+ const seedIdx = args.indexOf('--seed')
328
+ const seed = seedIdx !== -1 ? args[seedIdx + 1] : undefined
329
+ const modeIdx = args.indexOf('--mode')
330
+ const mode = modeIdx !== -1 ? args[modeIdx + 1] : undefined
331
+ const outIdx = args.indexOf('--out')
332
+ const out = outIdx !== -1 ? args[outIdx + 1] : undefined
333
+
334
+ const { forms } = await import('@beechcms/cli')
335
+ await forms({ framework, seed, mode, out, yes, json })
336
+ }
337
+
338
+ // New unified command wrappers:
339
+ async function cmdDbMigrate() {
340
+ const { dbMigrate } = await import('@beechcms/cli')
341
+ await dbMigrate({})
342
+ }
343
+
344
+ async function cmdDbReset() {
345
+ const { dbReset } = await import('@beechcms/cli')
346
+ await dbReset({})
347
+ }
348
+
349
+ async function cmdDev(args) {
350
+ const plain = args.includes('--plain')
351
+ const { dev } = await import('@beechcms/cli')
352
+ await dev({ plain })
353
+ }
354
+
355
+ async function cmdDevStop() {
356
+ const { devStop } = await import('@beechcms/cli')
357
+ await devStop()
358
+ }
359
+
360
+ async function cmdDevReset() {
361
+ const { devReset } = await import('@beechcms/cli')
362
+ await devReset()
363
+ }
364
+
365
+ async function cmdDevTunnel() {
366
+ const { devTunnel } = await import('@beechcms/cli')
367
+ await devTunnel()
368
+ }
369
+
370
+ async function cmdMailpitClear() {
371
+ const { mailpitClear } = await import('@beechcms/cli')
372
+ await mailpitClear()
373
+ }
374
+
375
+ async function cmdLogs(args) {
376
+ const service = args[0]
377
+ const { logs } = await import('@beechcms/cli')
378
+ await logs({ service })
379
+ }
380
+
381
+ async function cmdTest(args) {
382
+ const coverage = args.includes('--coverage')
383
+ const diff = args.includes('--diff')
384
+ const tierIdx = args.indexOf('--tier')
385
+ const tier = tierIdx !== -1 && args[tierIdx + 1] && !args[tierIdx + 1].startsWith('--')
386
+ ? args[tierIdx + 1]
387
+ : undefined
388
+ const { test } = await import('@beechcms/cli')
389
+ await test({ coverage, diff, tier })
390
+ }
391
+
392
+ async function cmdLint() {
393
+ const { lint } = await import('@beechcms/cli')
394
+ await lint()
395
+ }
396
+
397
+ async function cmdDoctor() {
398
+ const { doctor } = await import('@beechcms/cli')
399
+ await doctor()
400
+ }
401
+
402
+ async function cmdSetupCloudflare(args) {
403
+ const yes = args.includes('--yes') || args.includes('-y')
404
+ const nameIdx = args.indexOf('--name')
405
+ const projectName = nameIdx !== -1 ? args[nameIdx + 1] : undefined
406
+ const { setupCloudflare } = await import('@beechcms/cli')
407
+ await setupCloudflare({ projectName, nonInteractive: yes })
408
+ }
409
+
410
+ const handler = COMMANDS[command]
411
+ if (!handler) {
412
+ help()
413
+ if (command && command !== '--help' && command !== '-h' && command !== 'help') process.exit(1)
414
+ } else if (args.includes('--help') || args.includes('-h')) {
415
+ help()
416
+ } else {
417
+ await handler(args)
418
+ }