@nextsparkjs/cli 0.1.0-beta.10 → 0.1.0-beta.100

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/cli.js CHANGED
@@ -1,455 +1,72 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
- __require,
3
+ buildCommand,
4
+ devCommand,
5
+ generateCommand,
6
+ getAIWorkflowDir,
7
+ getCoreDir,
8
+ getProjectRoot,
9
+ registryBuildCommand,
10
+ registryWatchCommand
11
+ } from "./chunk-QXD4PBX6.js";
12
+ import {
4
13
  addPlugin,
5
14
  addPluginCommand,
6
15
  fetchPackage,
7
16
  installTheme,
8
17
  runPostinstall,
9
18
  validateTheme
10
- } from "./chunk-ALB2C27N.js";
19
+ } from "./chunk-5GBCARGX.js";
20
+ import {
21
+ __require
22
+ } from "./chunk-DGUM43GV.js";
11
23
 
12
24
  // src/cli.ts
25
+ import { config } from "dotenv";
13
26
  import { Command } from "commander";
14
- import chalk15 from "chalk";
15
-
16
- // src/commands/dev.ts
17
- import { spawn } from "child_process";
18
- import chalk from "chalk";
19
- import ora from "ora";
20
-
21
- // src/utils/paths.ts
22
- import { existsSync } from "fs";
23
- import { resolve, dirname } from "path";
24
- import { fileURLToPath } from "url";
25
- var __filename = fileURLToPath(import.meta.url);
26
- var __dirname = dirname(__filename);
27
- function getCoreDir() {
28
- const cwd = process.cwd();
29
- const npmCorePath = resolve(cwd, "node_modules", "@nextsparkjs", "core");
30
- if (existsSync(npmCorePath)) {
31
- return npmCorePath;
32
- }
33
- const monorepoCorePath = resolve(__dirname, "..", "..", "..", "..", "core");
34
- if (existsSync(monorepoCorePath)) {
35
- return monorepoCorePath;
36
- }
37
- const cwdMonorepoCorePath = resolve(cwd, "..", "..", "packages", "core");
38
- if (existsSync(cwdMonorepoCorePath)) {
39
- return cwdMonorepoCorePath;
40
- }
41
- throw new Error(
42
- "Could not find @nextsparkjs/core. Make sure it is installed as a dependency or you are running from within the monorepo."
43
- );
44
- }
45
- function getProjectRoot() {
46
- return process.cwd();
47
- }
48
- function isMonorepoMode() {
49
- const cwd = process.cwd();
50
- const npmCorePath = resolve(cwd, "node_modules", "@nextsparkjs", "core");
51
- return !existsSync(npmCorePath);
52
- }
53
-
54
- // src/commands/dev.ts
55
- async function devCommand(options) {
56
- const spinner = ora("Starting development environment...").start();
57
- try {
58
- const coreDir = getCoreDir();
59
- const projectRoot = getProjectRoot();
60
- const mode = isMonorepoMode() ? "monorepo" : "npm";
61
- spinner.succeed(`Core found at: ${coreDir} (${mode} mode)`);
62
- const processes = [];
63
- if (options.registry) {
64
- console.log(chalk.blue("\n[Registry] Starting registry builder with watch mode..."));
65
- const registryProcess = spawn("node", ["scripts/build/registry.mjs", "--watch"], {
66
- cwd: coreDir,
67
- stdio: "inherit",
68
- env: {
69
- ...process.env,
70
- NEXTSPARK_PROJECT_ROOT: projectRoot
71
- }
72
- });
73
- processes.push(registryProcess);
74
- registryProcess.on("error", (err) => {
75
- console.error(chalk.red(`[Registry] Error: ${err.message}`));
76
- });
77
- }
78
- console.log(chalk.green(`
79
- [Dev] Starting Next.js dev server on port ${options.port}...`));
80
- const devProcess = spawn("npx", ["next", "dev", "-p", options.port], {
81
- cwd: projectRoot,
82
- stdio: "inherit",
83
- env: {
84
- ...process.env,
85
- NEXTSPARK_CORE_DIR: coreDir
86
- }
87
- });
88
- processes.push(devProcess);
89
- devProcess.on("error", (err) => {
90
- console.error(chalk.red(`[Dev] Error: ${err.message}`));
91
- process.exit(1);
92
- });
93
- const cleanup = () => {
94
- console.log(chalk.yellow("\nShutting down..."));
95
- processes.forEach((p) => {
96
- if (!p.killed) {
97
- p.kill("SIGTERM");
98
- }
99
- });
100
- process.exit(0);
101
- };
102
- process.on("SIGINT", cleanup);
103
- process.on("SIGTERM", cleanup);
104
- devProcess.on("exit", (code) => {
105
- cleanup();
106
- process.exit(code ?? 0);
107
- });
108
- } catch (error) {
109
- spinner.fail("Failed to start development environment");
110
- if (error instanceof Error) {
111
- console.error(chalk.red(error.message));
112
- }
113
- process.exit(1);
114
- }
115
- }
116
-
117
- // src/commands/build.ts
118
- import { spawn as spawn2 } from "child_process";
119
- import { existsSync as existsSync2, readFileSync } from "fs";
120
- import { join } from "path";
121
- import chalk2 from "chalk";
122
- import ora2 from "ora";
123
- function loadProjectEnv(projectRoot) {
124
- const envPath = join(projectRoot, ".env");
125
- const envVars = {};
126
- if (existsSync2(envPath)) {
127
- const content = readFileSync(envPath, "utf-8");
128
- for (const line of content.split("\n")) {
129
- const trimmed = line.trim();
130
- if (trimmed && !trimmed.startsWith("#")) {
131
- const [key, ...valueParts] = trimmed.split("=");
132
- if (key && valueParts.length > 0) {
133
- let value = valueParts.join("=");
134
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
135
- value = value.slice(1, -1);
136
- }
137
- envVars[key] = value;
138
- }
139
- }
140
- }
141
- }
142
- return envVars;
143
- }
144
- async function buildCommand(options) {
145
- const spinner = ora2("Preparing production build...").start();
146
- try {
147
- const coreDir = getCoreDir();
148
- const projectRoot = getProjectRoot();
149
- spinner.succeed("Core package found");
150
- const projectEnv = loadProjectEnv(projectRoot);
151
- if (options.registry) {
152
- spinner.start("Generating registries...");
153
- await new Promise((resolve4, reject) => {
154
- const registryProcess = spawn2("node", ["scripts/build/registry.mjs"], {
155
- cwd: coreDir,
156
- stdio: "pipe",
157
- env: {
158
- ...projectEnv,
159
- ...process.env,
160
- NEXTSPARK_PROJECT_ROOT: projectRoot
161
- }
162
- });
163
- let stderr = "";
164
- registryProcess.stderr?.on("data", (data) => {
165
- stderr += data.toString();
166
- });
167
- registryProcess.on("close", (code) => {
168
- if (code === 0) {
169
- resolve4();
170
- } else {
171
- reject(new Error(`Registry generation failed: ${stderr}`));
172
- }
173
- });
174
- registryProcess.on("error", reject);
175
- });
176
- spinner.succeed("Registries generated");
177
- }
178
- spinner.start("Building for production...");
179
- const buildProcess = spawn2("npx", ["next", "build"], {
180
- cwd: projectRoot,
181
- stdio: "inherit",
182
- env: {
183
- ...projectEnv,
184
- ...process.env,
185
- NEXTSPARK_CORE_DIR: coreDir,
186
- NODE_ENV: "production"
187
- }
188
- });
189
- buildProcess.on("error", (err) => {
190
- spinner.fail("Build failed");
191
- console.error(chalk2.red(err.message));
192
- process.exit(1);
193
- });
194
- buildProcess.on("close", (code) => {
195
- if (code === 0) {
196
- console.log(chalk2.green("\nBuild completed successfully!"));
197
- process.exit(0);
198
- } else {
199
- console.error(chalk2.red(`
200
- Build failed with exit code ${code}`));
201
- process.exit(code ?? 1);
202
- }
203
- });
204
- } catch (error) {
205
- spinner.fail("Build preparation failed");
206
- if (error instanceof Error) {
207
- console.error(chalk2.red(error.message));
208
- }
209
- process.exit(1);
210
- }
211
- }
212
-
213
- // src/commands/generate.ts
214
- import { spawn as spawn3 } from "child_process";
215
- import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
216
- import { join as join2 } from "path";
217
- import chalk3 from "chalk";
218
- import ora3 from "ora";
219
- function loadProjectEnv2(projectRoot) {
220
- const envPath = join2(projectRoot, ".env");
221
- const envVars = {};
222
- if (existsSync3(envPath)) {
223
- const content = readFileSync2(envPath, "utf-8");
224
- for (const line of content.split("\n")) {
225
- const trimmed = line.trim();
226
- if (trimmed && !trimmed.startsWith("#")) {
227
- const [key, ...valueParts] = trimmed.split("=");
228
- if (key && valueParts.length > 0) {
229
- let value = valueParts.join("=");
230
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
231
- value = value.slice(1, -1);
232
- }
233
- envVars[key] = value;
234
- }
235
- }
236
- }
237
- }
238
- return envVars;
239
- }
240
- async function generateCommand(options) {
241
- const spinner = ora3("Preparing registry generation...").start();
242
- try {
243
- const coreDir = getCoreDir();
244
- const projectRoot = getProjectRoot();
245
- const mode = isMonorepoMode() ? "monorepo" : "npm";
246
- spinner.succeed(`Core found at: ${coreDir} (${mode} mode)`);
247
- const projectEnv = loadProjectEnv2(projectRoot);
248
- const watchArg = options.watch ? ["--watch"] : [];
249
- const action = options.watch ? "Watching" : "Generating";
250
- console.log(chalk3.blue(`
251
- ${action} registries...`));
252
- const generateProcess = spawn3("node", ["scripts/build/registry.mjs", ...watchArg], {
253
- cwd: coreDir,
254
- stdio: "inherit",
255
- env: {
256
- ...projectEnv,
257
- ...process.env,
258
- NEXTSPARK_PROJECT_ROOT: projectRoot
259
- }
260
- });
261
- generateProcess.on("error", (err) => {
262
- console.error(chalk3.red(`Error: ${err.message}`));
263
- process.exit(1);
264
- });
265
- generateProcess.on("close", (code) => {
266
- if (code === 0) {
267
- if (!options.watch) {
268
- console.log(chalk3.green("\nRegistry generation completed!"));
269
- }
270
- process.exit(0);
271
- } else {
272
- console.error(chalk3.red(`
273
- Registry generation failed with exit code ${code}`));
274
- process.exit(code ?? 1);
275
- }
276
- });
277
- if (options.watch) {
278
- const cleanup = () => {
279
- console.log(chalk3.yellow("\nStopping watcher..."));
280
- if (!generateProcess.killed) {
281
- generateProcess.kill("SIGTERM");
282
- }
283
- process.exit(0);
284
- };
285
- process.on("SIGINT", cleanup);
286
- process.on("SIGTERM", cleanup);
287
- }
288
- } catch (error) {
289
- spinner.fail("Registry generation failed");
290
- if (error instanceof Error) {
291
- console.error(chalk3.red(error.message));
292
- }
293
- process.exit(1);
294
- }
295
- }
296
-
297
- // src/commands/registry.ts
298
- import { spawn as spawn4 } from "child_process";
299
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
300
- import { join as join3 } from "path";
301
- import chalk4 from "chalk";
302
- import ora4 from "ora";
303
- function loadProjectEnv3(projectRoot) {
304
- const envPath = join3(projectRoot, ".env");
305
- const envVars = {};
306
- if (existsSync4(envPath)) {
307
- const content = readFileSync3(envPath, "utf-8");
308
- for (const line of content.split("\n")) {
309
- const trimmed = line.trim();
310
- if (trimmed && !trimmed.startsWith("#")) {
311
- const [key, ...valueParts] = trimmed.split("=");
312
- if (key && valueParts.length > 0) {
313
- let value = valueParts.join("=");
314
- if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
315
- value = value.slice(1, -1);
316
- }
317
- envVars[key] = value;
318
- }
319
- }
320
- }
321
- }
322
- return envVars;
323
- }
324
- async function registryBuildCommand() {
325
- const spinner = ora4("Building registries...").start();
326
- try {
327
- const coreDir = getCoreDir();
328
- const projectRoot = getProjectRoot();
329
- const mode = isMonorepoMode() ? "monorepo" : "npm";
330
- const projectEnv = loadProjectEnv3(projectRoot);
331
- spinner.text = `Building registries (${mode} mode)...`;
332
- const buildProcess = spawn4("node", ["scripts/build/registry.mjs"], {
333
- cwd: coreDir,
334
- stdio: "pipe",
335
- env: {
336
- ...projectEnv,
337
- ...process.env,
338
- NEXTSPARK_PROJECT_ROOT: projectRoot
339
- }
340
- });
341
- let stdout = "";
342
- let stderr = "";
343
- buildProcess.stdout?.on("data", (data) => {
344
- stdout += data.toString();
345
- });
346
- buildProcess.stderr?.on("data", (data) => {
347
- stderr += data.toString();
348
- });
349
- buildProcess.on("close", (code) => {
350
- if (code === 0) {
351
- spinner.succeed("Registries built successfully");
352
- if (stdout.trim()) {
353
- console.log(chalk4.gray(stdout.trim()));
354
- }
355
- process.exit(0);
356
- } else {
357
- spinner.fail("Registry build failed");
358
- if (stderr.trim()) {
359
- console.error(chalk4.red(stderr.trim()));
360
- }
361
- process.exit(code ?? 1);
362
- }
363
- });
364
- buildProcess.on("error", (err) => {
365
- spinner.fail("Registry build failed");
366
- console.error(chalk4.red(err.message));
367
- process.exit(1);
368
- });
369
- } catch (error) {
370
- spinner.fail("Registry build failed");
371
- if (error instanceof Error) {
372
- console.error(chalk4.red(error.message));
373
- }
374
- process.exit(1);
375
- }
376
- }
377
- async function registryWatchCommand() {
378
- const spinner = ora4("Starting registry watcher...").start();
379
- try {
380
- const coreDir = getCoreDir();
381
- const projectRoot = getProjectRoot();
382
- const mode = isMonorepoMode() ? "monorepo" : "npm";
383
- spinner.succeed(`Registry watcher started (${mode} mode)`);
384
- console.log(chalk4.blue("\nWatching for changes... Press Ctrl+C to stop.\n"));
385
- const projectEnv = loadProjectEnv3(projectRoot);
386
- const watchProcess = spawn4("node", ["scripts/build/registry.mjs", "--watch"], {
387
- cwd: coreDir,
388
- stdio: "inherit",
389
- env: {
390
- ...projectEnv,
391
- ...process.env,
392
- NEXTSPARK_PROJECT_ROOT: projectRoot
393
- }
394
- });
395
- watchProcess.on("error", (err) => {
396
- console.error(chalk4.red(`Watcher error: ${err.message}`));
397
- process.exit(1);
398
- });
399
- const cleanup = () => {
400
- console.log(chalk4.yellow("\nStopping registry watcher..."));
401
- if (!watchProcess.killed) {
402
- watchProcess.kill("SIGTERM");
403
- }
404
- process.exit(0);
405
- };
406
- process.on("SIGINT", cleanup);
407
- process.on("SIGTERM", cleanup);
408
- watchProcess.on("close", (code) => {
409
- if (code !== 0) {
410
- console.error(chalk4.red(`
411
- Watcher exited with code ${code}`));
412
- }
413
- process.exit(code ?? 0);
414
- });
415
- } catch (error) {
416
- spinner.fail("Failed to start registry watcher");
417
- if (error instanceof Error) {
418
- console.error(chalk4.red(error.message));
419
- }
420
- process.exit(1);
421
- }
422
- }
27
+ import chalk16 from "chalk";
28
+ import { readFileSync as readFileSync8 } from "fs";
423
29
 
424
30
  // src/commands/init.ts
425
- import { existsSync as existsSync8, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync7 } from "fs";
426
- import { join as join8 } from "path";
427
- import chalk12 from "chalk";
428
- import ora8 from "ora";
31
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync4 } from "fs";
32
+ import { join as join5 } from "path";
33
+ import chalk8 from "chalk";
34
+ import ora4 from "ora";
429
35
 
430
36
  // src/wizard/index.ts
431
- import chalk11 from "chalk";
432
- import ora7 from "ora";
433
- import { confirm as confirm5 } from "@inquirer/prompts";
37
+ import chalk7 from "chalk";
38
+ import ora3 from "ora";
39
+ import { confirm as confirm5, select as select6 } from "@inquirer/prompts";
434
40
  import { execSync } from "child_process";
435
- import { existsSync as existsSync7, readdirSync } from "fs";
436
- import { join as join7 } from "path";
41
+ import { existsSync as existsSync3, readdirSync } from "fs";
42
+ import { join as join4 } from "path";
437
43
 
438
44
  // src/wizard/banner.ts
439
- import chalk5 from "chalk";
440
- import { readFileSync as readFileSync4 } from "fs";
441
- import { fileURLToPath as fileURLToPath2 } from "url";
442
- import { dirname as dirname2, join as join4 } from "path";
443
- var __filename2 = fileURLToPath2(import.meta.url);
444
- var __dirname2 = dirname2(__filename2);
45
+ import chalk from "chalk";
46
+ import { readFileSync } from "fs";
47
+ import { fileURLToPath } from "url";
48
+ import { dirname, join } from "path";
49
+ var __filename = fileURLToPath(import.meta.url);
50
+ var __dirname = dirname(__filename);
445
51
  function getCliVersion() {
446
- try {
447
- const packageJsonPath = join4(__dirname2, "../../package.json");
448
- const packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
449
- return packageJson.version || "unknown";
450
- } catch {
451
- return "unknown";
52
+ const possiblePaths = [
53
+ join(__dirname, "../package.json"),
54
+ // from dist/
55
+ join(__dirname, "../../package.json"),
56
+ // from dist/wizard/ or src/wizard/
57
+ join(__dirname, "../../../package.json")
58
+ // fallback
59
+ ];
60
+ for (const packageJsonPath of possiblePaths) {
61
+ try {
62
+ const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
63
+ if (packageJson.name === "@nextsparkjs/cli" && packageJson.version) {
64
+ return packageJson.version;
65
+ }
66
+ } catch {
67
+ }
452
68
  }
69
+ return "unknown";
453
70
  }
454
71
  var BANNER = `
455
72
  _ __ __ _____ __
@@ -461,26 +78,29 @@ var BANNER = `
461
78
  `;
462
79
  function showBanner() {
463
80
  const version = getCliVersion();
464
- console.log(chalk5.cyan(BANNER));
465
- console.log(chalk5.bold.white(" Welcome to NextSpark - The Complete SaaS Framework for Next.js"));
466
- console.log(chalk5.gray(` Version ${version} - Create production-ready SaaS applications in minutes
81
+ console.log(chalk.cyan(BANNER));
82
+ console.log(chalk.bold.white(" Welcome to NextSpark - The Complete SaaS Framework for Next.js"));
83
+ console.log(chalk.gray(` Version ${version} - Create production-ready SaaS applications in minutes
467
84
  `));
468
- console.log(chalk5.gray(" " + "=".repeat(60) + "\n"));
85
+ console.log(chalk.gray(" " + "=".repeat(60) + "\n"));
469
86
  }
470
87
  function showSection(title, step, totalSteps) {
471
88
  console.log("");
472
- console.log(chalk5.cyan(` Step ${step}/${totalSteps}: ${title}`));
473
- console.log(chalk5.gray(" " + "-".repeat(40)));
89
+ console.log(chalk.cyan(` Step ${step}/${totalSteps}: ${title}`));
90
+ console.log(chalk.gray(" " + "-".repeat(40)));
474
91
  console.log("");
475
92
  }
93
+ function showSuccess(message) {
94
+ console.log(chalk.green(` \u2713 ${message}`));
95
+ }
476
96
  function showWarning(message) {
477
- console.log(chalk5.yellow(` \u26A0 ${message}`));
97
+ console.log(chalk.yellow(` \u26A0 ${message}`));
478
98
  }
479
99
  function showError(message) {
480
- console.log(chalk5.red(` \u2717 ${message}`));
100
+ console.log(chalk.red(` \u2717 ${message}`));
481
101
  }
482
102
  function showInfo(message) {
483
- console.log(chalk5.blue(` \u2139 ${message}`));
103
+ console.log(chalk.blue(` \u2139 ${message}`));
484
104
  }
485
105
 
486
106
  // src/wizard/prompts/project-info.ts
@@ -516,7 +136,7 @@ function validateSlug(slug) {
516
136
  return true;
517
137
  }
