@percepta/create 3.5.0 → 3.5.2

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/index.js CHANGED
@@ -524,6 +524,15 @@ function getCompatibleTemplateVersion(manifest, templateType) {
524
524
  //#endregion
525
525
  //#region src/commands/create.ts
526
526
  const PACKAGE_MANAGER = "pnpm";
527
+ const MAX_INSTALL_OUTPUT_CHARS = 64e3;
528
+ const MAX_INSTALL_OUTPUT_LINES = 80;
529
+ var PackageManagerCommandError = class extends Error {
530
+ constructor(message, output) {
531
+ super(message);
532
+ this.output = output;
533
+ this.name = "PackageManagerCommandError";
534
+ }
535
+ };
527
536
  /** Paths in copy-paste shell commands (POSIX-style). */
528
537
  function shPath(p) {
529
538
  return p.split(path.sep).join("/");
@@ -531,17 +540,42 @@ function shPath(p) {
531
540
  /** Non-blocking install so ora can animate (execSync would block timers). */
532
541
  function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
533
542
  return new Promise((resolve, reject) => {
543
+ let output = "";
544
+ const appendOutput = (chunk) => {
545
+ output += chunk.toString();
546
+ if (output.length > MAX_INSTALL_OUTPUT_CHARS) output = output.slice(-MAX_INSTALL_OUTPUT_CHARS);
547
+ };
534
548
  const child = spawn(packageManager, args, {
535
549
  cwd,
536
- stdio: "ignore"
550
+ stdio: [
551
+ "ignore",
552
+ "pipe",
553
+ "pipe"
554
+ ]
555
+ });
556
+ child.stdout?.on("data", appendOutput);
557
+ child.stderr?.on("data", appendOutput);
558
+ child.on("error", (error) => {
559
+ reject(new PackageManagerCommandError(`${packageManager} ${args.join(" ")} failed: ${error.message}`, output));
537
560
  });
538
- child.on("error", reject);
539
561
  child.on("close", (code) => {
540
562
  if (code === 0) resolve();
541
- else reject(/* @__PURE__ */ new Error(`${packageManager} ${args.join(" ")} exited with code ${code ?? "unknown"}`));
563
+ else reject(new PackageManagerCommandError(`${packageManager} ${args.join(" ")} exited with code ${code ?? "unknown"}`, output));
542
564
  });
543
565
  });
544
566
  }
567
+ function printInstallFailureOutput(error) {
568
+ if (!(error instanceof PackageManagerCommandError)) return;
569
+ const output = error.output.trim();
570
+ if (!output) return;
571
+ const lines = output.split(/\r?\n/);
572
+ const omitted = Math.max(0, lines.length - MAX_INSTALL_OUTPUT_LINES);
573
+ const visibleLines = lines.slice(-MAX_INSTALL_OUTPUT_LINES);
574
+ console.log();
575
+ console.log(chalk.bold(`Last ${visibleLines.length} lines from pnpm install:`));
576
+ if (omitted > 0) console.log(chalk.dim(`... omitted ${omitted} earlier lines ...`));
577
+ console.log(visibleLines.join("\n"));
578
+ }
545
579
  /**
546
580
  * Runs the monorepo-root `setup` script (docker + access + db + seed).
547
581
  * Uses `pnpm run setup` (not `pnpm setup`) because `pnpm setup` is a pnpm builtin that configures
@@ -792,8 +826,9 @@ async function installAtMonorepoRoot(monorepoRoot, installDeps) {
792
826
  await runPackageManagerInstall(PACKAGE_MANAGER, monorepoRoot);
793
827
  spinner.succeed("Installed dependencies");
794
828
  return true;
795
- } catch {
829
+ } catch (error) {
796
830
  spinner.warn(`Failed to install dependencies. Run '${PACKAGE_MANAGER} install' from monorepo root.`);
831
+ printInstallFailureOutput(error);
797
832
  return false;
798
833
  }
799
834
  }
@@ -963,7 +998,7 @@ async function createProject(options) {
963
998
  console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(path.relative(monorepoRoot, packageDir)));
964
999
  console.log();
965
1000
  if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
966
- printNextStepsExisting(answers, packageDir);
1001
+ printNextStepsExisting(answers, packageDir, !installSucceeded);
967
1002
  } else {
968
1003
  const isBareMonorepo = answers.projectType === "monorepo";
969
1004
  const monorepoRoot = answers.directory;
@@ -1007,7 +1042,7 @@ async function createProject(options) {
1007
1042
  if (!isBareMonorepo) console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(`packages/${answers.name}/`));
1008
1043
  console.log();
1009
1044
  if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
1010
- printNextStepsNew(answers, monorepoRoot);
1045
+ printNextStepsNew(answers, monorepoRoot, !installSucceeded);
1011
1046
  }
1012
1047
  }
1013
1048
  async function resolveCreateCwd(cwdOption) {
@@ -1026,7 +1061,7 @@ async function resolveCreateCwd(cwdOption) {
1026
1061
  return cwd;
1027
1062
  }
1028
1063
  function printWebappNextSteps(params) {
1029
- const { pm, answers, variant, monorepoRelativePath, packageRelativePathFromRoot } = params;
1064
+ const { pm, answers, variant, installNeeded, monorepoRelativePath, packageRelativePathFromRoot } = params;
1030
1065
  const repoRel = shPath(monorepoRelativePath) || ".";
1031
1066
  const pkgFromRoot = shPath(packageRelativePathFromRoot ?? `packages/${answers.name}`) || ".";
1032
1067
  const setupStep = "pnpm run setup";
@@ -1034,7 +1069,7 @@ function printWebappNextSteps(params) {
1034
1069
  if (variant === "new") {
1035
1070
  const oneLinerParts = [];
1036
1071
  if (repoRel !== ".") oneLinerParts.push(`cd ${repoRel}`);
1037
- if (!answers.installDeps) oneLinerParts.push(`${pm} install`);
1072
+ if (installNeeded) oneLinerParts.push(`${pm} install`);
1038
1073
  oneLinerParts.push(setupStep);
1039
1074
  oneLinerParts.push(`cd ${pkgFromRoot}`);
1040
1075
  oneLinerParts.push(devStep);
@@ -1046,7 +1081,7 @@ function printWebappNextSteps(params) {
1046
1081
  console.log();
1047
1082
  let step = 1;
1048
1083
  if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1049
- if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1084
+ if (installNeeded) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1050
1085
  console.log(chalk.dim(` ${step++}.`), setupStep);
1051
1086
  console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);
1052
1087
  console.log(chalk.dim(` ${step++}.`), devStep);
@@ -1054,7 +1089,7 @@ function printWebappNextSteps(params) {
1054
1089
  }
1055
1090
  const oneLinerParts = [];
1056
1091
  if (repoRel !== ".") oneLinerParts.push(`cd ${repoRel}`);
1057
- if (!answers.installDeps) oneLinerParts.push(`${pm} install`);
1092
+ if (installNeeded) oneLinerParts.push(`${pm} install`);
1058
1093
  oneLinerParts.push(setupStep, `cd ${pkgFromRoot}`, devStep);
1059
1094
  console.log(chalk.bold("Copy-paste (from your current directory):"));
1060
1095
  console.log();
@@ -1064,7 +1099,7 @@ function printWebappNextSteps(params) {
1064
1099
  console.log();
1065
1100
  let step = 1;
1066
1101
  if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1067
- if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1102
+ if (installNeeded) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1068
1103
  console.log(chalk.dim(` ${step++}.`), setupStep);
1069
1104
  console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);
1070
1105
  console.log(chalk.dim(` ${step++}.`), devStep);
@@ -1083,7 +1118,7 @@ async function warnIfMissingRootNpmrc(rootDir) {
1083
1118
  console.log(chalk.cyan(" //registry.npmjs.org/:_authToken=${NPM_TOKEN}"));
1084
1119
  console.log();
1085
1120
  }
1086
- function printNextStepsNew(answers, targetDir) {
1121
+ function printNextStepsNew(answers, targetDir, installNeeded) {
1087
1122
  const pm = PACKAGE_MANAGER;
1088
1123
  const relativePath = path.relative(process.cwd(), targetDir) || ".";
1089
1124
  console.log("Next steps:");
@@ -1093,7 +1128,7 @@ function printNextStepsNew(answers, targetDir) {
1093
1128
  let step = 1;
1094
1129
  const repoRel = shPath(relativePath);
1095
1130
  if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1096
- if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1131
+ if (installNeeded) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1097
1132
  console.log(chalk.dim(` ${step++}.`), `Add a package: ${chalk.cyan(`pnpm mosaic add webapp <name>`)}`);
1098
1133
  break;
1099
1134
  }
@@ -1102,6 +1137,7 @@ function printNextStepsNew(answers, targetDir) {
1102
1137
  pm,
1103
1138
  answers,
1104
1139
  variant: "new",
1140
+ installNeeded,
1105
1141
  monorepoRelativePath: relativePath
1106
1142
  });
1107
1143
  break;
@@ -1109,7 +1145,7 @@ function printNextStepsNew(answers, targetDir) {
1109
1145
  let step = 1;
1110
1146
  const repoRel = shPath(relativePath);
1111
1147
  if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1112
- if (!answers.installDeps) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1148
+ if (installNeeded) console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1113
1149
  console.log(chalk.dim(` ${step++}.`), `cd packages/${answers.name}`);
1114
1150
  console.log(chalk.dim(` ${step++}.`), `${pm} dev`);
1115
1151
  console.log(chalk.dim(` ${step++}.`), `Edit src/index.ts to add your library code`);
@@ -1120,7 +1156,7 @@ function printNextStepsNew(answers, targetDir) {
1120
1156
  console.log(chalk.dim("For more information, see the README.md in your project."));
1121
1157
  console.log();
1122
1158
  }
1123
- function printNextStepsExisting(answers, packageDir) {
1159
+ function printNextStepsExisting(answers, packageDir, installNeeded) {
1124
1160
  const pm = PACKAGE_MANAGER;
1125
1161
  const packageRelativePath = path.relative(process.cwd(), packageDir) || ".";
1126
1162
  const monorepoRoot = path.dirname(path.dirname(packageDir));
@@ -1134,14 +1170,22 @@ function printNextStepsExisting(answers, packageDir) {
1134
1170
  pm,
1135
1171
  answers,
1136
1172
  variant: "existing",
1173
+ installNeeded,
1137
1174
  monorepoRelativePath,
1138
1175
  packageRelativePathFromRoot
1139
1176
  });
1140
1177
  break;
1141
1178
  case "library": {
1142
1179
  let step = 1;
1143
- const pkgRel = shPath(packageRelativePath);
1144
- if (pkgRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${pkgRel}`);
1180
+ if (installNeeded) {
1181
+ const repoRel = shPath(monorepoRelativePath);
1182
+ if (repoRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);
1183
+ console.log(chalk.dim(` ${step++}.`), `${pm} install`);
1184
+ console.log(chalk.dim(` ${step++}.`), `cd ${shPath(packageRelativePathFromRoot)}`);
1185
+ } else {
1186
+ const pkgRel = shPath(packageRelativePath);
1187
+ if (pkgRel !== ".") console.log(chalk.dim(` ${step++}.`), `cd ${pkgRel}`);
1188
+ }
1145
1189
  console.log(chalk.dim(` ${step++}.`), `${pm} dev`);
1146
1190
  console.log(chalk.dim(` ${step++}.`), "Edit src/index.ts to add your library code");
1147
1191
  break;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["SKIP_DIRS","SKIP_FILES","exhaustiveCheck"],"sources":["../src/utils/case-converters.ts","../src/utils/copy-template.ts","../src/utils/detect-monorepo.ts","../src/utils/env-local.ts","../src/utils/manifest.ts","../src/utils/package-metadata.ts","../src/utils/validate.ts","../src/utils/prompts.ts","../src/utils/relocate-workflows.ts","../src/utils/replace-placeholders.ts","../src/utils/template-versions.ts","../src/utils/workspace-manifest.ts","../src/commands/create.ts","../src/index.ts"],"sourcesContent":["/** Lowercase, hyphenated, npm-package-name-safe form: \"My Cool App\" → \"my-cool-app\". */\nexport function toKebabCase(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Display form derived from a kebab-case name: \"my-cool-app\" → \"My Cool App\". */\nexport function toTitleCase(str: string): string {\n return str\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/** Identifier form for env vars and DB names: \"my-cool-app\" → \"my_cool_app\". */\nexport function toSnakeCase(str: string): string {\n return str.replace(/-/g, \"_\");\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\n// Template type includes \"monorepo\" for internal use even though\n// it's no longer a user-facing choice\nexport type TemplateType = \"monorepo\" | \"webapp\" | \"library\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Directories to skip during copy\nconst SKIP_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \".turbo\",\n \".vercel\",\n \".cursor\",\n]);\n\n// Files to skip during copy\nconst SKIP_FILES = new Set([\n \"pnpm-lock.yaml\",\n \"package-lock.json\",\n \"yarn.lock\",\n \".DS_Store\",\n]);\n\n// Files that have .template extension that should be renamed\nconst TEMPLATE_FILE_MAPPINGS: Record<string, string> = {\n \"package.json.template\": \"package.json\",\n \"gitignore.template\": \".gitignore\",\n \"env.example.template\": \".env.example\",\n \"npmrc.template\": \".npmrc\",\n};\n\nfunction shouldSkip(src: string): boolean {\n const basename = path.basename(src);\n\n if (SKIP_DIRS.has(basename)) return true;\n if (SKIP_FILES.has(basename)) return true;\n\n return false;\n}\n\nfunction getTemplateDir(templateType: TemplateType): string {\n // Templates are located relative to the bundled output at ../templates/<type>\n // When bundled, __dirname is dist/, so we go up one level to find templates/\n return path.resolve(__dirname, \"../templates\", templateType);\n}\n\nexport async function copyTemplate(\n targetDir: string,\n templateType: TemplateType,\n): Promise<void> {\n const templateDir = getTemplateDir(templateType);\n\n // Ensure template directory exists\n if (!(await fs.pathExists(templateDir))) {\n throw new Error(`Template directory not found: ${templateDir}`);\n }\n\n // Create target directory\n await fs.ensureDir(targetDir);\n\n // Copy template with filtering\n await fs.copy(templateDir, targetDir, {\n filter: (src) => !shouldSkip(src),\n });\n\n // Rename .template files\n for (const [templateName, targetName] of Object.entries(\n TEMPLATE_FILE_MAPPINGS,\n )) {\n const templatePath = path.join(targetDir, templateName);\n const targetPath = path.join(targetDir, targetName);\n\n if (await fs.pathExists(templatePath)) {\n await fs.move(templatePath, targetPath, { overwrite: true });\n }\n }\n\n // Recreate the Claude symlink after copy because published packages\n // cannot rely on template symlinks being preserved.\n if (templateType === \"webapp\") {\n const agentsPath = path.join(targetDir, \"AGENTS.md\");\n const claudePath = path.join(targetDir, \"CLAUDE.md\");\n\n if (await fs.pathExists(agentsPath)) {\n if (await fs.pathExists(claudePath)) {\n await fs.remove(claudePath);\n }\n await fs.ensureSymlink(\"AGENTS.md\", claudePath);\n }\n }\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport { parse } from \"yaml\";\n\nexport interface MonorepoContext {\n found: boolean;\n rootDir: string | null;\n workspacePatterns: string[];\n packageDir: string | null; // e.g., \"/abs/path/to/monorepo/packages\"\n}\n\nconst NOT_FOUND: MonorepoContext = {\n found: false,\n rootDir: null,\n workspacePatterns: [],\n packageDir: null,\n};\n\n/**\n * Walk up from `startDir` looking for pnpm-workspace.yaml.\n * If found, parse it and derive the package directory.\n */\nexport async function detectMonorepo(\n startDir: string,\n): Promise<MonorepoContext> {\n let current = path.resolve(startDir);\n const root = path.parse(current).root;\n\n while (current !== root) {\n const workspaceFile = path.join(current, \"pnpm-workspace.yaml\");\n\n if (await fs.pathExists(workspaceFile)) {\n try {\n const content = await fs.readFile(workspaceFile, \"utf-8\");\n const parsed = parse(content) as { packages?: string[] } | null;\n\n if (!parsed?.packages || !Array.isArray(parsed.packages)) {\n return NOT_FOUND;\n }\n\n const workspacePatterns = parsed.packages;\n\n // Derive package directory from the first pattern\n // e.g., \"packages/*\" → \"packages\"\n const firstPattern = workspacePatterns[0];\n if (!firstPattern) {\n return NOT_FOUND;\n }\n\n const baseDir = firstPattern.replace(/\\/?\\*.*$/, \"\").trim();\n const packageDir = path.join(current, baseDir);\n\n return {\n found: true,\n rootDir: current,\n workspacePatterns,\n packageDir,\n };\n } catch {\n return NOT_FOUND;\n }\n }\n\n current = path.dirname(current);\n }\n\n return NOT_FOUND;\n}\n","import { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport fs from \"fs-extra\";\n\n/**\n * Writes .env.local for the webapp template with real generated values for\n * BETTER_AUTH_SECRET and ENCRYPTION_SECRET_KEY so the user can run dev/seed\n * immediately. Also writes deploy/ryvn/percepta-test.secrets.env with separate\n * generated values that can be imported into Ryvn for the deployed installation.\n * The .env.example file remains the documentation source.\n *\n * Each generated file is a no-op if it already exists.\n */\nexport async function generateEnvLocal(packageDir: string): Promise<void> {\n const examplePath = path.join(packageDir, \".env.example\");\n const localPath = path.join(packageDir, \".env.local\");\n if (!(await fs.pathExists(examplePath))) return;\n\n if (!(await fs.pathExists(localPath))) {\n const authSecret = randomBytes(32).toString(\"base64\");\n const encKey = randomBytes(16).toString(\"hex\");\n\n const content = (await fs.readFile(examplePath, \"utf-8\"))\n .replace(/^BETTER_AUTH_SECRET=.*$/m, `BETTER_AUTH_SECRET=${authSecret}`)\n .replace(\n /^ENCRYPTION_SECRET_KEY=.*$/m,\n `ENCRYPTION_SECRET_KEY=${encKey}`,\n );\n\n await fs.writeFile(localPath, content);\n }\n\n const ryvnSecretsPath = path.join(\n packageDir,\n \"deploy\",\n \"ryvn\",\n \"percepta-test.secrets.env\",\n );\n if (await fs.pathExists(ryvnSecretsPath)) return;\n\n const deployAuthSecret = randomBytes(32).toString(\"base64\");\n const deployEncKey = randomBytes(16).toString(\"hex\");\n const deploySecrets = [\n `BETTER_AUTH_SECRET=${deployAuthSecret}`,\n `ENCRYPTION_SECRET_KEY=${deployEncKey}`,\n \"\",\n \"# Langfuse and LLM demo credentials are inherited from the demos-commons Ryvn variable group.\",\n \"\",\n ].join(\"\\n\");\n\n await fs.ensureDir(path.dirname(ryvnSecretsPath));\n await fs.writeFile(ryvnSecretsPath, deploySecrets);\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport interface MosaicManifest {\n templateType: string;\n templateVersion: string;\n templateCommit: string;\n createdAt: string;\n lastSyncedAt?: string;\n placeholders: Record<string, string>;\n source: {\n templatePath: string;\n };\n}\n\nconst MANIFEST_FILENAME = \".mosaic-template.json\";\n\nexport function getManifestPath(dir: string): string {\n return path.join(dir, MANIFEST_FILENAME);\n}\n\nexport async function readManifest(dir: string): Promise<MosaicManifest> {\n const manifestPath = getManifestPath(dir);\n if (!(await fs.pathExists(manifestPath))) {\n throw new Error(\n `No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`,\n );\n }\n const content = await fs.readFile(manifestPath, \"utf-8\");\n try {\n return JSON.parse(content) as MosaicManifest;\n } catch (error) {\n throw new Error(\n `Invalid JSON in ${MANIFEST_FILENAME}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeManifest(\n dir: string,\n manifest: MosaicManifest,\n): Promise<void> {\n const manifestPath = getManifestPath(dir);\n await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n}\n\nexport async function manifestExists(dir: string): Promise<boolean> {\n return fs.pathExists(getManifestPath(dir));\n}\n\nexport function derivePlaceholders(\n appName: string,\n appTitle: string,\n repoName = appName,\n): Record<string, string> {\n const nameSnake = appName.replace(/-/g, \"_\");\n const repoNameSnake = repoName.replace(/-/g, \"_\");\n return {\n __APP_NAME__: appName,\n __APP_TITLE__: appTitle,\n __DB_NAME__: nameSnake + \"_db\",\n __APP_NAME_UPPER__: appName.toUpperCase(),\n __APP_NAME_SNAKE__: nameSnake,\n __REPO_NAME__: repoName,\n __REPO_NAME_SNAKE__: repoNameSnake,\n };\n}\n\nexport function resolveMosaicTemplatePath(options: {\n mosaicTemplatePath?: string;\n}): string {\n if (options.mosaicTemplatePath)\n return path.resolve(options.mosaicTemplatePath);\n if (process.env.MOSAIC_TEMPLATE_PATH)\n return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);\n throw new Error(\n \"Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.\",\n );\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\nexport interface CreatePackageMetadata {\n name: string;\n version: string;\n}\n\nconst FALLBACK_METADATA: CreatePackageMetadata = {\n name: \"@percepta/create\",\n version: \"0.0.0\",\n};\n\nexport function readCreatePackageMetadata(): CreatePackageMetadata {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../package.json\"),\n path.resolve(currentDir, \"../../package.json\"),\n ];\n\n for (const packageJsonPath of candidates) {\n try {\n const pkg = fs.readJsonSync(packageJsonPath) as {\n name?: unknown;\n version?: unknown;\n };\n if (typeof pkg.name === \"string\" && typeof pkg.version === \"string\") {\n return { name: pkg.name, version: pkg.version };\n }\n } catch {\n // Source tests and bundled CLI resolve from different directories.\n }\n }\n\n return FALLBACK_METADATA;\n}\n","import validateNpmPackageName from \"validate-npm-package-name\";\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const result = validateNpmPackageName(name);\n\n if (!result.validForNewPackages) {\n const errors = [...(result.errors || []), ...(result.warnings || [])];\n return {\n valid: false,\n error: errors[0] || \"Invalid package name\",\n };\n }\n\n return { valid: true };\n}\n","import path from \"node:path\";\nimport inquirer from \"inquirer\";\nimport { toKebabCase, toTitleCase } from \"./case-converters.js\";\nimport type { MonorepoContext } from \"./detect-monorepo.js\";\nimport { validateProjectName } from \"./validate.js\";\n\nexport const VALID_PROJECT_TYPES = [\"monorepo\", \"webapp\", \"library\"] as const;\nexport type ProjectType = (typeof VALID_PROJECT_TYPES)[number];\n\nexport function isValidProjectType(value: unknown): value is ProjectType {\n return (\n typeof value === \"string\" &&\n VALID_PROJECT_TYPES.includes(value as ProjectType)\n );\n}\n\nexport interface ProjectAnswers {\n projectType: ProjectType;\n directory: string;\n name: string;\n title: string;\n installDeps: boolean;\n monorepoName?: string;\n monorepoTitle?: string;\n}\n\ninterface PromptDefaults {\n projectType?: ProjectType;\n name?: string;\n repoName?: string;\n skipInstall?: boolean;\n monorepoContext?: MonorepoContext;\n cwd?: string;\n beforeNamePrompt?: (projectType: ProjectType) => void | Promise<void>;\n}\n\nasync function promptName(message: string): Promise<string> {\n // We kebab-case both inside `validate` (inquirer runs validate on the raw\n // input, before filter) and via `filter` (so the stored answer is also\n // kebab-cased). That way \"My App\" → \"my-app\" without bouncing the user with\n // a capital-letters error.\n const { name } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"name\",\n message,\n filter: toKebabCase,\n validate: (input: string) => {\n const result = validateProjectName(toKebabCase(input));\n return result.valid || result.error || \"Invalid project name\";\n },\n },\n ]);\n return name;\n}\n\n/**\n * Outside a monorepo we collect the repo name first, then ask a single\n * \"is it a webapp?\" Y/n. Yes (default) → monorepo + webapp, no → bare\n * monorepo. Library at the top level is rare enough that we leave it as a\n * `--type library` flag rather than a third prompt option. The repo name is\n * collected separately from any initial package name so a customer monorepo\n * is not forced to share its first app's name.\n */\nasync function promptOutsideMonorepoType(): Promise<ProjectType> {\n const { webapp } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"webapp\",\n message: \"Initialize with a webapp?\",\n default: true,\n },\n ]);\n return webapp ? \"webapp\" : \"monorepo\";\n}\n\n/**\n * Inside a monorepo, both webapp and library are common, so present them as\n * a numbered rawlist — user presses 1 or 2 + Enter. Defaults to webapp.\n */\nasync function promptInsideMonorepoType(): Promise<ProjectType> {\n const { projectType } = await inquirer.prompt([\n {\n type: \"rawlist\",\n name: \"projectType\",\n message: \"What kind of package?\",\n // inquirer v12 / @inquirer/rawlist v5 matches `default` against the\n // choice's `value`, not its index. `default: 0` would be a no-op.\n default: \"webapp\",\n choices: [\n { name: \"Webapp — A Next.js webapp\", value: \"webapp\" },\n { name: \"Library — A TypeScript library\", value: \"library\" },\n ],\n },\n ]);\n return projectType;\n}\n\nexport async function promptProjectDetails(\n defaults: PromptDefaults,\n): Promise<ProjectAnswers> {\n const inMonorepo = defaults.monorepoContext?.found ?? false;\n const cwd = defaults.cwd ?? process.cwd();\n\n let projectType: ProjectType;\n let finalName: string;\n if (inMonorepo) {\n // Resolve type before name so webapp preflights (notably NPM_TOKEN) can\n // fail before the user spends time naming a package.\n projectType = defaults.projectType ?? (await promptInsideMonorepoType());\n await defaults.beforeNamePrompt?.(projectType);\n finalName = defaults.name || (await promptName(\"Package name?\"));\n } else {\n const repoName =\n defaults.repoName ||\n (defaults.projectType === \"monorepo\" ? defaults.name : undefined) ||\n (await promptName(\"Repo name?\"));\n const repoTitle = toTitleCase(repoName);\n\n projectType = defaults.projectType ?? (await promptOutsideMonorepoType());\n await defaults.beforeNamePrompt?.(projectType);\n\n if (projectType === \"monorepo\") {\n finalName = repoName;\n const finalTitle = repoTitle;\n const finalDirectory = path.resolve(cwd, repoName);\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n monorepoName: repoName,\n monorepoTitle: repoTitle,\n };\n }\n\n const packageNamePrompt =\n projectType === \"webapp\" ? \"Webapp name?\" : \"Library name?\";\n finalName = defaults.name || (await promptName(packageNamePrompt));\n const finalTitle = toTitleCase(finalName);\n const finalDirectory = path.resolve(cwd, repoName);\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n monorepoName: repoName,\n monorepoTitle: repoTitle,\n };\n }\n\n // Inside a monorepo the directory is empty here — create.ts derives it from\n // the workspace pattern.\n const finalTitle = finalName ? toTitleCase(finalName) : \"\";\n const finalDirectory =\n !inMonorepo && finalName ? path.resolve(cwd, finalName) : \"\";\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n };\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\n/**\n * Moves per-app GitHub Actions workflows from the package's .github/workflows\n * directory to the monorepo root, where GitHub Actions actually picks them up.\n *\n * Only moves files whose names start with the app name (e.g.\n * `myapp-ryvn-release.yaml`). Generic workflows like `ci.yml` are left in\n * place — those are an unrelated monorepo-vs-package concern.\n *\n * Cleans up an empty `.github/workflows` (and empty parent `.github`) after\n * the move.\n */\nexport async function relocateWorkflowsToRoot(\n packageDir: string,\n monorepoRoot: string,\n appName: string,\n): Promise<void> {\n const sourceDir = path.join(packageDir, \".github\", \"workflows\");\n if (!(await fs.pathExists(sourceDir))) return;\n\n const targetDir = path.join(monorepoRoot, \".github\", \"workflows\");\n await fs.ensureDir(targetDir);\n\n const entries = await fs.readdir(sourceDir);\n for (const name of entries) {\n if (!name.startsWith(`${appName}-`)) continue;\n if (!/\\.(ya?ml)$/.test(name)) continue;\n await fs.move(path.join(sourceDir, name), path.join(targetDir, name), {\n overwrite: true,\n });\n }\n\n // Tidy up empty directories left behind in the package.\n if ((await fs.readdir(sourceDir)).length === 0) {\n await fs.rmdir(sourceDir);\n const packageGithub = path.join(packageDir, \".github\");\n if ((await fs.readdir(packageGithub)).length === 0) {\n await fs.rmdir(packageGithub);\n }\n }\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport interface AppConfig {\n name: string; // kebab-case: my-awesome-app\n title: string; // Title Case: My Awesome App\n dbName: string; // snake_case: my_awesome_app_db\n nameUpper: string; // UPPER-CASE: MY-AWESOME-APP\n nameSnake: string; // snake_case: my_awesome_app\n repoName: string; // kebab-case GitHub repo name\n repoNameSnake: string; // snake_case GitHub repo name\n createPackage: string; // package used by generated monorepo helper scripts\n createVersion: string; // exact create package version pinned by generated monorepos\n}\n\nexport interface ReplaceStats {\n processed: number;\n modified: number;\n}\n\nconst PLACEHOLDERS = {\n __APP_NAME__: \"name\",\n __APP_TITLE__: \"title\",\n __DB_NAME__: \"dbName\",\n __APP_NAME_UPPER__: \"nameUpper\",\n __APP_NAME_SNAKE__: \"nameSnake\",\n __REPO_NAME__: \"repoName\",\n __REPO_NAME_SNAKE__: \"repoNameSnake\",\n __CREATE_PACKAGE__: \"createPackage\",\n __CREATE_VERSION__: \"createVersion\",\n} as const;\n\n// Directories to skip\nconst SKIP_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \".turbo\",\n \".vercel\",\n]);\n\n// Files to skip\nconst SKIP_FILES = new Set([\n \"pnpm-lock.yaml\",\n \"package-lock.json\",\n \"yarn.lock\",\n]);\n\n// File extensions to process\nconst PROCESSABLE_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".md\",\n \".env\",\n \".sql\",\n \".tf\",\n \".tfvars\",\n \".sh\",\n \".zed\",\n \".mjs\",\n \".cjs\",\n]);\n\n// Specific filenames to process (without extensions)\nconst PROCESSABLE_FILENAMES = new Set([\n \"Dockerfile\",\n \".env.example\",\n \".env.local\",\n \"terraform.tfvars.example\",\n]);\n\nfunction shouldProcessFile(filePath: string): boolean {\n const fileName = path.basename(filePath);\n const ext = path.extname(filePath);\n\n if (SKIP_FILES.has(fileName)) return false;\n if (PROCESSABLE_FILENAMES.has(fileName)) return true;\n\n return PROCESSABLE_EXTENSIONS.has(ext);\n}\n\nasync function replaceInFile(\n filePath: string,\n config: AppConfig,\n): Promise<boolean> {\n let content: string;\n try {\n content = await fs.readFile(filePath, \"utf-8\");\n } catch {\n return false;\n }\n\n let modified = false;\n let newContent = content;\n\n // Sort placeholders by length descending to prevent prefix collisions\n // (e.g., __APP_NAME__ must not be replaced before __APP_NAME_UPPER__)\n const sortedEntries = Object.entries(PLACEHOLDERS).sort(\n (a, b) => b[0].length - a[0].length,\n );\n\n for (const [placeholder, configKey] of sortedEntries) {\n const value = config[configKey as keyof AppConfig];\n if (newContent.includes(placeholder)) {\n newContent = newContent.split(placeholder).join(value);\n modified = true;\n }\n }\n\n if (modified) {\n await fs.writeFile(filePath, newContent);\n return true;\n }\n\n return false;\n}\n\nfunction substituteName(name: string, config: AppConfig): string {\n // Same precedence rule as content substitution: longer placeholders first\n // so __APP_NAME_UPPER__ is replaced before __APP_NAME__.\n const sortedEntries = Object.entries(PLACEHOLDERS).sort(\n (a, b) => b[0].length - a[0].length,\n );\n let result = name;\n for (const [placeholder, configKey] of sortedEntries) {\n if (result.includes(placeholder)) {\n result = result\n .split(placeholder)\n .join(config[configKey as keyof AppConfig]);\n }\n }\n return result;\n}\n\nasync function processDirectory(\n dirPath: string,\n config: AppConfig,\n stats: ReplaceStats,\n): Promise<void> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n if (!SKIP_DIRS.has(entry.name)) {\n await processDirectory(fullPath, config, stats);\n }\n } else if (entry.isFile() && shouldProcessFile(fullPath)) {\n stats.processed++;\n if (await replaceInFile(fullPath, config)) {\n stats.modified++;\n }\n const renamed = substituteName(entry.name, config);\n if (renamed !== entry.name) {\n await fs.move(fullPath, path.join(dirPath, renamed), {\n overwrite: true,\n });\n }\n }\n }\n}\n\nexport async function replacePlaceholders(\n targetDir: string,\n config: AppConfig,\n): Promise<ReplaceStats> {\n const stats: ReplaceStats = { processed: 0, modified: 0 };\n await processDirectory(targetDir, config, stats);\n return stats;\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\nconst FALLBACK_TEMPLATE_VERSION = \"1.0.0\";\n\nexport function readTemplateVersions(): Record<string, string> {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../template-versions.json\"),\n path.resolve(currentDir, \"../../template-versions.json\"),\n ];\n\n for (const versionsPath of candidates) {\n try {\n const content = fs.readFileSync(versionsPath, \"utf-8\");\n return JSON.parse(content);\n } catch {\n // Try the next path. Source tests and bundled CLI resolve differently.\n }\n }\n\n return {};\n}\n\nexport function getTemplateVersion(templateType: string): string {\n return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport { readCreatePackageMetadata } from \"./package-metadata.js\";\nimport { getTemplateVersion } from \"./template-versions.js\";\n\nexport const WORKSPACE_MANIFEST_FILENAME = \".mosaic-workspace.json\";\nexport const WORKSPACE_MANIFEST_SCHEMA_VERSION = 1;\n\nexport interface MosaicWorkspaceManifest {\n schemaVersion: typeof WORKSPACE_MANIFEST_SCHEMA_VERSION;\n createPackage: string;\n createVersion: string;\n monorepoTemplateVersion: string;\n compatibleTemplates: Record<string, string>;\n createdAt: string;\n}\n\nexport function getWorkspaceManifestPath(rootDir: string): string {\n return path.join(rootDir, WORKSPACE_MANIFEST_FILENAME);\n}\n\nexport function createWorkspaceManifest(\n createdAt = new Date().toISOString(),\n): MosaicWorkspaceManifest {\n const createPackage = readCreatePackageMetadata();\n return {\n schemaVersion: WORKSPACE_MANIFEST_SCHEMA_VERSION,\n createPackage: createPackage.name,\n createVersion: createPackage.version,\n monorepoTemplateVersion: getTemplateVersion(\"monorepo\"),\n compatibleTemplates: {\n webapp: getTemplateVersion(\"webapp\"),\n library: getTemplateVersion(\"library\"),\n },\n createdAt,\n };\n}\n\nexport async function readWorkspaceManifest(\n rootDir: string,\n): Promise<MosaicWorkspaceManifest | null> {\n const manifestPath = getWorkspaceManifestPath(rootDir);\n if (!(await fs.pathExists(manifestPath))) return null;\n\n const content = await fs.readFile(manifestPath, \"utf-8\");\n try {\n return JSON.parse(content) as MosaicWorkspaceManifest;\n } catch (error) {\n throw new Error(\n `Invalid JSON in ${WORKSPACE_MANIFEST_FILENAME}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeWorkspaceManifest(\n rootDir: string,\n manifest: MosaicWorkspaceManifest,\n): Promise<void> {\n const manifestPath = getWorkspaceManifestPath(rootDir);\n await fs.writeJson(manifestPath, manifest, { spaces: 2 });\n await fs.appendFile(manifestPath, \"\\n\");\n}\n\nexport function getCompatibleTemplateVersion(\n manifest: MosaicWorkspaceManifest | null,\n templateType: string,\n): string {\n return (\n manifest?.compatibleTemplates[templateType] ??\n getTemplateVersion(templateType)\n );\n}\n","import { execSync, spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport ora from \"ora\";\nimport {\n toKebabCase,\n toTitleCase,\n toSnakeCase,\n} from \"../utils/case-converters.js\";\nimport { copyTemplate, type TemplateType } from \"../utils/copy-template.js\";\nimport { detectMonorepo } from \"../utils/detect-monorepo.js\";\nimport { generateEnvLocal } from \"../utils/env-local.js\";\nimport {\n writeManifest,\n derivePlaceholders,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\nimport { readCreatePackageMetadata } from \"../utils/package-metadata.js\";\nimport {\n promptProjectDetails,\n type ProjectAnswers,\n type ProjectType,\n isValidProjectType,\n VALID_PROJECT_TYPES,\n} from \"../utils/prompts.js\";\nimport { relocateWorkflowsToRoot } from \"../utils/relocate-workflows.js\";\nimport {\n replacePlaceholders,\n type AppConfig,\n} from \"../utils/replace-placeholders.js\";\nimport { getTemplateVersion } from \"../utils/template-versions.js\";\nimport { validateProjectName } from \"../utils/validate.js\";\nimport {\n createWorkspaceManifest,\n getCompatibleTemplateVersion,\n readWorkspaceManifest,\n writeWorkspaceManifest,\n type MosaicWorkspaceManifest,\n} from \"../utils/workspace-manifest.js\";\n\nexport interface CreateOptions {\n type?: string;\n name?: string;\n repoName?: string;\n cwd?: string;\n skipInstall: boolean;\n yes: boolean;\n addOnly?: boolean;\n}\n\n// The webapp template enforces pnpm via `only-allow pnpm`; the monorepo and\n// library templates also assume pnpm. Hardcoding it removes a layer of\n// configuration that didn't reflect any real flexibility.\nconst PACKAGE_MANAGER = \"pnpm\";\n\n/** Paths in copy-paste shell commands (POSIX-style). */\nfunction shPath(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Non-blocking install so ora can animate (execSync would block timers). */\nfunction runPackageManagerInstall(\n packageManager: string,\n cwd: string,\n args: string[] = [\"install\"],\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(packageManager, args, {\n cwd,\n stdio: \"ignore\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `${packageManager} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n ),\n );\n });\n });\n}\n\n/**\n * Runs the monorepo-root `setup` script (docker + access + db + seed).\n * Uses `pnpm run setup` (not `pnpm setup`) because `pnpm setup` is a pnpm builtin that configures\n * PNPM_HOME in the user's shell rc — it ignores the package.json script of the same name.\n */\nfunction runWebappSetup(\n packageManager: string,\n monorepoRoot: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(packageManager, [\"run\", \"setup\"], {\n cwd: monorepoRoot,\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `${packageManager} run setup exited with code ${code ?? \"unknown\"}`,\n ),\n );\n });\n });\n}\n\n/**\n * Spawns `pnpm dev` in the package directory and resolves once Next reports\n * \"Ready in\" (so we know the server is accepting requests). Returns the child\n * process so the caller can await its exit when the user hits Ctrl+C.\n *\n * Captures the actual URL Next picked (Next falls back to 3001+ if 3000 is\n * taken) so the caller can open the right one in the browser.\n */\n// ANSI CSI sequences (color/control codes Next emits to TTY-detected stdout).\n// eslint-disable-next-line no-control-regex\nconst ANSI_PATTERN = /\\u001b\\[[0-9;]*[a-zA-Z]/g;\n\nfunction spawnDevServer(\n packageManager: string,\n cwd: string,\n): { child: ReturnType<typeof spawn>; ready: Promise<{ url: string }> } {\n // Use `run dev` rather than `<pm> dev` because npm has no `dev` shorthand\n // (it only aliases test/start/stop/restart). `run <script>` works uniformly\n // across pnpm/npm/yarn — same reason runWebappSetup uses `run setup`.\n const child = spawn(packageManager, [\"run\", \"dev\"], {\n cwd,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n });\n\n const ready = new Promise<{ url: string }>((resolve, reject) => {\n let resolved = false;\n let detectedUrl = \"http://localhost:3000\";\n // Pipe chunks aren't line-aligned, so accumulate stdout into a rolling\n // buffer and re-scan after each chunk. We trim aggressively to avoid\n // unbounded growth on a server that runs for hours.\n let buffer = \"\";\n const onChunk = (chunk: Buffer) => {\n const text = chunk.toString();\n process.stdout.write(text);\n buffer = (buffer + text).slice(-4096).replace(ANSI_PATTERN, \"\");\n const urlMatch = buffer.match(/Local:\\s+(https?:\\/\\/\\S+?)(?:\\s|$)/i);\n if (urlMatch?.[1]) detectedUrl = urlMatch[1].trim();\n if (!resolved && /Ready in /i.test(buffer)) {\n resolved = true;\n resolve({ url: detectedUrl });\n }\n };\n child.stdout?.on(\"data\", onChunk);\n child.stderr?.on(\"data\", (chunk) => process.stderr.write(chunk));\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (!resolved) {\n reject(\n new Error(\n `${packageManager} run dev exited with code ${code ?? \"unknown\"} before becoming ready`,\n ),\n );\n }\n });\n });\n\n return { child, ready };\n}\n\n/**\n * Cross-platform \"open this URL in the user's default browser\". Returns true\n * if the launcher process spawned, false on synchronous failure. The empty\n * `error` listener swallows the asynchronous error event that fires when the\n * launcher binary is missing (e.g. minimal Linux without `xdg-open`) — without\n * it, that event becomes an uncaught exception that kills the CLI mid-run.\n */\nfunction openInBrowser(url: string): boolean {\n const cmd =\n process.platform === \"darwin\"\n ? [\"open\", url]\n : process.platform === \"win32\"\n ? [\"cmd\", \"/c\", \"start\", \"\", url]\n : [\"xdg-open\", url];\n try {\n const child = spawn(cmd[0]!, cmd.slice(1), {\n stdio: \"ignore\",\n detached: true,\n });\n child.on(\"error\", () => {});\n child.unref();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Post-scaffold orchestration for webapps: run root setup (docker + access + db + seed),\n * start the dev server, open the served URL in the user's browser, then hand\n * control to the dev server until the user exits with Ctrl+C.\n *\n * Returns true if the dev server got running (caller should NOT print next\n * steps in that case — the user is already in the running app). Returns\n * false on any failure so the caller can print manual fallback steps.\n *\n * Callers should only invoke this when install actually succeeded —\n * starting setup without node_modules will leave an orphan Docker container.\n */\nasync function autoRunWebapp(\n packageDir: string,\n monorepoRoot: string,\n): Promise<boolean> {\n const packageManager = PACKAGE_MANAGER;\n\n console.log();\n console.log(chalk.bold(\"Running setup (docker, access, db, seed)...\"));\n console.log();\n try {\n await runWebappSetup(packageManager, monorepoRoot);\n } catch (error) {\n console.log();\n console.log(\n chalk.yellow(\"!\"),\n \"Setup failed. You can re-run it manually:\",\n chalk.cyan(`cd ${monorepoRoot} && ${packageManager} run setup`),\n );\n console.log(chalk.dim((error as Error).message));\n return false;\n }\n\n console.log();\n console.log(chalk.bold(\"Starting dev server...\"));\n console.log();\n const { child, ready } = spawnDevServer(packageManager, packageDir);\n\n // Register the \"dev server has exited\" listener BEFORE we await `ready`.\n // If the child exits during that await and we attached the listener\n // afterwards, we'd miss the event (Node EventEmitter doesn't replay past\n // events) and hang forever on the final `await closed`.\n const closed = new Promise<void>((resolve) => {\n child.on(\"close\", () => resolve());\n });\n\n let url = \"http://localhost:3000\";\n try {\n ({ url } = await ready);\n } catch (error) {\n console.log();\n console.log(chalk.yellow(\"!\"), \"Dev server failed to become ready.\");\n console.log(chalk.dim((error as Error).message));\n return false;\n }\n\n if (openInBrowser(url)) {\n console.log();\n console.log(chalk.green(\"✔\"), \"Opened\", chalk.cyan(url));\n } else {\n console.log();\n console.log(\n chalk.dim(\"Open\"),\n chalk.cyan(url),\n chalk.dim(\"in your browser.\"),\n );\n }\n\n // Hand control to the dev server until the user exits.\n await closed;\n return true;\n}\n\nasync function writeMosaicFiles(\n packageDir: string,\n config: {\n name: string;\n title: string;\n repoName?: string;\n },\n projectType: string,\n templateVersion = getTemplateVersion(projectType),\n templateCommit = \"npm\",\n): Promise<void> {\n const manifest: MosaicManifest = {\n templateType: projectType,\n templateVersion,\n templateCommit,\n createdAt: new Date().toISOString(),\n placeholders: derivePlaceholders(\n config.name,\n config.title,\n config.repoName,\n ),\n source: {\n templatePath: `packages/blueberry/templates/${projectType}`,\n },\n };\n\n await writeManifest(packageDir, manifest);\n\n // Write starter mosaic-template-notes.md\n const notesPath = path.join(packageDir, \"mosaic-template-notes.md\");\n await fs.writeFile(\n notesPath,\n `# Mosaic Divergence Notes\\n\\nDocument intentional differences from the ${projectType} template here.\\nClaude reads this file during sync to preserve your customizations.\\n\\n## Intentional Divergences\\n\\n_None yet — freshly created from template._\\n`,\n );\n}\n\nfunction buildAppConfig(\n name: string,\n title = toTitleCase(name),\n repoName = name,\n): AppConfig {\n const createPackage = readCreatePackageMetadata();\n return {\n name,\n title,\n dbName: `${toSnakeCase(name)}_db`,\n nameUpper: name.toUpperCase(),\n nameSnake: toSnakeCase(name),\n repoName,\n repoNameSnake: toSnakeCase(repoName),\n createPackage: createPackage.name,\n createVersion: createPackage.version,\n };\n}\n\n/** Copy the monorepo template into `targetDir` and replace its placeholders. */\nasync function scaffoldMonorepo(\n targetDir: string,\n config: AppConfig,\n): Promise<void> {\n const monoSpinner = ora(\"Copying monorepo template...\").start();\n try {\n await copyTemplate(targetDir, \"monorepo\" satisfies TemplateType);\n monoSpinner.succeed(\"Copied monorepo template\");\n } catch (error) {\n monoSpinner.fail(\"Failed to copy monorepo template\");\n console.error(error);\n process.exit(1);\n }\n\n const replaceSpinner = ora(\"Replacing monorepo placeholders...\").start();\n try {\n const stats = await replacePlaceholders(targetDir, config);\n replaceSpinner.succeed(\n `Replaced placeholders in ${stats.modified} monorepo files`,\n );\n } catch (error) {\n replaceSpinner.fail(\"Failed to replace monorepo placeholders\");\n console.error(error);\n process.exit(1);\n }\n}\n\n/**\n * Add a package (webapp or library) to a monorepo: copy the template into\n * `packageDir`, replace placeholders, write the Mosaic manifest, and run the\n * webapp-only post-copy steps (.env.local, workflow relocation) when applicable.\n */\nasync function addPackageToMonorepo(args: {\n packageDir: string;\n monorepoRoot: string;\n projectType: \"webapp\" | \"library\";\n config: AppConfig;\n templateVersion: string;\n templateCommit: string;\n}): Promise<void> {\n const {\n packageDir,\n monorepoRoot,\n projectType,\n config,\n templateVersion,\n templateCommit,\n } = args;\n\n const copySpinner = ora(\"Copying package template...\").start();\n try {\n await copyTemplate(packageDir, projectType);\n copySpinner.succeed(\"Copied package template\");\n } catch (error) {\n copySpinner.fail(\"Failed to copy package template\");\n console.error(error);\n process.exit(1);\n }\n\n const replaceSpinner = ora(\"Replacing package placeholders...\").start();\n try {\n const stats = await replacePlaceholders(packageDir, config);\n replaceSpinner.succeed(\n `Replaced placeholders in ${stats.modified} package files`,\n );\n } catch (error) {\n replaceSpinner.fail(\"Failed to replace package placeholders\");\n console.error(error);\n process.exit(1);\n }\n\n await writeMosaicFiles(\n packageDir,\n config,\n projectType,\n templateVersion,\n templateCommit,\n );\n\n if (projectType === \"webapp\") {\n await generateEnvLocal(packageDir);\n await relocateWorkflowsToRoot(packageDir, monorepoRoot, config.name);\n }\n}\n\n/** Initialize a git repo at `targetDir` with an initial commit. Best-effort. */\nfunction initGitRepo(targetDir: string): void {\n const gitSpinner = ora(\"Initializing git repository...\").start();\n try {\n execSync(\"git init\", { cwd: targetDir, stdio: \"ignore\" });\n execSync(\"git add -A\", { cwd: targetDir, stdio: \"ignore\" });\n execSync('git commit -m \"Initial commit from @percepta/create\"', {\n cwd: targetDir,\n stdio: \"ignore\",\n });\n gitSpinner.succeed(\"Initialized git repository\");\n } catch {\n gitSpinner.warn(\"Failed to initialize git repository\");\n }\n}\n\n/**\n * Run `pnpm install` at the monorepo root with a spinner. Returns true if\n * install ran successfully; false if it failed or was skipped.\n */\nasync function installAtMonorepoRoot(\n monorepoRoot: string,\n installDeps: boolean,\n): Promise<boolean> {\n if (!installDeps) return false;\n const spinner = ora(\n `Installing dependencies with ${PACKAGE_MANAGER}...`,\n ).start();\n try {\n await runPackageManagerInstall(PACKAGE_MANAGER, monorepoRoot);\n spinner.succeed(\"Installed dependencies\");\n return true;\n } catch {\n spinner.warn(\n `Failed to install dependencies. Run '${PACKAGE_MANAGER} install' from monorepo root.`,\n );\n return false;\n }\n}\n\n/**\n * For webapp scaffolds with a successful install, hand off to autoRunWebapp\n * (setup → dev → open browser). No-op otherwise. Returns true if the dev\n * server actually started — caller skips manual next-steps in that case.\n *\n * Gated on `installSucceeded` because starting setup without node_modules\n * leaves an orphan Docker container.\n */\nasync function maybeAutoRunWebapp(\n packageDir: string | null,\n monorepoRoot: string,\n projectType: ProjectType,\n installSucceeded: boolean,\n): Promise<boolean> {\n if (!packageDir || projectType !== \"webapp\" || !installSucceeded)\n return false;\n return autoRunWebapp(packageDir, monorepoRoot);\n}\n\nfunction getProjectTypeLabel(projectType: ProjectType): string {\n switch (projectType) {\n case \"monorepo\":\n return \"pnpm monorepo\";\n case \"webapp\":\n return \"Next.js webapp\";\n case \"library\":\n return \"TypeScript library\";\n default: {\n const exhaustiveCheck: never = projectType;\n throw new Error(`Unknown project type: ${String(exhaustiveCheck)}`);\n }\n }\n}\n\nfunction requireNpmTokenForWebappInstall(\n projectType: ProjectType,\n installDeps: boolean,\n): void {\n // The webapp template depends on private `@percepta/*` packages whose .npmrc\n // resolves auth from `${NPM_TOKEN}`. If the env var is empty, `pnpm install`\n // dies deep in cryptic 401/404 logs. Catch it before asking for names.\n if (projectType !== \"webapp\" || !installDeps || process.env.NPM_TOKEN) {\n return;\n }\n\n console.log();\n console.error(chalk.red(\"Error: NPM_TOKEN environment variable is not set.\"));\n console.error(\n chalk.dim(\" Required to install private @percepta/* packages.\"),\n );\n console.error();\n console.error(\" 1. Grab the npm token from 1Password:\");\n console.error(\n chalk.cyan(\n \" https://start.1password.com/open/i?a=5TX2B4O3QNE4FNQ2A7ZJZDRRBI&v=j7trpyuqh7gt635dtuj6y4pwjm&i=cmmdi5trji7ctkn3fseakf4mgi&h=aitco.1password.com\",\n ),\n );\n console.error(\" 2. Add to ~/.zshrc:\");\n console.error(chalk.cyan(' export NPM_TOKEN=\"<paste-token>\"'));\n console.error(\n \" 3. Open a new terminal (or \" +\n chalk.cyan(\"source ~/.zshrc\") +\n \") and re-run.\",\n );\n console.error();\n console.error(\n chalk.dim(\" Or pass --skip-install to scaffold without running install.\"),\n );\n process.exit(1);\n}\n\nfunction ensureWorkspaceCreateVersionCompatible(\n workspaceManifest: MosaicWorkspaceManifest | null,\n projectType: ProjectType,\n): void {\n if (!workspaceManifest || projectType === \"monorepo\") return;\n\n const createPackage = readCreatePackageMetadata();\n if (\n workspaceManifest.createPackage === createPackage.name &&\n workspaceManifest.createVersion === createPackage.version\n ) {\n return;\n }\n\n console.log();\n console.error(\n chalk.red(\n `Error: This workspace is pinned to ${workspaceManifest.createPackage}@${workspaceManifest.createVersion}, ` +\n `but you are running ${createPackage.name}@${createPackage.version}.`,\n ),\n );\n console.error();\n console.error(\n chalk.dim(\n \" Run the workspace-owned command so new packages match this monorepo:\",\n ),\n );\n console.error(chalk.cyan(` pnpm mosaic add ${projectType} <name>`));\n console.error();\n console.error(\n chalk.dim(\n \" To adopt newer templates, upgrade the workspace manifest first.\",\n ),\n );\n process.exit(1);\n}\n\nfunction getTemplateCommitSource(\n workspaceManifest: MosaicWorkspaceManifest | null,\n): string {\n if (!workspaceManifest) return \"npm\";\n return `${workspaceManifest.createPackage}@${workspaceManifest.createVersion}`;\n}\n\nexport async function createProject(options: CreateOptions): Promise<void> {\n const cwd = await resolveCreateCwd(options.cwd);\n\n // Validate --type if provided\n if (options.type !== undefined && !isValidProjectType(options.type)) {\n console.error(\n chalk.red(\n `Error: Invalid package type \"${options.type}\". Valid types are: ${VALID_PROJECT_TYPES.join(\", \")}`,\n ),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Creating a new Mosaic package...\"));\n console.log();\n\n // Step 1: Detect monorepo context\n const monorepoContext = await detectMonorepo(cwd);\n\n if (options.addOnly && !monorepoContext.found) {\n console.error(\n chalk.red(\n \"Error: 'create add' must be run inside an existing pnpm monorepo.\",\n ),\n );\n process.exit(1);\n }\n\n // Bare monorepo only makes sense outside an existing monorepo.\n if (options.type === \"monorepo\" && monorepoContext.found) {\n console.error(\n chalk.red(\n `Error: Already inside a monorepo at ${monorepoContext.rootDir}. ` +\n `Choose 'webapp' or 'library' to add a package, or run from outside the monorepo.`,\n ),\n );\n process.exit(1);\n }\n\n if (monorepoContext.found) {\n console.log(\n chalk.dim(\" Detected monorepo at\"),\n chalk.cyan(monorepoContext.rootDir),\n );\n } else {\n console.log(\n chalk.dim(\" No monorepo detected. A new monorepo will be created.\"),\n );\n }\n console.log();\n\n const workspaceManifest = monorepoContext.found\n ? await readWorkspaceManifest(monorepoContext.rootDir!)\n : null;\n\n if (workspaceManifest) {\n console.log(\n chalk.dim(\" Workspace create version:\"),\n chalk.cyan(\n `${workspaceManifest.createPackage}@${workspaceManifest.createVersion}`,\n ),\n );\n console.log();\n } else if (monorepoContext.found) {\n console.log(\n chalk.yellow(\"!\"),\n \"No .mosaic-workspace.json found; using this CLI's bundled templates.\",\n );\n console.log();\n }\n\n // --name skips the package-name prompt; --repo-name skips the new-monorepo\n // repo-name prompt. Both are used by automation but are not the canonical\n // CLI UX.\n const projectName = options.name;\n const repoName = options.repoName;\n if (options.yes && !projectName) {\n console.error(chalk.red(\"Error: --name is required when using --yes flag\"));\n process.exit(1);\n }\n\n if (projectName) {\n const validation = validateProjectName(toKebabCase(projectName));\n if (!validation.valid) {\n console.error(chalk.red(`Invalid project name: ${validation.error}`));\n process.exit(1);\n }\n }\n\n if (repoName) {\n const validation = validateProjectName(toKebabCase(repoName));\n if (!validation.valid) {\n console.error(chalk.red(`Invalid repo name: ${validation.error}`));\n process.exit(1);\n }\n }\n\n // Step 2 & 3: Get project details from prompts or options\n let answers: ProjectAnswers;\n\n if (options.yes) {\n // Non-interactive mode (used by automation). Defaults: type=webapp.\n const projectType: ProjectType = (options.type as ProjectType) || \"webapp\";\n ensureWorkspaceCreateVersionCompatible(workspaceManifest, projectType);\n requireNpmTokenForWebappInstall(projectType, !options.skipInstall);\n const kebabName = toKebabCase(projectName!);\n const kebabRepoName = repoName ? toKebabCase(repoName) : kebabName;\n const directory =\n monorepoContext.found && monorepoContext.packageDir\n ? path.join(monorepoContext.packageDir, kebabName)\n : path.resolve(cwd, kebabRepoName);\n\n answers = {\n projectType,\n directory,\n name: kebabName,\n title: toTitleCase(kebabName),\n installDeps: !options.skipInstall,\n monorepoName: monorepoContext.found ? undefined : kebabRepoName,\n monorepoTitle: monorepoContext.found\n ? undefined\n : toTitleCase(kebabRepoName),\n };\n } else {\n answers = await promptProjectDetails({\n projectType: options.type as ProjectType | undefined,\n name: projectName ? toKebabCase(projectName) : undefined,\n repoName: repoName ? toKebabCase(repoName) : undefined,\n skipInstall: options.skipInstall,\n monorepoContext,\n cwd,\n beforeNamePrompt: (projectType) => {\n ensureWorkspaceCreateVersionCompatible(workspaceManifest, projectType);\n requireNpmTokenForWebappInstall(projectType, !options.skipInstall);\n },\n });\n\n // If inside monorepo, compute directory from workspace pattern + name\n if (\n monorepoContext.found &&\n monorepoContext.packageDir &&\n !answers.directory\n ) {\n answers.directory = path.join(monorepoContext.packageDir, answers.name);\n }\n }\n\n const monorepoName = answers.monorepoName ?? answers.name;\n const monorepoTitle = answers.monorepoTitle ?? toTitleCase(monorepoName);\n const monorepoConfig = buildAppConfig(monorepoName, monorepoTitle);\n const configRepoName = monorepoContext.found\n ? path.basename(monorepoContext.rootDir!)\n : monorepoName;\n const config = buildAppConfig(answers.name, answers.title, configRepoName);\n\n const typeLabel = getProjectTypeLabel(answers.projectType);\n\n if (monorepoContext.found) {\n // --- ADDING TO EXISTING MONOREPO ---\n const monorepoRoot = monorepoContext.rootDir!;\n const packageDir = monorepoContext.packageDir\n ? path.join(monorepoContext.packageDir, answers.name)\n : answers.directory;\n\n console.log(chalk.dim(\" Package type:\"), typeLabel);\n console.log(chalk.dim(\" Target:\"), packageDir);\n console.log(chalk.dim(\" Name:\"), config.name);\n console.log(chalk.dim(\" Title:\"), config.title);\n if (answers.projectType === \"webapp\") {\n console.log(chalk.dim(\" Database:\"), config.dbName);\n }\n console.log();\n\n // Check if directory already exists and is not empty\n if (await fs.pathExists(packageDir)) {\n const files = await fs.readdir(packageDir);\n if (files.length > 0) {\n console.error(\n chalk.red(`Error: Directory ${packageDir} is not empty.`),\n );\n process.exit(1);\n }\n }\n\n if (answers.projectType !== \"monorepo\") {\n await addPackageToMonorepo({\n packageDir,\n monorepoRoot,\n projectType: answers.projectType,\n config,\n templateVersion: getCompatibleTemplateVersion(\n workspaceManifest,\n answers.projectType,\n ),\n templateCommit: getTemplateCommitSource(workspaceManifest),\n });\n }\n\n // pnpm reads .npmrc from the workspace root, not package dirs. If the\n // root .npmrc is missing the @percepta registry config, downstream\n // `pnpm install` won't authenticate.\n await warnIfMissingRootNpmrc(monorepoRoot);\n\n const installSucceeded = await installAtMonorepoRoot(\n monorepoRoot,\n answers.installDeps,\n );\n\n // Success\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(`Created ${typeLabel} at`),\n chalk.cyan(path.relative(monorepoRoot, packageDir)),\n );\n console.log();\n\n const devStarted = await maybeAutoRunWebapp(\n packageDir,\n monorepoRoot,\n answers.projectType,\n installSucceeded,\n );\n if (devStarted) return;\n\n printNextStepsExisting(answers, packageDir);\n } else {\n // --- CREATING NEW MONOREPO (with optional package inside) ---\n const isBareMonorepo = answers.projectType === \"monorepo\";\n const monorepoRoot = answers.directory;\n const packageDir = isBareMonorepo\n ? null\n : path.join(monorepoRoot, \"packages\", answers.name);\n\n if (isBareMonorepo) {\n console.log(chalk.dim(\" Type:\"), typeLabel);\n console.log(chalk.dim(\" Directory:\"), monorepoRoot);\n console.log(chalk.dim(\" Repo name:\"), monorepoConfig.name);\n console.log(chalk.dim(\" Title:\"), monorepoConfig.title);\n } else {\n console.log(chalk.dim(\" Package type:\"), typeLabel);\n console.log(chalk.dim(\" Monorepo directory:\"), monorepoRoot);\n console.log(chalk.dim(\" Repo name:\"), monorepoConfig.name);\n console.log(chalk.dim(\" Package:\"), `packages/${answers.name}/`);\n console.log(chalk.dim(\" Name:\"), config.name);\n console.log(chalk.dim(\" Title:\"), config.title);\n if (answers.projectType === \"webapp\") {\n console.log(chalk.dim(\" Database:\"), config.dbName);\n }\n }\n console.log();\n\n // Check if directory exists and is not empty.\n if (await fs.pathExists(monorepoRoot)) {\n const files = await fs.readdir(monorepoRoot);\n if (files.length > 0) {\n console.error(\n chalk.red(`Error: Directory ${monorepoRoot} is not empty.`),\n );\n process.exit(1);\n }\n }\n\n await scaffoldMonorepo(monorepoRoot, monorepoConfig);\n const newWorkspaceManifest = createWorkspaceManifest();\n await writeWorkspaceManifest(monorepoRoot, newWorkspaceManifest);\n\n if (packageDir && answers.projectType !== \"monorepo\") {\n await addPackageToMonorepo({\n packageDir,\n monorepoRoot,\n projectType: answers.projectType,\n config,\n templateVersion: getCompatibleTemplateVersion(\n newWorkspaceManifest,\n answers.projectType,\n ),\n templateCommit: getTemplateCommitSource(newWorkspaceManifest),\n });\n }\n\n initGitRepo(monorepoRoot);\n\n const installSucceeded = await installAtMonorepoRoot(\n monorepoRoot,\n answers.installDeps,\n );\n\n // Success\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(\n isBareMonorepo ? `Created ${typeLabel} at` : \"Created monorepo at\",\n ),\n chalk.cyan(monorepoRoot),\n );\n if (!isBareMonorepo) {\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(`Created ${typeLabel} at`),\n chalk.cyan(`packages/${answers.name}/`),\n );\n }\n console.log();\n\n const devStarted = await maybeAutoRunWebapp(\n packageDir,\n monorepoRoot,\n answers.projectType,\n installSucceeded,\n );\n if (devStarted) return;\n\n printNextStepsNew(answers, monorepoRoot);\n }\n}\n\nasync function resolveCreateCwd(\n cwdOption: string | undefined,\n): Promise<string> {\n const cwd = cwdOption ? path.resolve(cwdOption) : process.cwd();\n let stat;\n try {\n stat = await fs.stat(cwd);\n } catch {\n console.error(chalk.red(`Error: --cwd directory does not exist: ${cwd}`));\n process.exit(1);\n }\n\n if (!stat.isDirectory()) {\n console.error(chalk.red(`Error: --cwd is not a directory: ${cwd}`));\n process.exit(1);\n }\n\n return cwd;\n}\n\nfunction printWebappNextSteps(params: {\n pm: \"pnpm\" | \"npm\" | \"yarn\";\n answers: ProjectAnswers;\n variant: \"new\" | \"existing\";\n /** Relative path from cwd to monorepo root. */\n monorepoRelativePath: string;\n /** Relative path from monorepo root to the package directory. */\n packageRelativePathFromRoot?: string;\n}): void {\n const {\n pm,\n answers,\n variant,\n monorepoRelativePath,\n packageRelativePathFromRoot,\n } = params;\n const repoRel = shPath(monorepoRelativePath) || \".\";\n const pkgFromRoot =\n shPath(packageRelativePathFromRoot ?? `packages/${answers.name}`) || \".\";\n // Use `pnpm run setup` (not `pnpm setup`) — the latter is a pnpm builtin.\n const setupStep = \"pnpm run setup\";\n const devStep = \"pnpm dev\";\n\n if (variant === \"new\") {\n const oneLinerParts: string[] = [];\n if (repoRel !== \".\") oneLinerParts.push(`cd ${repoRel}`);\n if (!answers.installDeps) oneLinerParts.push(`${pm} install`);\n oneLinerParts.push(setupStep);\n oneLinerParts.push(`cd ${pkgFromRoot}`);\n oneLinerParts.push(devStep);\n\n console.log(chalk.bold(\"Copy-paste (from your current directory):\"));\n console.log();\n console.log(chalk.cyan(` ${oneLinerParts.join(\" && \")}`));\n console.log();\n console.log(chalk.bold(\"Or step by step:\"));\n console.log();\n\n let step = 1;\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (!answers.installDeps) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), setupStep);\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);\n console.log(chalk.dim(` ${step++}.`), devStep);\n return;\n }\n\n const oneLinerParts: string[] = [];\n\n if (repoRel !== \".\") oneLinerParts.push(`cd ${repoRel}`);\n if (!answers.installDeps) {\n oneLinerParts.push(`${pm} install`);\n }\n oneLinerParts.push(setupStep, `cd ${pkgFromRoot}`, devStep);\n\n console.log(chalk.bold(\"Copy-paste (from your current directory):\"));\n console.log();\n console.log(chalk.cyan(` ${oneLinerParts.join(\" && \")}`));\n console.log();\n console.log(chalk.bold(\"Or step by step:\"));\n console.log();\n\n let step = 1;\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (!answers.installDeps) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), setupStep);\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);\n console.log(chalk.dim(` ${step++}.`), devStep);\n}\n\nasync function warnIfMissingRootNpmrc(rootDir: string): Promise<void> {\n const rootNpmrc = path.join(rootDir, \".npmrc\");\n let contents = \"\";\n if (await fs.pathExists(rootNpmrc)) {\n contents = await fs.readFile(rootNpmrc, \"utf8\");\n }\n if (contents.includes(\"@percepta:registry\")) {\n return;\n }\n\n console.log();\n console.log(\n chalk.yellow(\"!\"),\n chalk.bold(\"Root .npmrc is missing @percepta registry config.\"),\n );\n console.log(\n chalk.dim(\n \" pnpm reads .npmrc from the workspace root, so add these lines to\",\n ),\n );\n console.log(chalk.dim(` ${path.join(rootDir, \".npmrc\")}:`));\n console.log();\n console.log(chalk.cyan(\" @percepta:registry=https://registry.npmjs.org/\"));\n console.log(chalk.cyan(\" //registry.npmjs.org/:_authToken=${NPM_TOKEN}\"));\n console.log();\n}\n\nfunction printNextStepsNew(answers: ProjectAnswers, targetDir: string): void {\n const pm = PACKAGE_MANAGER;\n const relativePath = path.relative(process.cwd(), targetDir) || \".\";\n\n console.log(\"Next steps:\");\n console.log();\n\n switch (answers.projectType) {\n case \"monorepo\": {\n let step = 1;\n const repoRel = shPath(relativePath);\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (!answers.installDeps) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(\n chalk.dim(` ${step++}.`),\n `Add a package: ${chalk.cyan(`pnpm mosaic add webapp <name>`)}`,\n );\n break;\n }\n\n case \"webapp\":\n printWebappNextSteps({\n pm,\n answers,\n variant: \"new\",\n monorepoRelativePath: relativePath,\n });\n break;\n\n case \"library\": {\n let step = 1;\n const repoRel = shPath(relativePath);\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (!answers.installDeps) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), `cd packages/${answers.name}`);\n console.log(chalk.dim(` ${step++}.`), `${pm} dev`);\n console.log(\n chalk.dim(` ${step++}.`),\n `Edit src/index.ts to add your library code`,\n );\n break;\n }\n }\n\n console.log();\n console.log(\n chalk.dim(\"For more information, see the README.md in your project.\"),\n );\n console.log();\n}\n\nfunction printNextStepsExisting(\n answers: ProjectAnswers,\n packageDir: string,\n): void {\n const pm = PACKAGE_MANAGER;\n const packageRelativePath = path.relative(process.cwd(), packageDir) || \".\";\n const monorepoRoot = path.dirname(path.dirname(packageDir));\n const monorepoRelativePath =\n path.relative(process.cwd(), monorepoRoot) || \".\";\n const packageRelativePathFromRoot =\n path.relative(monorepoRoot, packageDir) || \".\";\n\n console.log(\"Next steps:\");\n console.log();\n\n switch (answers.projectType) {\n case \"webapp\":\n printWebappNextSteps({\n pm,\n answers,\n variant: \"existing\",\n monorepoRelativePath,\n packageRelativePathFromRoot,\n });\n break;\n\n case \"library\": {\n let step = 1;\n const pkgRel = shPath(packageRelativePath);\n if (pkgRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgRel}`);\n }\n console.log(chalk.dim(` ${step++}.`), `${pm} dev`);\n console.log(\n chalk.dim(` ${step++}.`),\n \"Edit src/index.ts to add your library code\",\n );\n break;\n }\n }\n\n console.log();\n console.log(\n chalk.dim(\"For more information, see the README.md in your project.\"),\n );\n console.log();\n}\n","#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { createProject } from \"./commands/create.js\";\nimport { readCreatePackageMetadata } from \"./utils/package-metadata.js\";\n\nconst packageJson = readCreatePackageMetadata();\n\nprogram\n .name(\"create\")\n .description(\"Scaffold and manage Mosaic packages\")\n .version(packageJson.version);\n\n// Default command. Intentionally bare-bones: the canonical incantation is\n// just `npx @percepta/create` with no flags — everything else comes from\n// prompts. The flags below exist for test-template.sh and other automation,\n// not for end-user use.\nprogram\n .command(\"create\", { isDefault: true })\n .description(\"Scaffold a new Mosaic package\")\n .option(\"-t, --type <type>\", \"Package type: monorepo, webapp, or library\")\n .option(\"--name <name>\", \"Package/app name\")\n .option(\"--repo-name <name>\", \"Repository name when creating a new monorepo\")\n .option(\"--cwd <dir>\", \"Run create as if started from this directory\")\n .option(\n \"--skip-install\",\n \"Skip dependency installation (also skips the auto-run setup + dev + browser)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip all prompts and use defaults\", false)\n .action(createProject);\n\nprogram\n .command(\"add\")\n .description(\"Add a Mosaic package to the current monorepo\")\n .argument(\"[typeOrName]\", \"Package type (webapp/library) or package name\")\n .argument(\"[name]\", \"Package/app name\")\n .option(\"-t, --type <type>\", \"Package type: webapp or library\")\n .option(\"--name <name>\", \"Package/app name\")\n .option(\n \"--skip-install\",\n \"Skip dependency installation (also skips the auto-run setup + dev + browser)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip all prompts and use defaults\", false)\n .action(async (typeOrName, name, options) => {\n let projectType = options.type;\n let projectName = options.name;\n\n if (typeOrName === \"webapp\" || typeOrName === \"library\") {\n projectType = projectType ?? typeOrName;\n projectName = projectName ?? name;\n } else if (typeOrName === \"monorepo\") {\n program.error(\"error: 'add' only supports webapp or library packages\");\n } else if (typeOrName) {\n if (name) {\n program.error(\n `error: unknown package type \"${typeOrName}\". Expected webapp or library.`,\n );\n }\n projectName = projectName ?? typeOrName;\n }\n\n await createProject({\n ...options,\n type: projectType,\n name: projectName,\n addOnly: true,\n });\n });\n\n// Lazy-loaded subcommands\nprogram\n .command(\"status\")\n .description(\"Show template sync status for current app\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .action(async (options) => {\n const { statusCommand } = await import(\"./commands/status.js\");\n await statusCommand(options);\n });\n\nprogram\n .command(\"sync\")\n .description(\"Generate downstream sync context (template → app)\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .option(\"--to <version>\", \"Target template version (default: latest)\")\n .action(async (options) => {\n const { syncCommand } = await import(\"./commands/sync.js\");\n await syncCommand(options);\n });\n\nprogram\n .command(\"upstream\")\n .description(\"Generate upstream context (app → template)\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .option(\"--files <patterns...>\", \"Specific files to propose upstream\")\n .action(async (options) => {\n const { upstreamCommand } = await import(\"./commands/upstream.js\");\n await upstreamCommand(options);\n });\n\nprogram\n .command(\"init\")\n .description(\"Add .mosaic-template.json to an existing app\")\n .option(\"-t, --type <type>\", \"Template type (e.g., webapp, library)\")\n .option(\"--template-version <version>\", \"Template version to set\")\n .action(async (options) => {\n const { initCommand } = await import(\"./commands/init.js\");\n await initCommand(options);\n });\n\nprogram.parse();\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["SKIP_DIRS","SKIP_FILES","exhaustiveCheck"],"sources":["../src/utils/case-converters.ts","../src/utils/copy-template.ts","../src/utils/detect-monorepo.ts","../src/utils/env-local.ts","../src/utils/manifest.ts","../src/utils/package-metadata.ts","../src/utils/validate.ts","../src/utils/prompts.ts","../src/utils/relocate-workflows.ts","../src/utils/replace-placeholders.ts","../src/utils/template-versions.ts","../src/utils/workspace-manifest.ts","../src/commands/create.ts","../src/index.ts"],"sourcesContent":["/** Lowercase, hyphenated, npm-package-name-safe form: \"My Cool App\" → \"my-cool-app\". */\nexport function toKebabCase(str: string): string {\n return str\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, \"-\")\n .replace(/-+/g, \"-\")\n .replace(/^-|-$/g, \"\");\n}\n\n/** Display form derived from a kebab-case name: \"my-cool-app\" → \"My Cool App\". */\nexport function toTitleCase(str: string): string {\n return str\n .split(\"-\")\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(\" \");\n}\n\n/** Identifier form for env vars and DB names: \"my-cool-app\" → \"my_cool_app\". */\nexport function toSnakeCase(str: string): string {\n return str.replace(/-/g, \"_\");\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\n// Template type includes \"monorepo\" for internal use even though\n// it's no longer a user-facing choice\nexport type TemplateType = \"monorepo\" | \"webapp\" | \"library\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// Directories to skip during copy\nconst SKIP_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \".turbo\",\n \".vercel\",\n \".cursor\",\n]);\n\n// Files to skip during copy\nconst SKIP_FILES = new Set([\n \"pnpm-lock.yaml\",\n \"package-lock.json\",\n \"yarn.lock\",\n \".DS_Store\",\n]);\n\n// Files that have .template extension that should be renamed\nconst TEMPLATE_FILE_MAPPINGS: Record<string, string> = {\n \"package.json.template\": \"package.json\",\n \"gitignore.template\": \".gitignore\",\n \"env.example.template\": \".env.example\",\n \"npmrc.template\": \".npmrc\",\n};\n\nfunction shouldSkip(src: string): boolean {\n const basename = path.basename(src);\n\n if (SKIP_DIRS.has(basename)) return true;\n if (SKIP_FILES.has(basename)) return true;\n\n return false;\n}\n\nfunction getTemplateDir(templateType: TemplateType): string {\n // Templates are located relative to the bundled output at ../templates/<type>\n // When bundled, __dirname is dist/, so we go up one level to find templates/\n return path.resolve(__dirname, \"../templates\", templateType);\n}\n\nexport async function copyTemplate(\n targetDir: string,\n templateType: TemplateType,\n): Promise<void> {\n const templateDir = getTemplateDir(templateType);\n\n // Ensure template directory exists\n if (!(await fs.pathExists(templateDir))) {\n throw new Error(`Template directory not found: ${templateDir}`);\n }\n\n // Create target directory\n await fs.ensureDir(targetDir);\n\n // Copy template with filtering\n await fs.copy(templateDir, targetDir, {\n filter: (src) => !shouldSkip(src),\n });\n\n // Rename .template files\n for (const [templateName, targetName] of Object.entries(\n TEMPLATE_FILE_MAPPINGS,\n )) {\n const templatePath = path.join(targetDir, templateName);\n const targetPath = path.join(targetDir, targetName);\n\n if (await fs.pathExists(templatePath)) {\n await fs.move(templatePath, targetPath, { overwrite: true });\n }\n }\n\n // Recreate the Claude symlink after copy because published packages\n // cannot rely on template symlinks being preserved.\n if (templateType === \"webapp\") {\n const agentsPath = path.join(targetDir, \"AGENTS.md\");\n const claudePath = path.join(targetDir, \"CLAUDE.md\");\n\n if (await fs.pathExists(agentsPath)) {\n if (await fs.pathExists(claudePath)) {\n await fs.remove(claudePath);\n }\n await fs.ensureSymlink(\"AGENTS.md\", claudePath);\n }\n }\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport { parse } from \"yaml\";\n\nexport interface MonorepoContext {\n found: boolean;\n rootDir: string | null;\n workspacePatterns: string[];\n packageDir: string | null; // e.g., \"/abs/path/to/monorepo/packages\"\n}\n\nconst NOT_FOUND: MonorepoContext = {\n found: false,\n rootDir: null,\n workspacePatterns: [],\n packageDir: null,\n};\n\n/**\n * Walk up from `startDir` looking for pnpm-workspace.yaml.\n * If found, parse it and derive the package directory.\n */\nexport async function detectMonorepo(\n startDir: string,\n): Promise<MonorepoContext> {\n let current = path.resolve(startDir);\n const root = path.parse(current).root;\n\n while (current !== root) {\n const workspaceFile = path.join(current, \"pnpm-workspace.yaml\");\n\n if (await fs.pathExists(workspaceFile)) {\n try {\n const content = await fs.readFile(workspaceFile, \"utf-8\");\n const parsed = parse(content) as { packages?: string[] } | null;\n\n if (!parsed?.packages || !Array.isArray(parsed.packages)) {\n return NOT_FOUND;\n }\n\n const workspacePatterns = parsed.packages;\n\n // Derive package directory from the first pattern\n // e.g., \"packages/*\" → \"packages\"\n const firstPattern = workspacePatterns[0];\n if (!firstPattern) {\n return NOT_FOUND;\n }\n\n const baseDir = firstPattern.replace(/\\/?\\*.*$/, \"\").trim();\n const packageDir = path.join(current, baseDir);\n\n return {\n found: true,\n rootDir: current,\n workspacePatterns,\n packageDir,\n };\n } catch {\n return NOT_FOUND;\n }\n }\n\n current = path.dirname(current);\n }\n\n return NOT_FOUND;\n}\n","import { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport fs from \"fs-extra\";\n\n/**\n * Writes .env.local for the webapp template with real generated values for\n * BETTER_AUTH_SECRET and ENCRYPTION_SECRET_KEY so the user can run dev/seed\n * immediately. Also writes deploy/ryvn/percepta-test.secrets.env with separate\n * generated values that can be imported into Ryvn for the deployed installation.\n * The .env.example file remains the documentation source.\n *\n * Each generated file is a no-op if it already exists.\n */\nexport async function generateEnvLocal(packageDir: string): Promise<void> {\n const examplePath = path.join(packageDir, \".env.example\");\n const localPath = path.join(packageDir, \".env.local\");\n if (!(await fs.pathExists(examplePath))) return;\n\n if (!(await fs.pathExists(localPath))) {\n const authSecret = randomBytes(32).toString(\"base64\");\n const encKey = randomBytes(16).toString(\"hex\");\n\n const content = (await fs.readFile(examplePath, \"utf-8\"))\n .replace(/^BETTER_AUTH_SECRET=.*$/m, `BETTER_AUTH_SECRET=${authSecret}`)\n .replace(\n /^ENCRYPTION_SECRET_KEY=.*$/m,\n `ENCRYPTION_SECRET_KEY=${encKey}`,\n );\n\n await fs.writeFile(localPath, content);\n }\n\n const ryvnSecretsPath = path.join(\n packageDir,\n \"deploy\",\n \"ryvn\",\n \"percepta-test.secrets.env\",\n );\n if (await fs.pathExists(ryvnSecretsPath)) return;\n\n const deployAuthSecret = randomBytes(32).toString(\"base64\");\n const deployEncKey = randomBytes(16).toString(\"hex\");\n const deploySecrets = [\n `BETTER_AUTH_SECRET=${deployAuthSecret}`,\n `ENCRYPTION_SECRET_KEY=${deployEncKey}`,\n \"\",\n \"# Langfuse and LLM demo credentials are inherited from the demos-commons Ryvn variable group.\",\n \"\",\n ].join(\"\\n\");\n\n await fs.ensureDir(path.dirname(ryvnSecretsPath));\n await fs.writeFile(ryvnSecretsPath, deploySecrets);\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport interface MosaicManifest {\n templateType: string;\n templateVersion: string;\n templateCommit: string;\n createdAt: string;\n lastSyncedAt?: string;\n placeholders: Record<string, string>;\n source: {\n templatePath: string;\n };\n}\n\nconst MANIFEST_FILENAME = \".mosaic-template.json\";\n\nexport function getManifestPath(dir: string): string {\n return path.join(dir, MANIFEST_FILENAME);\n}\n\nexport async function readManifest(dir: string): Promise<MosaicManifest> {\n const manifestPath = getManifestPath(dir);\n if (!(await fs.pathExists(manifestPath))) {\n throw new Error(\n `No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`,\n );\n }\n const content = await fs.readFile(manifestPath, \"utf-8\");\n try {\n return JSON.parse(content) as MosaicManifest;\n } catch (error) {\n throw new Error(\n `Invalid JSON in ${MANIFEST_FILENAME}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeManifest(\n dir: string,\n manifest: MosaicManifest,\n): Promise<void> {\n const manifestPath = getManifestPath(dir);\n await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + \"\\n\");\n}\n\nexport async function manifestExists(dir: string): Promise<boolean> {\n return fs.pathExists(getManifestPath(dir));\n}\n\nexport function derivePlaceholders(\n appName: string,\n appTitle: string,\n repoName = appName,\n): Record<string, string> {\n const nameSnake = appName.replace(/-/g, \"_\");\n const repoNameSnake = repoName.replace(/-/g, \"_\");\n return {\n __APP_NAME__: appName,\n __APP_TITLE__: appTitle,\n __DB_NAME__: nameSnake + \"_db\",\n __APP_NAME_UPPER__: appName.toUpperCase(),\n __APP_NAME_SNAKE__: nameSnake,\n __REPO_NAME__: repoName,\n __REPO_NAME_SNAKE__: repoNameSnake,\n };\n}\n\nexport function resolveMosaicTemplatePath(options: {\n mosaicTemplatePath?: string;\n}): string {\n if (options.mosaicTemplatePath)\n return path.resolve(options.mosaicTemplatePath);\n if (process.env.MOSAIC_TEMPLATE_PATH)\n return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);\n throw new Error(\n \"Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.\",\n );\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\nexport interface CreatePackageMetadata {\n name: string;\n version: string;\n}\n\nconst FALLBACK_METADATA: CreatePackageMetadata = {\n name: \"@percepta/create\",\n version: \"0.0.0\",\n};\n\nexport function readCreatePackageMetadata(): CreatePackageMetadata {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../package.json\"),\n path.resolve(currentDir, \"../../package.json\"),\n ];\n\n for (const packageJsonPath of candidates) {\n try {\n const pkg = fs.readJsonSync(packageJsonPath) as {\n name?: unknown;\n version?: unknown;\n };\n if (typeof pkg.name === \"string\" && typeof pkg.version === \"string\") {\n return { name: pkg.name, version: pkg.version };\n }\n } catch {\n // Source tests and bundled CLI resolve from different directories.\n }\n }\n\n return FALLBACK_METADATA;\n}\n","import validateNpmPackageName from \"validate-npm-package-name\";\n\nexport interface ValidationResult {\n valid: boolean;\n error?: string;\n}\n\nexport function validateProjectName(name: string): ValidationResult {\n const result = validateNpmPackageName(name);\n\n if (!result.validForNewPackages) {\n const errors = [...(result.errors || []), ...(result.warnings || [])];\n return {\n valid: false,\n error: errors[0] || \"Invalid package name\",\n };\n }\n\n return { valid: true };\n}\n","import path from \"node:path\";\nimport inquirer from \"inquirer\";\nimport { toKebabCase, toTitleCase } from \"./case-converters.js\";\nimport type { MonorepoContext } from \"./detect-monorepo.js\";\nimport { validateProjectName } from \"./validate.js\";\n\nexport const VALID_PROJECT_TYPES = [\"monorepo\", \"webapp\", \"library\"] as const;\nexport type ProjectType = (typeof VALID_PROJECT_TYPES)[number];\n\nexport function isValidProjectType(value: unknown): value is ProjectType {\n return (\n typeof value === \"string\" &&\n VALID_PROJECT_TYPES.includes(value as ProjectType)\n );\n}\n\nexport interface ProjectAnswers {\n projectType: ProjectType;\n directory: string;\n name: string;\n title: string;\n installDeps: boolean;\n monorepoName?: string;\n monorepoTitle?: string;\n}\n\ninterface PromptDefaults {\n projectType?: ProjectType;\n name?: string;\n repoName?: string;\n skipInstall?: boolean;\n monorepoContext?: MonorepoContext;\n cwd?: string;\n beforeNamePrompt?: (projectType: ProjectType) => void | Promise<void>;\n}\n\nasync function promptName(message: string): Promise<string> {\n // We kebab-case both inside `validate` (inquirer runs validate on the raw\n // input, before filter) and via `filter` (so the stored answer is also\n // kebab-cased). That way \"My App\" → \"my-app\" without bouncing the user with\n // a capital-letters error.\n const { name } = await inquirer.prompt([\n {\n type: \"input\",\n name: \"name\",\n message,\n filter: toKebabCase,\n validate: (input: string) => {\n const result = validateProjectName(toKebabCase(input));\n return result.valid || result.error || \"Invalid project name\";\n },\n },\n ]);\n return name;\n}\n\n/**\n * Outside a monorepo we collect the repo name first, then ask a single\n * \"is it a webapp?\" Y/n. Yes (default) → monorepo + webapp, no → bare\n * monorepo. Library at the top level is rare enough that we leave it as a\n * `--type library` flag rather than a third prompt option. The repo name is\n * collected separately from any initial package name so a customer monorepo\n * is not forced to share its first app's name.\n */\nasync function promptOutsideMonorepoType(): Promise<ProjectType> {\n const { webapp } = await inquirer.prompt([\n {\n type: \"confirm\",\n name: \"webapp\",\n message: \"Initialize with a webapp?\",\n default: true,\n },\n ]);\n return webapp ? \"webapp\" : \"monorepo\";\n}\n\n/**\n * Inside a monorepo, both webapp and library are common, so present them as\n * a numbered rawlist — user presses 1 or 2 + Enter. Defaults to webapp.\n */\nasync function promptInsideMonorepoType(): Promise<ProjectType> {\n const { projectType } = await inquirer.prompt([\n {\n type: \"rawlist\",\n name: \"projectType\",\n message: \"What kind of package?\",\n // inquirer v12 / @inquirer/rawlist v5 matches `default` against the\n // choice's `value`, not its index. `default: 0` would be a no-op.\n default: \"webapp\",\n choices: [\n { name: \"Webapp — A Next.js webapp\", value: \"webapp\" },\n { name: \"Library — A TypeScript library\", value: \"library\" },\n ],\n },\n ]);\n return projectType;\n}\n\nexport async function promptProjectDetails(\n defaults: PromptDefaults,\n): Promise<ProjectAnswers> {\n const inMonorepo = defaults.monorepoContext?.found ?? false;\n const cwd = defaults.cwd ?? process.cwd();\n\n let projectType: ProjectType;\n let finalName: string;\n if (inMonorepo) {\n // Resolve type before name so webapp preflights (notably NPM_TOKEN) can\n // fail before the user spends time naming a package.\n projectType = defaults.projectType ?? (await promptInsideMonorepoType());\n await defaults.beforeNamePrompt?.(projectType);\n finalName = defaults.name || (await promptName(\"Package name?\"));\n } else {\n const repoName =\n defaults.repoName ||\n (defaults.projectType === \"monorepo\" ? defaults.name : undefined) ||\n (await promptName(\"Repo name?\"));\n const repoTitle = toTitleCase(repoName);\n\n projectType = defaults.projectType ?? (await promptOutsideMonorepoType());\n await defaults.beforeNamePrompt?.(projectType);\n\n if (projectType === \"monorepo\") {\n finalName = repoName;\n const finalTitle = repoTitle;\n const finalDirectory = path.resolve(cwd, repoName);\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n monorepoName: repoName,\n monorepoTitle: repoTitle,\n };\n }\n\n const packageNamePrompt =\n projectType === \"webapp\" ? \"Webapp name?\" : \"Library name?\";\n finalName = defaults.name || (await promptName(packageNamePrompt));\n const finalTitle = toTitleCase(finalName);\n const finalDirectory = path.resolve(cwd, repoName);\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n monorepoName: repoName,\n monorepoTitle: repoTitle,\n };\n }\n\n // Inside a monorepo the directory is empty here — create.ts derives it from\n // the workspace pattern.\n const finalTitle = finalName ? toTitleCase(finalName) : \"\";\n const finalDirectory =\n !inMonorepo && finalName ? path.resolve(cwd, finalName) : \"\";\n\n return {\n projectType,\n directory: finalDirectory,\n name: finalName,\n title: finalTitle,\n installDeps: !defaults.skipInstall,\n };\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\n/**\n * Moves per-app GitHub Actions workflows from the package's .github/workflows\n * directory to the monorepo root, where GitHub Actions actually picks them up.\n *\n * Only moves files whose names start with the app name (e.g.\n * `myapp-ryvn-release.yaml`). Generic workflows like `ci.yml` are left in\n * place — those are an unrelated monorepo-vs-package concern.\n *\n * Cleans up an empty `.github/workflows` (and empty parent `.github`) after\n * the move.\n */\nexport async function relocateWorkflowsToRoot(\n packageDir: string,\n monorepoRoot: string,\n appName: string,\n): Promise<void> {\n const sourceDir = path.join(packageDir, \".github\", \"workflows\");\n if (!(await fs.pathExists(sourceDir))) return;\n\n const targetDir = path.join(monorepoRoot, \".github\", \"workflows\");\n await fs.ensureDir(targetDir);\n\n const entries = await fs.readdir(sourceDir);\n for (const name of entries) {\n if (!name.startsWith(`${appName}-`)) continue;\n if (!/\\.(ya?ml)$/.test(name)) continue;\n await fs.move(path.join(sourceDir, name), path.join(targetDir, name), {\n overwrite: true,\n });\n }\n\n // Tidy up empty directories left behind in the package.\n if ((await fs.readdir(sourceDir)).length === 0) {\n await fs.rmdir(sourceDir);\n const packageGithub = path.join(packageDir, \".github\");\n if ((await fs.readdir(packageGithub)).length === 0) {\n await fs.rmdir(packageGithub);\n }\n }\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\n\nexport interface AppConfig {\n name: string; // kebab-case: my-awesome-app\n title: string; // Title Case: My Awesome App\n dbName: string; // snake_case: my_awesome_app_db\n nameUpper: string; // UPPER-CASE: MY-AWESOME-APP\n nameSnake: string; // snake_case: my_awesome_app\n repoName: string; // kebab-case GitHub repo name\n repoNameSnake: string; // snake_case GitHub repo name\n createPackage: string; // package used by generated monorepo helper scripts\n createVersion: string; // exact create package version pinned by generated monorepos\n}\n\nexport interface ReplaceStats {\n processed: number;\n modified: number;\n}\n\nconst PLACEHOLDERS = {\n __APP_NAME__: \"name\",\n __APP_TITLE__: \"title\",\n __DB_NAME__: \"dbName\",\n __APP_NAME_UPPER__: \"nameUpper\",\n __APP_NAME_SNAKE__: \"nameSnake\",\n __REPO_NAME__: \"repoName\",\n __REPO_NAME_SNAKE__: \"repoNameSnake\",\n __CREATE_PACKAGE__: \"createPackage\",\n __CREATE_VERSION__: \"createVersion\",\n} as const;\n\n// Directories to skip\nconst SKIP_DIRS = new Set([\n \"node_modules\",\n \".git\",\n \".next\",\n \"dist\",\n \".turbo\",\n \".vercel\",\n]);\n\n// Files to skip\nconst SKIP_FILES = new Set([\n \"pnpm-lock.yaml\",\n \"package-lock.json\",\n \"yarn.lock\",\n]);\n\n// File extensions to process\nconst PROCESSABLE_EXTENSIONS = new Set([\n \".ts\",\n \".tsx\",\n \".js\",\n \".jsx\",\n \".json\",\n \".yml\",\n \".yaml\",\n \".md\",\n \".env\",\n \".sql\",\n \".tf\",\n \".tfvars\",\n \".sh\",\n \".zed\",\n \".mjs\",\n \".cjs\",\n]);\n\n// Specific filenames to process (without extensions)\nconst PROCESSABLE_FILENAMES = new Set([\n \"Dockerfile\",\n \".env.example\",\n \".env.local\",\n \"terraform.tfvars.example\",\n]);\n\nfunction shouldProcessFile(filePath: string): boolean {\n const fileName = path.basename(filePath);\n const ext = path.extname(filePath);\n\n if (SKIP_FILES.has(fileName)) return false;\n if (PROCESSABLE_FILENAMES.has(fileName)) return true;\n\n return PROCESSABLE_EXTENSIONS.has(ext);\n}\n\nasync function replaceInFile(\n filePath: string,\n config: AppConfig,\n): Promise<boolean> {\n let content: string;\n try {\n content = await fs.readFile(filePath, \"utf-8\");\n } catch {\n return false;\n }\n\n let modified = false;\n let newContent = content;\n\n // Sort placeholders by length descending to prevent prefix collisions\n // (e.g., __APP_NAME__ must not be replaced before __APP_NAME_UPPER__)\n const sortedEntries = Object.entries(PLACEHOLDERS).sort(\n (a, b) => b[0].length - a[0].length,\n );\n\n for (const [placeholder, configKey] of sortedEntries) {\n const value = config[configKey as keyof AppConfig];\n if (newContent.includes(placeholder)) {\n newContent = newContent.split(placeholder).join(value);\n modified = true;\n }\n }\n\n if (modified) {\n await fs.writeFile(filePath, newContent);\n return true;\n }\n\n return false;\n}\n\nfunction substituteName(name: string, config: AppConfig): string {\n // Same precedence rule as content substitution: longer placeholders first\n // so __APP_NAME_UPPER__ is replaced before __APP_NAME__.\n const sortedEntries = Object.entries(PLACEHOLDERS).sort(\n (a, b) => b[0].length - a[0].length,\n );\n let result = name;\n for (const [placeholder, configKey] of sortedEntries) {\n if (result.includes(placeholder)) {\n result = result\n .split(placeholder)\n .join(config[configKey as keyof AppConfig]);\n }\n }\n return result;\n}\n\nasync function processDirectory(\n dirPath: string,\n config: AppConfig,\n stats: ReplaceStats,\n): Promise<void> {\n const entries = await fs.readdir(dirPath, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dirPath, entry.name);\n\n if (entry.isDirectory()) {\n if (!SKIP_DIRS.has(entry.name)) {\n await processDirectory(fullPath, config, stats);\n }\n } else if (entry.isFile() && shouldProcessFile(fullPath)) {\n stats.processed++;\n if (await replaceInFile(fullPath, config)) {\n stats.modified++;\n }\n const renamed = substituteName(entry.name, config);\n if (renamed !== entry.name) {\n await fs.move(fullPath, path.join(dirPath, renamed), {\n overwrite: true,\n });\n }\n }\n }\n}\n\nexport async function replacePlaceholders(\n targetDir: string,\n config: AppConfig,\n): Promise<ReplaceStats> {\n const stats: ReplaceStats = { processed: 0, modified: 0 };\n await processDirectory(targetDir, config, stats);\n return stats;\n}\n","import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport fs from \"fs-extra\";\n\nconst FALLBACK_TEMPLATE_VERSION = \"1.0.0\";\n\nexport function readTemplateVersions(): Record<string, string> {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../template-versions.json\"),\n path.resolve(currentDir, \"../../template-versions.json\"),\n ];\n\n for (const versionsPath of candidates) {\n try {\n const content = fs.readFileSync(versionsPath, \"utf-8\");\n return JSON.parse(content);\n } catch {\n // Try the next path. Source tests and bundled CLI resolve differently.\n }\n }\n\n return {};\n}\n\nexport function getTemplateVersion(templateType: string): string {\n return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport { readCreatePackageMetadata } from \"./package-metadata.js\";\nimport { getTemplateVersion } from \"./template-versions.js\";\n\nexport const WORKSPACE_MANIFEST_FILENAME = \".mosaic-workspace.json\";\nexport const WORKSPACE_MANIFEST_SCHEMA_VERSION = 1;\n\nexport interface MosaicWorkspaceManifest {\n schemaVersion: typeof WORKSPACE_MANIFEST_SCHEMA_VERSION;\n createPackage: string;\n createVersion: string;\n monorepoTemplateVersion: string;\n compatibleTemplates: Record<string, string>;\n createdAt: string;\n}\n\nexport function getWorkspaceManifestPath(rootDir: string): string {\n return path.join(rootDir, WORKSPACE_MANIFEST_FILENAME);\n}\n\nexport function createWorkspaceManifest(\n createdAt = new Date().toISOString(),\n): MosaicWorkspaceManifest {\n const createPackage = readCreatePackageMetadata();\n return {\n schemaVersion: WORKSPACE_MANIFEST_SCHEMA_VERSION,\n createPackage: createPackage.name,\n createVersion: createPackage.version,\n monorepoTemplateVersion: getTemplateVersion(\"monorepo\"),\n compatibleTemplates: {\n webapp: getTemplateVersion(\"webapp\"),\n library: getTemplateVersion(\"library\"),\n },\n createdAt,\n };\n}\n\nexport async function readWorkspaceManifest(\n rootDir: string,\n): Promise<MosaicWorkspaceManifest | null> {\n const manifestPath = getWorkspaceManifestPath(rootDir);\n if (!(await fs.pathExists(manifestPath))) return null;\n\n const content = await fs.readFile(manifestPath, \"utf-8\");\n try {\n return JSON.parse(content) as MosaicWorkspaceManifest;\n } catch (error) {\n throw new Error(\n `Invalid JSON in ${WORKSPACE_MANIFEST_FILENAME}: ${(error as Error).message}`,\n );\n }\n}\n\nexport async function writeWorkspaceManifest(\n rootDir: string,\n manifest: MosaicWorkspaceManifest,\n): Promise<void> {\n const manifestPath = getWorkspaceManifestPath(rootDir);\n await fs.writeJson(manifestPath, manifest, { spaces: 2 });\n await fs.appendFile(manifestPath, \"\\n\");\n}\n\nexport function getCompatibleTemplateVersion(\n manifest: MosaicWorkspaceManifest | null,\n templateType: string,\n): string {\n return (\n manifest?.compatibleTemplates[templateType] ??\n getTemplateVersion(templateType)\n );\n}\n","import { execSync, spawn } from \"node:child_process\";\nimport path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport ora from \"ora\";\nimport {\n toKebabCase,\n toTitleCase,\n toSnakeCase,\n} from \"../utils/case-converters.js\";\nimport { copyTemplate, type TemplateType } from \"../utils/copy-template.js\";\nimport { detectMonorepo } from \"../utils/detect-monorepo.js\";\nimport { generateEnvLocal } from \"../utils/env-local.js\";\nimport {\n writeManifest,\n derivePlaceholders,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\nimport { readCreatePackageMetadata } from \"../utils/package-metadata.js\";\nimport {\n promptProjectDetails,\n type ProjectAnswers,\n type ProjectType,\n isValidProjectType,\n VALID_PROJECT_TYPES,\n} from \"../utils/prompts.js\";\nimport { relocateWorkflowsToRoot } from \"../utils/relocate-workflows.js\";\nimport {\n replacePlaceholders,\n type AppConfig,\n} from \"../utils/replace-placeholders.js\";\nimport { getTemplateVersion } from \"../utils/template-versions.js\";\nimport { validateProjectName } from \"../utils/validate.js\";\nimport {\n createWorkspaceManifest,\n getCompatibleTemplateVersion,\n readWorkspaceManifest,\n writeWorkspaceManifest,\n type MosaicWorkspaceManifest,\n} from \"../utils/workspace-manifest.js\";\n\nexport interface CreateOptions {\n type?: string;\n name?: string;\n repoName?: string;\n cwd?: string;\n skipInstall: boolean;\n yes: boolean;\n addOnly?: boolean;\n}\n\n// The webapp template enforces pnpm via `only-allow pnpm`; the monorepo and\n// library templates also assume pnpm. Hardcoding it removes a layer of\n// configuration that didn't reflect any real flexibility.\nconst PACKAGE_MANAGER = \"pnpm\";\nconst MAX_INSTALL_OUTPUT_CHARS = 64_000;\nconst MAX_INSTALL_OUTPUT_LINES = 80;\n\nclass PackageManagerCommandError extends Error {\n public constructor(\n message: string,\n public readonly output: string,\n ) {\n super(message);\n this.name = \"PackageManagerCommandError\";\n }\n}\n\n/** Paths in copy-paste shell commands (POSIX-style). */\nfunction shPath(p: string): string {\n return p.split(path.sep).join(\"/\");\n}\n\n/** Non-blocking install so ora can animate (execSync would block timers). */\nfunction runPackageManagerInstall(\n packageManager: string,\n cwd: string,\n args: string[] = [\"install\"],\n): Promise<void> {\n return new Promise((resolve, reject) => {\n let output = \"\";\n const appendOutput = (chunk: Buffer) => {\n output += chunk.toString();\n if (output.length > MAX_INSTALL_OUTPUT_CHARS) {\n output = output.slice(-MAX_INSTALL_OUTPUT_CHARS);\n }\n };\n const child = spawn(packageManager, args, {\n cwd,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n child.stdout?.on(\"data\", appendOutput);\n child.stderr?.on(\"data\", appendOutput);\n child.on(\"error\", (error) => {\n reject(\n new PackageManagerCommandError(\n `${packageManager} ${args.join(\" \")} failed: ${error.message}`,\n output,\n ),\n );\n });\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new PackageManagerCommandError(\n `${packageManager} ${args.join(\" \")} exited with code ${code ?? \"unknown\"}`,\n output,\n ),\n );\n });\n });\n}\n\nfunction printInstallFailureOutput(error: unknown): void {\n if (!(error instanceof PackageManagerCommandError)) return;\n const output = error.output.trim();\n if (!output) return;\n\n const lines = output.split(/\\r?\\n/);\n const omitted = Math.max(0, lines.length - MAX_INSTALL_OUTPUT_LINES);\n const visibleLines = lines.slice(-MAX_INSTALL_OUTPUT_LINES);\n\n console.log();\n console.log(\n chalk.bold(`Last ${visibleLines.length} lines from pnpm install:`),\n );\n if (omitted > 0) {\n console.log(chalk.dim(`... omitted ${omitted} earlier lines ...`));\n }\n console.log(visibleLines.join(\"\\n\"));\n}\n\n/**\n * Runs the monorepo-root `setup` script (docker + access + db + seed).\n * Uses `pnpm run setup` (not `pnpm setup`) because `pnpm setup` is a pnpm builtin that configures\n * PNPM_HOME in the user's shell rc — it ignores the package.json script of the same name.\n */\nfunction runWebappSetup(\n packageManager: string,\n monorepoRoot: string,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const child = spawn(packageManager, [\"run\", \"setup\"], {\n cwd: monorepoRoot,\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else\n reject(\n new Error(\n `${packageManager} run setup exited with code ${code ?? \"unknown\"}`,\n ),\n );\n });\n });\n}\n\n/**\n * Spawns `pnpm dev` in the package directory and resolves once Next reports\n * \"Ready in\" (so we know the server is accepting requests). Returns the child\n * process so the caller can await its exit when the user hits Ctrl+C.\n *\n * Captures the actual URL Next picked (Next falls back to 3001+ if 3000 is\n * taken) so the caller can open the right one in the browser.\n */\n// ANSI CSI sequences (color/control codes Next emits to TTY-detected stdout).\n// eslint-disable-next-line no-control-regex\nconst ANSI_PATTERN = /\\u001b\\[[0-9;]*[a-zA-Z]/g;\n\nfunction spawnDevServer(\n packageManager: string,\n cwd: string,\n): { child: ReturnType<typeof spawn>; ready: Promise<{ url: string }> } {\n // Use `run dev` rather than `<pm> dev` because npm has no `dev` shorthand\n // (it only aliases test/start/stop/restart). `run <script>` works uniformly\n // across pnpm/npm/yarn — same reason runWebappSetup uses `run setup`.\n const child = spawn(packageManager, [\"run\", \"dev\"], {\n cwd,\n stdio: [\"inherit\", \"pipe\", \"pipe\"],\n });\n\n const ready = new Promise<{ url: string }>((resolve, reject) => {\n let resolved = false;\n let detectedUrl = \"http://localhost:3000\";\n // Pipe chunks aren't line-aligned, so accumulate stdout into a rolling\n // buffer and re-scan after each chunk. We trim aggressively to avoid\n // unbounded growth on a server that runs for hours.\n let buffer = \"\";\n const onChunk = (chunk: Buffer) => {\n const text = chunk.toString();\n process.stdout.write(text);\n buffer = (buffer + text).slice(-4096).replace(ANSI_PATTERN, \"\");\n const urlMatch = buffer.match(/Local:\\s+(https?:\\/\\/\\S+?)(?:\\s|$)/i);\n if (urlMatch?.[1]) detectedUrl = urlMatch[1].trim();\n if (!resolved && /Ready in /i.test(buffer)) {\n resolved = true;\n resolve({ url: detectedUrl });\n }\n };\n child.stdout?.on(\"data\", onChunk);\n child.stderr?.on(\"data\", (chunk) => process.stderr.write(chunk));\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (!resolved) {\n reject(\n new Error(\n `${packageManager} run dev exited with code ${code ?? \"unknown\"} before becoming ready`,\n ),\n );\n }\n });\n });\n\n return { child, ready };\n}\n\n/**\n * Cross-platform \"open this URL in the user's default browser\". Returns true\n * if the launcher process spawned, false on synchronous failure. The empty\n * `error` listener swallows the asynchronous error event that fires when the\n * launcher binary is missing (e.g. minimal Linux without `xdg-open`) — without\n * it, that event becomes an uncaught exception that kills the CLI mid-run.\n */\nfunction openInBrowser(url: string): boolean {\n const cmd =\n process.platform === \"darwin\"\n ? [\"open\", url]\n : process.platform === \"win32\"\n ? [\"cmd\", \"/c\", \"start\", \"\", url]\n : [\"xdg-open\", url];\n try {\n const child = spawn(cmd[0]!, cmd.slice(1), {\n stdio: \"ignore\",\n detached: true,\n });\n child.on(\"error\", () => {});\n child.unref();\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Post-scaffold orchestration for webapps: run root setup (docker + access + db + seed),\n * start the dev server, open the served URL in the user's browser, then hand\n * control to the dev server until the user exits with Ctrl+C.\n *\n * Returns true if the dev server got running (caller should NOT print next\n * steps in that case — the user is already in the running app). Returns\n * false on any failure so the caller can print manual fallback steps.\n *\n * Callers should only invoke this when install actually succeeded —\n * starting setup without node_modules will leave an orphan Docker container.\n */\nasync function autoRunWebapp(\n packageDir: string,\n monorepoRoot: string,\n): Promise<boolean> {\n const packageManager = PACKAGE_MANAGER;\n\n console.log();\n console.log(chalk.bold(\"Running setup (docker, access, db, seed)...\"));\n console.log();\n try {\n await runWebappSetup(packageManager, monorepoRoot);\n } catch (error) {\n console.log();\n console.log(\n chalk.yellow(\"!\"),\n \"Setup failed. You can re-run it manually:\",\n chalk.cyan(`cd ${monorepoRoot} && ${packageManager} run setup`),\n );\n console.log(chalk.dim((error as Error).message));\n return false;\n }\n\n console.log();\n console.log(chalk.bold(\"Starting dev server...\"));\n console.log();\n const { child, ready } = spawnDevServer(packageManager, packageDir);\n\n // Register the \"dev server has exited\" listener BEFORE we await `ready`.\n // If the child exits during that await and we attached the listener\n // afterwards, we'd miss the event (Node EventEmitter doesn't replay past\n // events) and hang forever on the final `await closed`.\n const closed = new Promise<void>((resolve) => {\n child.on(\"close\", () => resolve());\n });\n\n let url = \"http://localhost:3000\";\n try {\n ({ url } = await ready);\n } catch (error) {\n console.log();\n console.log(chalk.yellow(\"!\"), \"Dev server failed to become ready.\");\n console.log(chalk.dim((error as Error).message));\n return false;\n }\n\n if (openInBrowser(url)) {\n console.log();\n console.log(chalk.green(\"✔\"), \"Opened\", chalk.cyan(url));\n } else {\n console.log();\n console.log(\n chalk.dim(\"Open\"),\n chalk.cyan(url),\n chalk.dim(\"in your browser.\"),\n );\n }\n\n // Hand control to the dev server until the user exits.\n await closed;\n return true;\n}\n\nasync function writeMosaicFiles(\n packageDir: string,\n config: {\n name: string;\n title: string;\n repoName?: string;\n },\n projectType: string,\n templateVersion = getTemplateVersion(projectType),\n templateCommit = \"npm\",\n): Promise<void> {\n const manifest: MosaicManifest = {\n templateType: projectType,\n templateVersion,\n templateCommit,\n createdAt: new Date().toISOString(),\n placeholders: derivePlaceholders(\n config.name,\n config.title,\n config.repoName,\n ),\n source: {\n templatePath: `packages/blueberry/templates/${projectType}`,\n },\n };\n\n await writeManifest(packageDir, manifest);\n\n // Write starter mosaic-template-notes.md\n const notesPath = path.join(packageDir, \"mosaic-template-notes.md\");\n await fs.writeFile(\n notesPath,\n `# Mosaic Divergence Notes\\n\\nDocument intentional differences from the ${projectType} template here.\\nClaude reads this file during sync to preserve your customizations.\\n\\n## Intentional Divergences\\n\\n_None yet — freshly created from template._\\n`,\n );\n}\n\nfunction buildAppConfig(\n name: string,\n title = toTitleCase(name),\n repoName = name,\n): AppConfig {\n const createPackage = readCreatePackageMetadata();\n return {\n name,\n title,\n dbName: `${toSnakeCase(name)}_db`,\n nameUpper: name.toUpperCase(),\n nameSnake: toSnakeCase(name),\n repoName,\n repoNameSnake: toSnakeCase(repoName),\n createPackage: createPackage.name,\n createVersion: createPackage.version,\n };\n}\n\n/** Copy the monorepo template into `targetDir` and replace its placeholders. */\nasync function scaffoldMonorepo(\n targetDir: string,\n config: AppConfig,\n): Promise<void> {\n const monoSpinner = ora(\"Copying monorepo template...\").start();\n try {\n await copyTemplate(targetDir, \"monorepo\" satisfies TemplateType);\n monoSpinner.succeed(\"Copied monorepo template\");\n } catch (error) {\n monoSpinner.fail(\"Failed to copy monorepo template\");\n console.error(error);\n process.exit(1);\n }\n\n const replaceSpinner = ora(\"Replacing monorepo placeholders...\").start();\n try {\n const stats = await replacePlaceholders(targetDir, config);\n replaceSpinner.succeed(\n `Replaced placeholders in ${stats.modified} monorepo files`,\n );\n } catch (error) {\n replaceSpinner.fail(\"Failed to replace monorepo placeholders\");\n console.error(error);\n process.exit(1);\n }\n}\n\n/**\n * Add a package (webapp or library) to a monorepo: copy the template into\n * `packageDir`, replace placeholders, write the Mosaic manifest, and run the\n * webapp-only post-copy steps (.env.local, workflow relocation) when applicable.\n */\nasync function addPackageToMonorepo(args: {\n packageDir: string;\n monorepoRoot: string;\n projectType: \"webapp\" | \"library\";\n config: AppConfig;\n templateVersion: string;\n templateCommit: string;\n}): Promise<void> {\n const {\n packageDir,\n monorepoRoot,\n projectType,\n config,\n templateVersion,\n templateCommit,\n } = args;\n\n const copySpinner = ora(\"Copying package template...\").start();\n try {\n await copyTemplate(packageDir, projectType);\n copySpinner.succeed(\"Copied package template\");\n } catch (error) {\n copySpinner.fail(\"Failed to copy package template\");\n console.error(error);\n process.exit(1);\n }\n\n const replaceSpinner = ora(\"Replacing package placeholders...\").start();\n try {\n const stats = await replacePlaceholders(packageDir, config);\n replaceSpinner.succeed(\n `Replaced placeholders in ${stats.modified} package files`,\n );\n } catch (error) {\n replaceSpinner.fail(\"Failed to replace package placeholders\");\n console.error(error);\n process.exit(1);\n }\n\n await writeMosaicFiles(\n packageDir,\n config,\n projectType,\n templateVersion,\n templateCommit,\n );\n\n if (projectType === \"webapp\") {\n await generateEnvLocal(packageDir);\n await relocateWorkflowsToRoot(packageDir, monorepoRoot, config.name);\n }\n}\n\n/** Initialize a git repo at `targetDir` with an initial commit. Best-effort. */\nfunction initGitRepo(targetDir: string): void {\n const gitSpinner = ora(\"Initializing git repository...\").start();\n try {\n execSync(\"git init\", { cwd: targetDir, stdio: \"ignore\" });\n execSync(\"git add -A\", { cwd: targetDir, stdio: \"ignore\" });\n execSync('git commit -m \"Initial commit from @percepta/create\"', {\n cwd: targetDir,\n stdio: \"ignore\",\n });\n gitSpinner.succeed(\"Initialized git repository\");\n } catch {\n gitSpinner.warn(\"Failed to initialize git repository\");\n }\n}\n\n/**\n * Run `pnpm install` at the monorepo root with a spinner. Returns true if\n * install ran successfully; false if it failed or was skipped.\n */\nasync function installAtMonorepoRoot(\n monorepoRoot: string,\n installDeps: boolean,\n): Promise<boolean> {\n if (!installDeps) return false;\n const spinner = ora(\n `Installing dependencies with ${PACKAGE_MANAGER}...`,\n ).start();\n try {\n await runPackageManagerInstall(PACKAGE_MANAGER, monorepoRoot);\n spinner.succeed(\"Installed dependencies\");\n return true;\n } catch (error) {\n spinner.warn(\n `Failed to install dependencies. Run '${PACKAGE_MANAGER} install' from monorepo root.`,\n );\n printInstallFailureOutput(error);\n return false;\n }\n}\n\n/**\n * For webapp scaffolds with a successful install, hand off to autoRunWebapp\n * (setup → dev → open browser). No-op otherwise. Returns true if the dev\n * server actually started — caller skips manual next-steps in that case.\n *\n * Gated on `installSucceeded` because starting setup without node_modules\n * leaves an orphan Docker container.\n */\nasync function maybeAutoRunWebapp(\n packageDir: string | null,\n monorepoRoot: string,\n projectType: ProjectType,\n installSucceeded: boolean,\n): Promise<boolean> {\n if (!packageDir || projectType !== \"webapp\" || !installSucceeded)\n return false;\n return autoRunWebapp(packageDir, monorepoRoot);\n}\n\nfunction getProjectTypeLabel(projectType: ProjectType): string {\n switch (projectType) {\n case \"monorepo\":\n return \"pnpm monorepo\";\n case \"webapp\":\n return \"Next.js webapp\";\n case \"library\":\n return \"TypeScript library\";\n default: {\n const exhaustiveCheck: never = projectType;\n throw new Error(`Unknown project type: ${String(exhaustiveCheck)}`);\n }\n }\n}\n\nfunction requireNpmTokenForWebappInstall(\n projectType: ProjectType,\n installDeps: boolean,\n): void {\n // The webapp template depends on private `@percepta/*` packages whose .npmrc\n // resolves auth from `${NPM_TOKEN}`. If the env var is empty, `pnpm install`\n // dies deep in cryptic 401/404 logs. Catch it before asking for names.\n if (projectType !== \"webapp\" || !installDeps || process.env.NPM_TOKEN) {\n return;\n }\n\n console.log();\n console.error(chalk.red(\"Error: NPM_TOKEN environment variable is not set.\"));\n console.error(\n chalk.dim(\" Required to install private @percepta/* packages.\"),\n );\n console.error();\n console.error(\" 1. Grab the npm token from 1Password:\");\n console.error(\n chalk.cyan(\n \" https://start.1password.com/open/i?a=5TX2B4O3QNE4FNQ2A7ZJZDRRBI&v=j7trpyuqh7gt635dtuj6y4pwjm&i=cmmdi5trji7ctkn3fseakf4mgi&h=aitco.1password.com\",\n ),\n );\n console.error(\" 2. Add to ~/.zshrc:\");\n console.error(chalk.cyan(' export NPM_TOKEN=\"<paste-token>\"'));\n console.error(\n \" 3. Open a new terminal (or \" +\n chalk.cyan(\"source ~/.zshrc\") +\n \") and re-run.\",\n );\n console.error();\n console.error(\n chalk.dim(\" Or pass --skip-install to scaffold without running install.\"),\n );\n process.exit(1);\n}\n\nfunction ensureWorkspaceCreateVersionCompatible(\n workspaceManifest: MosaicWorkspaceManifest | null,\n projectType: ProjectType,\n): void {\n if (!workspaceManifest || projectType === \"monorepo\") return;\n\n const createPackage = readCreatePackageMetadata();\n if (\n workspaceManifest.createPackage === createPackage.name &&\n workspaceManifest.createVersion === createPackage.version\n ) {\n return;\n }\n\n console.log();\n console.error(\n chalk.red(\n `Error: This workspace is pinned to ${workspaceManifest.createPackage}@${workspaceManifest.createVersion}, ` +\n `but you are running ${createPackage.name}@${createPackage.version}.`,\n ),\n );\n console.error();\n console.error(\n chalk.dim(\n \" Run the workspace-owned command so new packages match this monorepo:\",\n ),\n );\n console.error(chalk.cyan(` pnpm mosaic add ${projectType} <name>`));\n console.error();\n console.error(\n chalk.dim(\n \" To adopt newer templates, upgrade the workspace manifest first.\",\n ),\n );\n process.exit(1);\n}\n\nfunction getTemplateCommitSource(\n workspaceManifest: MosaicWorkspaceManifest | null,\n): string {\n if (!workspaceManifest) return \"npm\";\n return `${workspaceManifest.createPackage}@${workspaceManifest.createVersion}`;\n}\n\nexport async function createProject(options: CreateOptions): Promise<void> {\n const cwd = await resolveCreateCwd(options.cwd);\n\n // Validate --type if provided\n if (options.type !== undefined && !isValidProjectType(options.type)) {\n console.error(\n chalk.red(\n `Error: Invalid package type \"${options.type}\". Valid types are: ${VALID_PROJECT_TYPES.join(\", \")}`,\n ),\n );\n process.exit(1);\n }\n\n console.log();\n console.log(chalk.bold(\"Creating a new Mosaic package...\"));\n console.log();\n\n // Step 1: Detect monorepo context\n const monorepoContext = await detectMonorepo(cwd);\n\n if (options.addOnly && !monorepoContext.found) {\n console.error(\n chalk.red(\n \"Error: 'create add' must be run inside an existing pnpm monorepo.\",\n ),\n );\n process.exit(1);\n }\n\n // Bare monorepo only makes sense outside an existing monorepo.\n if (options.type === \"monorepo\" && monorepoContext.found) {\n console.error(\n chalk.red(\n `Error: Already inside a monorepo at ${monorepoContext.rootDir}. ` +\n `Choose 'webapp' or 'library' to add a package, or run from outside the monorepo.`,\n ),\n );\n process.exit(1);\n }\n\n if (monorepoContext.found) {\n console.log(\n chalk.dim(\" Detected monorepo at\"),\n chalk.cyan(monorepoContext.rootDir),\n );\n } else {\n console.log(\n chalk.dim(\" No monorepo detected. A new monorepo will be created.\"),\n );\n }\n console.log();\n\n const workspaceManifest = monorepoContext.found\n ? await readWorkspaceManifest(monorepoContext.rootDir!)\n : null;\n\n if (workspaceManifest) {\n console.log(\n chalk.dim(\" Workspace create version:\"),\n chalk.cyan(\n `${workspaceManifest.createPackage}@${workspaceManifest.createVersion}`,\n ),\n );\n console.log();\n } else if (monorepoContext.found) {\n console.log(\n chalk.yellow(\"!\"),\n \"No .mosaic-workspace.json found; using this CLI's bundled templates.\",\n );\n console.log();\n }\n\n // --name skips the package-name prompt; --repo-name skips the new-monorepo\n // repo-name prompt. Both are used by automation but are not the canonical\n // CLI UX.\n const projectName = options.name;\n const repoName = options.repoName;\n if (options.yes && !projectName) {\n console.error(chalk.red(\"Error: --name is required when using --yes flag\"));\n process.exit(1);\n }\n\n if (projectName) {\n const validation = validateProjectName(toKebabCase(projectName));\n if (!validation.valid) {\n console.error(chalk.red(`Invalid project name: ${validation.error}`));\n process.exit(1);\n }\n }\n\n if (repoName) {\n const validation = validateProjectName(toKebabCase(repoName));\n if (!validation.valid) {\n console.error(chalk.red(`Invalid repo name: ${validation.error}`));\n process.exit(1);\n }\n }\n\n // Step 2 & 3: Get project details from prompts or options\n let answers: ProjectAnswers;\n\n if (options.yes) {\n // Non-interactive mode (used by automation). Defaults: type=webapp.\n const projectType: ProjectType = (options.type as ProjectType) || \"webapp\";\n ensureWorkspaceCreateVersionCompatible(workspaceManifest, projectType);\n requireNpmTokenForWebappInstall(projectType, !options.skipInstall);\n const kebabName = toKebabCase(projectName!);\n const kebabRepoName = repoName ? toKebabCase(repoName) : kebabName;\n const directory =\n monorepoContext.found && monorepoContext.packageDir\n ? path.join(monorepoContext.packageDir, kebabName)\n : path.resolve(cwd, kebabRepoName);\n\n answers = {\n projectType,\n directory,\n name: kebabName,\n title: toTitleCase(kebabName),\n installDeps: !options.skipInstall,\n monorepoName: monorepoContext.found ? undefined : kebabRepoName,\n monorepoTitle: monorepoContext.found\n ? undefined\n : toTitleCase(kebabRepoName),\n };\n } else {\n answers = await promptProjectDetails({\n projectType: options.type as ProjectType | undefined,\n name: projectName ? toKebabCase(projectName) : undefined,\n repoName: repoName ? toKebabCase(repoName) : undefined,\n skipInstall: options.skipInstall,\n monorepoContext,\n cwd,\n beforeNamePrompt: (projectType) => {\n ensureWorkspaceCreateVersionCompatible(workspaceManifest, projectType);\n requireNpmTokenForWebappInstall(projectType, !options.skipInstall);\n },\n });\n\n // If inside monorepo, compute directory from workspace pattern + name\n if (\n monorepoContext.found &&\n monorepoContext.packageDir &&\n !answers.directory\n ) {\n answers.directory = path.join(monorepoContext.packageDir, answers.name);\n }\n }\n\n const monorepoName = answers.monorepoName ?? answers.name;\n const monorepoTitle = answers.monorepoTitle ?? toTitleCase(monorepoName);\n const monorepoConfig = buildAppConfig(monorepoName, monorepoTitle);\n const configRepoName = monorepoContext.found\n ? path.basename(monorepoContext.rootDir!)\n : monorepoName;\n const config = buildAppConfig(answers.name, answers.title, configRepoName);\n\n const typeLabel = getProjectTypeLabel(answers.projectType);\n\n if (monorepoContext.found) {\n // --- ADDING TO EXISTING MONOREPO ---\n const monorepoRoot = monorepoContext.rootDir!;\n const packageDir = monorepoContext.packageDir\n ? path.join(monorepoContext.packageDir, answers.name)\n : answers.directory;\n\n console.log(chalk.dim(\" Package type:\"), typeLabel);\n console.log(chalk.dim(\" Target:\"), packageDir);\n console.log(chalk.dim(\" Name:\"), config.name);\n console.log(chalk.dim(\" Title:\"), config.title);\n if (answers.projectType === \"webapp\") {\n console.log(chalk.dim(\" Database:\"), config.dbName);\n }\n console.log();\n\n // Check if directory already exists and is not empty\n if (await fs.pathExists(packageDir)) {\n const files = await fs.readdir(packageDir);\n if (files.length > 0) {\n console.error(\n chalk.red(`Error: Directory ${packageDir} is not empty.`),\n );\n process.exit(1);\n }\n }\n\n if (answers.projectType !== \"monorepo\") {\n await addPackageToMonorepo({\n packageDir,\n monorepoRoot,\n projectType: answers.projectType,\n config,\n templateVersion: getCompatibleTemplateVersion(\n workspaceManifest,\n answers.projectType,\n ),\n templateCommit: getTemplateCommitSource(workspaceManifest),\n });\n }\n\n // pnpm reads .npmrc from the workspace root, not package dirs. If the\n // root .npmrc is missing the @percepta registry config, downstream\n // `pnpm install` won't authenticate.\n await warnIfMissingRootNpmrc(monorepoRoot);\n\n const installSucceeded = await installAtMonorepoRoot(\n monorepoRoot,\n answers.installDeps,\n );\n\n // Success\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(`Created ${typeLabel} at`),\n chalk.cyan(path.relative(monorepoRoot, packageDir)),\n );\n console.log();\n\n const devStarted = await maybeAutoRunWebapp(\n packageDir,\n monorepoRoot,\n answers.projectType,\n installSucceeded,\n );\n if (devStarted) return;\n\n printNextStepsExisting(answers, packageDir, !installSucceeded);\n } else {\n // --- CREATING NEW MONOREPO (with optional package inside) ---\n const isBareMonorepo = answers.projectType === \"monorepo\";\n const monorepoRoot = answers.directory;\n const packageDir = isBareMonorepo\n ? null\n : path.join(monorepoRoot, \"packages\", answers.name);\n\n if (isBareMonorepo) {\n console.log(chalk.dim(\" Type:\"), typeLabel);\n console.log(chalk.dim(\" Directory:\"), monorepoRoot);\n console.log(chalk.dim(\" Repo name:\"), monorepoConfig.name);\n console.log(chalk.dim(\" Title:\"), monorepoConfig.title);\n } else {\n console.log(chalk.dim(\" Package type:\"), typeLabel);\n console.log(chalk.dim(\" Monorepo directory:\"), monorepoRoot);\n console.log(chalk.dim(\" Repo name:\"), monorepoConfig.name);\n console.log(chalk.dim(\" Package:\"), `packages/${answers.name}/`);\n console.log(chalk.dim(\" Name:\"), config.name);\n console.log(chalk.dim(\" Title:\"), config.title);\n if (answers.projectType === \"webapp\") {\n console.log(chalk.dim(\" Database:\"), config.dbName);\n }\n }\n console.log();\n\n // Check if directory exists and is not empty.\n if (await fs.pathExists(monorepoRoot)) {\n const files = await fs.readdir(monorepoRoot);\n if (files.length > 0) {\n console.error(\n chalk.red(`Error: Directory ${monorepoRoot} is not empty.`),\n );\n process.exit(1);\n }\n }\n\n await scaffoldMonorepo(monorepoRoot, monorepoConfig);\n const newWorkspaceManifest = createWorkspaceManifest();\n await writeWorkspaceManifest(monorepoRoot, newWorkspaceManifest);\n\n if (packageDir && answers.projectType !== \"monorepo\") {\n await addPackageToMonorepo({\n packageDir,\n monorepoRoot,\n projectType: answers.projectType,\n config,\n templateVersion: getCompatibleTemplateVersion(\n newWorkspaceManifest,\n answers.projectType,\n ),\n templateCommit: getTemplateCommitSource(newWorkspaceManifest),\n });\n }\n\n initGitRepo(monorepoRoot);\n\n const installSucceeded = await installAtMonorepoRoot(\n monorepoRoot,\n answers.installDeps,\n );\n\n // Success\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(\n isBareMonorepo ? `Created ${typeLabel} at` : \"Created monorepo at\",\n ),\n chalk.cyan(monorepoRoot),\n );\n if (!isBareMonorepo) {\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(`Created ${typeLabel} at`),\n chalk.cyan(`packages/${answers.name}/`),\n );\n }\n console.log();\n\n const devStarted = await maybeAutoRunWebapp(\n packageDir,\n monorepoRoot,\n answers.projectType,\n installSucceeded,\n );\n if (devStarted) return;\n\n printNextStepsNew(answers, monorepoRoot, !installSucceeded);\n }\n}\n\nasync function resolveCreateCwd(\n cwdOption: string | undefined,\n): Promise<string> {\n const cwd = cwdOption ? path.resolve(cwdOption) : process.cwd();\n let stat;\n try {\n stat = await fs.stat(cwd);\n } catch {\n console.error(chalk.red(`Error: --cwd directory does not exist: ${cwd}`));\n process.exit(1);\n }\n\n if (!stat.isDirectory()) {\n console.error(chalk.red(`Error: --cwd is not a directory: ${cwd}`));\n process.exit(1);\n }\n\n return cwd;\n}\n\nfunction printWebappNextSteps(params: {\n pm: \"pnpm\" | \"npm\" | \"yarn\";\n answers: ProjectAnswers;\n variant: \"new\" | \"existing\";\n /** Whether dependencies still need to be installed before setup/dev. */\n installNeeded: boolean;\n /** Relative path from cwd to monorepo root. */\n monorepoRelativePath: string;\n /** Relative path from monorepo root to the package directory. */\n packageRelativePathFromRoot?: string;\n}): void {\n const {\n pm,\n answers,\n variant,\n installNeeded,\n monorepoRelativePath,\n packageRelativePathFromRoot,\n } = params;\n const repoRel = shPath(monorepoRelativePath) || \".\";\n const pkgFromRoot =\n shPath(packageRelativePathFromRoot ?? `packages/${answers.name}`) || \".\";\n // Use `pnpm run setup` (not `pnpm setup`) — the latter is a pnpm builtin.\n const setupStep = \"pnpm run setup\";\n const devStep = \"pnpm dev\";\n\n if (variant === \"new\") {\n const oneLinerParts: string[] = [];\n if (repoRel !== \".\") oneLinerParts.push(`cd ${repoRel}`);\n if (installNeeded) oneLinerParts.push(`${pm} install`);\n oneLinerParts.push(setupStep);\n oneLinerParts.push(`cd ${pkgFromRoot}`);\n oneLinerParts.push(devStep);\n\n console.log(chalk.bold(\"Copy-paste (from your current directory):\"));\n console.log();\n console.log(chalk.cyan(` ${oneLinerParts.join(\" && \")}`));\n console.log();\n console.log(chalk.bold(\"Or step by step:\"));\n console.log();\n\n let step = 1;\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (installNeeded) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), setupStep);\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);\n console.log(chalk.dim(` ${step++}.`), devStep);\n return;\n }\n\n const oneLinerParts: string[] = [];\n\n if (repoRel !== \".\") oneLinerParts.push(`cd ${repoRel}`);\n if (installNeeded) {\n oneLinerParts.push(`${pm} install`);\n }\n oneLinerParts.push(setupStep, `cd ${pkgFromRoot}`, devStep);\n\n console.log(chalk.bold(\"Copy-paste (from your current directory):\"));\n console.log();\n console.log(chalk.cyan(` ${oneLinerParts.join(\" && \")}`));\n console.log();\n console.log(chalk.bold(\"Or step by step:\"));\n console.log();\n\n let step = 1;\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (installNeeded) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), setupStep);\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgFromRoot}`);\n console.log(chalk.dim(` ${step++}.`), devStep);\n}\n\nasync function warnIfMissingRootNpmrc(rootDir: string): Promise<void> {\n const rootNpmrc = path.join(rootDir, \".npmrc\");\n let contents = \"\";\n if (await fs.pathExists(rootNpmrc)) {\n contents = await fs.readFile(rootNpmrc, \"utf8\");\n }\n if (contents.includes(\"@percepta:registry\")) {\n return;\n }\n\n console.log();\n console.log(\n chalk.yellow(\"!\"),\n chalk.bold(\"Root .npmrc is missing @percepta registry config.\"),\n );\n console.log(\n chalk.dim(\n \" pnpm reads .npmrc from the workspace root, so add these lines to\",\n ),\n );\n console.log(chalk.dim(` ${path.join(rootDir, \".npmrc\")}:`));\n console.log();\n console.log(chalk.cyan(\" @percepta:registry=https://registry.npmjs.org/\"));\n console.log(chalk.cyan(\" //registry.npmjs.org/:_authToken=${NPM_TOKEN}\"));\n console.log();\n}\n\nfunction printNextStepsNew(\n answers: ProjectAnswers,\n targetDir: string,\n installNeeded: boolean,\n): void {\n const pm = PACKAGE_MANAGER;\n const relativePath = path.relative(process.cwd(), targetDir) || \".\";\n\n console.log(\"Next steps:\");\n console.log();\n\n switch (answers.projectType) {\n case \"monorepo\": {\n let step = 1;\n const repoRel = shPath(relativePath);\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (installNeeded) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(\n chalk.dim(` ${step++}.`),\n `Add a package: ${chalk.cyan(`pnpm mosaic add webapp <name>`)}`,\n );\n break;\n }\n\n case \"webapp\":\n printWebappNextSteps({\n pm,\n answers,\n variant: \"new\",\n installNeeded,\n monorepoRelativePath: relativePath,\n });\n break;\n\n case \"library\": {\n let step = 1;\n const repoRel = shPath(relativePath);\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n if (installNeeded) {\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n }\n console.log(chalk.dim(` ${step++}.`), `cd packages/${answers.name}`);\n console.log(chalk.dim(` ${step++}.`), `${pm} dev`);\n console.log(\n chalk.dim(` ${step++}.`),\n `Edit src/index.ts to add your library code`,\n );\n break;\n }\n }\n\n console.log();\n console.log(\n chalk.dim(\"For more information, see the README.md in your project.\"),\n );\n console.log();\n}\n\nfunction printNextStepsExisting(\n answers: ProjectAnswers,\n packageDir: string,\n installNeeded: boolean,\n): void {\n const pm = PACKAGE_MANAGER;\n const packageRelativePath = path.relative(process.cwd(), packageDir) || \".\";\n const monorepoRoot = path.dirname(path.dirname(packageDir));\n const monorepoRelativePath =\n path.relative(process.cwd(), monorepoRoot) || \".\";\n const packageRelativePathFromRoot =\n path.relative(monorepoRoot, packageDir) || \".\";\n\n console.log(\"Next steps:\");\n console.log();\n\n switch (answers.projectType) {\n case \"webapp\":\n printWebappNextSteps({\n pm,\n answers,\n variant: \"existing\",\n installNeeded,\n monorepoRelativePath,\n packageRelativePathFromRoot,\n });\n break;\n\n case \"library\": {\n let step = 1;\n if (installNeeded) {\n const repoRel = shPath(monorepoRelativePath);\n if (repoRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${repoRel}`);\n }\n console.log(chalk.dim(` ${step++}.`), `${pm} install`);\n console.log(\n chalk.dim(` ${step++}.`),\n `cd ${shPath(packageRelativePathFromRoot)}`,\n );\n } else {\n const pkgRel = shPath(packageRelativePath);\n if (pkgRel !== \".\") {\n console.log(chalk.dim(` ${step++}.`), `cd ${pkgRel}`);\n }\n }\n console.log(chalk.dim(` ${step++}.`), `${pm} dev`);\n console.log(\n chalk.dim(` ${step++}.`),\n \"Edit src/index.ts to add your library code\",\n );\n break;\n }\n }\n\n console.log();\n console.log(\n chalk.dim(\"For more information, see the README.md in your project.\"),\n );\n console.log();\n}\n","#!/usr/bin/env node\n\nimport { program } from \"commander\";\nimport { createProject } from \"./commands/create.js\";\nimport { readCreatePackageMetadata } from \"./utils/package-metadata.js\";\n\nconst packageJson = readCreatePackageMetadata();\n\nprogram\n .name(\"create\")\n .description(\"Scaffold and manage Mosaic packages\")\n .version(packageJson.version);\n\n// Default command. Intentionally bare-bones: the canonical incantation is\n// just `npx @percepta/create` with no flags — everything else comes from\n// prompts. The flags below exist for test-template.sh and other automation,\n// not for end-user use.\nprogram\n .command(\"create\", { isDefault: true })\n .description(\"Scaffold a new Mosaic package\")\n .option(\"-t, --type <type>\", \"Package type: monorepo, webapp, or library\")\n .option(\"--name <name>\", \"Package/app name\")\n .option(\"--repo-name <name>\", \"Repository name when creating a new monorepo\")\n .option(\"--cwd <dir>\", \"Run create as if started from this directory\")\n .option(\n \"--skip-install\",\n \"Skip dependency installation (also skips the auto-run setup + dev + browser)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip all prompts and use defaults\", false)\n .action(createProject);\n\nprogram\n .command(\"add\")\n .description(\"Add a Mosaic package to the current monorepo\")\n .argument(\"[typeOrName]\", \"Package type (webapp/library) or package name\")\n .argument(\"[name]\", \"Package/app name\")\n .option(\"-t, --type <type>\", \"Package type: webapp or library\")\n .option(\"--name <name>\", \"Package/app name\")\n .option(\n \"--skip-install\",\n \"Skip dependency installation (also skips the auto-run setup + dev + browser)\",\n false,\n )\n .option(\"-y, --yes\", \"Skip all prompts and use defaults\", false)\n .action(async (typeOrName, name, options) => {\n let projectType = options.type;\n let projectName = options.name;\n\n if (typeOrName === \"webapp\" || typeOrName === \"library\") {\n projectType = projectType ?? typeOrName;\n projectName = projectName ?? name;\n } else if (typeOrName === \"monorepo\") {\n program.error(\"error: 'add' only supports webapp or library packages\");\n } else if (typeOrName) {\n if (name) {\n program.error(\n `error: unknown package type \"${typeOrName}\". Expected webapp or library.`,\n );\n }\n projectName = projectName ?? typeOrName;\n }\n\n await createProject({\n ...options,\n type: projectType,\n name: projectName,\n addOnly: true,\n });\n });\n\n// Lazy-loaded subcommands\nprogram\n .command(\"status\")\n .description(\"Show template sync status for current app\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .action(async (options) => {\n const { statusCommand } = await import(\"./commands/status.js\");\n await statusCommand(options);\n });\n\nprogram\n .command(\"sync\")\n .description(\"Generate downstream sync context (template → app)\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .option(\"--to <version>\", \"Target template version (default: latest)\")\n .action(async (options) => {\n const { syncCommand } = await import(\"./commands/sync.js\");\n await syncCommand(options);\n });\n\nprogram\n .command(\"upstream\")\n .description(\"Generate upstream context (app → template)\")\n .option(\"--mosaic-template-path <path>\", \"Path to local mosaic repo checkout\")\n .option(\"--files <patterns...>\", \"Specific files to propose upstream\")\n .action(async (options) => {\n const { upstreamCommand } = await import(\"./commands/upstream.js\");\n await upstreamCommand(options);\n });\n\nprogram\n .command(\"init\")\n .description(\"Add .mosaic-template.json to an existing app\")\n .option(\"-t, --type <type>\", \"Template type (e.g., webapp, library)\")\n .option(\"--template-version <version>\", \"Template version to set\")\n .action(async (options) => {\n const { initCommand } = await import(\"./commands/init.js\");\n await initCommand(options);\n });\n\nprogram.parse();\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percepta/create",
3
- "version": "3.5.0",
3
+ "version": "3.5.2",
4
4
  "description": "Scaffold a new Mosaic package",
5
5
  "keywords": [
6
6
  "cli",
@@ -1,5 +1,5 @@
1
1
  {
2
- "monorepo": "1.0.0",
3
- "webapp": "1.1.0",
2
+ "monorepo": "1.0.1",
3
+ "webapp": "1.1.1",
4
4
  "library": "1.0.0"
5
5
  }
@@ -18,7 +18,7 @@
18
18
  "db:setup-and-migrate": "pnpm db:setup && pnpm db:migrate"
19
19
  },
20
20
  "dependencies": {
21
- "@percepta/auth": "0.1.1",
21
+ "@percepta/auth": "0.1.3",
22
22
  "drizzle-orm": "^0.45.2"
23
23
  },
24
24
  "devDependencies": {
@@ -1,3 +1,16 @@
1
1
  packages:
2
2
  - "packages/*"
3
3
  - "auth"
4
+
5
+ # Keep first-party and frequently released tooling installable even when the
6
+ # user's pnpm config enforces minimumReleaseAge.
7
+ minimumReleaseAgeExclude:
8
+ - "@percepta/*"
9
+ - "better-auth"
10
+ - "@better-auth/*"
11
+ - "oxfmt"
12
+ - "@oxfmt/*"
13
+ - "oxlint"
14
+ - "@oxlint/*"
15
+ - "oxlint-tsgolint"
16
+ - "@oxlint-tsgolint/*"
@@ -1,9 +1,13 @@
1
- import { loadEnvConfig } from "@next/env";
1
+ import * as nextEnvModule from "@next/env";
2
2
  import { getPgSearchPathOption } from "@percepta/database";
3
3
  import type { Config } from "drizzle-kit";
4
4
  import { getEnvConfig } from "./src/config/getEnvConfig";
5
5
 
6
- loadEnvConfig(process.cwd());
6
+ const nextEnv =
7
+ (nextEnvModule as { default?: typeof nextEnvModule }).default ??
8
+ nextEnvModule;
9
+
10
+ nextEnv.loadEnvConfig(process.cwd());
7
11
 
8
12
  const {
9
13
  DATABASE_HOST: host,
@@ -1,6 +1,6 @@
1
1
  import { execFile } from "node:child_process";
2
2
  import { promisify } from "node:util";
3
- import { expect, test, type Page } from "@playwright/test";
3
+ import { expect, test, type Browser, type Page } from "@playwright/test";
4
4
 
5
5
  const execFileAsync = promisify(execFile);
6
6
  const password = "password";
@@ -13,7 +13,8 @@ const users = {
13
13
  } as const;
14
14
 
15
15
  test.describe("RBAC access", () => {
16
- test("customer admin can manage role mappings but cannot enter the main app", async ({
16
+ test("customer admin can manage role mappings and change app access", async ({
17
+ browser,
17
18
  page,
18
19
  }) => {
19
20
  test.setTimeout(60_000);
@@ -46,16 +47,24 @@ test.describe("RBAC access", () => {
46
47
  await expect(
47
48
  page.getByRole("button", { name: "User assignments" }),
48
49
  ).toContainText("App Non User");
50
+
51
+ await expectAppAccess(browser, users.nonAppUser, true);
49
52
  } finally {
50
53
  if (shouldResetSeedData) {
51
54
  await resetSeedData();
52
55
  }
53
56
  }
54
57
 
55
- await page.reload();
56
- await expect(
57
- page.getByRole("button", { name: "User assignments" }),
58
- ).not.toContainText("App Non User");
58
+ await expect
59
+ .poll(async () => {
60
+ await page.reload();
61
+ return page
62
+ .getByRole("button", { name: "User assignments" })
63
+ .textContent();
64
+ })
65
+ .not.toContain("App Non User");
66
+
67
+ await expectAppAccess(browser, users.nonAppUser, false);
59
68
 
60
69
  await page.goto("/");
61
70
  await expect(page.getByRole("heading", { name: /Welcome to/ })).toHaveCount(
@@ -131,6 +140,33 @@ async function expectNotFound(page: Page) {
131
140
  ).toBeVisible();
132
141
  }
133
142
 
143
+ async function expectAppAccess(
144
+ browser: Browser,
145
+ email: string,
146
+ expectedAccess: boolean,
147
+ ) {
148
+ const expectedHeadingCount = expectedAccess ? 1 : 0;
149
+
150
+ await expect
151
+ .poll(
152
+ async () => {
153
+ const context = await browser.newContext();
154
+ const page = await context.newPage();
155
+
156
+ try {
157
+ await signIn(page, email, "/");
158
+ return await page
159
+ .getByRole("heading", { name: /Welcome to/ })
160
+ .count();
161
+ } finally {
162
+ await context.close();
163
+ }
164
+ },
165
+ { timeout: 15_000 },
166
+ )
167
+ .toBe(expectedHeadingCount);
168
+ }
169
+
134
170
  async function resetSeedData() {
135
171
  await execFileAsync("pnpm", ["db:seed"], { cwd: process.cwd() });
136
172
  }
@@ -54,11 +54,11 @@
54
54
  "@__REPO_NAME__/auth": "workspace:*",
55
55
  "@percepta/access-control": "0.7.0",
56
56
  "@percepta/ai": "0.1.0",
57
- "@percepta/database": "0.1.0",
57
+ "@percepta/database": "0.1.1",
58
58
  "@percepta/design": "0.3.2",
59
59
  "@percepta/inngest": "0.1.0",
60
60
  "@percepta/logger": "0.1.0",
61
- "@percepta/next-utils": "0.2.0",
61
+ "@percepta/next-utils": "0.2.1",
62
62
  "@percepta/utils": "0.1.10",
63
63
  "@radix-ui/react-slot": "^1.2.3",
64
64
  "@tanstack/react-query": "^5.81.5",
@@ -1,7 +1,9 @@
1
1
  import { defineConfig, devices } from "@playwright/test";
2
2
 
3
3
  const port = Number(process.env.PLAYWRIGHT_PORT ?? 3000);
4
- const baseURL = process.env.PLAYWRIGHT_BASE_URL ?? `http://127.0.0.1:${port}`;
4
+ const host = process.env.PLAYWRIGHT_HOST ?? "localhost";
5
+ const baseURL =
6
+ process.env.PLAYWRIGHT_BASE_URL ?? `http://${host}:${port}`;
5
7
 
6
8
  export default defineConfig({
7
9
  testDir: "./e2e",
@@ -16,8 +18,9 @@ export default defineConfig({
16
18
  webServer: process.env.PLAYWRIGHT_BASE_URL
17
19
  ? undefined
18
20
  : {
19
- command: `pnpm dev -- --hostname 127.0.0.1 --port ${port}`,
21
+ command: `pnpm dev -- --hostname ${host} --port ${port}`,
20
22
  env: {
23
+ BETTER_AUTH_URL: baseURL,
21
24
  SKIP_INNGEST_SYNC: "true",
22
25
  },
23
26
  reuseExistingServer: !process.env.CI,
@@ -9,9 +9,13 @@
9
9
 
10
10
  import { AsyncLocalStorage } from "node:async_hooks";
11
11
  import { execFileSync } from "node:child_process";
12
- import { loadEnvConfig } from "@next/env";
12
+ import * as nextEnvModule from "@next/env";
13
13
  import type { SubjectRef } from "@percepta/access-control";
14
14
 
15
+ const nextEnv =
16
+ (nextEnvModule as { default?: typeof nextEnvModule }).default ??
17
+ nextEnvModule;
18
+
15
19
  const SEEDED_USERS = [
16
20
  {
17
21
  access: "customer_admin",
@@ -44,7 +48,7 @@ const SEEDED_USERS = [
44
48
  ] as const;
45
49
 
46
50
  async function main(): Promise<void> {
47
- loadEnvConfig(process.cwd());
51
+ nextEnv.loadEnvConfig(process.cwd());
48
52
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
53
  (globalThis as any).AsyncLocalStorage = AsyncLocalStorage;
50
54
 
@@ -1,5 +1,9 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import { loadEnvConfig } from "@next/env";
2
+ import * as nextEnvModule from "@next/env";
3
3
 
4
- loadEnvConfig(process.cwd());
4
+ const nextEnv =
5
+ (nextEnvModule as { default?: typeof nextEnvModule }).default ??
6
+ nextEnvModule;
7
+
8
+ nextEnv.loadEnvConfig(process.cwd());
5
9
  globalThis.AsyncLocalStorage = AsyncLocalStorage;