518
138
  async function promptProjectInfo() {
519
- showSection("Project Information", 1, 5);
139
+ showSection("Project Information", 2, 10);
520
140
  const projectName = await input({
521
141
  message: "What is your project name?",
522
142
  default: "My SaaS App",
@@ -539,8 +159,43 @@ async function promptProjectInfo() {
539
159
  };
540
160
  }
541
161
 
162
+ // src/wizard/prompts/project-type.ts
163
+ import { select } from "@inquirer/prompts";
164
+ var PROJECT_TYPE_OPTIONS = [
165
+ {
166
+ name: "Web only",
167
+ value: "web",
168
+ description: "Next.js web application with NextSpark. Standard flat project structure."
169
+ },
170
+ {
171
+ name: "Web + Mobile",
172
+ value: "web-mobile",
173
+ description: "Monorepo with Next.js web app and Expo mobile app sharing the same backend."
174
+ }
175
+ ];
176
+ async function promptProjectType() {
177
+ showSection("Project Type", 1, 10);
178
+ const projectType = await select({
179
+ message: "What type of project do you want to create?",
180
+ choices: PROJECT_TYPE_OPTIONS,
181
+ default: "web"
182
+ });
183
+ const selectedOption = PROJECT_TYPE_OPTIONS.find((o) => o.value === projectType);
184
+ if (selectedOption) {
185
+ showInfo(selectedOption.description);
186
+ }
187
+ if (projectType === "web-mobile") {
188
+ console.log("");
189
+ showInfo("Your project will be a pnpm monorepo with web/ and mobile/ directories.");
190
+ showInfo("The mobile app will use Expo and share the same backend API.");
191
+ }
192
+ return {
193
+ projectType
194
+ };
195
+ }
196
+
542
197
  // src/wizard/prompts/team-config.ts
543
- import { select, checkbox } from "@inquirer/prompts";
198
+ import { select as select2, checkbox } from "@inquirer/prompts";
544
199
 
545
200
  // src/wizard/types.ts
546
201
  var AVAILABLE_LOCALES = {
@@ -588,8 +243,8 @@ var ROLE_OPTIONS = [
588
243
  { name: "Viewer (Read-only access)", value: "viewer", checked: true }
589
244
  ];
590
245
  async function promptTeamConfig() {
591
- showSection("Team Configuration", 2, 5);
592
- const teamMode = await select({
246
+ showSection("Team Configuration", 3, 10);
247
+ const teamMode = await select2({
593
248
  message: "What team mode do you need?",
594
249
  choices: TEAM_MODE_OPTIONS,
595
250
  default: "multi-tenant"
@@ -620,7 +275,7 @@ async function promptTeamConfig() {
620
275
  }
621
276
 
622
277
  // src/wizard/prompts/i18n-config.ts
623
- import { select as select2, checkbox as checkbox2 } from "@inquirer/prompts";
278
+ import { select as select3, checkbox as checkbox2 } from "@inquirer/prompts";
624
279
  var LOCALE_OPTIONS = Object.entries(AVAILABLE_LOCALES).map(([value, name]) => ({
625
280
  name: `${name} (${value})`,
626
281
  value,
@@ -628,7 +283,7 @@ var LOCALE_OPTIONS = Object.entries(AVAILABLE_LOCALES).map(([value, name]) => ({
628
283
  // English selected by default
629
284
  }));
630
285
  async function promptI18nConfig() {
631
- showSection("Internationalization", 3, 5);
286
+ showSection("Internationalization", 4, 10);
632
287
  const supportedLocales = await checkbox2({
633
288
  message: "Which languages do you want to support?",
634
289
  choices: LOCALE_OPTIONS,
@@ -644,7 +299,7 @@ async function promptI18nConfig() {
644
299
  showInfo(`Default language set to ${AVAILABLE_LOCALES[defaultLocale]}`);
645
300
  } else {
646
301
  console.log("");
647
- defaultLocale = await select2({
302
+ defaultLocale = await select3({
648
303
  message: "Which should be the default language?",
649
304
  choices: supportedLocales.map((locale) => ({
650
305
  name: `${AVAILABLE_LOCALES[locale]} (${locale})`,
@@ -660,7 +315,7 @@ async function promptI18nConfig() {
660
315
  }
661
316
 
662
317
  // src/wizard/prompts/billing-config.ts
663
- import { select as select3 } from "@inquirer/prompts";
318
+ import { select as select4 } from "@inquirer/prompts";
664
319
  var BILLING_MODEL_OPTIONS = [
665
320
  {
666
321
  name: "Free (No payments)",
@@ -679,8 +334,8 @@ var BILLING_MODEL_OPTIONS = [
679
334
  }
680
335
  ];
681
336
  async function promptBillingConfig() {
682
- showSection("Billing Configuration", 4, 5);
683
- const billingModel = await select3({
337
+ showSection("Billing Configuration", 5, 10);
338
+ const billingModel = await select4({
684
339
  message: "What billing model do you want to use?",
685
340
  choices: BILLING_MODEL_OPTIONS,
686
341
  default: "freemium"
@@ -692,7 +347,7 @@ async function promptBillingConfig() {
692
347
  let currency = "usd";
693
348
  if (billingModel !== "free") {
694
349
  console.log("");
695
- currency = await select3({
350
+ currency = await select4({
696
351
  message: "What currency will you use?",
697
352
  choices: CURRENCIES.map((c) => ({
698
353
  name: c.label,
@@ -744,7 +399,7 @@ var FEATURE_OPTIONS = [
744
399
  }
745
400
  ];
746
401
  async function promptFeaturesConfig() {
747
- showSection("Features", 5, 5);
402
+ showSection("Features", 6, 10);
748
403
  showInfo("Select the features you want to include in your project.");
749
404
  showInfo("You can add or remove features later by editing your config files.");
750
405
  console.log("");
@@ -781,7 +436,7 @@ var CONTENT_FEATURE_OPTIONS = [
781
436
  }
782
437
  ];
783
438
  async function promptContentFeaturesConfig(mode = "interactive", totalSteps = 9) {
784
- showSection("Content Features", 6, totalSteps);
439
+ showSection("Content Features", 7, totalSteps);
785
440
  showInfo("Enable optional content features for your project.");
786
441
  showInfo("These features add entities and blocks for building pages and blog posts.");
787
442
  console.log("");
@@ -832,7 +487,7 @@ function getDefaultAuthConfig() {
832
487
  };
833
488
  }
834
489
  async function promptAuthConfig(mode = "interactive", totalSteps = 8) {
835
- showSection("Authentication", 6, totalSteps);
490
+ showSection("Authentication", 8, totalSteps);
836
491
  showInfo("Configure how users will authenticate to your application.");
837
492
  console.log("");
838
493
  const selectedMethods = await checkbox5({
@@ -916,7 +571,7 @@ function getDefaultDashboardConfig() {
916
571
  };
917
572
  }
918
573
  async function promptDashboardConfig(mode = "interactive", totalSteps = 8) {
919
- showSection("Dashboard", 7, totalSteps);
574
+ showSection("Dashboard", 9, totalSteps);
920
575
  showInfo("Configure your dashboard user interface.");
921
576
  showInfo("These settings can be changed later in your theme config.");
922
577
  console.log("");
@@ -968,7 +623,7 @@ function getDefaultDevConfig() {
968
623
  };
969
624
  }
970
625
  async function promptDevConfig(mode = "interactive", totalSteps = 8) {
971
- showSection("Development Tools", 8, totalSteps);
626
+ showSection("Development Tools", 10, totalSteps);
972
627
  showInfo("Configure development and debugging tools.");
973
628
  showWarning("These tools are disabled in production builds.");
974
629
  console.log("");
@@ -995,17 +650,17 @@ async function promptDevConfig(mode = "interactive", totalSteps = 8) {
995
650
  }
996
651
 
997
652
  // src/wizard/prompts/theme-selection.ts
998
- import { select as select4 } from "@inquirer/prompts";
999
- import chalk6 from "chalk";
653
+ import { select as select5 } from "@inquirer/prompts";
654
+ import chalk2 from "chalk";
1000
655
  async function promptThemeSelection() {
1001
656
  console.log("");
1002
- console.log(chalk6.cyan(" Reference Theme Installation"));
1003
- console.log(chalk6.gray(" " + "-".repeat(40)));
657
+ console.log(chalk2.cyan(" Reference Theme Installation"));
658
+ console.log(chalk2.gray(" " + "-".repeat(40)));
1004
659
  console.log("");
1005
- console.log(chalk6.gray(" A reference theme provides a complete example to learn from."));
1006
- console.log(chalk6.gray(" Your custom theme (based on starter) will be your active theme."));
660
+ console.log(chalk2.gray(" A reference theme provides a complete example to learn from."));
661
+ console.log(chalk2.gray(" Your custom theme (based on starter) will be your active theme."));
1007
662
  console.log("");
1008
- const theme = await select4({
663
+ const theme = await select5({
1009
664
  message: "Which reference theme would you like to install?",
1010
665
  choices: [
1011
666
  {
@@ -1041,7 +696,7 @@ async function promptThemeSelection() {
1041
696
 
1042
697
  // src/wizard/prompts/plugins-selection.ts
1043
698
  import { checkbox as checkbox8 } from "@inquirer/prompts";
1044
- import chalk7 from "chalk";
699
+ import chalk3 from "chalk";
1045
700
  var THEME_REQUIRED_PLUGINS = {
1046
701
  "default": ["langchain"],
1047
702
  "blog": [],
@@ -1051,11 +706,11 @@ var THEME_REQUIRED_PLUGINS = {
1051
706
  async function promptPluginsSelection(selectedTheme) {
1052
707
  const requiredPlugins = selectedTheme ? THEME_REQUIRED_PLUGINS[selectedTheme] || [] : [];
1053
708
  console.log("");
1054
- console.log(chalk7.cyan(" Plugin Selection"));
1055
- console.log(chalk7.gray(" " + "-".repeat(40)));
709
+ console.log(chalk3.cyan(" Plugin Selection"));
710
+ console.log(chalk3.gray(" " + "-".repeat(40)));
1056
711
  if (requiredPlugins.length > 0) {
1057
712
  console.log("");
1058
- console.log(chalk7.gray(` Note: ${requiredPlugins.join(", ")} will be installed (required by theme)`));
713
+ console.log(chalk3.gray(` Note: ${requiredPlugins.join(", ")} will be installed (required by theme)`));
1059
714
  }
1060
715
  console.log("");
1061
716
  const plugins = await checkbox8({
@@ -1098,17 +753,19 @@ import { confirm as confirm4, input as input3 } from "@inquirer/prompts";
1098
753
 
1099
754
  // src/wizard/prompts/index.ts
1100
755
  async function runAllPrompts() {
756
+ const projectTypeConfig = await promptProjectType();
1101
757
  const projectInfo = await promptProjectInfo();
1102
758
  const teamConfig = await promptTeamConfig();
1103
759
  const i18nConfig = await promptI18nConfig();
1104
760
  const billingConfig = await promptBillingConfig();
1105
761
  const featuresConfig = await promptFeaturesConfig();
1106
- const contentFeaturesConfig = await promptContentFeaturesConfig("interactive", 9);
1107
- const authConfig = await promptAuthConfig("interactive", 9);
1108
- const dashboardConfig = await promptDashboardConfig("interactive", 9);
1109
- const devConfig = await promptDevConfig("interactive", 9);
762
+ const contentFeaturesConfig = await promptContentFeaturesConfig("interactive", 10);
763
+ const authConfig = await promptAuthConfig("interactive", 10);
764
+ const dashboardConfig = await promptDashboardConfig("interactive", 10);
765
+ const devConfig = await promptDevConfig("interactive", 10);
1110
766
  return {
1111
767
  ...projectInfo,
768
+ ...projectTypeConfig,
1112
769
  ...teamConfig,
1113
770
  ...i18nConfig,
1114
771
  ...billingConfig,
@@ -1120,17 +777,19 @@ async function runAllPrompts() {
1120
777
  };
1121
778
  }
1122
779
  async function runQuickPrompts() {
780
+ const projectTypeConfig = await promptProjectType();
1123
781
  const projectInfo = await promptProjectInfo();
1124
782
  const teamConfig = await promptTeamConfig();
1125
783
  const i18nConfig = await promptI18nConfig();
1126
784
  const billingConfig = await promptBillingConfig();
1127
785
  const featuresConfig = await promptFeaturesConfig();
1128
- const contentFeaturesConfig = await promptContentFeaturesConfig("quick", 9);
786
+ const contentFeaturesConfig = await promptContentFeaturesConfig("quick", 10);
1129
787
  const authConfig = { auth: getDefaultAuthConfig() };
1130
788
  const dashboardConfig = { dashboard: getDefaultDashboardConfig() };
1131
789
  const devConfig = { dev: getDefaultDevConfig() };
1132
790
  return {
1133
791
  ...projectInfo,
792
+ ...projectTypeConfig,
1134
793
  ...teamConfig,
1135
794
  ...i18nConfig,
1136
795
  ...billingConfig,
@@ -1142,17 +801,19 @@ async function runQuickPrompts() {
1142
801
  };
1143
802
  }
1144
803
  async function runExpertPrompts() {
804
+ const projectTypeConfig = await promptProjectType();
1145
805
  const projectInfo = await promptProjectInfo();
1146
806
  const teamConfig = await promptTeamConfig();
1147
807
  const i18nConfig = await promptI18nConfig();
1148
808
  const billingConfig = await promptBillingConfig();
1149
809
  const featuresConfig = await promptFeaturesConfig();
1150
- const contentFeaturesConfig = await promptContentFeaturesConfig("expert", 9);
1151
- const authConfig = await promptAuthConfig("expert", 9);
1152
- const dashboardConfig = await promptDashboardConfig("expert", 9);
1153
- const devConfig = await promptDevConfig("expert", 9);
810
+ const contentFeaturesConfig = await promptContentFeaturesConfig("expert", 10);
811
+ const authConfig = await promptAuthConfig("expert", 10);
812
+ const dashboardConfig = await promptDashboardConfig("expert", 10);
813
+ const devConfig = await promptDevConfig("expert", 10);
1154
814
  return {
1155
815
  ...projectInfo,
816
+ ...projectTypeConfig,
1156
817
  ...teamConfig,
1157
818
  ...i18nConfig,
1158
819
  ...billingConfig,
@@ -1165,42 +826,42 @@ async function runExpertPrompts() {
1165
826
  }
1166
827
 
1167
828
  // src/wizard/generators/index.ts
1168
- import fs7 from "fs-extra";
1169
- import path5 from "path";
1170
- import { fileURLToPath as fileURLToPath5 } from "url";
829
+ import fs8 from "fs-extra";
830
+ import path7 from "path";
831
+ import { fileURLToPath as fileURLToPath6 } from "url";
1171
832
 
1172
833
  // src/wizard/generators/theme-renamer.ts
1173
834
  import fs from "fs-extra";
1174
835
  import path from "path";
1175
- import { fileURLToPath as fileURLToPath3 } from "url";
1176
- var __filename3 = fileURLToPath3(import.meta.url);
1177
- var __dirname3 = path.dirname(__filename3);
836
+ import { fileURLToPath as fileURLToPath2 } from "url";
837
+ var __filename2 = fileURLToPath2(import.meta.url);
838
+ var __dirname2 = path.dirname(__filename2);
1178
839
  function getTemplatesDir() {
1179
- try {
1180
- const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
1181
- return path.join(path.dirname(corePkgPath), "templates");
1182
- } catch {
1183
- const possiblePaths = [
1184
- path.resolve(__dirname3, "../../../../../core/templates"),
1185
- path.resolve(__dirname3, "../../../../core/templates"),
1186
- path.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
1187
- ];
1188
- for (const p of possiblePaths) {
1189
- if (fs.existsSync(p)) {
1190
- return p;
1191
- }
840
+ const rootDir = process.cwd();
841
+ const possiblePaths = [
842
+ // From project root node_modules (most common for installed packages)
843
+ path.resolve(rootDir, "node_modules/@nextsparkjs/core/templates"),
844
+ // From CLI dist folder for development
845
+ path.resolve(__dirname2, "../../core/templates"),
846
+ // Legacy paths for different build structures
847
+ path.resolve(__dirname2, "../../../../../core/templates"),
848
+ path.resolve(__dirname2, "../../../../core/templates")
849
+ ];
850
+ for (const p of possiblePaths) {
851
+ if (fs.existsSync(p)) {
852
+ return p;
1192
853
  }
1193
- throw new Error("Could not find @nextsparkjs/core templates directory");
1194
854
  }
855
+ throw new Error(`Could not find @nextsparkjs/core templates directory. Searched: ${possiblePaths.join(", ")}`);
1195
856
  }
1196
857
  function getTargetThemesDir() {
1197
858
  return path.resolve(process.cwd(), "contents", "themes");
1198
859
  }
1199
- async function copyStarterTheme(config) {
860
+ async function copyStarterTheme(config2) {
1200
861
  const templatesDir = getTemplatesDir();
1201
862
  const starterThemePath = path.join(templatesDir, "contents", "themes", "starter");
1202
863
  const targetThemesDir = getTargetThemesDir();
1203
- const newThemePath = path.join(targetThemesDir, config.projectSlug);
864
+ const newThemePath = path.join(targetThemesDir, config2.projectSlug);
1204
865
  if (!await fs.pathExists(starterThemePath)) {
1205
866
  throw new Error(`Starter theme not found at: ${starterThemePath}`);
1206
867
  }
@@ -1210,97 +871,97 @@ async function copyStarterTheme(config) {
1210
871
  await fs.ensureDir(targetThemesDir);
1211
872
  await fs.copy(starterThemePath, newThemePath);
1212
873
  }
1213
- async function updateThemeConfig(config) {
1214
- const themeConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "theme.config.ts");
874
+ async function updateThemeConfig(config2) {
875
+ const themeConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "theme.config.ts");
1215
876
  if (!await fs.pathExists(themeConfigPath)) {
1216
877
  throw new Error(`theme.config.ts not found at: ${themeConfigPath}`);
1217
878
  }
1218
879
  let content = await fs.readFile(themeConfigPath, "utf-8");
1219
880
  content = content.replace(
1220
881
  /name:\s*['"]starter['"]/g,
1221
- `name: '${config.projectSlug}'`
882
+ `name: '${config2.projectSlug}'`
1222
883
  );
1223
884
  content = content.replace(
1224
885
  /displayName:\s*['"]Starter['"]/g,
1225
- `displayName: '${config.projectName}'`
886
+ `displayName: '${config2.projectName}'`
1226
887
  );
1227
888
  content = content.replace(
1228
889
  /description:\s*['"]Minimal starter theme for NextSpark['"]/g,
1229
- `description: '${config.projectDescription}'`
890
+ `description: '${config2.projectDescription}'`
1230
891
  );
1231
892
  content = content.replace(
1232
893
  /export const starterThemeConfig/g,
1233
- `export const ${toCamelCase(config.projectSlug)}ThemeConfig`
894
+ `export const ${toCamelCase(config2.projectSlug)}ThemeConfig`
1234
895
  );
1235
896
  content = content.replace(
1236
897
  /export default starterThemeConfig/g,
1237
- `export default ${toCamelCase(config.projectSlug)}ThemeConfig`
898
+ `export default ${toCamelCase(config2.projectSlug)}ThemeConfig`
1238
899
  );
1239
900
  await fs.writeFile(themeConfigPath, content, "utf-8");
1240
901
  }
1241
- async function updateDevConfig(config) {
1242
- const devConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "dev.config.ts");
902
+ async function updateDevConfig(config2) {
903
+ const devConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "dev.config.ts");
1243
904
  if (!await fs.pathExists(devConfigPath)) {
1244
905
  return;
1245
906
  }
1246
907
  let content = await fs.readFile(devConfigPath, "utf-8");
1247
- content = content.replace(/STARTER THEME/g, `${config.projectName.toUpperCase()}`);
1248
- content = content.replace(/Starter Theme/g, config.projectName);
908
+ content = content.replace(/STARTER THEME/g, `${config2.projectName.toUpperCase()}`);
909
+ content = content.replace(/Starter Theme/g, config2.projectName);
1249
910
  await fs.writeFile(devConfigPath, content, "utf-8");
1250
911
  }
1251
- async function updateAppConfig(config) {
1252
- const appConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "app.config.ts");
912
+ async function updateAppConfig(config2) {
913
+ const appConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "app.config.ts");
1253
914
  if (!await fs.pathExists(appConfigPath)) {
1254
915
  throw new Error(`app.config.ts not found at: ${appConfigPath}`);
1255
916
  }
1256
917
  let content = await fs.readFile(appConfigPath, "utf-8");
1257
918
  content = content.replace(
1258
919
  /name:\s*['"]Starter['"]/g,
1259
- `name: '${config.projectName}'`
920
+ `name: '${config2.projectName}'`
1260
921
  );
1261
922
  content = content.replace(
1262
923
  /mode:\s*['"]multi-tenant['"]\s*as\s*const/g,
1263
- `mode: '${config.teamMode}' as const`
924
+ `mode: '${config2.teamMode}' as const`
1264
925
  );
1265
- const localesArray = config.supportedLocales.map((l) => `'${l}'`).join(", ");
926
+ const localesArray = config2.supportedLocales.map((l) => `'${l}'`).join(", ");
1266
927
  content = content.replace(
1267
928
  /supportedLocales:\s*\[.*?\]/g,
1268
929
  `supportedLocales: [${localesArray}]`
1269
930
  );
1270
931
  content = content.replace(
1271
932
  /defaultLocale:\s*['"]en['"]\s*as\s*const/g,
1272
- `defaultLocale: '${config.defaultLocale}' as const`
933
+ `defaultLocale: '${config2.defaultLocale}' as const`
1273
934
  );
1274
935
  content = content.replace(
1275
936
  /label:\s*['"]Starter['"]/g,
1276
- `label: '${config.projectName}'`
937
+ `label: '${config2.projectName}'`
1277
938
  );
1278
939
  await fs.writeFile(appConfigPath, content, "utf-8");
1279
940
  }
1280
- async function updateRolesConfig(config) {
1281
- const appConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "app.config.ts");
941
+ async function updateRolesConfig(config2) {
942
+ const appConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "app.config.ts");
1282
943
  if (!await fs.pathExists(appConfigPath)) {
1283
944
  return;
1284
945
  }
1285
946
  let content = await fs.readFile(appConfigPath, "utf-8");
1286
- const rolesArray = config.teamRoles.map((r) => `'${r}'`).join(", ");
947
+ const rolesArray = config2.teamRoles.map((r) => `'${r}'`).join(", ");
1287
948
  content = content.replace(
1288
949
  /availableTeamRoles:\s*\[.*?\]/g,
1289
950
  `availableTeamRoles: [${rolesArray}]`
1290
951
  );
1291
952
  await fs.writeFile(appConfigPath, content, "utf-8");
1292
953
  }
1293
- async function updateBillingConfig(config) {
1294
- const billingConfigPath = path.join(getTargetThemesDir(), config.projectSlug, "config", "billing.config.ts");
954
+ async function updateBillingConfig(config2) {
955
+ const billingConfigPath = path.join(getTargetThemesDir(), config2.projectSlug, "config", "billing.config.ts");
1295
956
  if (!await fs.pathExists(billingConfigPath)) {
1296
957
  return;
1297
958
  }
1298
959
  let content = await fs.readFile(billingConfigPath, "utf-8");
1299
960
  content = content.replace(
1300
961
  /currency:\s*['"]usd['"]/g,
1301
- `currency: '${config.currency}'`
962
+ `currency: '${config2.currency}'`
1302
963
  );
1303
- const plansContent = generateBillingPlans(config.billingModel, config.currency);
964
+ const plansContent = generateBillingPlans(config2.billingModel, config2.currency);
1304
965
  content = content.replace(
1305
966
  /plans:\s*\[[\s\S]*?\],\s*\n\s*\/\/ ===+\s*\n\s*\/\/ ACTION MAPPINGS/,
1306
967
  `plans: ${plansContent},
@@ -1450,8 +1111,8 @@ function generateBillingPlans(billingModel, currency) {
1450
1111
  },
1451
1112
  ]`;
1452
1113
  }
1453
- async function updateMigrations(config) {
1454
- const migrationsDir = path.join(getTargetThemesDir(), config.projectSlug, "migrations");
1114
+ async function updateMigrations(config2) {
1115
+ const migrationsDir = path.join(getTargetThemesDir(), config2.projectSlug, "migrations");
1455
1116
  if (!await fs.pathExists(migrationsDir)) {
1456
1117
  return;
1457
1118
  }
@@ -1460,12 +1121,39 @@ async function updateMigrations(config) {
1460
1121
  for (const file of sqlFiles) {
1461
1122
  const filePath = path.join(migrationsDir, file);
1462
1123
  let content = await fs.readFile(filePath, "utf-8");
1463
- content = content.replace(/@starter\.dev/g, `@${config.projectSlug}.dev`);
1464
- content = content.replace(/Starter Theme/g, config.projectName);
1465
- content = content.replace(/starter theme/g, config.projectSlug);
1124
+ content = content.replace(/@starter\.dev/g, `@${config2.projectSlug}.dev`);
1125
+ content = content.replace(/Starter Theme/g, config2.projectName);
1126
+ content = content.replace(/starter theme/g, config2.projectSlug);
1466
1127
  await fs.writeFile(filePath, content, "utf-8");
1467
1128
  }
1468
1129
  }
1130
+ async function updateTestFiles(config2) {
1131
+ const testsDir = path.join(getTargetThemesDir(), config2.projectSlug, "tests");
1132
+ if (!await fs.pathExists(testsDir)) {
1133
+ return;
1134
+ }
1135
+ const processDir = async (dir) => {
1136
+ const items = await fs.readdir(dir);
1137
+ for (const item of items) {
1138
+ const itemPath = path.join(dir, item);
1139
+ const stat = await fs.stat(itemPath);
1140
+ if (stat.isDirectory()) {
1141
+ await processDir(itemPath);
1142
+ } else if (item.endsWith(".ts") || item.endsWith(".tsx")) {
1143
+ let content = await fs.readFile(itemPath, "utf-8");
1144
+ const hasChanges = content.includes("@/contents/themes/starter/");
1145
+ if (hasChanges) {
1146
+ content = content.replace(
1147
+ /@\/contents\/themes\/starter\//g,
1148
+ `@/contents/themes/${config2.projectSlug}/`
1149
+ );
1150
+ await fs.writeFile(itemPath, content, "utf-8");
1151
+ }
1152
+ }
1153
+ }
1154
+ };
1155
+ await processDir(testsDir);
1156
+ }
1469
1157
  function toCamelCase(str) {
1470
1158
  return str.split("-").map((word, index) => {
1471
1159
  if (index === 0) {
@@ -1481,10 +1169,10 @@ import path2 from "path";
1481
1169
  function getTargetThemesDir2() {
1482
1170
  return path2.resolve(process.cwd(), "contents", "themes");
1483
1171
  }
1484
- async function updateAuthConfig(config) {
1172
+ async function updateAuthConfig(config2) {
1485
1173
  const authConfigPath = path2.join(
1486
1174
  getTargetThemesDir2(),
1487
- config.projectSlug,
1175
+ config2.projectSlug,
1488
1176
  "config",
1489
1177
  "auth.config.ts"
1490
1178
  );
@@ -1494,22 +1182,22 @@ async function updateAuthConfig(config) {
1494
1182
  let content = await fs2.readFile(authConfigPath, "utf-8");
1495
1183
  content = content.replace(
1496
1184
  /(emailPassword:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1497
- `$1${config.auth.emailPassword}`
1185
+ `$1${config2.auth.emailPassword}`
1498
1186
  );
1499
1187
  content = content.replace(
1500
1188
  /(google:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1501
- `$1${config.auth.googleOAuth}`
1189
+ `$1${config2.auth.googleOAuth}`
1502
1190
  );
1503
1191
  content = content.replace(
1504
1192
  /(emailVerification:\s*)(?:true|false)/g,
1505
- `$1${config.auth.emailVerification}`
1193
+ `$1${config2.auth.emailVerification}`
1506
1194
  );
1507
1195
  await fs2.writeFile(authConfigPath, content, "utf-8");
1508
1196
  }
1509
- async function updateDashboardUIConfig(config) {
1197
+ async function updateDashboardUIConfig(config2) {
1510
1198
  const dashboardConfigPath = path2.join(
1511
1199
  getTargetThemesDir2(),
1512
- config.projectSlug,
1200
+ config2.projectSlug,
1513
1201
  "config",
1514
1202
  "dashboard.config.ts"
1515
1203
  );
@@ -1519,42 +1207,42 @@ async function updateDashboardUIConfig(config) {
1519
1207
  let content = await fs2.readFile(dashboardConfigPath, "utf-8");
1520
1208
  content = content.replace(
1521
1209
  /(search:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1522
- `$1${config.dashboard.search}`
1210
+ `$1${config2.dashboard.search}`
1523
1211
  );
1524
1212
  content = content.replace(
1525
1213
  /(notifications:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1526
- `$1${config.dashboard.notifications}`
1214
+ `$1${config2.dashboard.notifications}`
1527
1215
  );
1528
1216
  content = content.replace(
1529
1217
  /(themeToggle:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1530
- `$1${config.dashboard.themeToggle}`
1218
+ `$1${config2.dashboard.themeToggle}`
1531
1219
  );
1532
1220
  content = content.replace(
1533
1221
  /(support:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1534
- `$1${config.dashboard.support}`
1222
+ `$1${config2.dashboard.support}`
1535
1223
  );
1536
1224
  content = content.replace(
1537
1225
  /(quickCreate:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1538
- `$1${config.dashboard.quickCreate}`
1226
+ `$1${config2.dashboard.quickCreate}`
1539
1227
  );
1540
1228
  content = content.replace(
1541
1229
  /(adminAccess:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1542
- `$1${config.dashboard.superadminAccess}`
1230
+ `$1${config2.dashboard.superadminAccess}`
1543
1231
  );
1544
1232
  content = content.replace(
1545
1233
  /(devtoolsAccess:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1546
- `$1${config.dashboard.devtoolsAccess}`
1234
+ `$1${config2.dashboard.devtoolsAccess}`
1547
1235
  );
1548
1236
  content = content.replace(
1549
1237
  /(defaultCollapsed:\s*)(?:true|false)/g,
1550
- `$1${config.dashboard.sidebarCollapsed}`
1238
+ `$1${config2.dashboard.sidebarCollapsed}`
1551
1239
  );
1552
1240
  await fs2.writeFile(dashboardConfigPath, content, "utf-8");
1553
1241
  }
1554
- async function updateDevToolsConfig(config) {
1242
+ async function updateDevToolsConfig(config2) {
1555
1243
  const devConfigPath = path2.join(
1556
1244
  getTargetThemesDir2(),
1557
- config.projectSlug,
1245
+ config2.projectSlug,
1558
1246
  "config",
1559
1247
  "dev.config.ts"
1560
1248
  );
@@ -1564,18 +1252,18 @@ async function updateDevToolsConfig(config) {
1564
1252
  let content = await fs2.readFile(devConfigPath, "utf-8");
1565
1253
  content = content.replace(
1566
1254
  /(devKeyring:\s*{[^}]*enabled:\s*)(?:true|false)/gs,
1567
- `$1${config.dev.devKeyring}`
1255
+ `$1${config2.dev.devKeyring}`
1568
1256
  );
1569
1257
  content = content.replace(
1570
1258
  /(debugMode:\s*)(?:true|false)/g,
1571
- `$1${config.dev.debugMode}`
1259
+ `$1${config2.dev.debugMode}`
1572
1260
  );
1573
1261
  await fs2.writeFile(devConfigPath, content, "utf-8");
1574
1262
  }
1575
- async function updatePermissionsConfig(config) {
1263
+ async function updatePermissionsConfig(config2) {
1576
1264
  const permissionsConfigPath = path2.join(
1577
1265
  getTargetThemesDir2(),
1578
- config.projectSlug,
1266
+ config2.projectSlug,
1579
1267
  "config",
1580
1268
  "permissions.config.ts"
1581
1269
  );
@@ -1583,7 +1271,7 @@ async function updatePermissionsConfig(config) {
1583
1271
  return;
1584
1272
  }
1585
1273
  let content = await fs2.readFile(permissionsConfigPath, "utf-8");
1586
- const availableRoles = config.teamRoles;
1274
+ const availableRoles = config2.teamRoles;
1587
1275
  const roleArrayPattern = /roles:\s*\[(.*?)\]/g;
1588
1276
  content = content.replace(roleArrayPattern, (match, rolesStr) => {
1589
1277
  const currentRoles = rolesStr.split(",").map((r) => r.trim().replace(/['"]/g, "")).filter((r) => r.length > 0);
@@ -1595,10 +1283,48 @@ async function updatePermissionsConfig(config) {
1595
1283
  });
1596
1284
  await fs2.writeFile(permissionsConfigPath, content, "utf-8");
1597
1285
  }
1598
- async function updateDashboardConfig(config) {
1286
+ async function updateEntityPermissions(config2) {
1287
+ const permissionsConfigPath = path2.join(
1288
+ getTargetThemesDir2(),
1289
+ config2.projectSlug,
1290
+ "config",
1291
+ "permissions.config.ts"
1292
+ );
1293
+ if (!await fs2.pathExists(permissionsConfigPath)) {
1294
+ return;
1295
+ }
1296
+ let content = await fs2.readFile(permissionsConfigPath, "utf-8");
1297
+ if (config2.contentFeatures.pages) {
1298
+ content = uncommentPermissionBlock(content, "PAGES");
1299
+ }
1300
+ if (config2.contentFeatures.blog) {
1301
+ content = uncommentPermissionBlock(content, "POSTS");
1302
+ }
1303
+ await fs2.writeFile(permissionsConfigPath, content, "utf-8");
1304
+ }
1305
+ function uncommentPermissionBlock(content, markerName) {
1306
+ const startMarker = `// __${markerName}_PERMISSIONS_START__`;
1307
+ const endMarker = `// __${markerName}_PERMISSIONS_END__`;
1308
+ const startIndex = content.indexOf(startMarker);
1309
+ const endIndex = content.indexOf(endMarker);
1310
+ if (startIndex === -1 || endIndex === -1) {
1311
+ return content;
1312
+ }
1313
+ const beforeBlock = content.slice(0, startIndex);
1314
+ const block = content.slice(startIndex + startMarker.length, endIndex);
1315
+ const afterBlock = content.slice(endIndex + endMarker.length);
1316
+ const uncommentedBlock = block.split("\n").map((line) => {
1317
+ if (line.match(/^\s*\/\/\s+/)) {
1318
+ return line.replace(/^(\s*)\/\/\s*/, "$1");
1319
+ }
1320
+ return line;
1321
+ }).join("\n");
1322
+ return beforeBlock + uncommentedBlock + afterBlock;
1323
+ }
1324
+ async function updateDashboardConfig(config2) {
1599
1325
  const dashboardConfigPath = path2.join(
1600
1326
  getTargetThemesDir2(),
1601
- config.projectSlug,
1327
+ config2.projectSlug,
1602
1328
  "config",
1603
1329
  "dashboard.config.ts"
1604
1330
  );
@@ -1606,19 +1332,19 @@ async function updateDashboardConfig(config) {
1606
1332
  return;
1607
1333
  }
1608
1334
  let content = await fs2.readFile(dashboardConfigPath, "utf-8");
1609
- if (!config.features.analytics) {
1335
+ if (!config2.features.analytics) {
1610
1336
  content = content.replace(
1611
1337
  /(id:\s*['"]analytics['"].*?enabled:\s*)true/gs,
1612
1338
  "$1false"
1613
1339
  );
1614
1340
  }
1615
- if (!config.features.billing) {
1341
+ if (!config2.features.billing) {
1616
1342
  content = content.replace(
1617
1343
  /(id:\s*['"]billing['"].*?enabled:\s*)true/gs,
1618
1344
  "$1false"
1619
1345
  );
1620
1346
  }
1621
- if (config.teamMode === "single-user") {
1347
+ if (config2.teamMode === "single-user") {
1622
1348
  content = content.replace(
1623
1349
  /(id:\s*['"]team['"].*?enabled:\s*)true/gs,
1624
1350
  "$1false"
@@ -1630,10 +1356,10 @@ async function updateDashboardConfig(config) {
1630
1356
  }
1631
1357
  await fs2.writeFile(dashboardConfigPath, content, "utf-8");
1632
1358
  }
1633
- async function generateEnvExample(config) {
1359
+ async function generateEnvExample(config2) {
1634
1360
  const envExamplePath = path2.resolve(process.cwd(), ".env.example");
1635
1361
  let oauthSection = "";
1636
- if (config.auth.googleOAuth) {
1362
+ if (config2.auth.googleOAuth) {
1637
1363
  oauthSection = `# =============================================================================
1638
1364
  # OAUTH PROVIDERS
1639
1365
  # =============================================================================
@@ -1649,7 +1375,7 @@ GOOGLE_CLIENT_SECRET="your-google-client-secret"
1649
1375
  `;
1650
1376
  }
1651
1377
  const envContent = `# NextSpark Environment Configuration
1652
- # Generated for: ${config.projectName}
1378
+ # Generated for: ${config2.projectName}
1653
1379
 
1654
1380
  # =============================================================================
1655
1381
  # DATABASE
@@ -1665,7 +1391,7 @@ BETTER_AUTH_SECRET="your-secret-key-here"
1665
1391
  # =============================================================================
1666
1392
  # THEME
1667
1393
  # =============================================================================
1668
- NEXT_PUBLIC_ACTIVE_THEME="${config.projectSlug}"
1394
+ NEXT_PUBLIC_ACTIVE_THEME="${config2.projectSlug}"
1669
1395
 
1670
1396
  # =============================================================================
1671
1397
  # APPLICATION
@@ -1673,7 +1399,7 @@ NEXT_PUBLIC_ACTIVE_THEME="${config.projectSlug}"
1673
1399
  NEXT_PUBLIC_APP_URL="http://localhost:3000"
1674
1400
  NODE_ENV="development"
1675
1401
 
1676
- ${config.features.billing ? `# =============================================================================
1402
+ ${config2.features.billing ? `# =============================================================================
1677
1403
  # STRIPE (Billing)
1678
1404
  # =============================================================================
1679
1405
  STRIPE_SECRET_KEY="sk_test_..."
@@ -1691,16 +1417,16 @@ ${oauthSection}`;
1691
1417
  await fs2.writeFile(envExamplePath, envContent, "utf-8");
1692
1418
  }
1693
1419
  }
1694
- async function updateReadme(config) {
1695
- const readmePath = path2.join(getTargetThemesDir2(), config.projectSlug, "README.md");
1420
+ async function updateReadme(config2) {
1421
+ const readmePath = path2.join(getTargetThemesDir2(), config2.projectSlug, "README.md");
1696
1422
  if (!await fs2.pathExists(readmePath)) {
1697
1423
  return;
1698
1424
  }
1699
1425
  let content = await fs2.readFile(readmePath, "utf-8");
1700
- content = content.replace(/# Starter Theme/g, `# ${config.projectName}`);
1426
+ content = content.replace(/# Starter Theme/g, `# ${config2.projectName}`);
1701
1427
  content = content.replace(
1702
1428
  /Minimal starter theme for NextSpark/g,
1703
- config.projectDescription
1429
+ config2.projectDescription
1704
1430
  );
1705
1431
  await fs2.writeFile(readmePath, content, "utf-8");
1706
1432
  }
@@ -1712,6 +1438,18 @@ async function copyEnvExampleToEnv() {
1712
1438
  await fs2.copy(envExamplePath, envPath);
1713
1439
  }
1714
1440
  }
1441
+ async function updateGlobalsCss(config2) {
1442
+ const globalsCssPath = path2.resolve(process.cwd(), "app", "globals.css");
1443
+ if (!await fs2.pathExists(globalsCssPath)) {
1444
+ return;
1445
+ }
1446
+ let content = await fs2.readFile(globalsCssPath, "utf-8");
1447
+ content = content.replace(
1448
+ /@import\s+["']\.\.\/contents\/themes\/[^/]+\/styles\/globals\.css["'];?/,
1449
+ `@import "../contents/themes/${config2.projectSlug}/styles/globals.css";`
1450
+ );
1451
+ await fs2.writeFile(globalsCssPath, content, "utf-8");
1452
+ }
1715
1453
 
1716
1454
  // src/wizard/generators/messages-generator.ts
1717
1455
  import fs3 from "fs-extra";
@@ -1719,8 +1457,8 @@ import path3 from "path";
1719
1457
  function getTargetThemesDir3() {
1720
1458
  return path3.resolve(process.cwd(), "contents", "themes");
1721
1459
  }
1722
- async function removeUnusedLanguages(config) {
1723
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1460
+ async function removeUnusedLanguages(config2) {
1461
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1724
1462
  if (!await fs3.pathExists(messagesDir)) {
1725
1463
  return;
1726
1464
  }
@@ -1730,13 +1468,13 @@ async function removeUnusedLanguages(config) {
1730
1468
  return fs3.statSync(folderPath).isDirectory() && Object.keys(AVAILABLE_LOCALES).includes(f);
1731
1469
  });
1732
1470
  for (const folder of languageFolders) {
1733
- if (!config.supportedLocales.includes(folder)) {
1471
+ if (!config2.supportedLocales.includes(folder)) {
1734
1472
  await fs3.remove(path3.join(messagesDir, folder));
1735
1473
  }
1736
1474
  }
1737
1475
  }
1738
- async function removeUnusedEntityMessages(config) {
1739
- const entitiesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "entities");
1476
+ async function removeUnusedEntityMessages(config2) {
1477
+ const entitiesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "entities");
1740
1478
  if (!await fs3.pathExists(entitiesDir)) {
1741
1479
  return;
1742
1480
  }
@@ -1749,45 +1487,45 @@ async function removeUnusedEntityMessages(config) {
1749
1487
  const files = await fs3.readdir(messagesDir);
1750
1488
  for (const file of files) {
1751
1489
  const locale = path3.basename(file, ".json");
1752
- if (Object.keys(AVAILABLE_LOCALES).includes(locale) && !config.supportedLocales.includes(locale)) {
1490
+ if (Object.keys(AVAILABLE_LOCALES).includes(locale) && !config2.supportedLocales.includes(locale)) {
1753
1491
  await fs3.remove(path3.join(messagesDir, file));
1754
1492
  }
1755
1493
  }
1756
1494
  }
1757
1495
  }
1758
- async function ensureLanguageFolders(config) {
1759
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1496
+ async function ensureLanguageFolders(config2) {
1497
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1760
1498
  if (!await fs3.pathExists(messagesDir)) {
1761
1499
  return;
1762
1500
  }
1763
- const defaultLocaleDir = path3.join(messagesDir, config.defaultLocale);
1501
+ const defaultLocaleDir = path3.join(messagesDir, config2.defaultLocale);
1764
1502
  if (!await fs3.pathExists(defaultLocaleDir)) {
1765
1503
  const enDir = path3.join(messagesDir, "en");
1766
1504
  if (await fs3.pathExists(enDir)) {
1767
1505
  await fs3.copy(enDir, defaultLocaleDir);
1768
1506
  }
1769
1507
  }
1770
- for (const locale of config.supportedLocales) {
1508
+ for (const locale of config2.supportedLocales) {
1771
1509
  const localeDir = path3.join(messagesDir, locale);
1772
1510
  if (!await fs3.pathExists(localeDir) && await fs3.pathExists(defaultLocaleDir)) {
1773
1511
  await fs3.copy(defaultLocaleDir, localeDir);
1774
1512
  }
1775
1513
  }
1776
1514
  }
1777
- async function updateMessageFiles(config) {
1778
- const messagesDir = path3.join(getTargetThemesDir3(), config.projectSlug, "messages");
1515
+ async function updateMessageFiles(config2) {
1516
+ const messagesDir = path3.join(getTargetThemesDir3(), config2.projectSlug, "messages");
1779
1517
  if (!await fs3.pathExists(messagesDir)) {
1780
1518
  return;
1781
1519
  }
1782
- for (const locale of config.supportedLocales) {
1520
+ for (const locale of config2.supportedLocales) {
1783
1521
  const commonPath = path3.join(messagesDir, locale, "common.json");
1784
1522
  if (await fs3.pathExists(commonPath)) {
1785
1523
  try {
1786
1524
  const content = await fs3.readJson(commonPath);
1787
1525
  if (content.app) {
1788
- content.app.name = config.projectName;
1526
+ content.app.name = config2.projectName;
1789
1527
  if (content.app.description) {
1790
- content.app.description = config.projectDescription;
1528
+ content.app.description = config2.projectDescription;
1791
1529
  }
1792
1530
  }
1793
1531
  await fs3.writeJson(commonPath, content, { spaces: 2 });
@@ -1796,36 +1534,36 @@ async function updateMessageFiles(config) {
1796
1534
  }
1797
1535
  }
1798
1536
  }
1799
- async function processI18n(config) {
1800
- await removeUnusedLanguages(config);
1801
- await removeUnusedEntityMessages(config);
1802
- await ensureLanguageFolders(config);
1803
- await updateMessageFiles(config);
1537
+ async function processI18n(config2) {
1538
+ await removeUnusedLanguages(config2);
1539
+ await removeUnusedEntityMessages(config2);
1540
+ await ensureLanguageFolders(config2);
1541
+ await updateMessageFiles(config2);
1804
1542
  }
1805
1543
 
1806
1544
  // src/wizard/generators/content-features-generator.ts
1807
1545
  import fs4 from "fs-extra";
1808
1546
  import path4 from "path";
1809
- import { fileURLToPath as fileURLToPath4 } from "url";
1810
- var __filename4 = fileURLToPath4(import.meta.url);
1811
- var __dirname4 = path4.dirname(__filename4);
1547
+ import { fileURLToPath as fileURLToPath3 } from "url";
1548
+ var __filename3 = fileURLToPath3(import.meta.url);
1549
+ var __dirname3 = path4.dirname(__filename3);
1812
1550
  function getTemplatesDir2() {
1813
- try {
1814
- const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
1815
- return path4.join(path4.dirname(corePkgPath), "templates");
1816
- } catch {
1817
- const possiblePaths = [
1818
- path4.resolve(__dirname4, "../../../../../core/templates"),
1819
- path4.resolve(__dirname4, "../../../../core/templates"),
1820
- path4.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
1821
- ];
1822
- for (const p of possiblePaths) {
1823
- if (fs4.existsSync(p)) {
1824
- return p;
1825
- }
1551
+ const rootDir = process.cwd();
1552
+ const possiblePaths = [
1553
+ // From project root node_modules (most common for installed packages)
1554
+ path4.resolve(rootDir, "node_modules/@nextsparkjs/core/templates"),
1555
+ // From CLI dist folder for development
1556
+ path4.resolve(__dirname3, "../../core/templates"),
1557
+ // Legacy paths for different build structures
1558
+ path4.resolve(__dirname3, "../../../../../core/templates"),
1559
+ path4.resolve(__dirname3, "../../../../core/templates")
1560
+ ];
1561
+ for (const p of possiblePaths) {
1562
+ if (fs4.existsSync(p)) {
1563
+ return p;
1826
1564
  }
1827
- throw new Error("Could not find @nextsparkjs/core templates directory");
1828
1565
  }
1566
+ throw new Error(`Could not find @nextsparkjs/core templates directory. Searched: ${possiblePaths.join(", ")}`);
1829
1567
  }
1830
1568
  function getFeaturesDir() {
1831
1569
  return path4.join(getTemplatesDir2(), "features");
@@ -1833,9 +1571,9 @@ function getFeaturesDir() {
1833
1571
  function getTargetThemeDir(projectSlug) {
1834
1572
  return path4.resolve(process.cwd(), "contents", "themes", projectSlug);
1835
1573
  }
1836
- async function copyPagesFeature(config) {
1574
+ async function copyPagesFeature(config2) {
1837
1575
  const featuresDir = getFeaturesDir();
1838
- const targetThemeDir = getTargetThemeDir(config.projectSlug);
1576
+ const targetThemeDir = getTargetThemeDir(config2.projectSlug);
1839
1577
  const sourcePagesEntity = path4.join(featuresDir, "pages", "entities", "pages");
1840
1578
  const targetEntitiesDir = path4.join(targetThemeDir, "entities", "pages");
1841
1579
  if (await fs4.pathExists(sourcePagesEntity)) {
@@ -1851,9 +1589,9 @@ async function copyPagesFeature(config) {
1851
1589
  console.warn(`Warning: Hero block not found at: ${sourceHeroBlock}`);
1852
1590
  }
1853
1591
  }
1854
- async function copyBlogFeature(config) {
1592
+ async function copyBlogFeature(config2) {
1855
1593
  const featuresDir = getFeaturesDir();
1856
- const targetThemeDir = getTargetThemeDir(config.projectSlug);
1594
+ const targetThemeDir = getTargetThemeDir(config2.projectSlug);
1857
1595
  const sourcePostsEntity = path4.join(featuresDir, "blog", "entities", "posts");
1858
1596
  const targetPostsEntity = path4.join(targetThemeDir, "entities", "posts");
1859
1597
  if (await fs4.pathExists(sourcePostsEntity)) {
@@ -1869,35 +1607,35 @@ async function copyBlogFeature(config) {
1869
1607
  console.warn(`Warning: Post-content block not found at: ${sourcePostContentBlock}`);
1870
1608
  }
1871
1609
  }
1872
- async function copyContentFeatures(config) {
1873
- if (!config.contentFeatures.pages && !config.contentFeatures.blog) {
1610
+ async function copyContentFeatures(config2) {
1611
+ if (!config2.contentFeatures.pages && !config2.contentFeatures.blog) {
1874
1612
  return;
1875
1613
  }
1876
- if (config.contentFeatures.pages) {
1877
- await copyPagesFeature(config);
1614
+ if (config2.contentFeatures.pages) {
1615
+ await copyPagesFeature(config2);
1878
1616
  }
1879
- if (config.contentFeatures.blog) {
1880
- await copyBlogFeature(config);
1617
+ if (config2.contentFeatures.blog) {
1618
+ await copyBlogFeature(config2);
1881
1619
  }
1882
1620
  }
1883
1621
 
1884
1622
  // src/wizard/generators/theme-plugins-installer.ts
1885
- import { existsSync as existsSync6, cpSync, mkdirSync, readFileSync as readFileSync6, writeFileSync } from "fs";
1886
- import { join as join6, resolve as resolve2 } from "path";
1887
- import chalk9 from "chalk";
1888
- import ora6 from "ora";
1623
+ import { existsSync as existsSync2, cpSync, mkdirSync, readFileSync as readFileSync3, writeFileSync } from "fs";
1624
+ import { join as join3, resolve } from "path";
1625
+ import chalk5 from "chalk";
1626
+ import ora2 from "ora";
1889
1627
 
1890
1628
  // src/commands/add-theme.ts
1891
- import chalk8 from "chalk";
1892
- import ora5 from "ora";
1893
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "fs";
1894
- import { join as join5 } from "path";
1629
+ import chalk4 from "chalk";
1630
+ import ora from "ora";
1631
+ import { existsSync, readFileSync as readFileSync2 } from "fs";
1632
+ import { join as join2 } from "path";
1895
1633
  async function addTheme(packageSpec, options = {}) {
1896
- const spinner = ora5(`Adding theme ${packageSpec}`).start();
1634
+ const spinner = ora(`Adding theme ${packageSpec}`).start();
1897
1635
  let cleanup = null;
1898
1636
  try {
1899
- const contentsDir = join5(process.cwd(), "contents");
1900
- if (!existsSync5(contentsDir)) {
1637
+ const contentsDir = join2(process.cwd(), "contents");
1638
+ if (!existsSync(contentsDir)) {
1901
1639
  spinner.fail('contents/ directory not found. Run "nextspark init" first.');
1902
1640
  return;
1903
1641
  }
@@ -1911,15 +1649,15 @@ async function addTheme(packageSpec, options = {}) {
1911
1649
  const validation = validateTheme(packageJson, extractedPath);
1912
1650
  if (!validation.valid) {
1913
1651
  spinner.fail("Invalid theme");
1914
- validation.errors.forEach((e) => console.log(chalk8.red(` \u2717 ${e}`)));
1652
+ validation.errors.forEach((e) => console.log(chalk4.red(` \u2717 ${e}`)));
1915
1653
  return;
1916
1654
  }
1917
1655
  if (validation.warnings.length > 0) {
1918
- validation.warnings.forEach((w) => console.log(chalk8.yellow(` \u26A0 ${w}`)));
1656
+ validation.warnings.forEach((w) => console.log(chalk4.yellow(` \u26A0 ${w}`)));
1919
1657
  }
1920
1658
  if (packageJson.requiredPlugins?.length && !options.skipPostinstall) {
1921
1659
  spinner.stop();
1922
- console.log(chalk8.blue("\n Installing required plugins..."));
1660
+ console.log(chalk4.blue("\n Installing required plugins..."));
1923
1661
  const installingPlugins = /* @__PURE__ */ new Set();
1924
1662
  for (const plugin of packageJson.requiredPlugins) {
1925
1663
  if (!checkPluginExists(plugin)) {
@@ -1943,14 +1681,14 @@ async function addTheme(packageSpec, options = {}) {
1943
1681
  };
1944
1682
  await runPostinstall(packageJson, result.installedPath, context);
1945
1683
  }
1946
- console.log(chalk8.green(`
1684
+ console.log(chalk4.green(`
1947
1685
  \u2713 Theme ${result.name} installed successfully!`));
1948
- console.log(chalk8.gray(` Location: contents/themes/${result.name}/`));
1949
- console.log(chalk8.gray(` Set NEXT_PUBLIC_ACTIVE_THEME=${result.name} to activate`));
1686
+ console.log(chalk4.gray(` Location: contents/themes/${result.name}/`));
1687
+ console.log(chalk4.gray(` Set NEXT_PUBLIC_ACTIVE_THEME=${result.name} to activate`));
1950
1688
  } catch (error) {
1951
1689
  spinner.fail("Failed to add theme");
1952
1690
  if (error instanceof Error) {
1953
- console.log(chalk8.red(` ${error.message}`));
1691
+ console.log(chalk4.red(` ${error.message}`));
1954
1692
  }
1955
1693
  throw error;
1956
1694
  } finally {
@@ -1959,14 +1697,14 @@ async function addTheme(packageSpec, options = {}) {
1959
1697
  }
1960
1698
  function checkPluginExists(pluginName) {
1961
1699
  const name = pluginName.replace(/^@[^/]+\//, "").replace(/^nextspark-plugin-/, "").replace(/^plugin-/, "");
1962
- return existsSync5(join5(process.cwd(), "contents", "plugins", name));
1700
+ return existsSync(join2(process.cwd(), "contents", "plugins", name));
1963
1701
  }
1964
1702
  function getCoreVersion() {
1965
- const pkgPath = join5(process.cwd(), "node_modules", "@nextsparkjs", "core", "package.json");
1966
- if (existsSync5(pkgPath)) {
1703
+ const pkgPath = join2(process.cwd(), "node_modules", "@nextsparkjs", "core", "package.json");
1704
+ if (existsSync(pkgPath)) {
1967
1705
  try {
1968
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
1969
- return pkg.version || "0.0.0";
1706
+ const pkg2 = JSON.parse(readFileSync2(pkgPath, "utf-8"));
1707
+ return pkg2.version || "0.0.0";
1970
1708
  } catch {
1971
1709
  return "0.0.0";
1972
1710
  }
@@ -2001,23 +1739,23 @@ var THEME_REQUIRED_PLUGINS2 = {
2001
1739
  "crm": [],
2002
1740
  "productivity": []
2003
1741
  };
2004
- function isMonorepoMode2() {
1742
+ function isMonorepoMode() {
2005
1743
  const possiblePaths = [
2006
- join6(process.cwd(), "pnpm-workspace.yaml"),
2007
- join6(process.cwd(), "..", "pnpm-workspace.yaml"),
2008
- join6(process.cwd(), "..", "..", "pnpm-workspace.yaml")
1744
+ join3(process.cwd(), "pnpm-workspace.yaml"),
1745
+ join3(process.cwd(), "..", "pnpm-workspace.yaml"),
1746
+ join3(process.cwd(), "..", "..", "pnpm-workspace.yaml")
2009
1747
  ];
2010
- return possiblePaths.some((p) => existsSync6(p));
1748
+ return possiblePaths.some((p) => existsSync2(p));
2011
1749
  }
2012
1750
  function getMonorepoRoot() {
2013
1751
  const possibleRoots = [
2014
1752
  process.cwd(),
2015
- join6(process.cwd(), ".."),
2016
- join6(process.cwd(), "..", "..")
1753
+ join3(process.cwd(), ".."),
1754
+ join3(process.cwd(), "..", "..")
2017
1755
  ];
2018
1756
  for (const root of possibleRoots) {
2019
- if (existsSync6(join6(root, "pnpm-workspace.yaml"))) {
2020
- return resolve2(root);
1757
+ if (existsSync2(join3(root, "pnpm-workspace.yaml"))) {
1758
+ return resolve(root);
2021
1759
  }
2022
1760
  }
2023
1761
  return null;
@@ -2026,20 +1764,20 @@ function getLocalPackageDir(type, name) {
2026
1764
  const monorepoRoot = getMonorepoRoot();
2027
1765
  if (!monorepoRoot) return null;
2028
1766
  const baseDir = type === "theme" ? "themes" : "plugins";
2029
- const packageDir = join6(monorepoRoot, baseDir, name);
2030
- if (existsSync6(packageDir) && existsSync6(join6(packageDir, "package.json"))) {
1767
+ const packageDir = join3(monorepoRoot, baseDir, name);
1768
+ if (existsSync2(packageDir) && existsSync2(join3(packageDir, "package.json"))) {
2031
1769
  return packageDir;
2032
1770
  }
2033
1771
  return null;
2034
1772
  }
2035
1773
  async function copyLocalTheme(name, sourceDir) {
2036
- const targetDir = join6(process.cwd(), "contents", "themes", name);
2037
- const themesDir = join6(process.cwd(), "contents", "themes");
2038
- if (!existsSync6(themesDir)) {
1774
+ const targetDir = join3(process.cwd(), "contents", "themes", name);
1775
+ const themesDir = join3(process.cwd(), "contents", "themes");
1776
+ if (!existsSync2(themesDir)) {
2039
1777
  mkdirSync(themesDir, { recursive: true });
2040
1778
  }
2041
- if (existsSync6(targetDir)) {
2042
- console.log(chalk9.gray(` Theme ${name} already exists, skipping...`));
1779
+ if (existsSync2(targetDir)) {
1780
+ console.log(chalk5.gray(` Theme ${name} already exists, skipping...`));
2043
1781
  return true;
2044
1782
  }
2045
1783
  cpSync(sourceDir, targetDir, { recursive: true });
@@ -2047,13 +1785,13 @@ async function copyLocalTheme(name, sourceDir) {
2047
1785
  return true;
2048
1786
  }
2049
1787
  async function copyLocalPlugin(name, sourceDir) {
2050
- const targetDir = join6(process.cwd(), "contents", "plugins", name);
2051
- const pluginsDir = join6(process.cwd(), "contents", "plugins");
2052
- if (!existsSync6(pluginsDir)) {
1788
+ const targetDir = join3(process.cwd(), "contents", "plugins", name);
1789
+ const pluginsDir = join3(process.cwd(), "contents", "plugins");
1790
+ if (!existsSync2(pluginsDir)) {
2053
1791
  mkdirSync(pluginsDir, { recursive: true });
2054
1792
  }
2055
- if (existsSync6(targetDir)) {
2056
- console.log(chalk9.gray(` Plugin ${name} already exists, skipping...`));
1793
+ if (existsSync2(targetDir)) {
1794
+ console.log(chalk5.gray(` Plugin ${name} already exists, skipping...`));
2057
1795
  return true;
2058
1796
  }
2059
1797
  cpSync(sourceDir, targetDir, { recursive: true });
@@ -2061,12 +1799,12 @@ async function copyLocalPlugin(name, sourceDir) {
2061
1799
  return true;
2062
1800
  }
2063
1801
  async function updateTsConfigPaths(name, type) {
2064
- const tsconfigPath = join6(process.cwd(), "tsconfig.json");
2065
- if (!existsSync6(tsconfigPath)) {
1802
+ const tsconfigPath = join3(process.cwd(), "tsconfig.json");
1803
+ if (!existsSync2(tsconfigPath)) {
2066
1804
  return;
2067
1805
  }
2068
1806
  try {
2069
- const content = readFileSync6(tsconfigPath, "utf-8");
1807
+ const content = readFileSync3(tsconfigPath, "utf-8");
2070
1808
  const tsconfig = JSON.parse(content);
2071
1809
  if (!tsconfig.compilerOptions) {
2072
1810
  tsconfig.compilerOptions = {};
@@ -2101,17 +1839,17 @@ async function installTheme2(theme) {
2101
1839
  if (!theme) {
2102
1840
  return true;
2103
1841
  }
2104
- const spinner = ora6({
1842
+ const spinner = ora2({
2105
1843
  text: `Installing reference theme: ${theme}...`,
2106
1844
  prefixText: " "
2107
1845
  }).start();
2108
1846
  try {
2109
- const targetDir = join6(process.cwd(), "contents", "themes", theme);
2110
- if (existsSync6(targetDir)) {
2111
- spinner.info(chalk9.gray(`Reference theme ${theme} already exists`));
1847
+ const targetDir = join3(process.cwd(), "contents", "themes", theme);
1848
+ if (existsSync2(targetDir)) {
1849
+ spinner.info(chalk5.gray(`Reference theme ${theme} already exists`));
2112
1850
  return true;
2113
1851
  }
2114
- if (isMonorepoMode2()) {
1852
+ if (isMonorepoMode()) {
2115
1853
  const localDir = getLocalPackageDir("theme", theme);
2116
1854
  if (localDir) {
2117
1855
  spinner.text = `Copying reference theme from local: ${theme}...`;
@@ -2124,7 +1862,7 @@ async function installTheme2(theme) {
2124
1862
  await copyLocalPlugin(plugin, pluginDir);
2125
1863
  }
2126
1864
  }
2127
- spinner.succeed(chalk9.green(`Reference theme ${theme} installed!`));
1865
+ spinner.succeed(chalk5.green(`Reference theme ${theme} installed!`));
2128
1866
  return true;
2129
1867
  }
2130
1868
  }
@@ -2133,17 +1871,17 @@ async function installTheme2(theme) {
2133
1871
  const packageSpec = THEME_PACKAGES[theme];
2134
1872
  const success = await installThemeViaCli(packageSpec);
2135
1873
  if (success) {
2136
- spinner.succeed(chalk9.green(`Reference theme ${theme} installed!`));
1874
+ spinner.succeed(chalk5.green(`Reference theme ${theme} installed!`));
2137
1875
  return true;
2138
1876
  } else {
2139
- spinner.fail(chalk9.red(`Failed to install theme: ${theme}`));
2140
- console.log(chalk9.gray(" Hint: Make sure @nextsparkjs/cli is installed or the theme package is published"));
1877
+ spinner.fail(chalk5.red(`Failed to install theme: ${theme}`));
1878
+ console.log(chalk5.gray(" Hint: Make sure @nextsparkjs/cli is installed or the theme package is published"));
2141
1879
  return false;
2142
1880
  }
2143
1881
  } catch (error) {
2144
- spinner.fail(chalk9.red(`Failed to install theme: ${theme}`));
1882
+ spinner.fail(chalk5.red(`Failed to install theme: ${theme}`));
2145
1883
  if (error instanceof Error) {
2146
- console.log(chalk9.red(` Error: ${error.message}`));
1884
+ console.log(chalk5.red(` Error: ${error.message}`));
2147
1885
  }
2148
1886
  return false;
2149
1887
  }
@@ -2154,23 +1892,23 @@ async function installPlugins(plugins) {
2154
1892
  }
2155
1893
  let allSuccess = true;
2156
1894
  for (const plugin of plugins) {
2157
- const spinner = ora6({
1895
+ const spinner = ora2({
2158
1896
  text: `Installing plugin: ${plugin}...`,
2159
1897
  prefixText: " "
2160
1898
  }).start();
2161
1899
  try {
2162
- const pluginDir = join6(process.cwd(), "contents", "plugins", plugin);
2163
- if (existsSync6(pluginDir)) {
2164
- spinner.info(chalk9.gray(`Plugin ${plugin} already installed`));
1900
+ const pluginDir = join3(process.cwd(), "contents", "plugins", plugin);
1901
+ if (existsSync2(pluginDir)) {
1902
+ spinner.info(chalk5.gray(`Plugin ${plugin} already installed`));
2165
1903
  continue;
2166
1904
  }
2167
- if (isMonorepoMode2()) {
1905
+ if (isMonorepoMode()) {
2168
1906
  const localDir = getLocalPackageDir("plugin", plugin);
2169
1907
  if (localDir) {
2170
1908
  spinner.text = `Copying plugin from local: ${plugin}...`;
2171
1909
  const success2 = await copyLocalPlugin(plugin, localDir);
2172
1910
  if (success2) {
2173
- spinner.succeed(chalk9.green(`Plugin ${plugin} installed!`));
1911
+ spinner.succeed(chalk5.green(`Plugin ${plugin} installed!`));
2174
1912
  continue;
2175
1913
  }
2176
1914
  }
@@ -2179,16 +1917,16 @@ async function installPlugins(plugins) {
2179
1917
  const packageSpec = PLUGIN_PACKAGES[plugin];
2180
1918
  const success = await installPluginViaCli(packageSpec);
2181
1919
  if (success) {
2182
- spinner.succeed(chalk9.green(`Plugin ${plugin} installed!`));
1920
+ spinner.succeed(chalk5.green(`Plugin ${plugin} installed!`));
2183
1921
  } else {
2184
- spinner.fail(chalk9.red(`Failed to install plugin: ${plugin}`));
2185
- console.log(chalk9.gray(" Hint: Make sure @nextsparkjs/cli is installed or the plugin package is published"));
1922
+ spinner.fail(chalk5.red(`Failed to install plugin: ${plugin}`));
1923
+ console.log(chalk5.gray(" Hint: Make sure @nextsparkjs/cli is installed or the plugin package is published"));
2186
1924
  allSuccess = false;
2187
1925
  }
2188
1926
  } catch (error) {
2189
- spinner.fail(chalk9.red(`Failed to install plugin: ${plugin}`));
1927
+ spinner.fail(chalk5.red(`Failed to install plugin: ${plugin}`));
2190
1928
  if (error instanceof Error) {
2191
- console.log(chalk9.red(` Error: ${error.message}`));
1929
+ console.log(chalk5.red(` Error: ${error.message}`));
2192
1930
  }
2193
1931
  allSuccess = false;
2194
1932
  }
@@ -2200,81 +1938,627 @@ async function installThemeAndPlugins(theme, plugins) {
2200
1938
  return true;
2201
1939
  }
2202
1940
  console.log("");
2203
- console.log(chalk9.cyan(" Installing Reference Theme & Plugins"));
2204
- console.log(chalk9.gray(" " + "-".repeat(40)));
1941
+ console.log(chalk5.cyan(" Installing Reference Theme & Plugins"));
1942
+ console.log(chalk5.gray(" " + "-".repeat(40)));
2205
1943
  console.log("");
2206
1944
  const themeSuccess = await installTheme2(theme);
2207
1945
  if (!themeSuccess && theme) {
2208
- console.log(chalk9.yellow(" Warning: Theme installation failed, continuing with plugins..."));
1946
+ console.log(chalk5.yellow(" Warning: Theme installation failed, continuing with plugins..."));
2209
1947
  }
2210
1948
  const pluginsSuccess = await installPlugins(plugins);
2211
1949
  console.log("");
2212
1950
  if (themeSuccess && pluginsSuccess) {
2213
- console.log(chalk9.green(" All installations completed successfully!"));
1951
+ console.log(chalk5.green(" All installations completed successfully!"));
2214
1952
  } else {
2215
- console.log(chalk9.yellow(" Some installations had issues. Check the messages above."));
1953
+ console.log(chalk5.yellow(" Some installations had issues. Check the messages above."));
2216
1954
  }
2217
1955
  return themeSuccess && pluginsSuccess;
2218
1956
  }
2219
1957
 
2220
1958
  // src/wizard/generators/env-setup.ts
2221
1959
  import fs5 from "fs-extra";
1960
+ import path5 from "path";
1961
+ import { fileURLToPath as fileURLToPath4 } from "url";
1962
+ var __filename4 = fileURLToPath4(import.meta.url);
1963
+ var __dirname4 = path5.dirname(__filename4);
1964
+ var ENV_TEMPLATE_PATH = path5.resolve(__dirname4, "../../../templates/env.template");
2222
1965
 
2223
1966
  // src/wizard/generators/git-init.ts
2224
1967
  import fs6 from "fs-extra";
2225
1968
 
2226
- // src/wizard/generators/index.ts
1969
+ // src/wizard/generators/monorepo-generator.ts
1970
+ import fs7 from "fs-extra";
1971
+ import path6 from "path";
1972
+ import { fileURLToPath as fileURLToPath5 } from "url";
2227
1973
  var __filename5 = fileURLToPath5(import.meta.url);
2228
- var __dirname5 = path5.dirname(__filename5);
2229
- function getTemplatesDir3() {
2230
- try {
2231
- const corePkgPath = __require.resolve("@nextsparkjs/core/package.json");
2232
- return path5.join(path5.dirname(corePkgPath), "templates");
2233
- } catch {
2234
- const possiblePaths = [
2235
- path5.resolve(__dirname5, "../../../../../core/templates"),
2236
- path5.resolve(__dirname5, "../../../../core/templates"),
2237
- path5.resolve(process.cwd(), "node_modules/@nextsparkjs/core/templates")
2238
- ];
2239
- for (const p of possiblePaths) {
2240
- if (fs7.existsSync(p)) {
2241
- return p;
2242
- }
1974
+ var __dirname5 = path6.dirname(__filename5);
1975
+ var DIRS = {
1976
+ WEB: "web",
1977
+ MOBILE: "mobile",
1978
+ ASSETS: "assets"
1979
+ };
1980
+ var FILES = {
1981
+ PNPM_WORKSPACE: "pnpm-workspace.yaml",
1982
+ NPMRC: ".npmrc",
1983
+ GITIGNORE: ".gitignore",
1984
+ TSCONFIG: "tsconfig.json",
1985
+ PACKAGE_JSON: "package.json",
1986
+ README: "README.md",
1987
+ APP_CONFIG: "app.config.ts"
1988
+ };
1989
+ var REQUIRED_MOBILE_TEMPLATE_FILES = [
1990
+ "app",
1991
+ "src",
1992
+ "babel.config.js",
1993
+ "metro.config.js"
1994
+ ];
1995
+ var MOBILE_TEMPLATE_FILES = [
1996
+ "app",
1997
+ "src",
1998
+ "assets",
1999
+ "babel.config.js",
2000
+ "metro.config.js",
2001
+ "tailwind.config.js",
2002
+ "tsconfig.json",
2003
+ "jest.config.js",
2004
+ "eas.json"
2005
+ ];
2006
+ var VERSIONS = {
2007
+ // NextSpark packages - use 'latest' for consistency with web packages
2008
+ NEXTSPARK_MOBILE: "latest",
2009
+ NEXTSPARK_UI: "latest",
2010
+ // Core dependencies
2011
+ TANSTACK_QUERY: "^5.62.0",
2012
+ EXPO: "^54.0.0",
2013
+ REACT: "19.1.0",
2014
+ REACT_NATIVE: "0.81.5",
2015
+ TYPESCRIPT: "^5.3.0",
2016
+ // Expo modules (use ~ for patch compatibility)
2017
+ EXPO_CONSTANTS: "~18.0.13",
2018
+ EXPO_LINKING: "~8.0.11",
2019
+ EXPO_ROUTER: "~6.0.22",
2020
+ EXPO_SECURE_STORE: "~15.0.8",
2021
+ EXPO_STATUS_BAR: "~3.0.9",
2022
+ // React Native modules
2023
+ RN_GESTURE_HANDLER: "~2.28.0",
2024
+ RN_REANIMATED: "~4.1.1",
2025
+ RN_SAFE_AREA: "~5.6.0",
2026
+ RN_SCREENS: "~4.16.0",
2027
+ RN_SVG: "15.12.1",
2028
+ RN_WEB: "^0.21.0",
2029
+ // Styling
2030
+ NATIVEWIND: "^4.2.1",
2031
+ TAILWINDCSS: "^3",
2032
+ TAILWIND_MERGE: "^3.4.0",
2033
+ LUCIDE_RN: "^0.563.0",
2034
+ // Dev dependencies
2035
+ BABEL_CORE: "^7.25.0",
2036
+ JEST: "^29.7.0",
2037
+ JEST_EXPO: "^54.0.16",
2038
+ TESTING_LIBRARY_JEST_NATIVE: "^5.4.3",
2039
+ TESTING_LIBRARY_RN: "^13.3.3"
2040
+ };
2041
+ function getMobileTemplatesDir() {
2042
+ const possiblePaths = [
2043
+ // From CWD node_modules (installed package)
2044
+ path6.resolve(process.cwd(), "node_modules/@nextsparkjs/mobile/templates"),
2045
+ // From CLI dist folder: ../../mobile/templates (development)
2046
+ path6.resolve(__dirname5, "../../mobile/templates"),
2047
+ // Legacy paths for different build structures
2048
+ path6.resolve(__dirname5, "../../../../../mobile/templates"),
2049
+ path6.resolve(__dirname5, "../../../../mobile/templates")
2050
+ ];
2051
+ for (const p of possiblePaths) {
2052
+ if (fs7.existsSync(p)) {
2053
+ return p;
2054
+ }
2055
+ }
2056
+ const searchedPaths = possiblePaths.map((p) => ` - ${p}`).join("\n");
2057
+ throw new Error(
2058
+ `Could not find @nextsparkjs/mobile templates directory.
2059
+
2060
+ Searched paths:
2061
+ ${searchedPaths}
2062
+
2063
+ To fix this, ensure @nextsparkjs/mobile is installed:
2064
+ pnpm add @nextsparkjs/mobile
2065
+
2066
+ If you're developing locally, make sure the mobile package is built:
2067
+ cd packages/mobile && pnpm build`
2068
+ );
2069
+ }
2070
+ async function validateMobileTemplate(templateDir) {
2071
+ const missing = [];
2072
+ for (const file of REQUIRED_MOBILE_TEMPLATE_FILES) {
2073
+ const filePath = path6.join(templateDir, file);
2074
+ if (!await fs7.pathExists(filePath)) {
2075
+ missing.push(file);
2076
+ }
2077
+ }
2078
+ if (missing.length > 0) {
2079
+ throw new Error(
2080
+ `Mobile template is incomplete. Missing required files:
2081
+ ` + missing.map((f) => ` - ${f}`).join("\n") + `
2082
+
2083
+ Template location: ${templateDir}
2084
+ Please ensure @nextsparkjs/mobile is properly installed and built.`
2085
+ );
2086
+ }
2087
+ }
2088
+ function slugToBundleId(slug) {
2089
+ return slug.toLowerCase().replace(/[^a-z0-9]+/g, ".").replace(/^\.+|\.+$/g, "").replace(/\.{2,}/g, ".");
2090
+ }
2091
+ async function createRootPackageJson(targetDir, config2) {
2092
+ const rootPkg = {
2093
+ name: config2.projectSlug,
2094
+ version: "0.1.0",
2095
+ private: true,
2096
+ scripts: {
2097
+ // Web commands
2098
+ "dev": `pnpm --filter ${DIRS.WEB} dev`,
2099
+ "build": `pnpm --filter ${DIRS.WEB} build`,
2100
+ "start": `pnpm --filter ${DIRS.WEB} start`,
2101
+ "lint": "pnpm -r lint",
2102
+ // Mobile commands
2103
+ "dev:mobile": `pnpm --filter ${DIRS.MOBILE} start`,
2104
+ "ios": `pnpm --filter ${DIRS.MOBILE} ios`,
2105
+ "android": `pnpm --filter ${DIRS.MOBILE} android`,
2106
+ // Shared commands
2107
+ "typecheck": "pnpm -r typecheck",
2108
+ "test": "pnpm -r test",
2109
+ // Web-specific CLI commands (run from root)
2110
+ "db:migrate": `pnpm --filter ${DIRS.WEB} db:migrate`,
2111
+ "db:seed": `pnpm --filter ${DIRS.WEB} db:seed`,
2112
+ "build:registries": `pnpm --filter ${DIRS.WEB} build:registries`
2113
+ },
2114
+ devDependencies: {
2115
+ "typescript": VERSIONS.TYPESCRIPT
2116
+ }
2117
+ };
2118
+ await fs7.writeJson(path6.join(targetDir, FILES.PACKAGE_JSON), rootPkg, { spaces: 2 });
2119
+ }
2120
+ async function createPnpmWorkspace(targetDir) {
2121
+ const workspaceContent = `packages:
2122
+ - '${DIRS.WEB}'
2123
+ - '${DIRS.MOBILE}'
2124
+ `;
2125
+ await fs7.writeFile(path6.join(targetDir, FILES.PNPM_WORKSPACE), workspaceContent);
2126
+ }
2127
+ async function createNpmrc(targetDir) {
2128
+ const npmrcContent = `# Enable proper hoisting for monorepo
2129
+ public-hoist-pattern[]=*@nextsparkjs/*
2130
+ public-hoist-pattern[]=*expo*
2131
+ public-hoist-pattern[]=*react-native*
2132
+ shamefully-hoist=true
2133
+ `;
2134
+ await fs7.writeFile(path6.join(targetDir, FILES.NPMRC), npmrcContent);
2135
+ }
2136
+ async function createGitignore(targetDir) {
2137
+ const gitignoreContent = `# Dependencies
2138
+ node_modules/
2139
+
2140
+ # Build outputs
2141
+ .next/
2142
+ dist/
2143
+ out/
2144
+ build/
2145
+ .expo/
2146
+
2147
+ # NextSpark
2148
+ .nextspark/
2149
+
2150
+ # Environment
2151
+ .env
2152
+ .env.local
2153
+ .env.*.local
2154
+
2155
+ # IDE
2156
+ .idea/
2157
+ .vscode/
2158
+ *.swp
2159
+ *.swo
2160
+
2161
+ # OS
2162
+ .DS_Store
2163
+ Thumbs.db
2164
+
2165
+ # Testing
2166
+ coverage/
2167
+ .nyc_output/
2168
+
2169
+ # Cypress (theme-based in web/)
2170
+ ${DIRS.WEB}/contents/themes/*/tests/cypress/videos
2171
+ ${DIRS.WEB}/contents/themes/*/tests/cypress/screenshots
2172
+ ${DIRS.WEB}/contents/themes/*/tests/cypress/allure-results
2173
+ ${DIRS.WEB}/contents/themes/*/tests/cypress/allure-report
2174
+
2175
+ # Jest (theme-based in web/)
2176
+ ${DIRS.WEB}/contents/themes/*/tests/jest/coverage
2177
+
2178
+ # Mobile specific
2179
+ ${DIRS.MOBILE}/.expo/
2180
+ *.jks
2181
+ *.p8
2182
+ *.p12
2183
+ *.key
2184
+ *.mobileprovision
2185
+ *.orig.*
2186
+ web-build/
2187
+ `;
2188
+ await fs7.writeFile(path6.join(targetDir, FILES.GITIGNORE), gitignoreContent);
2189
+ }
2190
+ async function createRootTsConfig(targetDir) {
2191
+ const tsConfig = {
2192
+ compilerOptions: {
2193
+ target: "ES2022",
2194
+ module: "ESNext",
2195
+ moduleResolution: "bundler",
2196
+ strict: true,
2197
+ skipLibCheck: true,
2198
+ esModuleInterop: true
2199
+ },
2200
+ references: [
2201
+ { path: `./${DIRS.WEB}` },
2202
+ { path: `./${DIRS.MOBILE}` }
2203
+ ]
2204
+ };
2205
+ await fs7.writeJson(path6.join(targetDir, FILES.TSCONFIG), tsConfig, { spaces: 2 });
2206
+ }
2207
+ async function copyMobileTemplate(targetDir, config2) {
2208
+ const mobileTemplatesDir = getMobileTemplatesDir();
2209
+ await validateMobileTemplate(mobileTemplatesDir);
2210
+ const mobileDir = path6.join(targetDir, DIRS.MOBILE);
2211
+ await fs7.ensureDir(mobileDir);
2212
+ for (const item of MOBILE_TEMPLATE_FILES) {
2213
+ const srcPath = path6.join(mobileTemplatesDir, item);
2214
+ const destPath = path6.join(mobileDir, item);
2215
+ if (await fs7.pathExists(srcPath)) {
2216
+ await fs7.copy(srcPath, destPath);
2217
+ }
2218
+ }
2219
+ await createMobilePackageJson(mobileDir, config2);
2220
+ await createMobileAppConfig(mobileDir, config2);
2221
+ await createMobileAssets(mobileDir, config2);
2222
+ }
2223
+ async function createMobilePackageJson(mobileDir, config2) {
2224
+ const mobileSlug = `${config2.projectSlug}-mobile`;
2225
+ const packageJson = {
2226
+ name: mobileSlug,
2227
+ version: "0.1.0",
2228
+ private: true,
2229
+ main: "expo-router/entry",
2230
+ scripts: {
2231
+ start: "expo start",
2232
+ android: "expo start --android",
2233
+ ios: "expo start --ios",
2234
+ web: "expo start --web",
2235
+ lint: "eslint .",
2236
+ typecheck: "tsc --noEmit",
2237
+ test: "jest",
2238
+ "test:watch": "jest --watch",
2239
+ "test:coverage": "jest --coverage"
2240
+ },
2241
+ dependencies: {
2242
+ "@nextsparkjs/mobile": VERSIONS.NEXTSPARK_MOBILE,
2243
+ "@nextsparkjs/ui": VERSIONS.NEXTSPARK_UI,
2244
+ "@tanstack/react-query": VERSIONS.TANSTACK_QUERY,
2245
+ "expo": VERSIONS.EXPO,
2246
+ "expo-constants": VERSIONS.EXPO_CONSTANTS,
2247
+ "expo-linking": VERSIONS.EXPO_LINKING,
2248
+ "expo-router": VERSIONS.EXPO_ROUTER,
2249
+ "expo-secure-store": VERSIONS.EXPO_SECURE_STORE,
2250
+ "expo-status-bar": VERSIONS.EXPO_STATUS_BAR,
2251
+ "lucide-react-native": VERSIONS.LUCIDE_RN,
2252
+ "nativewind": VERSIONS.NATIVEWIND,
2253
+ "react": VERSIONS.REACT,
2254
+ "react-dom": VERSIONS.REACT,
2255
+ "react-native": VERSIONS.REACT_NATIVE,
2256
+ "react-native-web": VERSIONS.RN_WEB,
2257
+ "react-native-gesture-handler": VERSIONS.RN_GESTURE_HANDLER,
2258
+ "react-native-reanimated": VERSIONS.RN_REANIMATED,
2259
+ "react-native-safe-area-context": VERSIONS.RN_SAFE_AREA,
2260
+ "react-native-screens": VERSIONS.RN_SCREENS,
2261
+ "react-native-svg": VERSIONS.RN_SVG,
2262
+ "tailwind-merge": VERSIONS.TAILWIND_MERGE,
2263
+ "tailwindcss": VERSIONS.TAILWINDCSS
2264
+ },
2265
+ devDependencies: {
2266
+ "@babel/core": VERSIONS.BABEL_CORE,
2267
+ "@testing-library/jest-native": VERSIONS.TESTING_LIBRARY_JEST_NATIVE,
2268
+ "@testing-library/react-native": VERSIONS.TESTING_LIBRARY_RN,
2269
+ "@types/jest": "^29.5.0",
2270
+ "@types/react": "^19",
2271
+ "jest": VERSIONS.JEST,
2272
+ "jest-expo": VERSIONS.JEST_EXPO,
2273
+ "react-test-renderer": VERSIONS.REACT,
2274
+ "typescript": VERSIONS.TYPESCRIPT
2275
+ }
2276
+ };
2277
+ await fs7.writeJson(path6.join(mobileDir, FILES.PACKAGE_JSON), packageJson, { spaces: 2 });
2278
+ }
2279
+ async function createMobileAppConfig(mobileDir, config2) {
2280
+ const bundleId = slugToBundleId(config2.projectSlug);
2281
+ const appConfigContent = `import { ExpoConfig, ConfigContext } from 'expo/config'
2282
+
2283
+ export default ({ config }: ConfigContext): ExpoConfig => ({
2284
+ ...config,
2285
+ name: '${config2.projectName}',
2286
+ slug: '${config2.projectSlug}',
2287
+ version: '1.0.0',
2288
+ orientation: 'portrait',
2289
+ icon: './${DIRS.ASSETS}/icon.png',
2290
+ userInterfaceStyle: 'automatic',
2291
+ splash: {
2292
+ image: './${DIRS.ASSETS}/splash.png',
2293
+ resizeMode: 'contain',
2294
+ backgroundColor: '#ffffff',
2295
+ },
2296
+ assetBundlePatterns: ['**/*'],
2297
+ ios: {
2298
+ supportsTablet: true,
2299
+ bundleIdentifier: 'com.${bundleId}.app',
2300
+ },
2301
+ android: {
2302
+ adaptiveIcon: {
2303
+ foregroundImage: './${DIRS.ASSETS}/adaptive-icon.png',
2304
+ backgroundColor: '#ffffff',
2305
+ },
2306
+ package: 'com.${bundleId}.app',
2307
+ },
2308
+ web: {
2309
+ favicon: './${DIRS.ASSETS}/favicon.png',
2310
+ },
2311
+ extra: {
2312
+ // API URL - Configure for your environment
2313
+ // Development: point to your local web app (e.g., http://localhost:3000)
2314
+ // Production: set via EAS environment variables
2315
+ apiUrl: process.env.EXPO_PUBLIC_API_URL,
2316
+ },
2317
+ plugins: ['expo-router', 'expo-secure-store'],
2318
+ scheme: '${config2.projectSlug}',
2319
+ })
2320
+ `;
2321
+ await fs7.writeFile(path6.join(mobileDir, FILES.APP_CONFIG), appConfigContent);
2322
+ }
2323
+ async function createMobileAssets(mobileDir, config2) {
2324
+ const assetsDir = path6.join(mobileDir, DIRS.ASSETS);
2325
+ await fs7.ensureDir(assetsDir);
2326
+ await fs7.writeFile(
2327
+ path6.join(assetsDir, ".gitkeep"),
2328
+ "# Placeholder - replace with your app icons and splash screens\n"
2329
+ );
2330
+ const assetsReadme = `# Mobile App Assets for ${config2.projectName}
2331
+
2332
+ This directory contains your mobile app icons and splash screens.
2333
+
2334
+ ## Required Files
2335
+
2336
+ | File | Size | Description |
2337
+ |------|------|-------------|
2338
+ | \`icon.png\` | 1024x1024px | Main app icon (iOS & Android) |
2339
+ | \`splash.png\` | 1284x2778px | Splash screen image |
2340
+ | \`adaptive-icon.png\` | 1024x1024px | Android adaptive icon (foreground) |
2341
+ | \`favicon.png\` | 48x48px | Web favicon |
2342
+
2343
+ ## How to Generate
2344
+
2345
+ ### Option 1: Expo Asset Generator (Recommended)
2346
+
2347
+ 1. Create a 1024x1024px icon image
2348
+ 2. Use Expo's icon generator:
2349
+ \`\`\`bash
2350
+ npx expo-optimize
2351
+ \`\`\`
2352
+
2353
+ ### Option 2: Online Tools
2354
+
2355
+ - [Expo Icon Generator](https://docs.expo.dev/develop/user-interface/app-icons/)
2356
+ - [App Icon Generator](https://appicon.co/)
2357
+ - [Figma App Icon Template](https://www.figma.com/community/file/824894885635013116)
2358
+
2359
+ ### Option 3: Manual Creation
2360
+
2361
+ Create each file at the specified sizes above. Use PNG format with transparency for icons.
2362
+
2363
+ ## Tips
2364
+
2365
+ - Use a simple, recognizable design that works at small sizes
2366
+ - Test your icon on both light and dark backgrounds
2367
+ - Avoid text in the icon (it becomes illegible at small sizes)
2368
+ - Keep important content within the "safe zone" (center 80%)
2369
+
2370
+ ## Resources
2371
+
2372
+ - [Expo App Icons Documentation](https://docs.expo.dev/develop/user-interface/app-icons/)
2373
+ - [iOS Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/app-icons)
2374
+ - [Android Adaptive Icons](https://developer.android.com/develop/ui/views/launch/icon_design_adaptive)
2375
+ `;
2376
+ await fs7.writeFile(path6.join(assetsDir, "README.md"), assetsReadme);
2377
+ }
2378
+ async function createMonorepoReadme(targetDir, config2) {
2379
+ const readmeContent = `# ${config2.projectName}
2380
+
2381
+ ${config2.projectDescription}
2382
+
2383
+ ## Project Structure
2384
+
2385
+ This is a monorepo containing both web and mobile applications:
2386
+
2387
+ \`\`\`
2388
+ ${config2.projectSlug}/
2389
+ \u251C\u2500\u2500 ${DIRS.WEB}/ # Next.js web application
2390
+ \u2502 \u251C\u2500\u2500 app/ # Next.js App Router
2391
+ \u2502 \u251C\u2500\u2500 contents/ # Themes and plugins
2392
+ \u2502 \u2514\u2500\u2500 package.json
2393
+ \u251C\u2500\u2500 ${DIRS.MOBILE}/ # Expo mobile application
2394
+ \u2502 \u251C\u2500\u2500 app/ # Expo Router screens
2395
+ \u2502 \u251C\u2500\u2500 src/ # Mobile-specific code
2396
+ \u2502 \u2514\u2500\u2500 package.json
2397
+ \u251C\u2500\u2500 package.json # Root monorepo
2398
+ \u2514\u2500\u2500 ${FILES.PNPM_WORKSPACE}
2399
+ \`\`\`
2400
+
2401
+ ## Getting Started
2402
+
2403
+ ### Prerequisites
2404
+
2405
+ - Node.js 20+
2406
+ - pnpm 9+
2407
+ - For mobile: Expo CLI (\`npm install -g expo-cli\`)
2408
+
2409
+ ### Installation
2410
+
2411
+ \`\`\`bash
2412
+ # Install all dependencies
2413
+ pnpm install
2414
+
2415
+ # Set up environment variables
2416
+ cp ${DIRS.WEB}/.env.example ${DIRS.WEB}/.env
2417
+ # Edit ${DIRS.WEB}/.env with your configuration
2418
+ \`\`\`
2419
+
2420
+ ### Development
2421
+
2422
+ **Web Application:**
2423
+ \`\`\`bash
2424
+ # From root directory
2425
+ pnpm dev
2426
+
2427
+ # Or from web directory
2428
+ cd ${DIRS.WEB} && pnpm dev
2429
+ \`\`\`
2430
+
2431
+ **Mobile Application:**
2432
+ \`\`\`bash
2433
+ # From root directory
2434
+ pnpm dev:mobile
2435
+
2436
+ # Or from mobile directory
2437
+ cd ${DIRS.MOBILE} && pnpm start
2438
+ \`\`\`
2439
+
2440
+ ### Running Tests
2441
+
2442
+ \`\`\`bash
2443
+ # Run all tests
2444
+ pnpm test
2445
+
2446
+ # Run web tests only
2447
+ pnpm --filter ${DIRS.WEB} test
2448
+
2449
+ # Run mobile tests only
2450
+ pnpm --filter ${DIRS.MOBILE} test
2451
+ \`\`\`
2452
+
2453
+ ## Mobile App Configuration
2454
+
2455
+ The mobile app connects to your web API. Configure the API URL:
2456
+
2457
+ - **Development:** The mobile app will auto-detect your local server
2458
+ - **Production:** Set \`EXPO_PUBLIC_API_URL\` in your EAS environment
2459
+
2460
+ ## Building for Production
2461
+
2462
+ **Web:**
2463
+ \`\`\`bash
2464
+ pnpm build
2465
+ \`\`\`
2466
+
2467
+ **Mobile:**
2468
+ \`\`\`bash
2469
+ cd ${DIRS.MOBILE}
2470
+ eas build --platform ios
2471
+ eas build --platform android
2472
+ \`\`\`
2473
+
2474
+ ## Learn More
2475
+
2476
+ - [NextSpark Documentation](https://nextspark.dev/docs)
2477
+ - [Expo Documentation](https://docs.expo.dev)
2478
+ - [Next.js Documentation](https://nextjs.org/docs)
2479
+ `;
2480
+ await fs7.writeFile(path6.join(targetDir, FILES.README), readmeContent);
2481
+ }
2482
+ async function generateMonorepoStructure(targetDir, config2) {
2483
+ await createRootPackageJson(targetDir, config2);
2484
+ await createPnpmWorkspace(targetDir);
2485
+ await createNpmrc(targetDir);
2486
+ await createGitignore(targetDir);
2487
+ await createRootTsConfig(targetDir);
2488
+ await createMonorepoReadme(targetDir, config2);
2489
+ const webDir = path6.join(targetDir, DIRS.WEB);
2490
+ await fs7.ensureDir(webDir);
2491
+ await copyMobileTemplate(targetDir, config2);
2492
+ }
2493
+ function isMonorepoProject(config2) {
2494
+ return config2.projectType === "web-mobile";
2495
+ }
2496
+ function getWebDir(targetDir, config2) {
2497
+ return config2.projectType === "web-mobile" ? path6.join(targetDir, DIRS.WEB) : targetDir;
2498
+ }
2499
+
2500
+ // src/wizard/generators/index.ts
2501
+ var __filename6 = fileURLToPath6(import.meta.url);
2502
+ var __dirname6 = path7.dirname(__filename6);
2503
+ function getTemplatesDir3(projectRoot) {
2504
+ const rootDir = projectRoot || process.cwd();
2505
+ const possiblePaths = [
2506
+ // From project root node_modules (most common for installed packages)
2507
+ path7.resolve(rootDir, "node_modules/@nextsparkjs/core/templates"),
2508
+ // From CLI dist folder for development
2509
+ path7.resolve(__dirname6, "../../core/templates"),
2510
+ // Legacy paths for different build structures
2511
+ path7.resolve(__dirname6, "../../../../../core/templates"),
2512
+ path7.resolve(__dirname6, "../../../../core/templates")
2513
+ ];
2514
+ for (const p of possiblePaths) {
2515
+ if (fs8.existsSync(p)) {
2516
+ return p;
2243
2517
  }
2244
- throw new Error("Could not find @nextsparkjs/core templates directory");
2245
2518
  }
2519
+ throw new Error(`Could not find @nextsparkjs/core templates directory. Searched: ${possiblePaths.join(", ")}`);
2246
2520
  }
2521
+ var cachedTemplatesDir = null;
2247
2522
  async function copyProjectFiles() {
2248
- const templatesDir = getTemplatesDir3();
2523
+ if (!cachedTemplatesDir) {
2524
+ throw new Error("Templates directory not cached. Call cacheTemplatesDir() first.");
2525
+ }
2526
+ const templatesDir = cachedTemplatesDir;
2249
2527
  const projectDir = process.cwd();
2250
2528
  const itemsToCopy = [
2251
2529
  { src: "app", dest: "app", force: true },
2252
2530
  { src: "public", dest: "public", force: true },
2531
+ { src: "proxy.ts", dest: "proxy.ts", force: true },
2532
+ // Next.js 16+ proxy (formerly middleware.ts)
2253
2533
  { src: "next.config.mjs", dest: "next.config.mjs", force: true },
2254
2534
  { src: "tsconfig.json", dest: "tsconfig.json", force: true },
2255
2535
  { src: "postcss.config.mjs", dest: "postcss.config.mjs", force: true },
2256
2536
  { src: "i18n.ts", dest: "i18n.ts", force: true },
2257
- { src: "npmrc", dest: ".npmrc", force: false },
2537
+ { src: "pnpm-workspace.yaml", dest: "pnpm-workspace.yaml", force: true },
2538
+ // Enable workspace for themes/plugins - REQUIRED
2539
+ // Note: .npmrc is NOT copied for web-only projects (not needed, pnpm default hoisting works fine)
2540
+ // For monorepo projects, monorepo-generator.ts creates a specific .npmrc with expo/react-native patterns
2258
2541
  { src: "tsconfig.cypress.json", dest: "tsconfig.cypress.json", force: false },
2259
2542
  { src: "cypress.d.ts", dest: "cypress.d.ts", force: false },
2260
- { src: "eslint.config.mjs", dest: "eslint.config.mjs", force: false }
2543
+ { src: "eslint.config.mjs", dest: "eslint.config.mjs", force: false },
2544
+ { src: "scripts/cy-run-prod.cjs", dest: "scripts/cy-run-prod.cjs", force: false }
2261
2545
  ];
2262
2546
  for (const item of itemsToCopy) {
2263
- const srcPath = path5.join(templatesDir, item.src);
2264
- const destPath = path5.join(projectDir, item.dest);
2265
- if (await fs7.pathExists(srcPath)) {
2266
- if (item.force || !await fs7.pathExists(destPath)) {
2267
- await fs7.copy(srcPath, destPath);
2547
+ const srcPath = path7.join(templatesDir, item.src);
2548
+ const destPath = path7.join(projectDir, item.dest);
2549
+ if (await fs8.pathExists(srcPath)) {
2550
+ if (item.force || !await fs8.pathExists(destPath)) {
2551
+ await fs8.copy(srcPath, destPath);
2268
2552
  }
2269
2553
  }
2270
2554
  }
2271
2555
  }
2272
- async function updatePackageJson(config) {
2273
- const packageJsonPath = path5.resolve(process.cwd(), "package.json");
2556
+ async function updatePackageJson(config2) {
2557
+ const packageJsonPath = path7.resolve(process.cwd(), "package.json");
2274
2558
  let packageJson;
2275
- if (!await fs7.pathExists(packageJsonPath)) {
2559
+ if (!await fs8.pathExists(packageJsonPath)) {
2276
2560
  packageJson = {
2277
- name: config.projectSlug,
2561
+ name: isMonorepoProject(config2) ? "web" : config2.projectSlug,
2278
2562
  version: "0.1.0",
2279
2563
  private: true,
2280
2564
  scripts: {},
@@ -2282,7 +2566,7 @@ async function updatePackageJson(config) {
2282
2566
  devDependencies: {}
2283
2567
  };
2284
2568
  } else {
2285
- packageJson = await fs7.readJson(packageJsonPath);
2569
+ packageJson = await fs8.readJson(packageJsonPath);
2286
2570
  }
2287
2571
  packageJson.scripts = packageJson.scripts || {};
2288
2572
  const scriptsToAdd = {
@@ -2293,11 +2577,13 @@ async function updatePackageJson(config) {
2293
2577
  "build:registries": "nextspark registry:build",
2294
2578
  "db:migrate": "nextspark db:migrate",
2295
2579
  "db:seed": "nextspark db:seed",
2296
- "test:theme": `jest --config contents/themes/${config.projectSlug}/tests/jest/jest.config.ts`,
2297
- "cy:open": `cypress open --config-file contents/themes/${config.projectSlug}/tests/cypress.config.ts`,
2298
- "cy:run": `cypress run --config-file contents/themes/${config.projectSlug}/tests/cypress.config.ts`,
2299
- "allure:generate": `allure generate contents/themes/${config.projectSlug}/tests/cypress/allure-results --clean -o contents/themes/${config.projectSlug}/tests/cypress/allure-report`,
2300
- "allure:open": `allure open contents/themes/${config.projectSlug}/tests/cypress/allure-report`
2580
+ "test": "node node_modules/@nextsparkjs/core/scripts/test/jest-theme.mjs",
2581
+ "cy:open": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs open",
2582
+ "cy:run": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs run",
2583
+ "cy:tags": "node node_modules/@nextsparkjs/core/scripts/test/cy.mjs tags",
2584
+ "cy:run:prod": "node scripts/cy-run-prod.cjs",
2585
+ "allure:generate": `allure generate contents/themes/${config2.projectSlug}/tests/cypress/allure-results --clean -o contents/themes/${config2.projectSlug}/tests/cypress/allure-report`,
2586
+ "allure:open": `allure open contents/themes/${config2.projectSlug}/tests/cypress/allure-report`
2301
2587
  };
2302
2588
  for (const [name, command] of Object.entries(scriptsToAdd)) {
2303
2589
  if (!packageJson.scripts[name]) {
@@ -2307,8 +2593,8 @@ async function updatePackageJson(config) {
2307
2593
  packageJson.dependencies = packageJson.dependencies || {};
2308
2594
  const depsToAdd = {
2309
2595
  // NextSpark
2310
- "@nextsparkjs/core": "^0.1.0-beta.4",
2311
- "@nextsparkjs/cli": "^0.1.0-beta.4",
2596
+ "@nextsparkjs/core": "latest",
2597
+ "@nextsparkjs/cli": "latest",
2312
2598
  // Next.js + React
2313
2599
  "next": "^15.1.0",
2314
2600
  "react": "^19.0.0",
@@ -2339,7 +2625,9 @@ async function updatePackageJson(config) {
2339
2625
  "slugify": "^1.6.6"
2340
2626
  };
2341
2627
  for (const [name, version] of Object.entries(depsToAdd)) {
2342
- if (!packageJson.dependencies[name]) {
2628
+ if (name.startsWith("@nextsparkjs/")) {
2629
+ packageJson.dependencies[name] = version;
2630
+ } else if (!packageJson.dependencies[name]) {
2343
2631
  packageJson.dependencies[name] = version;
2344
2632
  }
2345
2633
  }
@@ -2367,24 +2655,28 @@ async function updatePackageJson(config) {
2367
2655
  "@testing-library/react": "^16.3.0",
2368
2656
  "jest-environment-jsdom": "^29.7.0",
2369
2657
  // Cypress
2370
- "cypress": "^14.0.0",
2658
+ "cypress": "^15.8.2",
2371
2659
  "@testing-library/cypress": "^10.0.2",
2372
2660
  "@cypress/webpack-preprocessor": "^6.0.2",
2373
- "@cypress/grep": "^4.1.0",
2661
+ "@cypress/grep": "^5.0.1",
2374
2662
  "ts-loader": "^9.5.1",
2375
2663
  "webpack": "^5.97.0",
2376
2664
  "allure-cypress": "^3.0.0",
2377
- "allure-commandline": "^2.27.0"
2665
+ "allure-commandline": "^2.27.0",
2666
+ // NextSpark Testing
2667
+ "@nextsparkjs/testing": "latest"
2378
2668
  };
2379
2669
  for (const [name, version] of Object.entries(devDepsToAdd)) {
2380
- if (!packageJson.devDependencies[name]) {
2670
+ if (name.startsWith("@nextsparkjs/")) {
2671
+ packageJson.devDependencies[name] = version;
2672
+ } else if (!packageJson.devDependencies[name]) {
2381
2673
  packageJson.devDependencies[name] = version;
2382
2674
  }
2383
2675
  }
2384
- await fs7.writeJson(packageJsonPath, packageJson, { spaces: 2 });
2676
+ await fs8.writeJson(packageJsonPath, packageJson, { spaces: 2 });
2385
2677
  }
2386
- async function updateGitignore(config) {
2387
- const gitignorePath = path5.resolve(process.cwd(), ".gitignore");
2678
+ async function updateGitignore(config2) {
2679
+ const gitignorePath = path7.resolve(process.cwd(), ".gitignore");
2388
2680
  const entriesToAdd = `
2389
2681
  # NextSpark
2390
2682
  .nextspark/
@@ -2402,40 +2694,66 @@ contents/themes/*/tests/jest/coverage
2402
2694
  .env
2403
2695
  .env.local
2404
2696
  `;
2405
- if (await fs7.pathExists(gitignorePath)) {
2406
- const currentContent = await fs7.readFile(gitignorePath, "utf-8");
2697
+ if (await fs8.pathExists(gitignorePath)) {
2698
+ const currentContent = await fs8.readFile(gitignorePath, "utf-8");
2407
2699
  if (!currentContent.includes(".nextspark/")) {
2408
- await fs7.appendFile(gitignorePath, entriesToAdd);
2700
+ await fs8.appendFile(gitignorePath, entriesToAdd);
2409
2701
  }
2410
2702
  } else {
2411
- await fs7.writeFile(gitignorePath, entriesToAdd.trim());
2412
- }
2413
- }
2414
- async function generateProject(config) {
2415
- await copyProjectFiles();
2416
- await copyStarterTheme(config);
2417
- await copyContentFeatures(config);
2418
- await updateThemeConfig(config);
2419
- await updateDevConfig(config);
2420
- await updateAppConfig(config);
2421
- await updateBillingConfig(config);
2422
- await updateRolesConfig(config);
2423
- await updateMigrations(config);
2424
- await updatePermissionsConfig(config);
2425
- await updateDashboardConfig(config);
2426
- await updateAuthConfig(config);
2427
- await updateDashboardUIConfig(config);
2428
- await updateDevToolsConfig(config);
2429
- await processI18n(config);
2430
- await updatePackageJson(config);
2431
- await updateGitignore(config);
2432
- await generateEnvExample(config);
2433
- await updateReadme(config);
2434
- await copyEnvExampleToEnv();
2703
+ await fs8.writeFile(gitignorePath, entriesToAdd.trim());
2704
+ }
2705
+ }
2706
+ async function generateProject(config2) {
2707
+ const projectDir = process.cwd();
2708
+ cachedTemplatesDir = getTemplatesDir3(projectDir);
2709
+ const webDir = getWebDir(projectDir, config2);
2710
+ if (isMonorepoProject(config2)) {
2711
+ await generateMonorepoStructure(projectDir, config2);
2712
+ }
2713
+ const originalCwd = process.cwd();
2714
+ if (isMonorepoProject(config2)) {
2715
+ process.chdir(webDir);
2716
+ }
2717
+ try {
2718
+ await copyProjectFiles();
2719
+ await updateGlobalsCss(config2);
2720
+ await copyStarterTheme(config2);
2721
+ await fs8.ensureDir(path7.join(process.cwd(), "contents", "plugins"));
2722
+ await copyContentFeatures(config2);
2723
+ await updateThemeConfig(config2);
2724
+ await updateDevConfig(config2);
2725
+ await updateAppConfig(config2);
2726
+ await updateBillingConfig(config2);
2727
+ await updateRolesConfig(config2);
2728
+ await updateMigrations(config2);
2729
+ await updateTestFiles(config2);
2730
+ await updatePermissionsConfig(config2);
2731
+ await updateEntityPermissions(config2);
2732
+ await updateDashboardConfig(config2);
2733
+ await updateAuthConfig(config2);
2734
+ await updateDashboardUIConfig(config2);
2735
+ await updateDevToolsConfig(config2);
2736
+ await processI18n(config2);
2737
+ await updatePackageJson(config2);
2738
+ if (!isMonorepoProject(config2)) {
2739
+ await updateGitignore(config2);
2740
+ }
2741
+ await generateEnvExample(config2);
2742
+ if (!isMonorepoProject(config2)) {
2743
+ await updateReadme(config2);
2744
+ }
2745
+ await copyEnvExampleToEnv();
2746
+ } finally {
2747
+ if (isMonorepoProject(config2)) {
2748
+ process.chdir(originalCwd);
2749
+ }
2750
+ cachedTemplatesDir = null;
2751
+ }
2435
2752
  }
2436
2753
 
2437
2754
  // src/wizard/presets.ts
2438
2755
  var SAAS_PRESET = {
2756
+ projectType: "web",
2439
2757
  teamMode: "multi-tenant",
2440
2758
  teamRoles: ["owner", "admin", "member", "viewer"],
2441
2759
  defaultLocale: "en",
@@ -2474,6 +2792,7 @@ var SAAS_PRESET = {
2474
2792
  }
2475
2793
  };
2476
2794
  var BLOG_PRESET = {
2795
+ projectType: "web",
2477
2796
  teamMode: "single-user",
2478
2797
  teamRoles: ["owner"],
2479
2798
  defaultLocale: "en",
@@ -2512,6 +2831,7 @@ var BLOG_PRESET = {
2512
2831
  }
2513
2832
  };
2514
2833
  var CRM_PRESET = {
2834
+ projectType: "web",
2515
2835
  teamMode: "single-tenant",
2516
2836
  teamRoles: ["owner", "admin", "member"],
2517
2837
  defaultLocale: "en",
@@ -2566,16 +2886,18 @@ function getPreset(name) {
2566
2886
  }
2567
2887
  return preset;
2568
2888
  }
2569
- function applyPreset(projectInfo, presetName) {
2889
+ function applyPreset(projectInfo, presetName, typeOverride) {
2570
2890
  const preset = getPreset(presetName);
2571
2891
  return {
2572
2892
  ...projectInfo,
2573
- ...preset
2893
+ ...preset,
2894
+ // Apply type override if provided
2895
+ ...typeOverride && { projectType: typeOverride }
2574
2896
  };
2575
2897
  }
2576
2898
 
2577
2899
  // src/wizard/preview.ts
2578
- import chalk10 from "chalk";
2900
+ import chalk6 from "chalk";
2579
2901
  var BOX = {
2580
2902
  vertical: "\u2502",
2581
2903
  // |
@@ -2594,9 +2916,9 @@ var BOX = {
2594
2916
  bottomRight: "\u2518"
2595
2917
  // bottom-right corner
2596
2918
  };
2597
- function getFileTree(config) {
2919
+ function getFileTree(config2) {
2598
2920
  const files = [];
2599
- const themeDir = `contents/themes/${config.projectSlug}`;
2921
+ const themeDir = `contents/themes/${config2.projectSlug}`;
2600
2922
  files.push(`${themeDir}/config/app.config.ts`);
2601
2923
  files.push(`${themeDir}/config/billing.config.ts`);
2602
2924
  files.push(`${themeDir}/config/dashboard.config.ts`);
@@ -2612,7 +2934,7 @@ function getFileTree(config) {
2612
2934
  files.push(`${themeDir}/blocks/hero/block.tsx`);
2613
2935
  files.push(`${themeDir}/blocks/hero/schema.ts`);
2614
2936
  files.push(`${themeDir}/blocks/hero/styles.ts`);
2615
- for (const locale of config.supportedLocales) {
2937
+ for (const locale of config2.supportedLocales) {
2616
2938
  files.push(`${themeDir}/messages/${locale}/common.json`);
2617
2939
  files.push(`${themeDir}/messages/${locale}/auth.json`);
2618
2940
  files.push(`${themeDir}/messages/${locale}/dashboard.json`);
@@ -2625,7 +2947,7 @@ function getFileTree(config) {
2625
2947
  files.push(`${themeDir}/styles/theme.css`);
2626
2948
  files.push(`${themeDir}/styles/components.css`);
2627
2949
  files.push(`${themeDir}/tests/cypress.config.ts`);
2628
- files.push(`${themeDir}/tests/jest/jest.config.ts`);
2950
+ files.push(`${themeDir}/tests/jest/jest.config.cjs`);
2629
2951
  files.push(`${themeDir}/tests/cypress/e2e/auth.cy.ts`);
2630
2952
  files.push(`${themeDir}/tests/cypress/e2e/dashboard.cy.ts`);
2631
2953
  files.push(`${themeDir}/tests/jest/components/hero.test.tsx`);
@@ -2663,17 +2985,17 @@ function groupFilesByCategory(files) {
2663
2985
  function formatFilePath(file, themeDir) {
2664
2986
  return file.replace(`${themeDir}/`, "");
2665
2987
  }
2666
- function showConfigPreview(config) {
2667
- const files = getFileTree(config);
2988
+ function showConfigPreview(config2) {
2989
+ const files = getFileTree(config2);
2668
2990
  const groups = groupFilesByCategory(files);
2669
- const themeDir = `contents/themes/${config.projectSlug}`;
2991
+ const themeDir = `contents/themes/${config2.projectSlug}`;
2670
2992
  console.log("");
2671
- console.log(chalk10.cyan.bold(" Theme Preview"));
2672
- console.log(chalk10.gray(" " + "=".repeat(50)));
2993
+ console.log(chalk6.cyan.bold(" Theme Preview"));
2994
+ console.log(chalk6.gray(" " + "=".repeat(50)));
2673
2995
  console.log("");
2674
- console.log(chalk10.white.bold(` ${BOX.topLeft}${"\u2500".repeat(48)}${BOX.topRight}`));
2675
- console.log(chalk10.white.bold(` ${BOX.vertical}`) + chalk10.cyan(` ${themeDir}/`) + " ".repeat(48 - themeDir.length - 2) + chalk10.white.bold(BOX.vertical));
2676
- console.log(chalk10.white.bold(` ${BOX.vertical}${"\u2500".repeat(48)}${BOX.vertical}`));
2996
+ console.log(chalk6.white.bold(` ${BOX.topLeft}${"\u2500".repeat(48)}${BOX.topRight}`));
2997
+ console.log(chalk6.white.bold(` ${BOX.vertical}`) + chalk6.cyan(` ${themeDir}/`) + " ".repeat(48 - themeDir.length - 2) + chalk6.white.bold(BOX.vertical));
2998
+ console.log(chalk6.white.bold(` ${BOX.vertical}${"\u2500".repeat(48)}${BOX.vertical}`));
2677
2999
  const categoryLabels = {
2678
3000
  config: "Configuration",
2679
3001
  entities: "Entities",
@@ -2698,40 +3020,40 @@ function showConfigPreview(config) {
2698
3020
  if (categoryFiles.length === 0) continue;
2699
3021
  const label = categoryLabels[category];
2700
3022
  const icon = categoryIcons[category];
2701
- console.log(chalk10.white.bold(` ${BOX.vertical} `) + chalk10.yellow(`[${icon}] ${label}`) + chalk10.gray(` (${categoryFiles.length} files)`));
3023
+ console.log(chalk6.white.bold(` ${BOX.vertical} `) + chalk6.yellow(`[${icon}] ${label}`) + chalk6.gray(` (${categoryFiles.length} files)`));
2702
3024
  for (let i = 0; i < categoryFiles.length; i++) {
2703
3025
  const file = categoryFiles[i];
2704
3026
  const isLast = i === categoryFiles.length - 1;
2705
3027
  const prefix = isLast ? BOX.corner : BOX.tee;
2706
3028
  const formattedPath = formatFilePath(file, themeDir);
2707
- console.log(chalk10.white.bold(` ${BOX.vertical} `) + chalk10.gray(` ${prefix}${BOX.horizontal} `) + chalk10.white(formattedPath));
3029
+ console.log(chalk6.white.bold(` ${BOX.vertical} `) + chalk6.gray(` ${prefix}${BOX.horizontal} `) + chalk6.white(formattedPath));
2708
3030
  }
2709
- console.log(chalk10.white.bold(` ${BOX.vertical}`));
3031
+ console.log(chalk6.white.bold(` ${BOX.vertical}`));
2710
3032
  }
2711
- console.log(chalk10.white.bold(` ${BOX.bottomLeft}${"\u2500".repeat(48)}${BOX.bottomRight}`));
3033
+ console.log(chalk6.white.bold(` ${BOX.bottomLeft}${"\u2500".repeat(48)}${BOX.bottomRight}`));
2712
3034
  console.log("");
2713
- console.log(chalk10.cyan.bold(" Summary"));
2714
- console.log(chalk10.gray(" " + "-".repeat(30)));
3035
+ console.log(chalk6.cyan.bold(" Summary"));
3036
+ console.log(chalk6.gray(" " + "-".repeat(30)));
2715
3037
  console.log("");
2716
3038
  const totalFiles = files.length;
2717
3039
  const estimatedSize = "~350KB";
2718
- console.log(chalk10.white(` Total files: `) + chalk10.green.bold(totalFiles.toString()));
2719
- console.log(chalk10.white(` Estimated size: `) + chalk10.green.bold(estimatedSize));
3040
+ console.log(chalk6.white(` Total files: `) + chalk6.green.bold(totalFiles.toString()));
3041
+ console.log(chalk6.white(` Estimated size: `) + chalk6.green.bold(estimatedSize));
2720
3042
  console.log("");
2721
- console.log(chalk10.gray(" By category:"));
3043
+ console.log(chalk6.gray(" By category:"));
2722
3044
  for (const category of categoryOrder) {
2723
3045
  const count = groups[category].length;
2724
3046
  if (count > 0) {
2725
3047
  const label = categoryLabels[category].padEnd(16);
2726
- console.log(chalk10.gray(` ${label}`) + chalk10.white(count.toString().padStart(3)) + chalk10.gray(" files"));
3048
+ console.log(chalk6.gray(` ${label}`) + chalk6.white(count.toString().padStart(3)) + chalk6.gray(" files"));
2727
3049
  }
2728
3050
  }
2729
3051
  console.log("");
2730
- console.log(chalk10.gray(" Locales configured:"));
2731
- for (const locale of config.supportedLocales) {
2732
- const isDefault = locale === config.defaultLocale;
2733
- const suffix = isDefault ? chalk10.cyan(" (default)") : "";
2734
- console.log(chalk10.gray(` - `) + chalk10.white(locale) + suffix);
3052
+ console.log(chalk6.gray(" Locales configured:"));
3053
+ for (const locale of config2.supportedLocales) {
3054
+ const isDefault = locale === config2.defaultLocale;
3055
+ const suffix = isDefault ? chalk6.cyan(" (default)") : "";
3056
+ console.log(chalk6.gray(` - `) + chalk6.white(locale) + suffix);
2735
3057
  }
2736
3058
  console.log("");
2737
3059
  }
@@ -2753,43 +3075,41 @@ async function runWizard(options = { mode: "interactive" }) {
2753
3075
  try {
2754
3076
  let selectedTheme = null;
2755
3077
  let selectedPlugins = [];
2756
- if (!options.preset) {
2757
- if (options.theme !== void 0) {
2758
- selectedTheme = options.theme === "none" ? null : options.theme;
2759
- showInfo(`Reference theme: ${selectedTheme || "None"}`);
2760
- } else if (options.mode !== "quick") {
2761
- selectedTheme = await promptThemeSelection();
2762
- }
2763
- if (options.plugins !== void 0) {
2764
- selectedPlugins = options.plugins;
2765
- if (selectedPlugins.length > 0) {
2766
- showInfo(`Selected plugins: ${selectedPlugins.join(", ")}`);
2767
- }
2768
- } else if (options.mode !== "quick" && !options.yes) {
2769
- selectedPlugins = await promptPluginsSelection(selectedTheme);
2770
- } else if (selectedTheme) {
2771
- selectedPlugins = getRequiredPlugins(selectedTheme);
2772
- }
2773
- }
2774
- let config;
3078
+ let config2;
2775
3079
  if (options.preset) {
2776
- config = await runPresetMode(options.preset, options);
3080
+ config2 = await runPresetMode(options.preset, options);
2777
3081
  } else {
2778
3082
  switch (options.mode) {
2779
3083
  case "quick":
2780
- config = await runQuickPrompts();
3084
+ config2 = await runQuickPrompts();
2781
3085
  break;
2782
3086
  case "expert":
2783
- config = await runExpertPrompts();
3087
+ config2 = await runExpertPrompts();
2784
3088
  break;
2785
3089
  case "interactive":
2786
3090
  default:
2787
- config = await runAllPrompts();
3091
+ config2 = await runAllPrompts();
2788
3092
  break;
2789
3093
  }
2790
3094
  }
2791
- showConfigSummary(config);
2792
- showConfigPreview(config);
3095
+ if (options.theme !== void 0) {
3096
+ selectedTheme = options.theme === "none" ? null : options.theme;
3097
+ showInfo(`Reference theme: ${selectedTheme || "None"}`);
3098
+ } else if (!options.preset && options.mode !== "quick") {
3099
+ selectedTheme = await promptThemeSelection();
3100
+ }
3101
+ if (options.plugins !== void 0) {
3102
+ selectedPlugins = options.plugins;
3103
+ if (selectedPlugins.length > 0) {
3104
+ showInfo(`Selected plugins: ${selectedPlugins.join(", ")}`);
3105
+ }
3106
+ } else if (!options.preset && options.mode !== "quick" && !options.yes) {
3107
+ selectedPlugins = await promptPluginsSelection(selectedTheme);
3108
+ } else if (selectedTheme) {
3109
+ selectedPlugins = getRequiredPlugins(selectedTheme);
3110
+ }
3111
+ showConfigSummary(config2);
3112
+ showConfigPreview(config2);
2793
3113
  if (!options.yes) {
2794
3114
  console.log("");
2795
3115
  const proceed = await confirm5({
@@ -2802,20 +3122,27 @@ async function runWizard(options = { mode: "interactive" }) {
2802
3122
  process.exit(0);
2803
3123
  }
2804
3124
  }
2805
- await copyNpmrc();
2806
3125
  console.log("");
2807
3126
  const coreInstalled = await installCore();
2808
3127
  if (!coreInstalled) {
2809
3128
  showError("Failed to install @nextsparkjs/core. Cannot generate project.");
2810
3129
  process.exit(1);
2811
3130
  }
3131
+ if (config2.projectType === "web-mobile") {
3132
+ console.log("");
3133
+ const mobileInstalled = await installMobile();
3134
+ if (!mobileInstalled) {
3135
+ showError("Failed to install @nextsparkjs/mobile. Cannot generate monorepo project.");
3136
+ process.exit(1);
3137
+ }
3138
+ }
2812
3139
  console.log("");
2813
- const spinner = ora7({
3140
+ const spinner = ora3({
2814
3141
  text: "Generating your NextSpark project...",
2815
3142
  prefixText: " "
2816
3143
  }).start();
2817
3144
  try {
2818
- await generateProject(config);
3145
+ await generateProject(config2);
2819
3146
  spinner.succeed("Project generated successfully!");
2820
3147
  } catch (error) {
2821
3148
  spinner.fail("Failed to generate project");
@@ -2824,41 +3151,49 @@ async function runWizard(options = { mode: "interactive" }) {
2824
3151
  if (selectedTheme || selectedPlugins.length > 0) {
2825
3152
  await installThemeAndPlugins(selectedTheme, selectedPlugins);
2826
3153
  }
2827
- const installSpinner = ora7({
2828
- text: "Installing dependencies...",
3154
+ const projectRoot = process.cwd();
3155
+ const webDir = getWebDir(projectRoot, config2);
3156
+ const isMonorepo = isMonorepoProject(config2);
3157
+ const installSpinner = ora3({
3158
+ text: isMonorepo ? "Installing dependencies (monorepo)..." : "Installing dependencies...",
2829
3159
  prefixText: " "
2830
3160
  }).start();
2831
3161
  try {
3162
+ installSpinner.stop();
2832
3163
  execSync("pnpm install --force", {
2833
- cwd: process.cwd(),
2834
- stdio: "pipe"
3164
+ cwd: projectRoot,
3165
+ // Always install from root (works for both flat and monorepo)
3166
+ stdio: "inherit"
2835
3167
  });
2836
3168
  installSpinner.succeed("Dependencies installed!");
2837
3169
  } catch (error) {
2838
3170
  installSpinner.fail("Failed to install dependencies");
2839
- console.log(chalk11.yellow(' Run "pnpm install" manually to install dependencies'));
3171
+ console.log(chalk7.yellow(' Run "pnpm install" manually to install dependencies'));
2840
3172
  }
2841
- const registrySpinner = ora7({
3173
+ const registrySpinner = ora3({
2842
3174
  text: "Building registries...",
2843
3175
  prefixText: " "
2844
3176
  }).start();
2845
3177
  try {
2846
- const projectRoot = process.cwd();
2847
- const registryScript = join7(projectRoot, "node_modules/@nextsparkjs/core/scripts/build/registry.mjs");
3178
+ const registryScript = join4(webDir, "node_modules/@nextsparkjs/core/scripts/build/registry.mjs");
3179
+ registrySpinner.stop();
2848
3180
  execSync(`node "${registryScript}" --build`, {
2849
- cwd: projectRoot,
2850
- stdio: "pipe",
3181
+ cwd: webDir,
3182
+ // Run from web directory
3183
+ stdio: "inherit",
2851
3184
  env: {
2852
3185
  ...process.env,
2853
- NEXTSPARK_PROJECT_ROOT: projectRoot
3186
+ NEXTSPARK_PROJECT_ROOT: webDir
2854
3187
  }
2855
3188
  });
2856
3189
  registrySpinner.succeed("Registries built!");
2857
3190
  } catch (error) {
2858
3191
  registrySpinner.fail("Failed to build registries");
2859
- console.log(chalk11.yellow(' Registries will be built automatically when you run "pnpm dev"'));
3192
+ const devCmd = isMonorepo ? "pnpm dev" : "pnpm dev";
3193
+ console.log(chalk7.yellow(` Registries will be built automatically when you run "${devCmd}"`));
2860
3194
  }
2861
- showNextSteps(config, selectedTheme);
3195
+ const aiChoice = await promptAIWorkflowSetup(config2);
3196
+ showNextSteps(config2, selectedTheme, aiChoice);
2862
3197
  } catch (error) {
2863
3198
  if (error instanceof Error) {
2864
3199
  if (error.message.includes("User force closed")) {
@@ -2873,7 +3208,7 @@ async function runWizard(options = { mode: "interactive" }) {
2873
3208
  }
2874
3209
  function showModeIndicator(options) {
2875
3210
  if (options.preset) {
2876
- showInfo(`Using preset: ${chalk11.cyan(options.preset)} - ${PRESET_DESCRIPTIONS[options.preset]}`);
3211
+ showInfo(`Using preset: ${chalk7.cyan(options.preset)} - ${PRESET_DESCRIPTIONS[options.preset]}`);
2877
3212
  console.log("");
2878
3213
  } else if (options.mode === "quick") {
2879
3214
  showInfo("Quick mode: Running essential prompts only (steps 1-5)");
@@ -2898,47 +3233,48 @@ async function runPresetMode(presetName, options) {
2898
3233
  console.log("");
2899
3234
  projectInfo = await promptProjectInfo();
2900
3235
  }
2901
- const config = applyPreset(projectInfo, presetName);
2902
- return config;
3236
+ const config2 = applyPreset(projectInfo, presetName, options.type);
3237
+ return config2;
2903
3238
  }
2904
- function showConfigSummary(config) {
3239
+ function showConfigSummary(config2) {
2905
3240
  console.log("");
2906
- console.log(chalk11.cyan(" " + "=".repeat(60)));
2907
- console.log(chalk11.bold.white(" Configuration Summary"));
2908
- console.log(chalk11.cyan(" " + "=".repeat(60)));
3241
+ console.log(chalk7.cyan(" " + "=".repeat(60)));
3242
+ console.log(chalk7.bold.white(" Configuration Summary"));
3243
+ console.log(chalk7.cyan(" " + "=".repeat(60)));
2909
3244
  console.log("");
2910
- console.log(chalk11.white(" Project:"));
2911
- console.log(chalk11.gray(` Name: ${chalk11.white(config.projectName)}`));
2912
- console.log(chalk11.gray(` Slug: ${chalk11.white(config.projectSlug)}`));
2913
- console.log(chalk11.gray(` Description: ${chalk11.white(config.projectDescription)}`));
3245
+ console.log(chalk7.white(" Project:"));
3246
+ console.log(chalk7.gray(` Name: ${chalk7.white(config2.projectName)}`));
3247
+ console.log(chalk7.gray(` Slug: ${chalk7.white(config2.projectSlug)}`));
3248
+ console.log(chalk7.gray(` Description: ${chalk7.white(config2.projectDescription)}`));
3249
+ console.log(chalk7.gray(` Type: ${chalk7.white(config2.projectType === "web-mobile" ? "Web + Mobile (Monorepo)" : "Web only")}`));
2914
3250
  console.log("");
2915
- console.log(chalk11.white(" Team Mode:"));
2916
- console.log(chalk11.gray(` Mode: ${chalk11.white(config.teamMode)}`));
2917
- console.log(chalk11.gray(` Roles: ${chalk11.white(config.teamRoles.join(", "))}`));
3251
+ console.log(chalk7.white(" Team Mode:"));
3252
+ console.log(chalk7.gray(` Mode: ${chalk7.white(config2.teamMode)}`));
3253
+ console.log(chalk7.gray(` Roles: ${chalk7.white(config2.teamRoles.join(", "))}`));
2918
3254
  console.log("");
2919
- console.log(chalk11.white(" Internationalization:"));
2920
- console.log(chalk11.gray(` Default: ${chalk11.white(config.defaultLocale)}`));
2921
- console.log(chalk11.gray(` Languages: ${chalk11.white(config.supportedLocales.join(", "))}`));
3255
+ console.log(chalk7.white(" Internationalization:"));
3256
+ console.log(chalk7.gray(` Default: ${chalk7.white(config2.defaultLocale)}`));
3257
+ console.log(chalk7.gray(` Languages: ${chalk7.white(config2.supportedLocales.join(", "))}`));
2922
3258
  console.log("");
2923
- console.log(chalk11.white(" Billing:"));
2924
- console.log(chalk11.gray(` Model: ${chalk11.white(config.billingModel)}`));
2925
- console.log(chalk11.gray(` Currency: ${chalk11.white(config.currency.toUpperCase())}`));
3259
+ console.log(chalk7.white(" Billing:"));
3260
+ console.log(chalk7.gray(` Model: ${chalk7.white(config2.billingModel)}`));
3261
+ console.log(chalk7.gray(` Currency: ${chalk7.white(config2.currency.toUpperCase())}`));
2926
3262
  console.log("");
2927
- console.log(chalk11.white(" Features:"));
2928
- const enabledFeatures = Object.entries(config.features).filter(([_, enabled]) => enabled).map(([feature]) => feature);
2929
- console.log(chalk11.gray(` Enabled: ${chalk11.white(enabledFeatures.join(", ") || "None")}`));
3263
+ console.log(chalk7.white(" Features:"));
3264
+ const enabledFeatures = Object.entries(config2.features).filter(([_, enabled]) => enabled).map(([feature]) => feature);
3265
+ console.log(chalk7.gray(` Enabled: ${chalk7.white(enabledFeatures.join(", ") || "None")}`));
2930
3266
  console.log("");
2931
- console.log(chalk11.white(" Authentication:"));
2932
- const enabledAuth = Object.entries(config.auth).filter(([_, enabled]) => enabled).map(([method]) => formatAuthMethod(method));
2933
- console.log(chalk11.gray(` Methods: ${chalk11.white(enabledAuth.join(", ") || "None")}`));
3267
+ console.log(chalk7.white(" Authentication:"));
3268
+ const enabledAuth = Object.entries(config2.auth).filter(([_, enabled]) => enabled).map(([method]) => formatAuthMethod(method));
3269
+ console.log(chalk7.gray(` Methods: ${chalk7.white(enabledAuth.join(", ") || "None")}`));
2934
3270
  console.log("");
2935
- console.log(chalk11.white(" Dashboard:"));
2936
- const enabledDashboard = Object.entries(config.dashboard).filter(([_, enabled]) => enabled).map(([feature]) => formatDashboardFeature(feature));
2937
- console.log(chalk11.gray(` Features: ${chalk11.white(enabledDashboard.join(", ") || "None")}`));
3271
+ console.log(chalk7.white(" Dashboard:"));
3272
+ const enabledDashboard = Object.entries(config2.dashboard).filter(([_, enabled]) => enabled).map(([feature]) => formatDashboardFeature(feature));
3273
+ console.log(chalk7.gray(` Features: ${chalk7.white(enabledDashboard.join(", ") || "None")}`));
2938
3274
  console.log("");
2939
- console.log(chalk11.white(" Dev Tools:"));
2940
- const enabledDevTools = Object.entries(config.dev).filter(([_, enabled]) => enabled).map(([tool]) => formatDevTool(tool));
2941
- console.log(chalk11.gray(` Enabled: ${chalk11.white(enabledDevTools.join(", ") || "None")}`));
3275
+ console.log(chalk7.white(" Dev Tools:"));
3276
+ const enabledDevTools = Object.entries(config2.dev).filter(([_, enabled]) => enabled).map(([tool]) => formatDevTool(tool));
3277
+ console.log(chalk7.gray(` Enabled: ${chalk7.white(enabledDevTools.join(", ") || "None")}`));
2942
3278
  }
2943
3279
  function formatAuthMethod(method) {
2944
3280
  const mapping = {
@@ -2964,39 +3300,112 @@ function formatDevTool(tool) {
2964
3300
  };
2965
3301
  return mapping[tool] || tool;
2966
3302
  }
2967
- function showNextSteps(config, referenceTheme = null) {
3303
+ function showNextSteps(config2, referenceTheme = null, aiChoice = "skip") {
3304
+ const isMonorepo = config2.projectType === "web-mobile";
3305
+ const aiSetupDone = aiChoice === "claude";
2968
3306
  console.log("");
2969
- console.log(chalk11.cyan(" " + "=".repeat(60)));
2970
- console.log(chalk11.bold.green(" \u2728 NextSpark project ready!"));
2971
- console.log(chalk11.cyan(" " + "=".repeat(60)));
3307
+ console.log(chalk7.cyan(" " + "=".repeat(60)));
3308
+ console.log(chalk7.bold.green(" \u2728 NextSpark project ready!"));
3309
+ console.log(chalk7.cyan(" " + "=".repeat(60)));
2972
3310
  console.log("");
2973
- console.log(chalk11.bold.white(" Next steps:"));
3311
+ console.log(chalk7.bold.white(" Next steps:"));
2974
3312
  console.log("");
2975
- console.log(chalk11.white(" 1. Configure your .env file:"));
2976
- console.log(chalk11.gray(" Edit these values in .env:"));
3313
+ const envPath = isMonorepo ? "web/.env" : ".env";
3314
+ console.log(chalk7.white(" 1. Configure your environment:"));
3315
+ console.log(chalk7.gray(` Edit ${envPath} with your credentials:`));
2977
3316
  console.log("");
2978
- console.log(chalk11.yellow(" DATABASE_URL"));
2979
- console.log(chalk11.gray(" PostgreSQL connection string"));
2980
- console.log(chalk11.dim(" Example: postgresql://user:pass@localhost:5432/mydb"));
3317
+ console.log(chalk7.yellow(" DATABASE_URL"));
3318
+ console.log(chalk7.gray(" PostgreSQL connection string"));
3319
+ console.log(chalk7.gray(` Recommended: ${chalk7.cyan("https://supabase.com")} | ${chalk7.cyan("https://neon.com")}`));
2981
3320
  console.log("");
2982
- console.log(chalk11.yellow(" BETTER_AUTH_SECRET"));
2983
- console.log(chalk11.gray(" Generate with:"));
2984
- console.log(chalk11.cyan(" openssl rand -base64 32"));
3321
+ console.log(chalk7.yellow(" BETTER_AUTH_SECRET"));
3322
+ console.log(chalk7.gray(" Generate with:"));
3323
+ console.log(chalk7.cyan(" openssl rand -base64 32"));
2985
3324
  console.log("");
2986
- console.log(chalk11.white(" 2. Run database migrations:"));
2987
- console.log(chalk11.cyan(" pnpm db:migrate"));
3325
+ console.log(chalk7.white(" 2. Run database migrations:"));
3326
+ console.log(chalk7.cyan(" pnpm db:migrate"));
2988
3327
  console.log("");
2989
- console.log(chalk11.white(" 3. Start the development server:"));
2990
- console.log(chalk11.cyan(" pnpm dev"));
3328
+ console.log(chalk7.white(" 3. Start the development server:"));
3329
+ console.log(chalk7.cyan(" pnpm dev"));
2991
3330
  console.log("");
2992
- console.log(chalk11.gray(" " + "-".repeat(60)));
2993
- console.log(chalk11.gray(` Theme: ${chalk11.white(`contents/themes/${config.projectSlug}/`)}`));
2994
- console.log(chalk11.gray(` Active theme: ${chalk11.green(`NEXT_PUBLIC_ACTIVE_THEME=${config.projectSlug}`)}`));
3331
+ let nextStep = 4;
3332
+ if (isMonorepo) {
3333
+ console.log(chalk7.white(` ${nextStep}. (Optional) Start the mobile app:`));
3334
+ console.log(chalk7.cyan(" pnpm dev:mobile"));
3335
+ console.log(chalk7.gray(" Or: cd mobile && pnpm start"));
3336
+ console.log("");
3337
+ nextStep++;
3338
+ }
3339
+ if (aiSetupDone) {
3340
+ console.log(chalk7.white(` ${nextStep}. Start building with AI:`));
3341
+ console.log(chalk7.gray(" Open Claude Code in your project and run:"));
3342
+ console.log(chalk7.cyan(" /how-to:start"));
3343
+ console.log("");
3344
+ } else {
3345
+ console.log(chalk7.white(` ${nextStep}. (Optional) Setup AI workflows:`));
3346
+ console.log(chalk7.cyan(" nextspark setup:ai"));
3347
+ console.log("");
3348
+ }
3349
+ console.log(chalk7.gray(" " + "-".repeat(60)));
3350
+ if (isMonorepo) {
3351
+ console.log(chalk7.gray(` Structure: ${chalk7.white("Monorepo (web/ + mobile/)")}`));
3352
+ console.log(chalk7.gray(` Web theme: ${chalk7.white(`web/contents/themes/${config2.projectSlug}/`)}`));
3353
+ console.log(chalk7.gray(` Mobile app: ${chalk7.white("mobile/")}`));
3354
+ console.log(chalk7.gray(` Active theme: ${chalk7.green(`NEXT_PUBLIC_ACTIVE_THEME=${config2.projectSlug}`)}`));
3355
+ } else {
3356
+ console.log(chalk7.gray(` Theme: ${chalk7.white(`contents/themes/${config2.projectSlug}/`)}`));
3357
+ console.log(chalk7.gray(` Active theme: ${chalk7.green(`NEXT_PUBLIC_ACTIVE_THEME=${config2.projectSlug}`)}`));
3358
+ }
2995
3359
  if (referenceTheme) {
2996
- console.log(chalk11.gray(` Reference: ${chalk11.white(`contents/themes/${referenceTheme}/`)}`));
3360
+ const refPath = isMonorepo ? `web/contents/themes/${referenceTheme}/` : `contents/themes/${referenceTheme}/`;
3361
+ console.log(chalk7.gray(` Reference: ${chalk7.white(refPath)}`));
2997
3362
  }
2998
- console.log(chalk11.gray(" Docs: https://nextspark.dev/docs"));
3363
+ console.log(chalk7.gray(` Docs: ${chalk7.cyan("https://nextspark.dev/docs")}`));
3364
+ console.log("");
3365
+ }
3366
+ async function promptAIWorkflowSetup(config2) {
2999
3367
  console.log("");
3368
+ const choice = await select6({
3369
+ message: "Setup AI-assisted development workflows?",
3370
+ choices: [
3371
+ { name: "Claude Code (Recommended)", value: "claude" },
3372
+ { name: "Cursor (Coming soon)", value: "cursor" },
3373
+ { name: "Antigravity (Coming soon)", value: "antigravity" },
3374
+ { name: "Skip for now", value: "skip" }
3375
+ ]
3376
+ });
3377
+ if (choice === "skip") {
3378
+ return "skip";
3379
+ }
3380
+ if (choice === "cursor" || choice === "antigravity") {
3381
+ showInfo(`${choice} support is coming soon. For now, use Claude Code.`);
3382
+ return "skip";
3383
+ }
3384
+ const projectRoot = process.cwd();
3385
+ const isMonorepo = isMonorepoProject(config2);
3386
+ try {
3387
+ execSync("pnpm add -D -w @nextsparkjs/ai-workflow", {
3388
+ cwd: projectRoot,
3389
+ stdio: "inherit"
3390
+ });
3391
+ let setupScript = join4(projectRoot, "node_modules", "@nextsparkjs", "ai-workflow", "scripts", "setup.mjs");
3392
+ if (!existsSync3(setupScript) && isMonorepo) {
3393
+ setupScript = join4(projectRoot, "web", "node_modules", "@nextsparkjs", "ai-workflow", "scripts", "setup.mjs");
3394
+ }
3395
+ if (existsSync3(setupScript)) {
3396
+ execSync(`node "${setupScript}" ${choice}`, {
3397
+ cwd: projectRoot,
3398
+ stdio: "inherit"
3399
+ });
3400
+ showSuccess("AI workflow setup complete!");
3401
+ } else {
3402
+ showWarning('AI workflow package installed but setup script not found. Run "nextspark setup:ai" manually.');
3403
+ }
3404
+ } catch (error) {
3405
+ showError('Failed to install AI workflow package. Run "nextspark setup:ai" later.');
3406
+ return "skip";
3407
+ }
3408
+ return choice;
3000
3409
  }
3001
3410
  function findLocalCoreTarball() {
3002
3411
  const cwd = process.cwd();
@@ -3005,34 +3414,93 @@ function findLocalCoreTarball() {
3005
3414
  const coreTarball = files.find(
3006
3415
  (f) => f.includes("nextsparkjs-core") && f.endsWith(".tgz")
3007
3416
  );
3008
- if (coreTarball) {
3009
- return join7(cwd, coreTarball);
3417
+ if (coreTarball) {
3418
+ return join4(cwd, coreTarball);
3419
+ }
3420
+ } catch {
3421
+ }
3422
+ return null;
3423
+ }
3424
+ function isCoreInstalled() {
3425
+ const corePath = join4(process.cwd(), "node_modules", "@nextsparkjs", "core");
3426
+ return existsSync3(corePath);
3427
+ }
3428
+ async function installCore() {
3429
+ if (isCoreInstalled()) {
3430
+ return true;
3431
+ }
3432
+ const spinner = ora3({
3433
+ text: "Installing @nextsparkjs/core...",
3434
+ prefixText: " "
3435
+ }).start();
3436
+ try {
3437
+ const localTarball = findLocalCoreTarball();
3438
+ let packageSpec = "@nextsparkjs/core";
3439
+ if (localTarball) {
3440
+ packageSpec = localTarball;
3441
+ spinner.text = "Installing @nextsparkjs/core from local tarball...";
3442
+ }
3443
+ const useYarn = existsSync3(join4(process.cwd(), "yarn.lock"));
3444
+ const usePnpm = existsSync3(join4(process.cwd(), "pnpm-lock.yaml"));
3445
+ let installCmd;
3446
+ if (usePnpm) {
3447
+ installCmd = `pnpm add ${packageSpec}`;
3448
+ } else if (useYarn) {
3449
+ installCmd = `yarn add ${packageSpec}`;
3450
+ } else {
3451
+ installCmd = `npm install ${packageSpec}`;
3452
+ }
3453
+ spinner.stop();
3454
+ execSync(installCmd, {
3455
+ stdio: "inherit",
3456
+ cwd: process.cwd()
3457
+ });
3458
+ spinner.succeed(chalk7.green("@nextsparkjs/core installed successfully!"));
3459
+ return true;
3460
+ } catch (error) {
3461
+ spinner.fail(chalk7.red("Failed to install @nextsparkjs/core"));
3462
+ if (error instanceof Error) {
3463
+ console.log(chalk7.red(` Error: ${error.message}`));
3464
+ }
3465
+ console.log(chalk7.gray(" Hint: Make sure the package is available (npm registry or local tarball)"));
3466
+ return false;
3467
+ }
3468
+ }
3469
+ function isMobileInstalled() {
3470
+ const mobilePath = join4(process.cwd(), "node_modules", "@nextsparkjs", "mobile");
3471
+ return existsSync3(mobilePath);
3472
+ }
3473
+ function findLocalMobileTarball() {
3474
+ const cwd = process.cwd();
3475
+ try {
3476
+ const files = readdirSync(cwd);
3477
+ const mobileTarball = files.find(
3478
+ (f) => f.includes("nextsparkjs-mobile") && f.endsWith(".tgz")
3479
+ );
3480
+ if (mobileTarball) {
3481
+ return join4(cwd, mobileTarball);
3010
3482
  }
3011
3483
  } catch {
3012
3484
  }
3013
3485
  return null;
3014
3486
  }
3015
- function isCoreInstalled() {
3016
- const corePath = join7(process.cwd(), "node_modules", "@nextsparkjs", "core");
3017
- return existsSync7(corePath);
3018
- }
3019
- async function installCore() {
3020
- if (isCoreInstalled()) {
3487
+ async function installMobile() {
3488
+ if (isMobileInstalled()) {
3021
3489
  return true;
3022
3490
  }
3023
- const spinner = ora7({
3024
- text: "Installing @nextsparkjs/core...",
3491
+ const spinner = ora3({
3492
+ text: "Installing @nextsparkjs/mobile...",
3025
3493
  prefixText: " "
3026
3494
  }).start();
3027
3495
  try {
3028
- const localTarball = findLocalCoreTarball();
3029
- let packageSpec = "@nextsparkjs/core";
3496
+ const localTarball = findLocalMobileTarball();
3497
+ let packageSpec = "@nextsparkjs/mobile";
3030
3498
  if (localTarball) {
3031
3499
  packageSpec = localTarball;
3032
- spinner.text = "Installing @nextsparkjs/core from local tarball...";
3500
+ spinner.text = "Installing @nextsparkjs/mobile from local tarball...";
3033
3501
  }
3034
- const useYarn = existsSync7(join7(process.cwd(), "yarn.lock"));
3035
- const usePnpm = existsSync7(join7(process.cwd(), "pnpm-lock.yaml"));
3502
+ const useYarn = existsSync3(join4(process.cwd(), "yarn.lock"));
3503
+ const usePnpm = existsSync3(join4(process.cwd(), "pnpm-lock.yaml"));
3036
3504
  let installCmd;
3037
3505
  if (usePnpm) {
3038
3506
  installCmd = `pnpm add ${packageSpec}`;
@@ -3041,33 +3509,22 @@ async function installCore() {
3041
3509
  } else {
3042
3510
  installCmd = `npm install ${packageSpec}`;
3043
3511
  }
3512
+ spinner.stop();
3044
3513
  execSync(installCmd, {
3045
- stdio: "pipe",
3514
+ stdio: "inherit",
3046
3515
  cwd: process.cwd()
3047
3516
  });
3048
- spinner.succeed(chalk11.green("@nextsparkjs/core installed successfully!"));
3517
+ spinner.succeed(chalk7.green("@nextsparkjs/mobile installed successfully!"));
3049
3518
  return true;
3050
3519
  } catch (error) {
3051
- spinner.fail(chalk11.red("Failed to install @nextsparkjs/core"));
3520
+ spinner.fail(chalk7.red("Failed to install @nextsparkjs/mobile"));
3052
3521
  if (error instanceof Error) {
3053
- console.log(chalk11.red(` Error: ${error.message}`));
3522
+ console.log(chalk7.red(` Error: ${error.message}`));
3054
3523
  }
3055
- console.log(chalk11.gray(" Hint: Make sure the package is available (npm registry or local tarball)"));
3524
+ console.log(chalk7.gray(" Hint: Make sure the package is available (npm registry or local tarball)"));
3056
3525
  return false;
3057
3526
  }
3058
3527
  }
3059
- async function copyNpmrc() {
3060
- const npmrcPath = join7(process.cwd(), ".npmrc");
3061
- if (existsSync7(npmrcPath)) {
3062
- return;
3063
- }
3064
- const npmrcContent = `# Hoist @nextsparkjs/core dependencies so they're accessible from the project
3065
- # This is required for pnpm to make peer dependencies available
3066
- public-hoist-pattern[]=*
3067
- `;
3068
- const { writeFileSync: writeFileSync3 } = await import("fs");
3069
- writeFileSync3(npmrcPath, npmrcContent);
3070
- }
3071
3528
 
3072
3529
  // src/commands/init.ts
3073
3530
  function getWizardMode(options) {
@@ -3081,10 +3538,10 @@ function parsePlugins(pluginsStr) {
3081
3538
  }
3082
3539
  function hasExistingProject() {
3083
3540
  const projectRoot = process.cwd();
3084
- return existsSync8(join8(projectRoot, "contents")) || existsSync8(join8(projectRoot, ".nextspark"));
3541
+ return existsSync4(join5(projectRoot, "contents")) || existsSync4(join5(projectRoot, ".nextspark"));
3085
3542
  }
3086
3543
  function generateInitialRegistries(registriesDir) {
3087
- writeFileSync2(join8(registriesDir, "block-registry.ts"), `// Auto-generated by nextspark init
3544
+ writeFileSync2(join5(registriesDir, "block-registry.ts"), `// Auto-generated by nextspark init
3088
3545
  import type { ComponentType } from 'react'
3089
3546
 
3090
3547
  export const BLOCK_REGISTRY: Record<string, {
@@ -3097,26 +3554,26 @@ export const BLOCK_REGISTRY: Record<string, {
3097
3554
 
3098
3555
  export const BLOCK_COMPONENTS: Record<string, React.LazyExoticComponent<ComponentType<any>>> = {}
3099
3556
  `);
3100
- writeFileSync2(join8(registriesDir, "theme-registry.ts"), `// Auto-generated by nextspark init
3557
+ writeFileSync2(join5(registriesDir, "theme-registry.ts"), `// Auto-generated by nextspark init
3101
3558
  export const THEME_REGISTRY: Record<string, unknown> = {}
3102
3559
  `);
3103
- writeFileSync2(join8(registriesDir, "entity-registry.ts"), `// Auto-generated by nextspark init
3560
+ writeFileSync2(join5(registriesDir, "entity-registry.ts"), `// Auto-generated by nextspark init
3104
3561
  export const ENTITY_REGISTRY: Record<string, unknown> = {}
3105
3562
  `);
3106
- writeFileSync2(join8(registriesDir, "entity-registry.client.ts"), `// Auto-generated by nextspark init
3563
+ writeFileSync2(join5(registriesDir, "entity-registry.client.ts"), `// Auto-generated by nextspark init
3107
3564
  export const CLIENT_ENTITY_REGISTRY: Record<string, unknown> = {}
3108
3565
  export function parseChildEntity(path: string) { return null }
3109
3566
  export function getEntityApiPath(entity: string) { return \`/api/\${entity}\` }
3110
3567
  export function clientMetaSystemAdapter() { return {} }
3111
3568
  export type ClientEntityConfig = Record<string, unknown>
3112
3569
  `);
3113
- writeFileSync2(join8(registriesDir, "billing-registry.ts"), `// Auto-generated by nextspark init
3570
+ writeFileSync2(join5(registriesDir, "billing-registry.ts"), `// Auto-generated by nextspark init
3114
3571
  export const BILLING_REGISTRY = { plans: [], features: [] }
3115
3572
  `);
3116
- writeFileSync2(join8(registriesDir, "plugin-registry.ts"), `// Auto-generated by nextspark init
3573
+ writeFileSync2(join5(registriesDir, "plugin-registry.ts"), `// Auto-generated by nextspark init
3117
3574
  export const PLUGIN_REGISTRY: Record<string, unknown> = {}
3118
3575
  `);
3119
- writeFileSync2(join8(registriesDir, "testing-registry.ts"), `// Auto-generated by nextspark init
3576
+ writeFileSync2(join5(registriesDir, "testing-registry.ts"), `// Auto-generated by nextspark init
3120
3577
  export const FLOW_REGISTRY: Record<string, unknown> = {}
3121
3578
  export const FEATURE_REGISTRY: Record<string, unknown> = {}
3122
3579
  export const TAGS_REGISTRY: Record<string, unknown> = {}
@@ -3124,11 +3581,11 @@ export const COVERAGE_SUMMARY = { total: 0, covered: 0 }
3124
3581
  export type FlowEntry = unknown
3125
3582
  export type FeatureEntry = unknown
3126
3583
  `);
3127
- writeFileSync2(join8(registriesDir, "docs-registry.ts"), `// Auto-generated by nextspark init
3584
+ writeFileSync2(join5(registriesDir, "docs-registry.ts"), `// Auto-generated by nextspark init
3128
3585
  export const DOCS_REGISTRY = { sections: [], pages: [] }
3129
3586
  export type DocSectionMeta = { title: string; slug: string }
3130
3587
  `);
3131
- writeFileSync2(join8(registriesDir, "index.ts"), `// Auto-generated by nextspark init
3588
+ writeFileSync2(join5(registriesDir, "index.ts"), `// Auto-generated by nextspark init
3132
3589
  export * from './block-registry'
3133
3590
  export * from './theme-registry'
3134
3591
  export * from './entity-registry'
@@ -3140,21 +3597,21 @@ export * from './docs-registry'
3140
3597
  `);
3141
3598
  }
3142
3599
  async function simpleInit(options) {
3143
- const spinner = ora8("Initializing NextSpark project...").start();
3600
+ const spinner = ora4("Initializing NextSpark project...").start();
3144
3601
  const projectRoot = process.cwd();
3145
3602
  try {
3146
- const nextspark = join8(projectRoot, ".nextspark");
3147
- const registriesDir = join8(nextspark, "registries");
3148
- if (!existsSync8(registriesDir) || options.force) {
3603
+ const nextspark = join5(projectRoot, ".nextspark");
3604
+ const registriesDir = join5(nextspark, "registries");
3605
+ if (!existsSync4(registriesDir) || options.force) {
3149
3606
  mkdirSync2(registriesDir, { recursive: true });
3150
3607
  spinner.text = "Creating .nextspark/registries/";
3151
3608
  generateInitialRegistries(registriesDir);
3152
3609
  spinner.text = "Generated initial registries";
3153
3610
  }
3154
- const tsconfigPath = join8(projectRoot, "tsconfig.json");
3155
- if (existsSync8(tsconfigPath)) {
3611
+ const tsconfigPath = join5(projectRoot, "tsconfig.json");
3612
+ if (existsSync4(tsconfigPath)) {
3156
3613
  spinner.text = "Updating tsconfig.json paths...";
3157
- const tsconfig = JSON.parse(readFileSync7(tsconfigPath, "utf-8"));
3614
+ const tsconfig = JSON.parse(readFileSync4(tsconfigPath, "utf-8"));
3158
3615
  tsconfig.compilerOptions = tsconfig.compilerOptions || {};
3159
3616
  tsconfig.compilerOptions.paths = {
3160
3617
  ...tsconfig.compilerOptions.paths,
@@ -3163,8 +3620,8 @@ async function simpleInit(options) {
3163
3620
  };
3164
3621
  writeFileSync2(tsconfigPath, JSON.stringify(tsconfig, null, 2));
3165
3622
  }
3166
- const envExample = join8(projectRoot, ".env.example");
3167
- if (!existsSync8(envExample)) {
3623
+ const envExample = join5(projectRoot, ".env.example");
3624
+ if (!existsSync4(envExample)) {
3168
3625
  const envContent = `# NextSpark Configuration
3169
3626
  DATABASE_URL="postgresql://user:password@localhost:5432/db"
3170
3627
  BETTER_AUTH_SECRET="your-secret-here-min-32-chars"
@@ -3175,14 +3632,14 @@ NEXT_PUBLIC_APP_URL=http://localhost:3000
3175
3632
  spinner.text = "Created .env.example";
3176
3633
  }
3177
3634
  spinner.succeed("NextSpark initialized successfully!");
3178
- console.log(chalk12.blue("\nNext steps:"));
3635
+ console.log(chalk8.blue("\nNext steps:"));
3179
3636
  console.log(" 1. Copy .env.example to .env and configure");
3180
3637
  console.log(" 2. Run: nextspark generate");
3181
3638
  console.log(" 3. Run: nextspark dev");
3182
3639
  } catch (error) {
3183
3640
  spinner.fail("Initialization failed");
3184
3641
  if (error instanceof Error) {
3185
- console.error(chalk12.red(error.message));
3642
+ console.error(chalk8.red(error.message));
3186
3643
  }
3187
3644
  process.exit(1);
3188
3645
  }
@@ -3193,8 +3650,8 @@ async function initCommand(options) {
3193
3650
  return;
3194
3651
  }
3195
3652
  if (hasExistingProject() && !options.wizard && !options.preset && !options.theme) {
3196
- console.log(chalk12.yellow("Existing NextSpark project detected."));
3197
- console.log(chalk12.gray("Use --wizard to run the full wizard, or --registries-only for just registries."));
3653
+ console.log(chalk8.yellow("Existing NextSpark project detected."));
3654
+ console.log(chalk8.gray("Use --wizard to run the full wizard, or --registries-only for just registries."));
3198
3655
  await simpleInit(options);
3199
3656
  return;
3200
3657
  }
@@ -3206,25 +3663,133 @@ async function initCommand(options) {
3206
3663
  yes: options.yes,
3207
3664
  name: options.name,
3208
3665
  slug: options.slug,
3209
- description: options.description
3666
+ description: options.description,
3667
+ type: options.type
3210
3668
  };
3211
3669
  await runWizard(wizardOptions);
3212
3670
  }
3213
3671
 
3672
+ // src/commands/add-mobile.ts
3673
+ import { existsSync as existsSync5, cpSync as cpSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, renameSync, mkdirSync as mkdirSync3 } from "fs";
3674
+ import { join as join6 } from "path";
3675
+ import chalk9 from "chalk";
3676
+ import ora5 from "ora";
3677
+ import { execSync as execSync2 } from "child_process";
3678
+ function findMobileCoreDir() {
3679
+ const projectRoot = process.cwd();
3680
+ const npmPath = join6(projectRoot, "node_modules", "@nextsparkjs", "mobile");
3681
+ if (existsSync5(npmPath)) {
3682
+ return npmPath;
3683
+ }
3684
+ const monoPath = join6(projectRoot, "packages", "mobile");
3685
+ if (existsSync5(monoPath)) {
3686
+ return monoPath;
3687
+ }
3688
+ const parentNpmPath = join6(projectRoot, "..", "node_modules", "@nextsparkjs", "mobile");
3689
+ if (existsSync5(parentNpmPath)) {
3690
+ return parentNpmPath;
3691
+ }
3692
+ throw new Error(
3693
+ "Could not find @nextsparkjs/mobile package.\nRun: npm install @nextsparkjs/mobile"
3694
+ );
3695
+ }
3696
+ async function addMobileCommand(options = {}) {
3697
+ const projectRoot = process.cwd();
3698
+ const mobileDir = join6(projectRoot, "mobile");
3699
+ console.log();
3700
+ console.log(chalk9.bold("Adding NextSpark Mobile App"));
3701
+ console.log();
3702
+ if (existsSync5(mobileDir) && !options.force) {
3703
+ console.log(chalk9.red("Error: Mobile app already exists at mobile/"));
3704
+ console.log(chalk9.gray("Use --force to overwrite"));
3705
+ process.exit(1);
3706
+ }
3707
+ let mobileCoreDir;
3708
+ try {
3709
+ mobileCoreDir = findMobileCoreDir();
3710
+ } catch (error) {
3711
+ console.log(chalk9.red(error.message));
3712
+ process.exit(1);
3713
+ }
3714
+ const templatesDir = join6(mobileCoreDir, "templates");
3715
+ if (!existsSync5(templatesDir)) {
3716
+ console.log(chalk9.red("Error: Could not find mobile templates"));
3717
+ console.log(chalk9.gray(`Expected at: ${templatesDir}`));
3718
+ process.exit(1);
3719
+ }
3720
+ const copySpinner = ora5("Copying mobile app template...").start();
3721
+ try {
3722
+ mkdirSync3(mobileDir, { recursive: true });
3723
+ cpSync2(templatesDir, mobileDir, { recursive: true });
3724
+ const pkgTemplatePath = join6(mobileDir, "package.json.template");
3725
+ const pkgPath = join6(mobileDir, "package.json");
3726
+ if (existsSync5(pkgTemplatePath)) {
3727
+ renameSync(pkgTemplatePath, pkgPath);
3728
+ const pkg2 = JSON.parse(readFileSync5(pkgPath, "utf-8"));
3729
+ const rootPkgPath = join6(projectRoot, "package.json");
3730
+ if (existsSync5(rootPkgPath)) {
3731
+ const rootPkg = JSON.parse(readFileSync5(rootPkgPath, "utf-8"));
3732
+ const rawName = rootPkg.name || "my-project";
3733
+ if (rawName.startsWith("@")) {
3734
+ const scopeMatch = rawName.match(/^@[\w-]+/);
3735
+ const scope = scopeMatch ? scopeMatch[0] : "";
3736
+ const projectName = rawName.replace(/^@[\w-]+\//, "");
3737
+ pkg2.name = `${scope}/${projectName}-mobile`;
3738
+ } else {
3739
+ pkg2.name = `${rawName}-mobile`;
3740
+ }
3741
+ }
3742
+ writeFileSync3(pkgPath, JSON.stringify(pkg2, null, 2));
3743
+ }
3744
+ copySpinner.succeed("Mobile app template copied");
3745
+ } catch (error) {
3746
+ copySpinner.fail("Failed to copy templates");
3747
+ console.log(chalk9.red(error.message));
3748
+ process.exit(1);
3749
+ }
3750
+ if (!options.skipInstall) {
3751
+ const installSpinner = ora5("Installing dependencies...").start();
3752
+ try {
3753
+ execSync2("npm install", {
3754
+ cwd: mobileDir,
3755
+ stdio: "pipe",
3756
+ timeout: 3e5
3757
+ // 5 minutes
3758
+ });
3759
+ installSpinner.succeed("Dependencies installed");
3760
+ } catch (error) {
3761
+ installSpinner.fail("Failed to install dependencies");
3762
+ console.log(chalk9.yellow(" Run `npm install` in mobile/ manually"));
3763
+ }
3764
+ }
3765
+ console.log();
3766
+ console.log(chalk9.green.bold(" Mobile app created successfully!"));
3767
+ console.log();
3768
+ console.log(chalk9.bold(" Next steps:"));
3769
+ console.log();
3770
+ console.log(` ${chalk9.cyan("1.")} cd mobile`);
3771
+ console.log(` ${chalk9.cyan("2.")} Update ${chalk9.bold("app.config.ts")} with your app name and bundle ID`);
3772
+ console.log(` ${chalk9.cyan("3.")} Add your entities in ${chalk9.bold("src/entities/")}`);
3773
+ console.log(` ${chalk9.cyan("4.")} npm start`);
3774
+ console.log();
3775
+ console.log(chalk9.gray(" Documentation: https://nextspark.dev/docs/mobile"));
3776
+ console.log();
3777
+ }
3778
+
3214
3779
  // src/commands/doctor.ts
3215
- import chalk14 from "chalk";
3780
+ import chalk11 from "chalk";
3216
3781
 
3217
3782
  // src/doctor/index.ts
3218
- import chalk13 from "chalk";
3783
+ import chalk10 from "chalk";
3219
3784
 
3220
3785
  // src/doctor/checks/dependencies.ts
3221
- import fs8 from "fs-extra";
3222
- import path6 from "path";
3786
+ import fs9 from "fs-extra";
3787
+ import path8 from "path";
3223
3788
  async function checkDependencies() {
3224
3789
  const cwd = process.cwd();
3225
- const nodeModulesPath = path6.join(cwd, "node_modules");
3226
- const packageJsonPath = path6.join(cwd, "package.json");
3227
- if (!await fs8.pathExists(packageJsonPath)) {
3790
+ const nodeModulesPath = path8.join(cwd, "node_modules");
3791
+ const packageJsonPath = path8.join(cwd, "package.json");
3792
+ if (!await fs9.pathExists(packageJsonPath)) {
3228
3793
  return {
3229
3794
  name: "Dependencies",
3230
3795
  status: "fail",
@@ -3232,7 +3797,7 @@ async function checkDependencies() {
3232
3797
  fix: "Ensure you are in a NextSpark project directory"
3233
3798
  };
3234
3799
  }
3235
- if (!await fs8.pathExists(nodeModulesPath)) {
3800
+ if (!await fs9.pathExists(nodeModulesPath)) {
3236
3801
  return {
3237
3802
  name: "Dependencies",
3238
3803
  status: "fail",
@@ -3242,7 +3807,7 @@ async function checkDependencies() {
3242
3807
  }
3243
3808
  let packageJson;
3244
3809
  try {
3245
- packageJson = await fs8.readJson(packageJsonPath);
3810
+ packageJson = await fs9.readJson(packageJsonPath);
3246
3811
  } catch {
3247
3812
  return {
3248
3813
  name: "Dependencies",
@@ -3259,14 +3824,14 @@ async function checkDependencies() {
3259
3824
  const criticalDeps = ["next", "react", "react-dom"];
3260
3825
  for (const dep of criticalDeps) {
3261
3826
  if (allDependencies[dep]) {
3262
- const depPath = path6.join(nodeModulesPath, dep);
3263
- if (!await fs8.pathExists(depPath)) {
3827
+ const depPath = path8.join(nodeModulesPath, dep);
3828
+ if (!await fs9.pathExists(depPath)) {
3264
3829
  missingDeps.push(dep);
3265
3830
  }
3266
3831
  }
3267
3832
  }
3268
- const nextsparksCorePath = path6.join(nodeModulesPath, "@nextsparkjs", "core");
3269
- const hasNextSparkCore = await fs8.pathExists(nextsparksCorePath);
3833
+ const nextsparksCorePath = path8.join(nodeModulesPath, "@nextsparkjs", "core");
3834
+ const hasNextSparkCore = await fs9.pathExists(nextsparksCorePath);
3270
3835
  if (missingDeps.length > 0) {
3271
3836
  return {
3272
3837
  name: "Dependencies",
@@ -3291,8 +3856,8 @@ async function checkDependencies() {
3291
3856
  }
3292
3857
 
3293
3858
  // src/doctor/checks/config.ts
3294
- import fs9 from "fs-extra";
3295
- import path7 from "path";
3859
+ import fs10 from "fs-extra";
3860
+ import path9 from "path";
3296
3861
  var REQUIRED_CONFIG_FILES = [
3297
3862
  "next.config.ts",
3298
3863
  "tailwind.config.ts",
@@ -3303,13 +3868,13 @@ async function checkConfigs() {
3303
3868
  const missingFiles = [];
3304
3869
  const invalidFiles = [];
3305
3870
  for (const file of REQUIRED_CONFIG_FILES) {
3306
- const filePath = path7.join(cwd, file);
3871
+ const filePath = path9.join(cwd, file);
3307
3872
  if (file.endsWith(".ts")) {
3308
- const jsPath = path7.join(cwd, file.replace(".ts", ".js"));
3309
- const mjsPath = path7.join(cwd, file.replace(".ts", ".mjs"));
3310
- const tsExists = await fs9.pathExists(filePath);
3311
- const jsExists = await fs9.pathExists(jsPath);
3312
- const mjsExists = await fs9.pathExists(mjsPath);
3873
+ const jsPath = path9.join(cwd, file.replace(".ts", ".js"));
3874
+ const mjsPath = path9.join(cwd, file.replace(".ts", ".mjs"));
3875
+ const tsExists = await fs10.pathExists(filePath);
3876
+ const jsExists = await fs10.pathExists(jsPath);
3877
+ const mjsExists = await fs10.pathExists(mjsPath);
3313
3878
  if (!tsExists && !jsExists && !mjsExists) {
3314
3879
  if (!file.includes("next.config") && !file.includes("tailwind.config")) {
3315
3880
  missingFiles.push(file);
@@ -3317,14 +3882,14 @@ async function checkConfigs() {
3317
3882
  }
3318
3883
  continue;
3319
3884
  }
3320
- if (!await fs9.pathExists(filePath)) {
3885
+ if (!await fs10.pathExists(filePath)) {
3321
3886
  missingFiles.push(file);
3322
3887
  }
3323
3888
  }
3324
- const tsconfigPath = path7.join(cwd, "tsconfig.json");
3325
- if (await fs9.pathExists(tsconfigPath)) {
3889
+ const tsconfigPath = path9.join(cwd, "tsconfig.json");
3890
+ if (await fs10.pathExists(tsconfigPath)) {
3326
3891
  try {
3327
- const content = await fs9.readFile(tsconfigPath, "utf-8");
3892
+ const content = await fs10.readFile(tsconfigPath, "utf-8");
3328
3893
  JSON.parse(content);
3329
3894
  } catch {
3330
3895
  invalidFiles.push("tsconfig.json");
@@ -3332,14 +3897,14 @@ async function checkConfigs() {
3332
3897
  } else {
3333
3898
  missingFiles.push("tsconfig.json");
3334
3899
  }
3335
- const configDir = path7.join(cwd, "config");
3336
- if (await fs9.pathExists(configDir)) {
3337
- const configFiles = await fs9.readdir(configDir);
3900
+ const configDir = path9.join(cwd, "config");
3901
+ if (await fs10.pathExists(configDir)) {
3902
+ const configFiles = await fs10.readdir(configDir);
3338
3903
  const tsConfigFiles = configFiles.filter((f) => f.endsWith(".ts"));
3339
3904
  for (const file of tsConfigFiles) {
3340
- const filePath = path7.join(configDir, file);
3905
+ const filePath = path9.join(configDir, file);
3341
3906
  try {
3342
- const content = await fs9.readFile(filePath, "utf-8");
3907
+ const content = await fs10.readFile(filePath, "utf-8");
3343
3908
  if (content.trim().length === 0) {
3344
3909
  invalidFiles.push(`config/${file}`);
3345
3910
  }
@@ -3372,8 +3937,8 @@ async function checkConfigs() {
3372
3937
  }
3373
3938
 
3374
3939
  // src/doctor/checks/database.ts
3375
- import fs10 from "fs-extra";
3376
- import path8 from "path";
3940
+ import fs11 from "fs-extra";
3941
+ import path10 from "path";
3377
3942
  function parseEnvFile(content) {
3378
3943
  const result = {};
3379
3944
  const lines = content.split("\n");
@@ -3396,25 +3961,25 @@ function parseEnvFile(content) {
3396
3961
  }
3397
3962
  async function checkDatabase() {
3398
3963
  const cwd = process.cwd();
3399
- const envPath = path8.join(cwd, ".env");
3400
- const envLocalPath = path8.join(cwd, ".env.local");
3964
+ const envPath = path10.join(cwd, ".env");
3965
+ const envLocalPath = path10.join(cwd, ".env.local");
3401
3966
  let envVars = {};
3402
- if (await fs10.pathExists(envLocalPath)) {
3967
+ if (await fs11.pathExists(envLocalPath)) {
3403
3968
  try {
3404
- const content = await fs10.readFile(envLocalPath, "utf-8");
3969
+ const content = await fs11.readFile(envLocalPath, "utf-8");
3405
3970
  envVars = { ...envVars, ...parseEnvFile(content) };
3406
3971
  } catch {
3407
3972
  }
3408
3973
  }
3409
- if (await fs10.pathExists(envPath)) {
3974
+ if (await fs11.pathExists(envPath)) {
3410
3975
  try {
3411
- const content = await fs10.readFile(envPath, "utf-8");
3976
+ const content = await fs11.readFile(envPath, "utf-8");
3412
3977
  envVars = { ...envVars, ...parseEnvFile(content) };
3413
3978
  } catch {
3414
3979
  }
3415
3980
  }
3416
3981
  const databaseUrl = envVars["DATABASE_URL"] || process.env.DATABASE_URL;
3417
- if (!await fs10.pathExists(envPath) && !await fs10.pathExists(envLocalPath)) {
3982
+ if (!await fs11.pathExists(envPath) && !await fs11.pathExists(envLocalPath)) {
3418
3983
  return {
3419
3984
  name: "Database",
3420
3985
  status: "warn",
@@ -3455,22 +4020,22 @@ async function checkDatabase() {
3455
4020
  }
3456
4021
 
3457
4022
  // src/doctor/checks/imports.ts
3458
- import fs11 from "fs-extra";
3459
- import path9 from "path";
4023
+ import fs12 from "fs-extra";
4024
+ import path11 from "path";
3460
4025
  async function checkCorePackage(cwd) {
3461
- const nodeModulesPath = path9.join(cwd, "node_modules", "@nextsparkjs", "core");
3462
- if (!await fs11.pathExists(nodeModulesPath)) {
4026
+ const nodeModulesPath = path11.join(cwd, "node_modules", "@nextsparkjs", "core");
4027
+ if (!await fs12.pathExists(nodeModulesPath)) {
3463
4028
  return { accessible: false, reason: "@nextsparkjs/core not found in node_modules" };
3464
4029
  }
3465
- const packageJsonPath = path9.join(nodeModulesPath, "package.json");
3466
- if (!await fs11.pathExists(packageJsonPath)) {
4030
+ const packageJsonPath = path11.join(nodeModulesPath, "package.json");
4031
+ if (!await fs12.pathExists(packageJsonPath)) {
3467
4032
  return { accessible: false, reason: "@nextsparkjs/core package.json not found" };
3468
4033
  }
3469
4034
  try {
3470
- const packageJson = await fs11.readJson(packageJsonPath);
4035
+ const packageJson = await fs12.readJson(packageJsonPath);
3471
4036
  const mainEntry = packageJson.main || packageJson.module || "./dist/index.js";
3472
- const mainPath = path9.join(nodeModulesPath, mainEntry);
3473
- if (!await fs11.pathExists(mainPath)) {
4037
+ const mainPath = path11.join(nodeModulesPath, mainEntry);
4038
+ if (!await fs12.pathExists(mainPath)) {
3474
4039
  return { accessible: false, reason: "@nextsparkjs/core entry point not found" };
3475
4040
  }
3476
4041
  } catch {
@@ -3483,8 +4048,8 @@ async function scanForBrokenImports(cwd) {
3483
4048
  const srcDirs = ["src", "app", "pages", "components", "lib"];
3484
4049
  const existingDirs = [];
3485
4050
  for (const dir of srcDirs) {
3486
- const dirPath = path9.join(cwd, dir);
3487
- if (await fs11.pathExists(dirPath)) {
4051
+ const dirPath = path11.join(cwd, dir);
4052
+ if (await fs12.pathExists(dirPath)) {
3488
4053
  existingDirs.push(dirPath);
3489
4054
  }
3490
4055
  }
@@ -3493,32 +4058,32 @@ async function scanForBrokenImports(cwd) {
3493
4058
  for (const dir of existingDirs) {
3494
4059
  if (filesScanned >= maxFilesToScan) break;
3495
4060
  try {
3496
- const files = await fs11.readdir(dir, { withFileTypes: true });
4061
+ const files = await fs12.readdir(dir, { withFileTypes: true });
3497
4062
  for (const file of files) {
3498
4063
  if (filesScanned >= maxFilesToScan) break;
3499
4064
  if (file.isFile() && (file.name.endsWith(".ts") || file.name.endsWith(".tsx"))) {
3500
- const filePath = path9.join(dir, file.name);
4065
+ const filePath = path11.join(dir, file.name);
3501
4066
  try {
3502
- const content = await fs11.readFile(filePath, "utf-8");
4067
+ const content = await fs12.readFile(filePath, "utf-8");
3503
4068
  const importMatches = content.match(/from ['"](@?[^'"]+)['"]/g);
3504
4069
  if (importMatches) {
3505
4070
  for (const match of importMatches) {
3506
4071
  const importPath = match.replace(/from ['"]/g, "").replace(/['"]/g, "");
3507
4072
  if (importPath.startsWith(".")) {
3508
- const absoluteImportPath = path9.resolve(path9.dirname(filePath), importPath);
4073
+ const absoluteImportPath = path11.resolve(path11.dirname(filePath), importPath);
3509
4074
  const possiblePaths = [
3510
4075
  absoluteImportPath,
3511
4076
  `${absoluteImportPath}.ts`,
3512
4077
  `${absoluteImportPath}.tsx`,
3513
4078
  `${absoluteImportPath}.js`,
3514
4079
  `${absoluteImportPath}.jsx`,
3515
- path9.join(absoluteImportPath, "index.ts"),
3516
- path9.join(absoluteImportPath, "index.tsx"),
3517
- path9.join(absoluteImportPath, "index.js")
4080
+ path11.join(absoluteImportPath, "index.ts"),
4081
+ path11.join(absoluteImportPath, "index.tsx"),
4082
+ path11.join(absoluteImportPath, "index.js")
3518
4083
  ];
3519
4084
  const exists = await Promise.any(
3520
4085
  possiblePaths.map(async (p) => {
3521
- if (await fs11.pathExists(p)) return true;
4086
+ if (await fs12.pathExists(p)) return true;
3522
4087
  throw new Error("Not found");
3523
4088
  })
3524
4089
  ).catch(() => false);
@@ -3542,11 +4107,11 @@ async function checkImports() {
3542
4107
  const cwd = process.cwd();
3543
4108
  const coreCheck = await checkCorePackage(cwd);
3544
4109
  if (!coreCheck.accessible) {
3545
- const packageJsonPath = path9.join(cwd, "package.json");
4110
+ const packageJsonPath = path11.join(cwd, "package.json");
3546
4111
  let hasCoreDep = false;
3547
- if (await fs11.pathExists(packageJsonPath)) {
4112
+ if (await fs12.pathExists(packageJsonPath)) {
3548
4113
  try {
3549
- const packageJson = await fs11.readJson(packageJsonPath);
4114
+ const packageJson = await fs12.readJson(packageJsonPath);
3550
4115
  hasCoreDep = !!(packageJson.dependencies?.["@nextsparkjs/core"] || packageJson.devDependencies?.["@nextsparkjs/core"]);
3551
4116
  } catch {
3552
4117
  }
@@ -3583,25 +4148,25 @@ async function checkImports() {
3583
4148
 
3584
4149
  // src/doctor/index.ts
3585
4150
  var STATUS_ICONS = {
3586
- pass: chalk13.green("\u2713"),
3587
- warn: chalk13.yellow("\u26A0"),
3588
- fail: chalk13.red("\u2717")
4151
+ pass: chalk10.green("\u2713"),
4152
+ warn: chalk10.yellow("\u26A0"),
4153
+ fail: chalk10.red("\u2717")
3589
4154
  };
3590
4155
  function formatResult(result) {
3591
4156
  const icon = STATUS_ICONS[result.status];
3592
- const nameColor = result.status === "fail" ? chalk13.red : result.status === "warn" ? chalk13.yellow : chalk13.white;
4157
+ const nameColor = result.status === "fail" ? chalk10.red : result.status === "warn" ? chalk10.yellow : chalk10.white;
3593
4158
  const name = nameColor(result.name.padEnd(18));
3594
- const message = chalk13.gray(result.message);
4159
+ const message = chalk10.gray(result.message);
3595
4160
  let output = `${icon} ${name} ${message}`;
3596
4161
  if (result.fix && result.status !== "pass") {
3597
4162
  output += `
3598
- ${" ".repeat(22)}${chalk13.cyan("\u2192")} ${chalk13.cyan(result.fix)}`;
4163
+ ${" ".repeat(22)}${chalk10.cyan("\u2192")} ${chalk10.cyan(result.fix)}`;
3599
4164
  }
3600
4165
  return output;
3601
4166
  }
3602
4167
  function showHeader() {
3603
4168
  console.log("");
3604
- console.log(chalk13.cyan("\u{1FA7A} NextSpark Health Check"));
4169
+ console.log(chalk10.cyan("\u{1FA7A} NextSpark Health Check"));
3605
4170
  console.log("");
3606
4171
  }
3607
4172
  function showSummary(results) {
@@ -3609,11 +4174,11 @@ function showSummary(results) {
3609
4174
  const warnings = results.filter((r) => r.status === "warn").length;
3610
4175
  const failed = results.filter((r) => r.status === "fail").length;
3611
4176
  console.log("");
3612
- console.log(chalk13.gray("-".repeat(50)));
4177
+ console.log(chalk10.gray("-".repeat(50)));
3613
4178
  const summary = [
3614
- chalk13.green(`${passed} passed`),
3615
- warnings > 0 ? chalk13.yellow(`${warnings} warning${warnings > 1 ? "s" : ""}`) : null,
3616
- failed > 0 ? chalk13.red(`${failed} failed`) : null
4179
+ chalk10.green(`${passed} passed`),
4180
+ warnings > 0 ? chalk10.yellow(`${warnings} warning${warnings > 1 ? "s" : ""}`) : null,
4181
+ failed > 0 ? chalk10.red(`${failed} failed`) : null
3617
4182
  ].filter(Boolean).join(", ");
3618
4183
  console.log(`Summary: ${summary}`);
3619
4184
  console.log("");
@@ -3655,7 +4220,7 @@ async function runDoctorCommand() {
3655
4220
  var isDirectExecution = process.argv[1]?.includes("doctor") || process.argv.includes("doctor");
3656
4221
  if (isDirectExecution && typeof __require !== "undefined") {
3657
4222
  runDoctorCommand().catch((error) => {
3658
- console.error(chalk13.red("An unexpected error occurred:"), error.message);
4223
+ console.error(chalk10.red("An unexpected error occurred:"), error.message);
3659
4224
  process.exit(1);
3660
4225
  });
3661
4226
  }
@@ -3666,16 +4231,458 @@ async function doctorCommand() {
3666
4231
  await runDoctorCommand();
3667
4232
  } catch (error) {
3668
4233
  if (error instanceof Error) {
3669
- console.error(chalk14.red(`Error: ${error.message}`));
4234
+ console.error(chalk11.red(`Error: ${error.message}`));
4235
+ }
4236
+ process.exit(1);
4237
+ }
4238
+ }
4239
+
4240
+ // src/commands/db.ts
4241
+ import { spawn } from "child_process";
4242
+ import { existsSync as existsSync6, readFileSync as readFileSync6 } from "fs";
4243
+ import { join as join7 } from "path";
4244
+ import chalk12 from "chalk";
4245
+ import ora6 from "ora";
4246
+ function loadProjectEnv(projectRoot) {
4247
+ const envPath = join7(projectRoot, ".env");
4248
+ const envVars = {};
4249
+ if (existsSync6(envPath)) {
4250
+ const content = readFileSync6(envPath, "utf-8");
4251
+ for (const line of content.split("\n")) {
4252
+ const trimmed = line.trim();
4253
+ if (trimmed && !trimmed.startsWith("#")) {
4254
+ const [key, ...valueParts] = trimmed.split("=");
4255
+ if (key && valueParts.length > 0) {
4256
+ let value = valueParts.join("=");
4257
+ if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
4258
+ value = value.slice(1, -1);
4259
+ }
4260
+ envVars[key] = value;
4261
+ }
4262
+ }
4263
+ }
4264
+ }
4265
+ return envVars;
4266
+ }
4267
+ async function dbMigrateCommand() {
4268
+ const spinner = ora6("Preparing to run migrations...").start();
4269
+ try {
4270
+ const coreDir = getCoreDir();
4271
+ const projectRoot = getProjectRoot();
4272
+ const migrationsScript = join7(coreDir, "scripts", "db", "run-migrations.mjs");
4273
+ if (!existsSync6(migrationsScript)) {
4274
+ spinner.fail("Migrations script not found");
4275
+ console.error(chalk12.red(`Expected script at: ${migrationsScript}`));
4276
+ process.exit(1);
4277
+ }
4278
+ spinner.succeed("Core package found");
4279
+ const projectEnv = loadProjectEnv(projectRoot);
4280
+ if (!projectEnv.DATABASE_URL) {
4281
+ spinner.fail("DATABASE_URL not found in .env file");
4282
+ console.error(chalk12.red("Please configure DATABASE_URL in your .env file"));
4283
+ process.exit(1);
4284
+ }
4285
+ if (!projectEnv.NEXT_PUBLIC_ACTIVE_THEME) {
4286
+ spinner.fail("NEXT_PUBLIC_ACTIVE_THEME not found in .env file");
4287
+ console.error(chalk12.red("Please configure NEXT_PUBLIC_ACTIVE_THEME in your .env file"));
4288
+ process.exit(1);
4289
+ }
4290
+ spinner.start("Running database migrations...");
4291
+ const migrateProcess = spawn("node", [migrationsScript], {
4292
+ cwd: projectRoot,
4293
+ stdio: "inherit",
4294
+ env: {
4295
+ ...projectEnv,
4296
+ ...process.env,
4297
+ NEXTSPARK_PROJECT_ROOT: projectRoot,
4298
+ NEXTSPARK_CORE_DIR: coreDir
4299
+ }
4300
+ });
4301
+ migrateProcess.on("error", (err) => {
4302
+ spinner.fail("Migration failed");
4303
+ console.error(chalk12.red(err.message));
4304
+ process.exit(1);
4305
+ });
4306
+ migrateProcess.on("close", (code) => {
4307
+ if (code === 0) {
4308
+ console.log(chalk12.green("\n\u2705 Migrations completed successfully!"));
4309
+ process.exit(0);
4310
+ } else {
4311
+ console.error(chalk12.red(`
4312
+ \u274C Migrations failed with exit code ${code}`));
4313
+ process.exit(code ?? 1);
4314
+ }
4315
+ });
4316
+ } catch (error) {
4317
+ spinner.fail("Migration preparation failed");
4318
+ if (error instanceof Error) {
4319
+ console.error(chalk12.red(error.message));
4320
+ }
4321
+ process.exit(1);
4322
+ }
4323
+ }
4324
+ async function dbSeedCommand() {
4325
+ console.log(chalk12.cyan("\u2139\uFE0F Sample data is included as part of the migration process."));
4326
+ console.log(chalk12.cyan(" Running db:migrate to apply all migrations including sample data...\n"));
4327
+ await dbMigrateCommand();
4328
+ }
4329
+
4330
+ // src/commands/sync-app.ts
4331
+ import { existsSync as existsSync7, readdirSync as readdirSync2, mkdirSync as mkdirSync4, copyFileSync, readFileSync as readFileSync7 } from "fs";
4332
+ import { join as join8, dirname as dirname3, relative } from "path";
4333
+ import chalk13 from "chalk";
4334
+ import ora7 from "ora";
4335
+ var EXCLUDED_TEMPLATE_PATTERNS = ["(templates)"];
4336
+ var ROOT_TEMPLATE_FILES = [
4337
+ "proxy.ts",
4338
+ // Next.js 16+ proxy (formerly middleware.ts) - required for auth/permission validation
4339
+ "next.config.mjs",
4340
+ // Required for webpack aliases, transpilePackages, security headers
4341
+ "tsconfig.json",
4342
+ // Required for proper path aliases and test file exclusions
4343
+ "i18n.ts"
4344
+ // Required for next-intl configuration
4345
+ ];
4346
+ var MAX_VERBOSE_FILES = 10;
4347
+ var MAX_SUMMARY_FILES = 5;
4348
+ function getAllFiles(dir, baseDir = dir) {
4349
+ const files = [];
4350
+ if (!existsSync7(dir)) {
4351
+ return files;
4352
+ }
4353
+ const entries = readdirSync2(dir, { withFileTypes: true });
4354
+ for (const entry of entries) {
4355
+ const fullPath = join8(dir, entry.name);
4356
+ const relativePath = relative(baseDir, fullPath);
4357
+ if (entry.isDirectory()) {
4358
+ files.push(...getAllFiles(fullPath, baseDir));
4359
+ } else if (entry.isFile()) {
4360
+ if (entry.name !== ".DS_Store" && entry.name !== "README.md") {
4361
+ files.push(relativePath);
4362
+ }
4363
+ }
4364
+ }
4365
+ return files;
4366
+ }
4367
+ function copyFile(source, target) {
4368
+ const targetDir = dirname3(target);
4369
+ if (!existsSync7(targetDir)) {
4370
+ mkdirSync4(targetDir, { recursive: true });
4371
+ }
4372
+ copyFileSync(source, target);
4373
+ }
4374
+ function backupDirectory(source, target) {
4375
+ if (!existsSync7(source)) {
4376
+ return;
4377
+ }
4378
+ const files = getAllFiles(source);
4379
+ for (const file of files) {
4380
+ const sourcePath = join8(source, file);
4381
+ const targetPath = join8(target, file);
4382
+ copyFile(sourcePath, targetPath);
4383
+ }
4384
+ }
4385
+ function getCoreVersion2(coreDir) {
4386
+ try {
4387
+ const pkgPath = join8(coreDir, "package.json");
4388
+ const pkg2 = JSON.parse(readFileSync7(pkgPath, "utf-8"));
4389
+ return pkg2.version || "unknown";
4390
+ } catch {
4391
+ return "unknown";
4392
+ }
4393
+ }
4394
+ async function syncAppCommand(options) {
4395
+ const spinner = ora7({ text: "Preparing sync...", isSilent: options.dryRun }).start();
4396
+ try {
4397
+ const coreDir = getCoreDir();
4398
+ const projectRoot = getProjectRoot();
4399
+ const coreVersion = getCoreVersion2(coreDir);
4400
+ const templatesDir = join8(coreDir, "templates", "app");
4401
+ const appDir = join8(projectRoot, "app");
4402
+ if (!existsSync7(templatesDir)) {
4403
+ spinner.fail("Templates directory not found in @nextsparkjs/core");
4404
+ console.error(chalk13.red(`
4405
+ Expected path: ${templatesDir}`));
4406
+ process.exit(1);
4407
+ }
4408
+ if (!existsSync7(appDir)) {
4409
+ spinner.fail("No /app directory found");
4410
+ console.error(chalk13.red("\n This project does not have an /app folder."));
4411
+ console.error(chalk13.yellow(' Run "nextspark init" first to initialize your project.\n'));
4412
+ process.exit(1);
4413
+ }
4414
+ spinner.text = "Scanning template files...";
4415
+ const templateFiles = getAllFiles(templatesDir).filter((f) => !EXCLUDED_TEMPLATE_PATTERNS.some((pattern) => f.startsWith(pattern)));
4416
+ const existingAppFiles = getAllFiles(appDir);
4417
+ const customFiles = existingAppFiles.filter((f) => !templateFiles.includes(f));
4418
+ const filesToUpdate = templateFiles;
4419
+ spinner.succeed("Scan complete");
4420
+ console.log(chalk13.cyan(`
4421
+ Syncing /app with @nextsparkjs/core@${coreVersion}...
4422
+ `));
4423
+ if (options.dryRun) {
4424
+ console.log(chalk13.yellow(" [DRY RUN] No changes will be made\n"));
4425
+ }
4426
+ if (options.verbose) {
4427
+ console.log(chalk13.gray(" Files to sync:"));
4428
+ for (const file of filesToUpdate) {
4429
+ const targetPath = join8(appDir, file);
4430
+ const status = existsSync7(targetPath) ? chalk13.yellow("update") : chalk13.green("create");
4431
+ console.log(chalk13.gray(` ${status} ${file}`));
4432
+ }
4433
+ console.log();
4434
+ }
4435
+ console.log(chalk13.white(` Template files: ${filesToUpdate.length}`));
4436
+ console.log(chalk13.white(` Custom files preserved: ${customFiles.length}`));
4437
+ if (customFiles.length > 0 && options.verbose) {
4438
+ console.log(chalk13.gray("\n Custom files (will be preserved):"));
4439
+ for (const file of customFiles.slice(0, MAX_VERBOSE_FILES)) {
4440
+ console.log(chalk13.gray(` - ${file}`));
4441
+ }
4442
+ if (customFiles.length > MAX_VERBOSE_FILES) {
4443
+ console.log(chalk13.gray(` ... and ${customFiles.length - MAX_VERBOSE_FILES} more`));
4444
+ }
4445
+ }
4446
+ if (!options.force && !options.dryRun) {
4447
+ console.log(chalk13.yellow(`
4448
+ This will overwrite ${filesToUpdate.length} core template files.`));
4449
+ console.log(chalk13.gray(" Run with --dry-run to preview changes, or --force to skip this prompt.\n"));
4450
+ try {
4451
+ const { confirm: confirm6 } = await import("@inquirer/prompts");
4452
+ const confirmed = await confirm6({
4453
+ message: "Proceed with sync?",
4454
+ default: true
4455
+ });
4456
+ if (!confirmed) {
4457
+ console.log(chalk13.yellow("\n Sync cancelled.\n"));
4458
+ process.exit(0);
4459
+ }
4460
+ } catch (promptError) {
4461
+ console.error(chalk13.red("\n Failed to load confirmation prompt. Use --force to skip.\n"));
4462
+ process.exit(1);
4463
+ }
4464
+ }
4465
+ if (options.backup && !options.dryRun) {
4466
+ const backupDir = join8(projectRoot, `app.backup.v${coreVersion}.${Date.now()}`);
4467
+ spinner.start("Creating backup...");
4468
+ backupDirectory(appDir, backupDir);
4469
+ spinner.succeed(`Backup created: ${relative(projectRoot, backupDir)}`);
4470
+ }
4471
+ if (!options.dryRun) {
4472
+ spinner.start("Syncing files...");
4473
+ let updated = 0;
4474
+ let created = 0;
4475
+ for (const file of filesToUpdate) {
4476
+ const sourcePath = join8(templatesDir, file);
4477
+ const targetPath = join8(appDir, file);
4478
+ const isNew = !existsSync7(targetPath);
4479
+ copyFile(sourcePath, targetPath);
4480
+ if (isNew) {
4481
+ created++;
4482
+ } else {
4483
+ updated++;
4484
+ }
4485
+ if (options.verbose) {
4486
+ spinner.text = `Syncing: ${file}`;
4487
+ }
4488
+ }
4489
+ spinner.succeed(`Synced ${filesToUpdate.length} files (${updated} updated, ${created} created)`);
4490
+ const rootTemplatesDir = join8(coreDir, "templates");
4491
+ let rootUpdated = 0;
4492
+ let rootCreated = 0;
4493
+ for (const file of ROOT_TEMPLATE_FILES) {
4494
+ const sourcePath = join8(rootTemplatesDir, file);
4495
+ const targetPath = join8(projectRoot, file);
4496
+ if (existsSync7(sourcePath)) {
4497
+ const isNew = !existsSync7(targetPath);
4498
+ copyFile(sourcePath, targetPath);
4499
+ if (isNew) {
4500
+ rootCreated++;
4501
+ if (options.verbose) {
4502
+ console.log(chalk13.green(` + Created: ${file}`));
4503
+ }
4504
+ } else {
4505
+ rootUpdated++;
4506
+ if (options.verbose) {
4507
+ console.log(chalk13.yellow(` ~ Updated: ${file}`));
4508
+ }
4509
+ }
4510
+ }
4511
+ }
4512
+ if (rootUpdated + rootCreated > 0) {
4513
+ console.log(chalk13.gray(` Root files: ${rootUpdated} updated, ${rootCreated} created`));
4514
+ }
4515
+ }
4516
+ console.log(chalk13.green("\n \u2705 Sync complete!\n"));
4517
+ if (customFiles.length > 0) {
4518
+ console.log(chalk13.gray(` Preserved ${customFiles.length} custom file(s):`));
4519
+ for (const file of customFiles.slice(0, MAX_SUMMARY_FILES)) {
4520
+ console.log(chalk13.gray(` - app/${file}`));
4521
+ }
4522
+ if (customFiles.length > MAX_SUMMARY_FILES) {
4523
+ console.log(chalk13.gray(` ... and ${customFiles.length - MAX_SUMMARY_FILES} more
4524
+ `));
4525
+ } else {
4526
+ console.log();
4527
+ }
4528
+ }
4529
+ } catch (error) {
4530
+ spinner.fail("Sync failed");
4531
+ if (error instanceof Error) {
4532
+ console.error(chalk13.red(`
4533
+ Error: ${error.message}
4534
+ `));
4535
+ if (options.verbose && error.stack) {
4536
+ console.error(chalk13.gray(` Stack trace:
4537
+ ${error.stack}
4538
+ `));
4539
+ }
4540
+ }
4541
+ process.exit(1);
4542
+ }
4543
+ }
4544
+
4545
+ // src/commands/setup-ai.ts
4546
+ import { existsSync as existsSync8 } from "fs";
4547
+ import { join as join9 } from "path";
4548
+ import { execSync as execSync3 } from "child_process";
4549
+ import chalk14 from "chalk";
4550
+ import ora8 from "ora";
4551
+ var VALID_EDITORS = ["claude", "cursor", "antigravity", "all"];
4552
+ async function setupAICommand(options) {
4553
+ const editor = options.editor || "claude";
4554
+ if (!VALID_EDITORS.includes(editor)) {
4555
+ console.log(chalk14.red(` Unknown editor: ${editor}`));
4556
+ console.log(chalk14.gray(` Available: ${VALID_EDITORS.join(", ")}`));
4557
+ process.exit(1);
4558
+ }
4559
+ console.log("");
4560
+ console.log(chalk14.cyan(" AI Workflow Setup"));
4561
+ console.log(chalk14.gray(" " + "-".repeat(40)));
4562
+ console.log("");
4563
+ const pkgPath = getAIWorkflowDir();
4564
+ if (!pkgPath) {
4565
+ console.log(chalk14.red(" @nextsparkjs/ai-workflow package not found."));
4566
+ console.log("");
4567
+ console.log(chalk14.gray(" Install it first:"));
4568
+ console.log(chalk14.cyan(" pnpm add -D -w @nextsparkjs/ai-workflow"));
4569
+ console.log("");
4570
+ process.exit(1);
4571
+ }
4572
+ const setupScript = join9(pkgPath, "scripts", "setup.mjs");
4573
+ if (!existsSync8(setupScript)) {
4574
+ console.log(chalk14.red(" Setup script not found in ai-workflow package."));
4575
+ console.log(chalk14.gray(` Expected: ${setupScript}`));
4576
+ process.exit(1);
4577
+ }
4578
+ const spinner = ora8({
4579
+ text: `Setting up AI workflow for ${editor}...`,
4580
+ prefixText: " "
4581
+ }).start();
4582
+ try {
4583
+ spinner.stop();
4584
+ execSync3(`node "${setupScript}" ${editor}`, {
4585
+ cwd: process.cwd(),
4586
+ stdio: "inherit"
4587
+ });
4588
+ } catch (error) {
4589
+ console.log("");
4590
+ console.log(chalk14.red(" AI workflow setup failed."));
4591
+ if (error instanceof Error) {
4592
+ console.log(chalk14.gray(` ${error.message}`));
4593
+ }
4594
+ process.exit(1);
4595
+ }
4596
+ }
4597
+
4598
+ // src/commands/sync-ai.ts
4599
+ import { existsSync as existsSync9 } from "fs";
4600
+ import { join as join10 } from "path";
4601
+ import { execSync as execSync4 } from "child_process";
4602
+ import chalk15 from "chalk";
4603
+ import ora9 from "ora";
4604
+ var VALID_EDITORS2 = ["claude", "cursor", "antigravity", "all"];
4605
+ async function syncAICommand(options) {
4606
+ const editor = options.editor || "claude";
4607
+ if (!VALID_EDITORS2.includes(editor)) {
4608
+ console.log(chalk15.red(` Unknown editor: ${editor}`));
4609
+ console.log(chalk15.gray(` Available: ${VALID_EDITORS2.join(", ")}`));
4610
+ process.exit(1);
4611
+ }
4612
+ console.log("");
4613
+ console.log(chalk15.cyan(" AI Workflow Sync"));
4614
+ console.log(chalk15.gray(" " + "-".repeat(40)));
4615
+ console.log("");
4616
+ const editorDir = editor === "cursor" ? ".cursor" : ".claude";
4617
+ const editorDirPath = join10(process.cwd(), editorDir);
4618
+ if (!existsSync9(editorDirPath)) {
4619
+ console.log(chalk15.red(` No ${editorDir}/ directory found.`));
4620
+ console.log("");
4621
+ console.log(chalk15.gray(" AI workflow must be set up first. Run:"));
4622
+ console.log(chalk15.cyan(" nextspark setup:ai --editor " + editor));
4623
+ console.log("");
4624
+ process.exit(1);
4625
+ }
4626
+ const pkgPath = getAIWorkflowDir();
4627
+ if (!pkgPath) {
4628
+ console.log(chalk15.red(" @nextsparkjs/ai-workflow package not found."));
4629
+ console.log("");
4630
+ console.log(chalk15.gray(" Install it first:"));
4631
+ console.log(chalk15.cyan(" pnpm add -D -w @nextsparkjs/ai-workflow"));
4632
+ console.log("");
4633
+ process.exit(1);
4634
+ }
4635
+ const setupScript = join10(pkgPath, "scripts", "setup.mjs");
4636
+ if (!existsSync9(setupScript)) {
4637
+ console.log(chalk15.red(" Setup script not found in ai-workflow package."));
4638
+ console.log(chalk15.gray(` Expected: ${setupScript}`));
4639
+ process.exit(1);
4640
+ }
4641
+ if (!options.force) {
4642
+ console.log(chalk15.yellow(" This will sync AI workflow files from @nextsparkjs/ai-workflow."));
4643
+ console.log(chalk15.gray(" Framework files will be overwritten. Custom files will be preserved."));
4644
+ console.log(chalk15.gray(" Config JSON files will never be overwritten.\n"));
4645
+ try {
4646
+ const { confirm: confirm6 } = await import("@inquirer/prompts");
4647
+ const confirmed = await confirm6({
4648
+ message: "Proceed with sync?",
4649
+ default: true
4650
+ });
4651
+ if (!confirmed) {
4652
+ console.log(chalk15.yellow("\n Sync cancelled.\n"));
4653
+ process.exit(0);
4654
+ }
4655
+ } catch {
4656
+ console.error(chalk15.red("\n Failed to load confirmation prompt. Use --force to skip.\n"));
4657
+ process.exit(1);
4658
+ }
4659
+ }
4660
+ const spinner = ora9({
4661
+ text: `Syncing AI workflow for ${editor}...`,
4662
+ prefixText: " "
4663
+ }).start();
4664
+ try {
4665
+ spinner.stop();
4666
+ execSync4(`node "${setupScript}" ${editor}`, {
4667
+ cwd: process.cwd(),
4668
+ stdio: "inherit"
4669
+ });
4670
+ } catch (error) {
4671
+ console.log("");
4672
+ console.log(chalk15.red(" AI workflow sync failed."));
4673
+ if (error instanceof Error) {
4674
+ console.log(chalk15.gray(` ${error.message}`));
3670
4675
  }
3671
4676
  process.exit(1);
3672
4677
  }
3673
4678
  }
3674
4679
 
3675
4680
  // src/cli.ts
4681
+ config();
4682
+ var pkg = JSON.parse(readFileSync8(new URL("../package.json", import.meta.url), "utf-8"));
3676
4683
  var program = new Command();
3677
- program.name("nextspark").description("NextSpark CLI - Professional SaaS Boilerplate").version("0.1.0-beta.4");
3678
- program.command("dev").description("Start development server with registry watcher").option("-p, --port <port>", "Port to run the dev server on", "3000").option("--no-registry", "Disable registry watcher").action(devCommand);
4684
+ program.name("nextspark").description("NextSpark CLI - Professional SaaS Boilerplate").version(pkg.version);
4685
+ program.command("dev").description("Start development server with registry watcher").option("-p, --port <port>", "Port to run the dev server on", process.env.PORT || "3000").option("--no-registry", "Disable registry watcher").action(devCommand);
3679
4686
  program.command("build").description("Build for production").option("--no-registry", "Skip registry generation before build").action(buildCommand);
3680
4687
  program.command("generate").description("Generate all registries").option("-w, --watch", "Watch for changes").action(generateCommand);
3681
4688
  var registry = program.command("registry").description("Registry management commands");
@@ -3683,12 +4690,22 @@ registry.command("build").description("Build all registries").action(registryBui
3683
4690
  registry.command("watch").description("Watch and rebuild registries on changes").action(registryWatchCommand);
3684
4691
  program.command("registry:build").description("Build all registries (alias)").action(registryBuildCommand);
3685
4692
  program.command("registry:watch").description("Watch and rebuild registries (alias)").action(registryWatchCommand);
3686
- program.command("init").description("Initialize NextSpark project").option("-f, --force", "Overwrite existing configuration").option("--wizard", "Run full project wizard").option("--quick", "Quick wizard mode (essential steps only)").option("--expert", "Expert wizard mode (all options)").option("--preset <name>", "Use preset configuration (saas, blog, crm)").option("--theme <name>", "Pre-select theme (default, blog, crm, productivity, none)").option("--plugins <list>", "Pre-select plugins (comma-separated)").option("-y, --yes", "Skip confirmations").option("--registries-only", "Only create registries (no wizard)").option("--name <name>", "Project name (non-interactive mode)").option("--slug <slug>", "Project slug (non-interactive mode)").option("--description <desc>", "Project description (non-interactive mode)").action(initCommand);
4693
+ program.command("init").description("Initialize NextSpark project").option("-f, --force", "Overwrite existing configuration").option("--wizard", "Run full project wizard").option("--quick", "Quick wizard mode (essential steps only)").option("--expert", "Expert wizard mode (all options)").option("--preset <name>", "Use preset configuration (saas, blog, crm)").option("--theme <name>", "Pre-select theme (default, blog, crm, productivity, none)").option("--plugins <list>", "Pre-select plugins (comma-separated)").option("-y, --yes", "Skip confirmations").option("--registries-only", "Only create registries (no wizard)").option("--name <name>", "Project name (non-interactive mode)").option("--slug <slug>", "Project slug (non-interactive mode)").option("--description <desc>", "Project description (non-interactive mode)").option("--type <type>", "Project type: web or web-mobile (non-interactive mode)").action(initCommand);
3687
4694
  program.command("add:plugin <package>").description("Add a plugin to your project").option("-v, --version <version>", "Specific version to install").option("-f, --force", "Overwrite if already exists").option("--skip-postinstall", "Skip postinstall hooks").option("--no-deps", "Skip installing dependencies").option("--dry-run", "Show what would be done without making changes").action(addPluginCommand);
3688
4695
  program.command("add:theme <package>").description("Add a theme to your project").option("-v, --version <version>", "Specific version to install").option("-f, --force", "Overwrite if already exists").option("--skip-postinstall", "Skip postinstall hooks").option("--no-deps", "Skip installing dependencies").option("--dry-run", "Show what would be done without making changes").action(addThemeCommand);
4696
+ program.command("add:mobile").description("Add mobile app to your project").option("-f, --force", "Overwrite if already exists").option("--skip-install", "Skip npm install").action(addMobileCommand);
3689
4697
  program.command("doctor").description("Run health check on NextSpark project").action(doctorCommand);
4698
+ var db = program.command("db").description("Database management commands");
4699
+ db.command("migrate").description("Run database migrations").action(dbMigrateCommand);
4700
+ db.command("seed").description("Seed database with sample data").action(dbSeedCommand);
4701
+ program.command("db:migrate").description("Run database migrations (alias)").action(dbMigrateCommand);
4702
+ program.command("db:seed").description("Seed database with sample data (alias)").action(dbSeedCommand);
4703
+ program.command("sync:app").description("Sync /app folder with @nextsparkjs/core templates").option("--dry-run", "Preview changes without applying").option("-f, --force", "Skip confirmation prompt").option("--backup", "Backup existing files before overwriting").option("-v, --verbose", "Show detailed file operations").action(syncAppCommand);
4704
+ program.command("setup:ai").description("Setup AI workflow for your editor (Claude Code, Cursor, Antigravity)").option("-e, --editor <editor>", "Editor to setup (claude, cursor, antigravity, all)", "claude").action(setupAICommand);
4705
+ program.command("sync:ai").description("Sync AI workflow files from @nextsparkjs/ai-workflow").option("-e, --editor <editor>", "Editor to sync (claude, cursor, antigravity, all)", "claude").option("-f, --force", "Skip confirmation prompt").action(syncAICommand);
3690
4706
  program.showHelpAfterError();
3691
4707
  program.configureOutput({
3692
- writeErr: (str) => process.stderr.write(chalk15.red(str))
4708
+ writeErr: (str) => process.stderr.write(chalk16.red(str))
3693
4709
  });
3694
4710
  program.parse();
4711
+ //# sourceMappingURL=cli.js.map