@percepta/create 4.1.5 → 4.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/index.js +13 -10
  2. package/dist/index.js.map +1 -1
  3. package/dist/{init-COp0nGdk.js → init-BD3EyyLO.js} +12 -4
  4. package/dist/init-BD3EyyLO.js.map +1 -0
  5. package/dist/{manifest-CqIDnbgs.js → manifest-By1SgOjC.js} +3 -2
  6. package/dist/manifest-By1SgOjC.js.map +1 -0
  7. package/dist/{register-app-DeyAp8hw.js → register-app-DZg-Pmtd.js} +20 -37
  8. package/dist/register-app-DZg-Pmtd.js.map +1 -0
  9. package/dist/{status-BXYaQ4a2.js → status-K6raTwwu.js} +2 -2
  10. package/dist/{status-BXYaQ4a2.js.map → status-K6raTwwu.js.map} +1 -1
  11. package/dist/{sync-BayU4w1j.js → sync-Bi958-2W.js} +2 -2
  12. package/dist/{sync-BayU4w1j.js.map → sync-Bi958-2W.js.map} +1 -1
  13. package/dist/{upstream-CZEzLrS4.js → upstream-CAraZeSS.js} +2 -2
  14. package/dist/{upstream-CZEzLrS4.js.map → upstream-CAraZeSS.js.map} +1 -1
  15. package/package.json +1 -1
  16. package/templates/infra/os.blueprint.yaml.template +0 -12
  17. package/templates/library/package.json.template +1 -1
  18. package/templates/monorepo/.envrc.example +1 -0
  19. package/templates/monorepo/.github/workflows/access-control.yml +0 -3
  20. package/templates/monorepo/.node-version +1 -1
  21. package/templates/monorepo/.vscode/extensions.json +3 -0
  22. package/templates/monorepo/.vscode/settings.json +26 -0
  23. package/templates/monorepo/auth/README.md +2 -2
  24. package/templates/monorepo/auth/package.json +2 -1
  25. package/templates/monorepo/auth/src/drizzle/db.ts +6 -1
  26. package/templates/monorepo/gitignore.template +1 -1
  27. package/templates/monorepo/pnpm-workspace.yaml +0 -8
  28. package/templates/webapp/.github/workflows/__APP_NAME__-ryvn-release.yaml +3 -2
  29. package/templates/webapp/AGENTS.md +1 -1
  30. package/templates/webapp/Dockerfile +20 -19
  31. package/templates/webapp/README.md +1 -1
  32. package/templates/webapp/agent-skills/access-control.md +3 -3
  33. package/templates/webapp/agent-skills/database.md +1 -1
  34. package/templates/webapp/next.config.ts +1 -1
  35. package/templates/webapp/package.json.template +2 -2
  36. package/templates/webapp/scripts/seed.ts +3 -3
  37. package/templates/webapp/src/app/(settings)/settings/page.tsx +3 -2
  38. package/templates/webapp/src/drizzle/schema/index.ts +1 -1
  39. package/templates/webapp/src/lib/auth/index.ts +2 -2
  40. package/templates/webapp/src/startup-checks.ts +1 -1
  41. package/dist/init-COp0nGdk.js.map +0 -1
  42. package/dist/manifest-CqIDnbgs.js.map +0 -1
  43. package/dist/register-app-DeyAp8hw.js.map +0 -1
  44. package/templates/webapp/.node-version +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init-BD3EyyLO.js","names":[],"sources":["../src/commands/init.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport inquirer from \"inquirer\";\nimport {\n writeManifest,\n manifestExists,\n derivePlaceholders,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\nimport { isValidProjectType, VALID_PROJECT_TYPES } from \"../utils/prompts.js\";\nimport { getTemplateVersion } from \"../utils/template-versions.js\";\n\nexport interface InitOptions {\n type?: string;\n templateVersion?: string;\n}\n\nexport async function initCommand(options: InitOptions): Promise<void> {\n const cwd = process.cwd();\n\n if (await manifestExists(cwd)) {\n console.error(\n chalk.red(\".mosaic-template.json already exists in this directory.\"),\n );\n process.exit(1);\n }\n\n // Auto-detect app name + npm scope from package.json. A scoped name like\n // `@summa/flux` contributes both `summa` (customer slug) and `flux` (app\n // name); a bare name contributes only the app name and leaves the customer\n // slug to fall back via `derivePlaceholders`.\n const pkgPath = path.join(cwd, \"package.json\");\n let appName = path.basename(cwd);\n let customerSlug: string | undefined;\n if (await fs.pathExists(pkgPath)) {\n const pkg = JSON.parse(await fs.readFile(pkgPath, \"utf-8\"));\n if (typeof pkg.name === \"string\") {\n const scopeMatch = pkg.name.match(/^@([^/]+)\\//);\n if (scopeMatch) customerSlug = scopeMatch[1];\n appName = pkg.name.replace(/^@[^/]+\\//, \"\") || appName;\n }\n }\n\n // Determine template type\n let templateType = options.type;\n if (templateType && !isValidProjectType(templateType)) {\n console.error(\n chalk.red(\n `Invalid template type \"${templateType}\". Valid types: ${VALID_PROJECT_TYPES.join(\", \")}`,\n ),\n );\n process.exit(1);\n }\n if (!templateType) {\n const answer = await inquirer.prompt([\n {\n type: \"list\",\n name: \"type\",\n message: \"Template type:\",\n choices: [\"webapp\", \"library\"],\n },\n ]);\n templateType = answer.type;\n }\n\n const templateVersion =\n options.templateVersion || getTemplateVersion(templateType!);\n\n // Derive placeholders from app name\n const appTitle = appName\n .split(\"-\")\n .map((w: string) => w.charAt(0).toUpperCase() + w.slice(1))\n .join(\" \");\n\n const manifest: MosaicManifest = {\n templateType: templateType!,\n templateVersion,\n templateCommit: \"unknown\",\n createdAt: new Date().toISOString(),\n placeholders: derivePlaceholders(\n appName,\n appTitle,\n undefined,\n customerSlug,\n ),\n source: {\n templatePath: `packages/blueberry/templates/${templateType}`,\n },\n };\n\n await writeManifest(cwd, manifest);\n\n // Create mosaic-template-notes.md if it doesn't exist\n const notesPath = path.join(cwd, \"mosaic-template-notes.md\");\n if (!(await fs.pathExists(notesPath))) {\n await fs.writeFile(\n notesPath,\n `# Mosaic Divergence Notes\\n\\nDocument intentional differences from the ${templateType} template here.\\nClaude reads this file during sync to preserve your customizations.\\n\\n## Intentional Divergences\\n\\n`,\n );\n }\n\n console.log();\n console.log(\n chalk.green(\"\\u2714\"),\n chalk.bold(\"Initialized .mosaic-template.json\"),\n );\n console.log();\n console.log(chalk.dim(\" Template:\"), templateType);\n console.log(chalk.dim(\" Version:\"), templateVersion);\n console.log(chalk.dim(\" App name:\"), appName);\n console.log();\n console.log(\n chalk.dim(\n \"Review .mosaic-template.json and mosaic-template-notes.md, then commit them.\",\n ),\n );\n console.log();\n}\n"],"mappings":";;;;;;;;AAkBA,eAAsB,YAAY,SAAqC;CACrE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI,MAAM,eAAe,IAAI,EAAE;AAC7B,UAAQ,MACN,MAAM,IAAI,0DAA0D,CACrE;AACD,UAAQ,KAAK,EAAE;;CAOjB,MAAM,UAAU,KAAK,KAAK,KAAK,eAAe;CAC9C,IAAI,UAAU,KAAK,SAAS,IAAI;CAChC,IAAI;AACJ,KAAI,MAAM,GAAG,WAAW,QAAQ,EAAE;EAChC,MAAM,MAAM,KAAK,MAAM,MAAM,GAAG,SAAS,SAAS,QAAQ,CAAC;AAC3D,MAAI,OAAO,IAAI,SAAS,UAAU;GAChC,MAAM,aAAa,IAAI,KAAK,MAAM,cAAc;AAChD,OAAI,WAAY,gBAAe,WAAW;AAC1C,aAAU,IAAI,KAAK,QAAQ,aAAa,GAAG,IAAI;;;CAKnD,IAAI,eAAe,QAAQ;AAC3B,KAAI,gBAAgB,CAAC,mBAAmB,aAAa,EAAE;AACrD,UAAQ,MACN,MAAM,IACJ,0BAA0B,aAAa,kBAAkB,oBAAoB,KAAK,KAAK,GACxF,CACF;AACD,UAAQ,KAAK,EAAE;;AAEjB,KAAI,CAAC,aASH,iBAAe,MARM,SAAS,OAAO,CACnC;EACE,MAAM;EACN,MAAM;EACN,SAAS;EACT,SAAS,CAAC,UAAU,UAAU;EAC/B,CACF,CAAC,EACoB;CAGxB,MAAM,kBACJ,QAAQ,mBAAmB,mBAAmB,aAAc;CAG9D,MAAM,WAAW,QACd,MAAM,IAAI,CACV,KAAK,MAAc,EAAE,OAAO,EAAE,CAAC,aAAa,GAAG,EAAE,MAAM,EAAE,CAAC,CAC1D,KAAK,IAAI;AAkBZ,OAAM,cAAc,KAAK;EAfT;EACd;EACA,gBAAgB;EAChB,4BAAW,IAAI,MAAM,EAAC,aAAa;EACnC,cAAc,mBACZ,SACA,UACA,KAAA,GACA,aACD;EACD,QAAQ,EACN,cAAc,gCAAgC,gBAC/C;EAG8B,CAAC;CAGlC,MAAM,YAAY,KAAK,KAAK,KAAK,2BAA2B;AAC5D,KAAI,CAAE,MAAM,GAAG,WAAW,UAAU,CAClC,OAAM,GAAG,UACP,WACA,0EAA0E,aAAa,wHACxF;AAGH,SAAQ,KAAK;AACb,SAAQ,IACN,MAAM,MAAM,IAAS,EACrB,MAAM,KAAK,oCAAoC,CAChD;AACD,SAAQ,KAAK;AACb,SAAQ,IAAI,MAAM,IAAI,cAAc,EAAE,aAAa;AACnD,SAAQ,IAAI,MAAM,IAAI,aAAa,EAAE,gBAAgB;AACrD,SAAQ,IAAI,MAAM,IAAI,cAAc,EAAE,QAAQ;AAC9C,SAAQ,KAAK;AACb,SAAQ,IACN,MAAM,IACJ,+EACD,CACF;AACD,SAAQ,KAAK"}
@@ -33,7 +33,7 @@ async function writeManifest(dir, manifest) {
33
33
  async function manifestExists(dir) {
34
34
  return fs.pathExists(getManifestPath(dir));
35
35
  }
36
- function derivePlaceholders(appName, appTitle, repoName = appName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
36
+ function derivePlaceholders(appName, appTitle, repoName = appName, customerSlug = repoName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
37
37
  const nameSnake = appName.replace(/-/g, "_");
38
38
  const repoNameSnake = repoName.replace(/-/g, "_");
39
39
  return {
@@ -44,6 +44,7 @@ function derivePlaceholders(appName, appTitle, repoName = appName, designTheme =
44
44
  __APP_NAME_SNAKE__: nameSnake,
45
45
  __REPO_NAME__: repoName,
46
46
  __REPO_NAME_SNAKE__: repoNameSnake,
47
+ __CUSTOMER_SLUG__: customerSlug,
47
48
  __MOSAIC_DESIGN_THEME__: designTheme
48
49
  };
49
50
  }
@@ -55,4 +56,4 @@ function resolveMosaicTemplatePath(options) {
55
56
  //#endregion
56
57
  export { writeManifest as a, isValidMosaicDesignTheme as c, resolveMosaicTemplatePath as i, manifestExists as n, DEFAULT_MOSAIC_DESIGN_THEME as o, readManifest as r, VALID_MOSAIC_DESIGN_THEMES as s, derivePlaceholders as t };
57
58
 
58
- //# sourceMappingURL=manifest-CqIDnbgs.js.map
59
+ //# sourceMappingURL=manifest-By1SgOjC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifest-By1SgOjC.js","names":[],"sources":["../src/utils/design-theme.ts","../src/utils/manifest.ts"],"sourcesContent":["export const VALID_MOSAIC_DESIGN_THEMES = [\"paper\", \"modern\", \"dense\"] as const;\n\nexport type MosaicDesignTheme = (typeof VALID_MOSAIC_DESIGN_THEMES)[number];\n\nexport const DEFAULT_MOSAIC_DESIGN_THEME: MosaicDesignTheme = \"modern\";\n\nexport function isValidMosaicDesignTheme(\n value: unknown,\n): value is MosaicDesignTheme {\n return (\n typeof value === \"string\" &&\n VALID_MOSAIC_DESIGN_THEMES.includes(value as MosaicDesignTheme)\n );\n}\n","import path from \"node:path\";\nimport fs from \"fs-extra\";\nimport {\n DEFAULT_MOSAIC_DESIGN_THEME,\n type MosaicDesignTheme,\n} from \"./design-theme.js\";\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 customerSlug = repoName,\n designTheme: MosaicDesignTheme = DEFAULT_MOSAIC_DESIGN_THEME,\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 __CUSTOMER_SLUG__: customerSlug,\n __MOSAIC_DESIGN_THEME__: designTheme,\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"],"mappings":";;;AAAA,MAAa,6BAA6B;CAAC;CAAS;CAAU;CAAQ;AAItE,MAAa,8BAAiD;AAE9D,SAAgB,yBACd,OAC4B;AAC5B,QACE,OAAO,UAAU,YACjB,2BAA2B,SAAS,MAA2B;;;;ACQnE,MAAM,oBAAoB;AAE1B,SAAgB,gBAAgB,KAAqB;AACnD,QAAO,KAAK,KAAK,KAAK,kBAAkB;;AAG1C,eAAsB,aAAa,KAAsC;CACvE,MAAM,eAAe,gBAAgB,IAAI;AACzC,KAAI,CAAE,MAAM,GAAG,WAAW,aAAa,CACrC,OAAM,IAAI,MACR,MAAM,kBAAkB,YAAY,IAAI,oCACzC;CAEH,MAAM,UAAU,MAAM,GAAG,SAAS,cAAc,QAAQ;AACxD,KAAI;AACF,SAAO,KAAK,MAAM,QAAQ;UACnB,OAAO;AACd,QAAM,IAAI,MACR,mBAAmB,kBAAkB,IAAK,MAAgB,UAC3D;;;AAIL,eAAsB,cACpB,KACA,UACe;CACf,MAAM,eAAe,gBAAgB,IAAI;AACzC,OAAM,GAAG,UAAU,cAAc,KAAK,UAAU,UAAU,MAAM,EAAE,GAAG,KAAK;;AAG5E,eAAsB,eAAe,KAA+B;AAClE,QAAO,GAAG,WAAW,gBAAgB,IAAI,CAAC;;AAG5C,SAAgB,mBACd,SACA,UACA,WAAW,SACX,eAAe,UACf,cAAiC,6BACT;CACxB,MAAM,YAAY,QAAQ,QAAQ,MAAM,IAAI;CAC5C,MAAM,gBAAgB,SAAS,QAAQ,MAAM,IAAI;AACjD,QAAO;EACL,cAAc;EACd,eAAe;EACf,aAAa,YAAY;EACzB,oBAAoB,QAAQ,aAAa;EACzC,oBAAoB;EACpB,eAAe;EACf,qBAAqB;EACrB,mBAAmB;EACnB,yBAAyB;EAC1B;;AAGH,SAAgB,0BAA0B,SAE/B;AACT,KAAI,QAAQ,mBACV,QAAO,KAAK,QAAQ,QAAQ,mBAAmB;AACjD,KAAI,QAAQ,IAAI,qBACd,QAAO,KAAK,QAAQ,QAAQ,IAAI,qBAAqB;AACvD,OAAM,IAAI,MACR,qFACD"}
@@ -141,8 +141,6 @@ function updateBlueprint(blueprintContent, appName, options) {
141
141
  if (options.appInputs) {
142
142
  changed = addAppInput(document, inputs, renderIngressDomainInput()) || changed;
143
143
  changed = addAppInput(document, inputs, renderBetterAuthSecretInput(appName)) || changed;
144
- changed = addAppInput(document, inputs, renderInngestEventKeyInput()) || changed;
145
- changed = addAppInput(document, inputs, renderInngestSigningKeyInput()) || changed;
146
144
  changed = addAppInput(document, inputs, renderLangfusePublicKeyInput()) || changed;
147
145
  changed = addAppInput(document, inputs, renderLangfuseSecretKeyInput()) || changed;
148
146
  }
@@ -233,26 +231,6 @@ function renderBetterAuthSecretInput(appName) {
233
231
  }
234
232
  };
235
233
  }
236
- function renderInngestEventKeyInput() {
237
- return {
238
- name: inngestEventKeyInputName(),
239
- type: "string",
240
- isSecret: true,
241
- group: "applications",
242
- displayName: "Inngest Event Key",
243
- description: "Shared Inngest event key for generated OS webapps. Leave unset when the target Inngest installation does not require one."
244
- };
245
- }
246
- function renderInngestSigningKeyInput() {
247
- return {
248
- name: inngestSigningKeyInputName(),
249
- type: "string",
250
- isSecret: true,
251
- group: "applications",
252
- displayName: "Inngest Signing Key",
253
- description: "Shared Inngest signing key for generated OS webapps. Leave unset when the target Inngest installation does not require one."
254
- };
255
- }
256
234
  function renderLangfusePublicKeyInput() {
257
235
  return {
258
236
  name: langfusePublicKeyInputName(),
@@ -310,16 +288,6 @@ function renderAppInstallationEnv(appName) {
310
288
  key: "INNGEST_BASE_URL",
311
289
  value: "{{ (blueprintInstallation \"mosaic\").outputs.inngest_base_url }}"
312
290
  },
313
- {
314
- key: "INNGEST_EVENT_KEY",
315
- isSecret: true,
316
- valueFromInput: { name: inngestEventKeyInputName() }
317
- },
318
- {
319
- key: "INNGEST_SIGNING_KEY",
320
- isSecret: true,
321
- valueFromInput: { name: inngestSigningKeyInputName() }
322
- },
323
291
  {
324
292
  key: "LANGFUSE_BASE_URL",
325
293
  value: "{{ (blueprintInstallation \"mosaic\").outputs.langfuse_base_url }}"
@@ -364,6 +332,18 @@ function renderAppInstallationConfig(appName) {
364
332
  "readinessEnabled: true",
365
333
  "startupEnabled: true",
366
334
  "",
335
+ "env:",
336
+ " - name: INNGEST_EVENT_KEY",
337
+ " valueFrom:",
338
+ " secretKeyRef:",
339
+ ` name: '{{ (blueprintInstallation "mosaic").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,
340
+ ` key: '{{ (blueprintInstallation "mosaic").outputs.${mosaicInngestEventKeySecretKeyOutput()} }}'`,
341
+ " - name: INNGEST_SIGNING_KEY",
342
+ " valueFrom:",
343
+ " secretKeyRef:",
344
+ ` name: '{{ (blueprintInstallation "mosaic").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,
345
+ ` key: '{{ (blueprintInstallation "mosaic").outputs.${mosaicInngestSigningKeySecretKeyOutput()} }}'`,
346
+ "",
367
347
  "resources:",
368
348
  " requests:",
369
349
  " cpu: \"100m\"",
@@ -409,11 +389,14 @@ function ingressDomainInputName() {
409
389
  function betterAuthSecretInputName(appName) {
410
390
  return `${toSnakeCase(appName)}_better_auth_secret`;
411
391
  }
412
- function inngestEventKeyInputName() {
413
- return "inngest_event_key";
392
+ function mosaicInngestKeysSecretNameOutput() {
393
+ return "inngest_keys_secret_name";
394
+ }
395
+ function mosaicInngestEventKeySecretKeyOutput() {
396
+ return "inngest_event_key_secret_key";
414
397
  }
415
- function inngestSigningKeyInputName() {
416
- return "inngest_signing_key";
398
+ function mosaicInngestSigningKeySecretKeyOutput() {
399
+ return "inngest_signing_key_secret_key";
417
400
  }
418
401
  function langfusePublicKeyInputName() {
419
402
  return "langfuse_public_key";
@@ -430,4 +413,4 @@ function normalizeAppName(appNameInput) {
430
413
  //#endregion
431
414
  export { registerAppCommand };
432
415
 
433
- //# sourceMappingURL=register-app-DeyAp8hw.js.map
416
+ //# sourceMappingURL=register-app-DZg-Pmtd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register-app-DZg-Pmtd.js","names":[],"sources":["../src/commands/infra/register-app.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { isMap, isSeq, parseDocument } from \"yaml\";\nimport {\n toKebabCase,\n toSnakeCase,\n toTitleCase,\n} from \"../../utils/case-converters.js\";\nimport { detectMonorepo } from \"../../utils/detect-monorepo.js\";\nimport { validateProjectName } from \"../../utils/validate.js\";\nimport { readWorkspaceManifest } from \"../../utils/workspace-manifest.js\";\nimport {\n createInfraGitHubApi,\n createOrUpdateInfraPullRequestFiles,\n INFRA_BASE_BRANCH,\n INFRA_REPOSITORY,\n type InfraGitHubApi,\n type InfraPullRequestFile,\n resolveGitHubToken,\n} from \"./github.js\";\n\nconst OS_POSTGRESQL_TERRAFORM_ALIAS = \"os-postgresql-terraform\";\nconst OS_POSTGRESQL_TERRAFORM_SERVICES = new Set([\n \"os-postgresql-terraform-aws\",\n \"os-postgresql-terraform-azure\",\n]);\nconst OS_BLUEPRINT_INPUT_GROUPS = [\n {\n name: \"general\",\n displayName: \"General\",\n description: \"Shared OS infrastructure settings.\",\n },\n {\n name: \"applications\",\n displayName: \"Applications\",\n description: \"Generated OS webapp settings.\",\n },\n {\n name: \"aws_postgresql\",\n displayName: \"AWS PostgreSQL\",\n description: \"AWS Aurora PostgreSQL settings.\",\n condition: '{{ eq EnvironmentProviderType \"aws\" }}',\n },\n {\n name: \"azure_postgresql\",\n displayName: \"Azure PostgreSQL\",\n description: \"Azure PostgreSQL Flexible Server settings.\",\n condition: '{{ eq EnvironmentProviderType \"azure\" }}',\n },\n];\n\nexport interface RegisterAppResult {\n appName: string;\n blueprintName: string;\n blueprintPath: string;\n branchName: string;\n customerSlug: string;\n pullRequestUrl: string | null;\n repository: typeof INFRA_REPOSITORY;\n status: \"already_registered\" | \"created_pr\" | \"updated_pr\";\n servicePath: string;\n targetPath: string;\n}\n\nexport async function registerApp(\n appNameInput: string,\n args: {\n cwd?: string;\n github?: InfraGitHubApi;\n } = {},\n): Promise<RegisterAppResult> {\n const appName = normalizeAppName(appNameInput);\n const cwd = args.cwd ?? process.cwd();\n const monorepoContext = await detectMonorepo(cwd);\n if (!monorepoContext.found || !monorepoContext.rootDir) {\n throw new Error(\n \"Run this command from a Mosaic customer monorepo with a .mosaic-workspace.json file.\",\n );\n }\n\n const workspaceManifest = await readWorkspaceManifest(\n monorepoContext.rootDir,\n );\n const customerSlug = workspaceManifest?.customerSlug;\n if (!customerSlug) {\n throw new Error(\n \".mosaic-workspace.json is missing customerSlug. Recreate the monorepo with a current @percepta/create.\",\n );\n }\n\n const github = args.github ?? createInfraGitHubApi(resolveGitHubToken());\n const blueprintName = `${customerSlug}-os`;\n const branchName = `blueberry/register-${customerSlug}-${appName}`;\n const blueprintPath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"blueprints\",\n `${blueprintName}.blueprint.yaml`,\n ].join(\"/\");\n const servicePath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"services\",\n `${appName}.service.yaml`,\n ].join(\"/\");\n\n const mainBlueprintFile = await github.getFile(\n blueprintPath,\n INFRA_BASE_BRANCH,\n );\n if (!mainBlueprintFile) {\n throw new Error(\n `${blueprintPath} does not exist in ${INFRA_REPOSITORY}. Run \\`pnpm mosaic infra register-os-blueprint\\` and merge that infra PR first.`,\n );\n }\n\n const mainServiceFile = await github.getFile(servicePath, INFRA_BASE_BRANCH);\n const serviceContent =\n mainServiceFile == null\n ? await readLocalServiceDefinition(monorepoContext.rootDir, appName)\n : null;\n const blueprintContent = registerAppInBlueprint(\n mainBlueprintFile.content,\n appName,\n );\n\n const files: InfraPullRequestFile[] = [];\n if (blueprintContent !== mainBlueprintFile.content) {\n files.push({\n baseFileSha: mainBlueprintFile.sha,\n content: blueprintContent,\n message: `Register ${appName} in ${blueprintName}`,\n path: blueprintPath,\n });\n }\n if (serviceContent != null) {\n files.push({\n content: serviceContent,\n message: `Register ${appName} service`,\n path: servicePath,\n });\n }\n\n if (files.length === 0) {\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: null,\n repository: INFRA_REPOSITORY,\n status: \"already_registered\",\n servicePath,\n targetPath: blueprintPath,\n };\n }\n\n const pullRequest = await createOrUpdateInfraPullRequestFiles({\n branchName,\n github,\n files,\n title: `Register ${appName} app`,\n body: [\n `Registers the ${appName} service and deployment in ${blueprintName}.`,\n \"\",\n \"Generated by `mosaic infra register-app`.\",\n ].join(\"\\n\"),\n });\n\n return {\n appName,\n blueprintName,\n blueprintPath,\n branchName,\n customerSlug,\n pullRequestUrl: pullRequest.pullRequestUrl,\n repository: INFRA_REPOSITORY,\n status: pullRequest.status,\n servicePath,\n targetPath: blueprintPath,\n };\n}\n\nexport async function registerAppCommand(appName: string): Promise<void> {\n try {\n const result = await registerApp(appName);\n\n if (result.status === \"already_registered\") {\n console.log(\n chalk.green(\"✔\"),\n `${result.appName} is already registered in ${result.repository} at`,\n chalk.cyan(result.targetPath),\n );\n return;\n }\n\n const verb =\n result.status === \"created_pr\" ? \"Created\" : \"Updated existing\";\n console.log(\n chalk.green(\"✔\"),\n `${verb} infra PR for ${result.appName}:`,\n chalk.cyan(result.pullRequestUrl),\n );\n } catch (error) {\n console.error(chalk.red(\"Error:\"), (error as Error).message);\n process.exit(1);\n }\n}\n\nexport function addAppDatabaseToBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: false,\n appInputs: false,\n });\n}\n\nexport function registerAppInBlueprint(\n blueprintContent: string,\n appName: string,\n): string {\n return updateBlueprint(blueprintContent, appName, {\n appDatabase: true,\n appInstallation: true,\n appInputs: true,\n });\n}\n\nfunction updateBlueprint(\n blueprintContent: string,\n appName: string,\n options: {\n appDatabase: boolean;\n appInstallation: boolean;\n appInputs: boolean;\n },\n): string {\n const document = parseDocument(blueprintContent);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid OS blueprint YAML: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const spec = document.get(\"spec\", true);\n if (!isMap(spec)) {\n throw new Error(\"OS blueprint must include a spec map.\");\n }\n\n let changed = false;\n const inputs = spec.get(\"inputs\", true);\n if (!isSeq(inputs)) {\n throw new Error(\"OS blueprint spec.inputs must be a sequence.\");\n }\n\n changed = ensureInputGroups(document, spec) || changed;\n\n if (options.appInputs) {\n changed =\n addAppInput(document, inputs, renderIngressDomainInput()) || changed;\n changed =\n addAppInput(document, inputs, renderBetterAuthSecretInput(appName)) ||\n changed;\n changed =\n addAppInput(document, inputs, renderLangfusePublicKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderLangfuseSecretKeyInput()) || changed;\n }\n\n if (options.appDatabase) {\n changed = addAppDatabase(document, inputs, appName) || changed;\n }\n\n if (options.appInstallation) {\n const installations = spec.get(\"installations\", true);\n if (!isSeq(installations)) {\n throw new Error(\"OS blueprint spec.installations must be a sequence.\");\n }\n changed = ensureOsPostgresqlInstallationAlias(installations) || changed;\n changed = addAppInstallation(document, installations, appName) || changed;\n }\n\n return changed ? document.toString() : blueprintContent;\n}\n\nfunction ensureInputGroups(\n document: ReturnType<typeof parseDocument>,\n spec: {\n get(key: string, keepScalar?: true): unknown;\n set(key: string, value: unknown): void;\n },\n): boolean {\n let changed = false;\n const inputGroups = spec.get(\"inputGroups\", true);\n\n if (inputGroups == null) {\n spec.set(\"inputGroups\", document.createNode(OS_BLUEPRINT_INPUT_GROUPS));\n return true;\n }\n\n if (!isSeq(inputGroups)) {\n throw new Error(\"OS blueprint spec.inputGroups must be a sequence.\");\n }\n\n for (const group of OS_BLUEPRINT_INPUT_GROUPS) {\n const exists = inputGroups.items.some(\n (item) => isMap(item) && item.get(\"name\") === group.name,\n );\n if (exists) continue;\n\n inputGroups.add(document.createNode(group));\n changed = true;\n }\n\n return changed;\n}\n\nfunction ensureOsPostgresqlInstallationAlias(installations: {\n items: unknown[];\n}): boolean {\n let changed = false;\n\n for (const installation of installations.items) {\n if (!isMap(installation)) continue;\n\n const service = installation.get(\"service\");\n if (\n typeof service !== \"string\" ||\n !OS_POSTGRESQL_TERRAFORM_SERVICES.has(service)\n ) {\n continue;\n }\n\n if (installation.get(\"name\") === OS_POSTGRESQL_TERRAFORM_ALIAS) continue;\n\n installation.set(\"name\", OS_POSTGRESQL_TERRAFORM_ALIAS);\n changed = true;\n }\n\n return changed;\n}\n\nfunction addAppInput(\n document: ReturnType<typeof parseDocument>,\n inputs: { add(value: unknown): void; items: unknown[] },\n input: Record<string, unknown> & { name: string },\n): boolean {\n if (\n inputs.items.some((item) => isMap(item) && item.get(\"name\") === input.name)\n ) {\n return false;\n }\n\n inputs.add(document.createNode(input));\n return true;\n}\n\nfunction addAppDatabase(\n document: ReturnType<typeof parseDocument>,\n inputs: { items: unknown[] },\n appName: string,\n): boolean {\n const appDatabasesInput = inputs.items.find(\n (item) => isMap(item) && item.get(\"name\") === \"app_databases\",\n );\n if (!isMap(appDatabasesInput)) {\n throw new Error(\"OS blueprint must include an app_databases input.\");\n }\n\n const defaultValue = appDatabasesInput.get(\"default\", true);\n if (!isMap(defaultValue)) {\n throw new Error(\"OS blueprint app_databases default must be a map.\");\n }\n\n if (defaultValue.has(appName)) return false;\n\n defaultValue.flow = false;\n const appDatabaseValue = document.createNode({});\n if (isMap(appDatabaseValue)) appDatabaseValue.flow = true;\n defaultValue.set(appName, appDatabaseValue);\n return true;\n}\n\nfunction addAppInstallation(\n document: ReturnType<typeof parseDocument>,\n installations: { add(value: unknown): void; items: unknown[] },\n appName: string,\n): boolean {\n if (\n installations.items.some(\n (item) => isMap(item) && item.get(\"service\") === appName,\n )\n ) {\n return false;\n }\n\n installations.add(\n document.createNode({\n service: appName,\n env: renderAppInstallationEnv(appName),\n config: renderAppInstallationConfig(appName),\n }),\n );\n return true;\n}\n\nfunction renderIngressDomainInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: ingressDomainInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Ingress Domain\",\n description: \"Shared ingress domain for generated OS webapps.\",\n default: '{{ default \"example.local\" .ryvn.env.state.public_domain.name }}',\n };\n}\n\nfunction renderBetterAuthSecretInput(\n appName: string,\n): Record<string, unknown> & { name: string } {\n return {\n name: betterAuthSecretInputName(appName),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: `${toTitleCase(appName)} Better Auth Secret`,\n description: `Generated Better Auth signing secret for ${appName}.`,\n hidden: true,\n generated: {\n type: \"random-bytes\",\n length: 32,\n },\n };\n}\n\nfunction renderLangfusePublicKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfusePublicKeyInputName(),\n type: \"string\",\n group: \"applications\",\n displayName: \"Langfuse Public Key\",\n description:\n \"Shared Langfuse public key for generated OS webapps. Leave empty to disable Langfuse export.\",\n default: \"\",\n };\n}\n\nfunction renderLangfuseSecretKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: langfuseSecretKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Langfuse Secret Key\",\n description:\n \"Shared Langfuse secret key for generated OS webapps. Leave unset to disable Langfuse export.\",\n condition: `{{ ne (input \"${langfusePublicKeyInputName()}\") \"\" }}`,\n };\n}\n\nfunction renderAppInstallationEnv(\n appName: string,\n): Array<Record<string, unknown>> {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n {\n key: \"DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: `app_database_urls.${appName}`,\n },\n },\n {\n key: \"AUTH_DATABASE_URL\",\n isSecret: true,\n valueFromOutput: {\n serviceInstallation: \"os-postgresql-terraform\",\n name: \"auth_database_url\",\n },\n },\n {\n key: \"APP_BASE_URL\",\n value: `https://${appHost}`,\n },\n {\n key: \"DEPLOYMENT_ENVIRONMENT\",\n value: \"{{ EnvironmentName }}\",\n },\n {\n key: \"BETTER_AUTH_SECRET\",\n isSecret: true,\n valueFromInput: {\n name: betterAuthSecretInputName(appName),\n },\n },\n {\n key: \"INNGEST_BASE_URL\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.inngest_base_url }}',\n },\n {\n key: \"LANGFUSE_BASE_URL\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.langfuse_base_url }}',\n },\n {\n key: \"LANGFUSE_PUBLIC_KEY\",\n valueFromInput: {\n name: langfusePublicKeyInputName(),\n },\n },\n {\n key: \"LANGFUSE_SECRET_KEY\",\n isSecret: true,\n valueFromInput: {\n name: langfuseSecretKeyInputName(),\n },\n },\n {\n key: \"OTEL_EXPORTER_OTLP_ENDPOINT\",\n value:\n '{{ (blueprintInstallation \"mosaic\").outputs.otel_exporter_otlp_endpoint }}',\n },\n {\n key: \"SPICEDB_ENDPOINT\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_endpoint }}',\n },\n {\n key: \"SPICEDB_PRESHARED_KEY\",\n isSecret: true,\n value:\n '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_preshared_key }}',\n },\n {\n key: \"SPICEDB_INSECURE\",\n value: '{{ (blueprintInstallation \"mosaic\").outputs.spicedb_insecure }}',\n },\n ];\n}\n\nfunction renderAppInstallationConfig(appName: string): string {\n const appHost = `${appName}.{{ input \"${ingressDomainInputName()}\" }}`;\n\n return [\n \"replicaCount: 1\",\n \"\",\n \"service:\",\n \" port: 3000\",\n \"\",\n \"livenessEnabled: true\",\n \"readinessEnabled: true\",\n \"startupEnabled: true\",\n \"\",\n \"env:\",\n \" - name: INNGEST_EVENT_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestEventKeySecretKeyOutput()} }}'`,\n \" - name: INNGEST_SIGNING_KEY\",\n \" valueFrom:\",\n \" secretKeyRef:\",\n ` name: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestKeysSecretNameOutput()} }}'`,\n ` key: '{{ (blueprintInstallation \"mosaic\").outputs.${mosaicInngestSigningKeySecretKeyOutput()} }}'`,\n \"\",\n \"resources:\",\n \" requests:\",\n ' cpu: \"100m\"',\n \" memory: 256Mi\",\n \" limits:\",\n ' cpu: \"500m\"',\n \" memory: 512Mi\",\n \"\",\n \"ingress:\",\n \" enabled: true\",\n \" className: external-nginx\",\n \" annotations:\",\n \" cert-manager.io/cluster-issuer: external-issuer\",\n ' nginx.ingress.kubernetes.io/ssl-redirect: \"true\"',\n \" hosts:\",\n ` - host: '${appHost}'`,\n \" paths:\",\n \" - path: /\",\n \" pathType: Prefix\",\n \" tls:\",\n ` - secretName: ${appName}-tls`,\n \" hosts:\",\n ` - '${appHost}'`,\n \"\",\n ].join(\"\\n\");\n}\n\nasync function readLocalServiceDefinition(\n monorepoRoot: string,\n appName: string,\n): Promise<string> {\n const serviceDefinitionPath = path.join(\n monorepoRoot,\n \"packages\",\n appName,\n \"deploy\",\n \"ryvn\",\n `${appName}.service.yaml`,\n );\n if (!(await fs.pathExists(serviceDefinitionPath))) {\n throw new Error(\n `${serviceDefinitionPath} does not exist. Add the app's Ryvn service definition before registering it in infra.`,\n );\n }\n\n const content = await fs.readFile(serviceDefinitionPath, \"utf-8\");\n validateLocalServiceDefinition(content, appName, serviceDefinitionPath);\n return content.endsWith(\"\\n\") ? content : `${content}\\n`;\n}\n\nfunction validateLocalServiceDefinition(\n content: string,\n appName: string,\n serviceDefinitionPath: string,\n): void {\n const document = parseDocument(content);\n if (document.errors.length > 0) {\n throw new Error(\n `Invalid Ryvn service YAML at ${serviceDefinitionPath}: ${document.errors.map((error) => error.message).join(\"; \")}`,\n );\n }\n\n const service = document.toJS() as {\n kind?: unknown;\n metadata?: { name?: unknown };\n };\n if (service.kind !== \"Service\" || service.metadata?.name !== appName) {\n throw new Error(\n `${serviceDefinitionPath} must define kind: Service with metadata.name: ${appName}.`,\n );\n }\n}\n\nfunction ingressDomainInputName(): string {\n return \"ingress_domain\";\n}\n\nfunction betterAuthSecretInputName(appName: string): string {\n return `${toSnakeCase(appName)}_better_auth_secret`;\n}\n\nfunction mosaicInngestKeysSecretNameOutput(): string {\n return \"inngest_keys_secret_name\";\n}\n\nfunction mosaicInngestEventKeySecretKeyOutput(): string {\n return \"inngest_event_key_secret_key\";\n}\n\nfunction mosaicInngestSigningKeySecretKeyOutput(): string {\n return \"inngest_signing_key_secret_key\";\n}\n\nfunction langfusePublicKeyInputName(): string {\n return \"langfuse_public_key\";\n}\n\nfunction langfuseSecretKeyInputName(): string {\n return \"langfuse_secret_key\";\n}\n\nfunction normalizeAppName(appNameInput: string): string {\n const appName = toKebabCase(appNameInput);\n const validation = validateProjectName(appName);\n if (!validation.valid) {\n throw new Error(`Invalid app name: ${validation.error}`);\n }\n return appName;\n}\n"],"mappings":";;;;;;;;;AAsBA,MAAM,gCAAgC;AACtC,MAAM,mCAAmC,IAAI,IAAI,CAC/C,+BACA,gCACD,CAAC;AACF,MAAM,4BAA4B;CAChC;EACE,MAAM;EACN,aAAa;EACb,aAAa;EACd;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;EACd;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;EACb,WAAW;EACZ;CACD;EACE,MAAM;EACN,aAAa;EACb,aAAa;EACb,WAAW;EACZ;CACF;AAeD,eAAsB,YACpB,cACA,OAGI,EAAE,EACsB;CAC5B,MAAM,UAAU,iBAAiB,aAAa;CAE9C,MAAM,kBAAkB,MAAM,eADlB,KAAK,OAAO,QAAQ,KAAK,CACY;AACjD,KAAI,CAAC,gBAAgB,SAAS,CAAC,gBAAgB,QAC7C,OAAM,IAAI,MACR,uFACD;CAMH,MAAM,gBAAe,MAHW,sBAC9B,gBAAgB,QACjB,GACuC;AACxC,KAAI,CAAC,aACH,OAAM,IAAI,MACR,yGACD;CAGH,MAAM,SAAS,KAAK,UAAU,qBAAqB,oBAAoB,CAAC;CACxE,MAAM,gBAAgB,GAAG,aAAa;CACtC,MAAM,aAAa,sBAAsB,aAAa,GAAG;CACzD,MAAM,gBAAgB;EACpB;EACA;EACA;EACA;EACA,GAAG,cAAc;EAClB,CAAC,KAAK,IAAI;CACX,MAAM,cAAc;EAClB;EACA;EACA;EACA;EACA,GAAG,QAAQ;EACZ,CAAC,KAAK,IAAI;CAEX,MAAM,oBAAoB,MAAM,OAAO,QACrC,eACA,kBACD;AACD,KAAI,CAAC,kBACH,OAAM,IAAI,MACR,GAAG,cAAc,qBAAqB,iBAAiB,kFACxD;CAIH,MAAM,iBACJ,MAF4B,OAAO,QAAQ,aAAA,OAA+B,IAEvD,OACf,MAAM,2BAA2B,gBAAgB,SAAS,QAAQ,GAClE;CACN,MAAM,mBAAmB,uBACvB,kBAAkB,SAClB,QACD;CAED,MAAM,QAAgC,EAAE;AACxC,KAAI,qBAAqB,kBAAkB,QACzC,OAAM,KAAK;EACT,aAAa,kBAAkB;EAC/B,SAAS;EACT,SAAS,YAAY,QAAQ,MAAM;EACnC,MAAM;EACP,CAAC;AAEJ,KAAI,kBAAkB,KACpB,OAAM,KAAK;EACT,SAAS;EACT,SAAS,YAAY,QAAQ;EAC7B,MAAM;EACP,CAAC;AAGJ,KAAI,MAAM,WAAW,EACnB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,gBAAgB;EAChB,YAAY;EACZ,QAAQ;EACR;EACA,YAAY;EACb;CAGH,MAAM,cAAc,MAAM,oCAAoC;EAC5D;EACA;EACA;EACA,OAAO,YAAY,QAAQ;EAC3B,MAAM;GACJ,iBAAiB,QAAQ,6BAA6B,cAAc;GACpE;GACA;GACD,CAAC,KAAK,KAAK;EACb,CAAC;AAEF,QAAO;EACL;EACA;EACA;EACA;EACA;EACA,gBAAgB,YAAY;EAC5B,YAAY;EACZ,QAAQ,YAAY;EACpB;EACA,YAAY;EACb;;AAGH,eAAsB,mBAAmB,SAAgC;AACvE,KAAI;EACF,MAAM,SAAS,MAAM,YAAY,QAAQ;AAEzC,MAAI,OAAO,WAAW,sBAAsB;AAC1C,WAAQ,IACN,MAAM,MAAM,IAAI,EAChB,GAAG,OAAO,QAAQ,4BAA4B,OAAO,WAAW,MAChE,MAAM,KAAK,OAAO,WAAW,CAC9B;AACD;;EAGF,MAAM,OACJ,OAAO,WAAW,eAAe,YAAY;AAC/C,UAAQ,IACN,MAAM,MAAM,IAAI,EAChB,GAAG,KAAK,gBAAgB,OAAO,QAAQ,IACvC,MAAM,KAAK,OAAO,eAAe,CAClC;UACM,OAAO;AACd,UAAQ,MAAM,MAAM,IAAI,SAAS,EAAG,MAAgB,QAAQ;AAC5D,UAAQ,KAAK,EAAE;;;AAenB,SAAgB,uBACd,kBACA,SACQ;AACR,QAAO,gBAAgB,kBAAkB,SAAS;EAChD,aAAa;EACb,iBAAiB;EACjB,WAAW;EACZ,CAAC;;AAGJ,SAAS,gBACP,kBACA,SACA,SAKQ;CACR,MAAM,WAAW,cAAc,iBAAiB;AAChD,KAAI,SAAS,OAAO,SAAS,EAC3B,OAAM,IAAI,MACR,8BAA8B,SAAS,OAAO,KAAK,UAAU,MAAM,QAAQ,CAAC,KAAK,KAAK,GACvF;CAGH,MAAM,OAAO,SAAS,IAAI,QAAQ,KAAK;AACvC,KAAI,CAAC,MAAM,KAAK,CACd,OAAM,IAAI,MAAM,wCAAwC;CAG1D,IAAI,UAAU;CACd,MAAM,SAAS,KAAK,IAAI,UAAU,KAAK;AACvC,KAAI,CAAC,MAAM,OAAO,CAChB,OAAM,IAAI,MAAM,+CAA+C;AAGjE,WAAU,kBAAkB,UAAU,KAAK,IAAI;AAE/C,KAAI,QAAQ,WAAW;AACrB,YACE,YAAY,UAAU,QAAQ,0BAA0B,CAAC,IAAI;AAC/D,YACE,YAAY,UAAU,QAAQ,4BAA4B,QAAQ,CAAC,IACnE;AACF,YACE,YAAY,UAAU,QAAQ,8BAA8B,CAAC,IAAI;AACnE,YACE,YAAY,UAAU,QAAQ,8BAA8B,CAAC,IAAI;;AAGrE,KAAI,QAAQ,YACV,WAAU,eAAe,UAAU,QAAQ,QAAQ,IAAI;AAGzD,KAAI,QAAQ,iBAAiB;EAC3B,MAAM,gBAAgB,KAAK,IAAI,iBAAiB,KAAK;AACrD,MAAI,CAAC,MAAM,cAAc,CACvB,OAAM,IAAI,MAAM,sDAAsD;AAExE,YAAU,oCAAoC,cAAc,IAAI;AAChE,YAAU,mBAAmB,UAAU,eAAe,QAAQ,IAAI;;AAGpE,QAAO,UAAU,SAAS,UAAU,GAAG;;AAGzC,SAAS,kBACP,UACA,MAIS;CACT,IAAI,UAAU;CACd,MAAM,cAAc,KAAK,IAAI,eAAe,KAAK;AAEjD,KAAI,eAAe,MAAM;AACvB,OAAK,IAAI,eAAe,SAAS,WAAW,0BAA0B,CAAC;AACvE,SAAO;;AAGT,KAAI,CAAC,MAAM,YAAY,CACrB,OAAM,IAAI,MAAM,oDAAoD;AAGtE,MAAK,MAAM,SAAS,2BAA2B;AAI7C,MAHe,YAAY,MAAM,MAC9B,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,KAE5C,CAAE;AAEZ,cAAY,IAAI,SAAS,WAAW,MAAM,CAAC;AAC3C,YAAU;;AAGZ,QAAO;;AAGT,SAAS,oCAAoC,eAEjC;CACV,IAAI,UAAU;AAEd,MAAK,MAAM,gBAAgB,cAAc,OAAO;AAC9C,MAAI,CAAC,MAAM,aAAa,CAAE;EAE1B,MAAM,UAAU,aAAa,IAAI,UAAU;AAC3C,MACE,OAAO,YAAY,YACnB,CAAC,iCAAiC,IAAI,QAAQ,CAE9C;AAGF,MAAI,aAAa,IAAI,OAAO,KAAK,8BAA+B;AAEhE,eAAa,IAAI,QAAQ,8BAA8B;AACvD,YAAU;;AAGZ,QAAO;;AAGT,SAAS,YACP,UACA,QACA,OACS;AACT,KACE,OAAO,MAAM,MAAM,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,MAAM,KAAK,CAE3E,QAAO;AAGT,QAAO,IAAI,SAAS,WAAW,MAAM,CAAC;AACtC,QAAO;;AAGT,SAAS,eACP,UACA,QACA,SACS;CACT,MAAM,oBAAoB,OAAO,MAAM,MACpC,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,OAAO,KAAK,gBAC/C;AACD,KAAI,CAAC,MAAM,kBAAkB,CAC3B,OAAM,IAAI,MAAM,oDAAoD;CAGtE,MAAM,eAAe,kBAAkB,IAAI,WAAW,KAAK;AAC3D,KAAI,CAAC,MAAM,aAAa,CACtB,OAAM,IAAI,MAAM,oDAAoD;AAGtE,KAAI,aAAa,IAAI,QAAQ,CAAE,QAAO;AAEtC,cAAa,OAAO;CACpB,MAAM,mBAAmB,SAAS,WAAW,EAAE,CAAC;AAChD,KAAI,MAAM,iBAAiB,CAAE,kBAAiB,OAAO;AACrD,cAAa,IAAI,SAAS,iBAAiB;AAC3C,QAAO;;AAGT,SAAS,mBACP,UACA,eACA,SACS;AACT,KACE,cAAc,MAAM,MACjB,SAAS,MAAM,KAAK,IAAI,KAAK,IAAI,UAAU,KAAK,QAClD,CAED,QAAO;AAGT,eAAc,IACZ,SAAS,WAAW;EAClB,SAAS;EACT,KAAK,yBAAyB,QAAQ;EACtC,QAAQ,4BAA4B,QAAQ;EAC7C,CAAC,CACH;AACD,QAAO;;AAGT,SAAS,2BAEP;AACA,QAAO;EACL,MAAM,wBAAwB;EAC9B,MAAM;EACN,OAAO;EACP,aAAa;EACb,aAAa;EACb,SAAS;EACV;;AAGH,SAAS,4BACP,SAC4C;AAC5C,QAAO;EACL,MAAM,0BAA0B,QAAQ;EACxC,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa,GAAG,YAAY,QAAQ,CAAC;EACrC,aAAa,4CAA4C,QAAQ;EACjE,QAAQ;EACR,WAAW;GACT,MAAM;GACN,QAAQ;GACT;EACF;;AAGH,SAAS,+BAEP;AACA,QAAO;EACL,MAAM,4BAA4B;EAClC,MAAM;EACN,OAAO;EACP,aAAa;EACb,aACE;EACF,SAAS;EACV;;AAGH,SAAS,+BAEP;AACA,QAAO;EACL,MAAM,4BAA4B;EAClC,MAAM;EACN,UAAU;EACV,OAAO;EACP,aAAa;EACb,aACE;EACF,WAAW,iBAAiB,4BAA4B,CAAC;EAC1D;;AAGH,SAAS,yBACP,SACgC;CAChC,MAAM,UAAU,GAAG,QAAQ,aAAa,wBAAwB,CAAC;AAEjE,QAAO;EACL;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM,qBAAqB;IAC5B;GACF;EACD;GACE,KAAK;GACL,UAAU;GACV,iBAAiB;IACf,qBAAqB;IACrB,MAAM;IACP;GACF;EACD;GACE,KAAK;GACL,OAAO,WAAW;GACnB;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,0BAA0B,QAAQ,EACzC;GACF;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,gBAAgB,EACd,MAAM,4BAA4B,EACnC;GACF;EACD;GACE,KAAK;GACL,UAAU;GACV,gBAAgB,EACd,MAAM,4BAA4B,EACnC;GACF;EACD;GACE,KAAK;GACL,OACE;GACH;EACD;GACE,KAAK;GACL,OAAO;GACR;EACD;GACE,KAAK;GACL,UAAU;GACV,OACE;GACH;EACD;GACE,KAAK;GACL,OAAO;GACR;EACF;;AAGH,SAAS,4BAA4B,SAAyB;CAC5D,MAAM,UAAU,GAAG,QAAQ,aAAa,wBAAwB,CAAC;AAEjE,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,8DAA8D,mCAAmC,CAAC;EAClG,6DAA6D,sCAAsC,CAAC;EACpG;EACA;EACA;EACA,8DAA8D,mCAAmC,CAAC;EAClG,6DAA6D,wCAAwC,CAAC;EACtG;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,gBAAgB,QAAQ;EACxB;EACA;EACA;EACA;EACA,qBAAqB,QAAQ;EAC7B;EACA,cAAc,QAAQ;EACtB;EACD,CAAC,KAAK,KAAK;;AAGd,eAAe,2BACb,cACA,SACiB;CACjB,MAAM,wBAAwB,KAAK,KACjC,cACA,YACA,SACA,UACA,QACA,GAAG,QAAQ,eACZ;AACD,KAAI,CAAE,MAAM,GAAG,WAAW,sBAAsB,CAC9C,OAAM,IAAI,MACR,GAAG,sBAAsB,wFAC1B;CAGH,MAAM,UAAU,MAAM,GAAG,SAAS,uBAAuB,QAAQ;AACjE,gCAA+B,SAAS,SAAS,sBAAsB;AACvE,QAAO,QAAQ,SAAS,KAAK,GAAG,UAAU,GAAG,QAAQ;;AAGvD,SAAS,+BACP,SACA,SACA,uBACM;CACN,MAAM,WAAW,cAAc,QAAQ;AACvC,KAAI,SAAS,OAAO,SAAS,EAC3B,OAAM,IAAI,MACR,gCAAgC,sBAAsB,IAAI,SAAS,OAAO,KAAK,UAAU,MAAM,QAAQ,CAAC,KAAK,KAAK,GACnH;CAGH,MAAM,UAAU,SAAS,MAAM;AAI/B,KAAI,QAAQ,SAAS,aAAa,QAAQ,UAAU,SAAS,QAC3D,OAAM,IAAI,MACR,GAAG,sBAAsB,iDAAiD,QAAQ,GACnF;;AAIL,SAAS,yBAAiC;AACxC,QAAO;;AAGT,SAAS,0BAA0B,SAAyB;AAC1D,QAAO,GAAG,YAAY,QAAQ,CAAC;;AAGjC,SAAS,oCAA4C;AACnD,QAAO;;AAGT,SAAS,uCAA+C;AACtD,QAAO;;AAGT,SAAS,yCAAiD;AACxD,QAAO;;AAGT,SAAS,6BAAqC;AAC5C,QAAO;;AAGT,SAAS,6BAAqC;AAC5C,QAAO;;AAGT,SAAS,iBAAiB,cAA8B;CACtD,MAAM,UAAU,YAAY,aAAa;CACzC,MAAM,aAAa,oBAAoB,QAAQ;AAC/C,KAAI,CAAC,WAAW,MACd,OAAM,IAAI,MAAM,qBAAqB,WAAW,QAAQ;AAE1D,QAAO"}
@@ -1,4 +1,4 @@
1
- import { r as readManifest } from "./manifest-CqIDnbgs.js";
1
+ import { r as readManifest } from "./manifest-By1SgOjC.js";
2
2
  import { i as getTemplateVersionFromTag, n as getLatestTemplateTag } from "./git-ops-BD7JNnal.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
@@ -45,4 +45,4 @@ async function statusCommand(options) {
45
45
  //#endregion
46
46
  export { statusCommand };
47
47
 
48
- //# sourceMappingURL=status-BXYaQ4a2.js.map
48
+ //# sourceMappingURL=status-K6raTwwu.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"status-BXYaQ4a2.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n} from \"../utils/git-ops.js\";\nimport { readManifest } from \"../utils/manifest.js\";\n\nexport interface StatusOptions {\n mosaicTemplatePath?: string;\n}\n\nexport async function statusCommand(options: StatusOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n\n console.log();\n console.log(chalk.bold(\"Mosaic Template Status\"));\n console.log();\n console.log(chalk.dim(\" Template type:\"), manifest.templateType);\n console.log(chalk.dim(\" Current version:\"), manifest.templateVersion);\n console.log(chalk.dim(\" Template commit:\"), manifest.templateCommit);\n console.log(chalk.dim(\" Created:\"), manifest.createdAt);\n if (manifest.lastSyncedAt) {\n console.log(chalk.dim(\" Last synced:\"), manifest.lastSyncedAt);\n }\n\n const rawPath =\n options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;\n const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : undefined;\n\n if (mosaicTemplatePath) {\n const latestTag = getLatestTemplateTag(\n manifest.templateType,\n mosaicTemplatePath,\n );\n if (latestTag) {\n const latestVersion = getTemplateVersionFromTag(latestTag);\n console.log(chalk.dim(\" Latest version:\"), latestVersion);\n console.log();\n\n if (latestVersion !== manifest.templateVersion) {\n console.log(\n chalk.yellow(\n ` Update available: ${manifest.templateVersion} → ${latestVersion}`,\n ),\n );\n console.log(\n chalk.dim(\" Run:\"),\n `create sync --mosaic-template-path ${mosaicTemplatePath}`,\n );\n } else {\n console.log(chalk.green(\" Up to date\"));\n }\n } else {\n console.log();\n console.log(chalk.yellow(\" No template tags found in mosaic repo.\"));\n console.log(\n chalk.dim(\" Run:\"),\n `cd ${mosaicTemplatePath} && pnpm template:tag`,\n );\n }\n } else {\n console.log();\n console.log(\n chalk.dim(\n \" Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates\",\n ),\n );\n }\n\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;AAYA,eAAsB,cAAc,SAAuC;CACzE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,yBAAyB,CAAC;AACjD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,mBAAmB,EAAE,SAAS,aAAa;AACjE,UAAQ,IAAI,MAAM,IAAI,qBAAqB,EAAE,SAAS,gBAAgB;AACtE,UAAQ,IAAI,MAAM,IAAI,qBAAqB,EAAE,SAAS,eAAe;AACrE,UAAQ,IAAI,MAAM,IAAI,aAAa,EAAE,SAAS,UAAU;AACxD,MAAI,SAAS,aACX,SAAQ,IAAI,MAAM,IAAI,iBAAiB,EAAE,SAAS,aAAa;EAGjE,MAAM,UACJ,QAAQ,sBAAsB,QAAQ,IAAI;EAC5C,MAAM,qBAAqB,UAAU,KAAK,QAAQ,QAAQ,GAAG,KAAA;AAE7D,MAAI,oBAAoB;GACtB,MAAM,YAAY,qBAChB,SAAS,cACT,mBACD;AACD,OAAI,WAAW;IACb,MAAM,gBAAgB,0BAA0B,UAAU;AAC1D,YAAQ,IAAI,MAAM,IAAI,oBAAoB,EAAE,cAAc;AAC1D,YAAQ,KAAK;AAEb,QAAI,kBAAkB,SAAS,iBAAiB;AAC9C,aAAQ,IACN,MAAM,OACJ,uBAAuB,SAAS,gBAAgB,KAAK,gBACtD,CACF;AACD,aAAQ,IACN,MAAM,IAAI,SAAS,EACnB,sCAAsC,qBACvC;UAED,SAAQ,IAAI,MAAM,MAAM,eAAe,CAAC;UAErC;AACL,YAAQ,KAAK;AACb,YAAQ,IAAI,MAAM,OAAO,2CAA2C,CAAC;AACrE,YAAQ,IACN,MAAM,IAAI,SAAS,EACnB,MAAM,mBAAmB,uBAC1B;;SAEE;AACL,WAAQ,KAAK;AACb,WAAQ,IACN,MAAM,IACJ,gFACD,CACF;;AAGH,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"status-K6raTwwu.js","names":[],"sources":["../src/commands/status.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n} from \"../utils/git-ops.js\";\nimport { readManifest } from \"../utils/manifest.js\";\n\nexport interface StatusOptions {\n mosaicTemplatePath?: string;\n}\n\nexport async function statusCommand(options: StatusOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n\n console.log();\n console.log(chalk.bold(\"Mosaic Template Status\"));\n console.log();\n console.log(chalk.dim(\" Template type:\"), manifest.templateType);\n console.log(chalk.dim(\" Current version:\"), manifest.templateVersion);\n console.log(chalk.dim(\" Template commit:\"), manifest.templateCommit);\n console.log(chalk.dim(\" Created:\"), manifest.createdAt);\n if (manifest.lastSyncedAt) {\n console.log(chalk.dim(\" Last synced:\"), manifest.lastSyncedAt);\n }\n\n const rawPath =\n options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;\n const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : undefined;\n\n if (mosaicTemplatePath) {\n const latestTag = getLatestTemplateTag(\n manifest.templateType,\n mosaicTemplatePath,\n );\n if (latestTag) {\n const latestVersion = getTemplateVersionFromTag(latestTag);\n console.log(chalk.dim(\" Latest version:\"), latestVersion);\n console.log();\n\n if (latestVersion !== manifest.templateVersion) {\n console.log(\n chalk.yellow(\n ` Update available: ${manifest.templateVersion} → ${latestVersion}`,\n ),\n );\n console.log(\n chalk.dim(\" Run:\"),\n `create sync --mosaic-template-path ${mosaicTemplatePath}`,\n );\n } else {\n console.log(chalk.green(\" Up to date\"));\n }\n } else {\n console.log();\n console.log(chalk.yellow(\" No template tags found in mosaic repo.\"));\n console.log(\n chalk.dim(\" Run:\"),\n `cd ${mosaicTemplatePath} && pnpm template:tag`,\n );\n }\n } else {\n console.log();\n console.log(\n chalk.dim(\n \" Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates\",\n ),\n );\n }\n\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;AAYA,eAAsB,cAAc,SAAuC;CACzE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,yBAAyB,CAAC;AACjD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,mBAAmB,EAAE,SAAS,aAAa;AACjE,UAAQ,IAAI,MAAM,IAAI,qBAAqB,EAAE,SAAS,gBAAgB;AACtE,UAAQ,IAAI,MAAM,IAAI,qBAAqB,EAAE,SAAS,eAAe;AACrE,UAAQ,IAAI,MAAM,IAAI,aAAa,EAAE,SAAS,UAAU;AACxD,MAAI,SAAS,aACX,SAAQ,IAAI,MAAM,IAAI,iBAAiB,EAAE,SAAS,aAAa;EAGjE,MAAM,UACJ,QAAQ,sBAAsB,QAAQ,IAAI;EAC5C,MAAM,qBAAqB,UAAU,KAAK,QAAQ,QAAQ,GAAG,KAAA;AAE7D,MAAI,oBAAoB;GACtB,MAAM,YAAY,qBAChB,SAAS,cACT,mBACD;AACD,OAAI,WAAW;IACb,MAAM,gBAAgB,0BAA0B,UAAU;AAC1D,YAAQ,IAAI,MAAM,IAAI,oBAAoB,EAAE,cAAc;AAC1D,YAAQ,KAAK;AAEb,QAAI,kBAAkB,SAAS,iBAAiB;AAC9C,aAAQ,IACN,MAAM,OACJ,uBAAuB,SAAS,gBAAgB,KAAK,gBACtD,CACF;AACD,aAAQ,IACN,MAAM,IAAI,SAAS,EACnB,sCAAsC,qBACvC;UAED,SAAQ,IAAI,MAAM,MAAM,eAAe,CAAC;UAErC;AACL,YAAQ,KAAK;AACb,YAAQ,IAAI,MAAM,OAAO,2CAA2C,CAAC;AACrE,YAAQ,IACN,MAAM,IAAI,SAAS,EACnB,MAAM,mBAAmB,uBAC1B;;SAEE;AACL,WAAQ,KAAK;AACb,WAAQ,IACN,MAAM,IACJ,gFACD,CACF;;AAGH,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
@@ -1,4 +1,4 @@
1
- import { i as resolveMosaicTemplatePath, r as readManifest } from "./manifest-CqIDnbgs.js";
1
+ import { i as resolveMosaicTemplatePath, r as readManifest } from "./manifest-By1SgOjC.js";
2
2
  import { i as getTemplateVersionFromTag, n as getLatestTemplateTag, r as getTemplateDiff } from "./git-ops-BD7JNnal.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
@@ -98,4 +98,4 @@ async function syncCommand(options) {
98
98
  //#endregion
99
99
  export { syncCommand };
100
100
 
101
- //# sourceMappingURL=sync-BayU4w1j.js.map
101
+ //# sourceMappingURL=sync-Bi958-2W.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-BayU4w1j.js","names":[],"sources":["../src/commands/sync.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n getTemplateDiff,\n} from \"../utils/git-ops.js\";\nimport {\n readManifest,\n resolveMosaicTemplatePath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface SyncOptions {\n mosaicTemplatePath?: string;\n to?: string;\n}\n\nfunction generateSyncContext(\n manifest: MosaicManifest,\n toVersion: string,\n diff: string,\n notes: string,\n): string {\n let content = `# Mosaic Sync Context\n\n## App Info\n- **Template:** ${manifest.templateType}\n- **Current version:** ${manifest.templateVersion}\n- **Target version:** ${toVersion}\n\n## Placeholder Mappings\n\nWhen applying template changes, replace these placeholder tokens with the actual values:\n\n| Placeholder | Value |\n|------------|-------|\n${Object.entries(manifest.placeholders)\n .map(([k, v]) => `| \\`${k}\\` | \\`${v}\\` |`)\n .join(\"\\n\")}\n\n## Template Changes (${manifest.templateVersion} → ${toVersion})\n\n\\`\\`\\`diff\n${diff}\n\\`\\`\\`\n`;\n\n if (notes.trim()) {\n content += `\n## Divergence Notes (from mosaic-template-notes.md)\n\n${notes}\n`;\n }\n\n content += `\n## Instructions\n\n1. Apply the template changes above to this app\n2. When you see placeholder tokens (e.g. \\`__APP_NAME__\\`), replace them with the actual values from the mapping table\n3. Check the divergence notes — preserve intentional divergences\n4. For files not modified locally: apply changes directly\n5. For files modified locally: merge intelligently, preserving local customizations\n6. After applying all changes, run: \\`pnpm install && pnpm build && pnpm lint\\`\n7. Update \\`.mosaic-template.json\\`: set \\`templateVersion\\` to \\`\"${toVersion}\"\\` and update \\`templateCommit\\`\n8. If you made decisions about merge conflicts, add notes to \\`mosaic-template-notes.md\\`\n9. Delete this file (\\`.mosaic-sync-context.md\\`) when done\n`;\n\n return content;\n}\n\nexport async function syncCommand(options: SyncOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n const mosaicTemplatePath = resolveMosaicTemplatePath(options);\n\n const fromTag = `template/${manifest.templateType}/${manifest.templateVersion}`;\n\n let toTag: string;\n if (options.to) {\n toTag = `template/${manifest.templateType}/${options.to}`;\n } else {\n const latest = getLatestTemplateTag(\n manifest.templateType,\n mosaicTemplatePath,\n );\n if (!latest) {\n console.error(\n chalk.red(\n \"No template tags found. Run 'pnpm template:tag' in the mosaic repo first.\",\n ),\n );\n process.exit(1);\n }\n toTag = latest;\n }\n\n const toVersion = getTemplateVersionFromTag(toTag);\n\n if (toVersion === manifest.templateVersion) {\n console.log(chalk.green(\"Already up to date.\"));\n return;\n }\n\n const diff = getTemplateDiff(\n mosaicTemplatePath,\n manifest.source.templatePath,\n fromTag,\n toTag,\n );\n\n if (!diff.trim()) {\n console.log(chalk.green(\"No template file changes between versions.\"));\n return;\n }\n\n // Read mosaic-template-notes.md if it exists\n const notesPath = path.join(cwd, \"mosaic-template-notes.md\");\n let notes = \"\";\n if (await fs.pathExists(notesPath)) {\n notes = await fs.readFile(notesPath, \"utf-8\");\n }\n\n const context = generateSyncContext(manifest, toVersion, diff, notes);\n const contextPath = path.join(cwd, \".mosaic-sync-context.md\");\n await fs.writeFile(contextPath, context);\n\n console.log();\n console.log(chalk.bold(\"Sync Context Generated\"));\n console.log();\n console.log(chalk.dim(\" From:\"), manifest.templateVersion);\n console.log(chalk.dim(\" To:\"), toVersion);\n console.log(chalk.dim(\" Context file:\"), \".mosaic-sync-context.md\");\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code in this directory\");\n console.log(\n chalk.dim(\" 2.\"),\n 'Tell Claude: \"Read .mosaic-sync-context.md and apply the template changes\"',\n );\n console.log(\n chalk.dim(\" 3.\"),\n \"Review Claude's changes, then delete .mosaic-sync-context.md\",\n );\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;AAmBA,SAAS,oBACP,UACA,WACA,MACA,OACQ;CACR,IAAI,UAAU;;;kBAGE,SAAS,aAAa;yBACf,SAAS,gBAAgB;wBAC1B,UAAU;;;;;;;;EAQhC,OAAO,QAAQ,SAAS,aAAa,CACpC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,MAAM,CAC1C,KAAK,KAAK,CAAC;;uBAES,SAAS,gBAAgB,KAAK,UAAU;;;EAG7D,KAAK;;;AAIL,KAAI,MAAM,MAAM,CACd,YAAW;;;EAGb,MAAM;;AAIN,YAAW;;;;;;;;;qEASwD,UAAU;;;;AAK7E,QAAO;;AAGT,eAAsB,YAAY,SAAqC;CACrE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;EACxC,MAAM,qBAAqB,0BAA0B,QAAQ;EAE7D,MAAM,UAAU,YAAY,SAAS,aAAa,GAAG,SAAS;EAE9D,IAAI;AACJ,MAAI,QAAQ,GACV,SAAQ,YAAY,SAAS,aAAa,GAAG,QAAQ;OAChD;GACL,MAAM,SAAS,qBACb,SAAS,cACT,mBACD;AACD,OAAI,CAAC,QAAQ;AACX,YAAQ,MACN,MAAM,IACJ,4EACD,CACF;AACD,YAAQ,KAAK,EAAE;;AAEjB,WAAQ;;EAGV,MAAM,YAAY,0BAA0B,MAAM;AAElD,MAAI,cAAc,SAAS,iBAAiB;AAC1C,WAAQ,IAAI,MAAM,MAAM,sBAAsB,CAAC;AAC/C;;EAGF,MAAM,OAAO,gBACX,oBACA,SAAS,OAAO,cAChB,SACA,MACD;AAED,MAAI,CAAC,KAAK,MAAM,EAAE;AAChB,WAAQ,IAAI,MAAM,MAAM,6CAA6C,CAAC;AACtE;;EAIF,MAAM,YAAY,KAAK,KAAK,KAAK,2BAA2B;EAC5D,IAAI,QAAQ;AACZ,MAAI,MAAM,GAAG,WAAW,UAAU,CAChC,SAAQ,MAAM,GAAG,SAAS,WAAW,QAAQ;EAG/C,MAAM,UAAU,oBAAoB,UAAU,WAAW,MAAM,MAAM;EACrE,MAAM,cAAc,KAAK,KAAK,KAAK,0BAA0B;AAC7D,QAAM,GAAG,UAAU,aAAa,QAAQ;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,yBAAyB,CAAC;AACjD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,UAAU,EAAE,SAAS,gBAAgB;AAC3D,UAAQ,IAAI,MAAM,IAAI,QAAQ,EAAE,UAAU;AAC1C,UAAQ,IAAI,MAAM,IAAI,kBAAkB,EAAE,0BAA0B;AACpE,UAAQ,KAAK;AACb,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,qCAAqC;AACpE,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,+EACD;AACD,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,+DACD;AACD,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"sync-Bi958-2W.js","names":[],"sources":["../src/commands/sync.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport {\n getLatestTemplateTag,\n getTemplateVersionFromTag,\n getTemplateDiff,\n} from \"../utils/git-ops.js\";\nimport {\n readManifest,\n resolveMosaicTemplatePath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface SyncOptions {\n mosaicTemplatePath?: string;\n to?: string;\n}\n\nfunction generateSyncContext(\n manifest: MosaicManifest,\n toVersion: string,\n diff: string,\n notes: string,\n): string {\n let content = `# Mosaic Sync Context\n\n## App Info\n- **Template:** ${manifest.templateType}\n- **Current version:** ${manifest.templateVersion}\n- **Target version:** ${toVersion}\n\n## Placeholder Mappings\n\nWhen applying template changes, replace these placeholder tokens with the actual values:\n\n| Placeholder | Value |\n|------------|-------|\n${Object.entries(manifest.placeholders)\n .map(([k, v]) => `| \\`${k}\\` | \\`${v}\\` |`)\n .join(\"\\n\")}\n\n## Template Changes (${manifest.templateVersion} → ${toVersion})\n\n\\`\\`\\`diff\n${diff}\n\\`\\`\\`\n`;\n\n if (notes.trim()) {\n content += `\n## Divergence Notes (from mosaic-template-notes.md)\n\n${notes}\n`;\n }\n\n content += `\n## Instructions\n\n1. Apply the template changes above to this app\n2. When you see placeholder tokens (e.g. \\`__APP_NAME__\\`), replace them with the actual values from the mapping table\n3. Check the divergence notes — preserve intentional divergences\n4. For files not modified locally: apply changes directly\n5. For files modified locally: merge intelligently, preserving local customizations\n6. After applying all changes, run: \\`pnpm install && pnpm build && pnpm lint\\`\n7. Update \\`.mosaic-template.json\\`: set \\`templateVersion\\` to \\`\"${toVersion}\"\\` and update \\`templateCommit\\`\n8. If you made decisions about merge conflicts, add notes to \\`mosaic-template-notes.md\\`\n9. Delete this file (\\`.mosaic-sync-context.md\\`) when done\n`;\n\n return content;\n}\n\nexport async function syncCommand(options: SyncOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n const mosaicTemplatePath = resolveMosaicTemplatePath(options);\n\n const fromTag = `template/${manifest.templateType}/${manifest.templateVersion}`;\n\n let toTag: string;\n if (options.to) {\n toTag = `template/${manifest.templateType}/${options.to}`;\n } else {\n const latest = getLatestTemplateTag(\n manifest.templateType,\n mosaicTemplatePath,\n );\n if (!latest) {\n console.error(\n chalk.red(\n \"No template tags found. Run 'pnpm template:tag' in the mosaic repo first.\",\n ),\n );\n process.exit(1);\n }\n toTag = latest;\n }\n\n const toVersion = getTemplateVersionFromTag(toTag);\n\n if (toVersion === manifest.templateVersion) {\n console.log(chalk.green(\"Already up to date.\"));\n return;\n }\n\n const diff = getTemplateDiff(\n mosaicTemplatePath,\n manifest.source.templatePath,\n fromTag,\n toTag,\n );\n\n if (!diff.trim()) {\n console.log(chalk.green(\"No template file changes between versions.\"));\n return;\n }\n\n // Read mosaic-template-notes.md if it exists\n const notesPath = path.join(cwd, \"mosaic-template-notes.md\");\n let notes = \"\";\n if (await fs.pathExists(notesPath)) {\n notes = await fs.readFile(notesPath, \"utf-8\");\n }\n\n const context = generateSyncContext(manifest, toVersion, diff, notes);\n const contextPath = path.join(cwd, \".mosaic-sync-context.md\");\n await fs.writeFile(contextPath, context);\n\n console.log();\n console.log(chalk.bold(\"Sync Context Generated\"));\n console.log();\n console.log(chalk.dim(\" From:\"), manifest.templateVersion);\n console.log(chalk.dim(\" To:\"), toVersion);\n console.log(chalk.dim(\" Context file:\"), \".mosaic-sync-context.md\");\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code in this directory\");\n console.log(\n chalk.dim(\" 2.\"),\n 'Tell Claude: \"Read .mosaic-sync-context.md and apply the template changes\"',\n );\n console.log(\n chalk.dim(\" 3.\"),\n \"Review Claude's changes, then delete .mosaic-sync-context.md\",\n );\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;AAmBA,SAAS,oBACP,UACA,WACA,MACA,OACQ;CACR,IAAI,UAAU;;;kBAGE,SAAS,aAAa;yBACf,SAAS,gBAAgB;wBAC1B,UAAU;;;;;;;;EAQhC,OAAO,QAAQ,SAAS,aAAa,CACpC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,MAAM,CAC1C,KAAK,KAAK,CAAC;;uBAES,SAAS,gBAAgB,KAAK,UAAU;;;EAG7D,KAAK;;;AAIL,KAAI,MAAM,MAAM,CACd,YAAW;;;EAGb,MAAM;;AAIN,YAAW;;;;;;;;;qEASwD,UAAU;;;;AAK7E,QAAO;;AAGT,eAAsB,YAAY,SAAqC;CACrE,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;EACxC,MAAM,qBAAqB,0BAA0B,QAAQ;EAE7D,MAAM,UAAU,YAAY,SAAS,aAAa,GAAG,SAAS;EAE9D,IAAI;AACJ,MAAI,QAAQ,GACV,SAAQ,YAAY,SAAS,aAAa,GAAG,QAAQ;OAChD;GACL,MAAM,SAAS,qBACb,SAAS,cACT,mBACD;AACD,OAAI,CAAC,QAAQ;AACX,YAAQ,MACN,MAAM,IACJ,4EACD,CACF;AACD,YAAQ,KAAK,EAAE;;AAEjB,WAAQ;;EAGV,MAAM,YAAY,0BAA0B,MAAM;AAElD,MAAI,cAAc,SAAS,iBAAiB;AAC1C,WAAQ,IAAI,MAAM,MAAM,sBAAsB,CAAC;AAC/C;;EAGF,MAAM,OAAO,gBACX,oBACA,SAAS,OAAO,cAChB,SACA,MACD;AAED,MAAI,CAAC,KAAK,MAAM,EAAE;AAChB,WAAQ,IAAI,MAAM,MAAM,6CAA6C,CAAC;AACtE;;EAIF,MAAM,YAAY,KAAK,KAAK,KAAK,2BAA2B;EAC5D,IAAI,QAAQ;AACZ,MAAI,MAAM,GAAG,WAAW,UAAU,CAChC,SAAQ,MAAM,GAAG,SAAS,WAAW,QAAQ;EAG/C,MAAM,UAAU,oBAAoB,UAAU,WAAW,MAAM,MAAM;EACrE,MAAM,cAAc,KAAK,KAAK,KAAK,0BAA0B;AAC7D,QAAM,GAAG,UAAU,aAAa,QAAQ;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,yBAAyB,CAAC;AACjD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,UAAU,EAAE,SAAS,gBAAgB;AAC3D,UAAQ,IAAI,MAAM,IAAI,QAAQ,EAAE,UAAU;AAC1C,UAAQ,IAAI,MAAM,IAAI,kBAAkB,EAAE,0BAA0B;AACpE,UAAQ,KAAK;AACb,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,qCAAqC;AACpE,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,+EACD;AACD,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,+DACD;AACD,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
@@ -1,4 +1,4 @@
1
- import { i as resolveMosaicTemplatePath, r as readManifest } from "./manifest-CqIDnbgs.js";
1
+ import { i as resolveMosaicTemplatePath, r as readManifest } from "./manifest-By1SgOjC.js";
2
2
  import { t as getFileAtTag } from "./git-ops-BD7JNnal.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
@@ -82,4 +82,4 @@ async function upstreamCommand(options) {
82
82
  //#endregion
83
83
  export { upstreamCommand };
84
84
 
85
- //# sourceMappingURL=upstream-CZEzLrS4.js.map
85
+ //# sourceMappingURL=upstream-CAraZeSS.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upstream-CZEzLrS4.js","names":[],"sources":["../src/commands/upstream.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { getFileAtTag } from \"../utils/git-ops.js\";\nimport {\n readManifest,\n resolveMosaicTemplatePath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface UpstreamOptions {\n mosaicTemplatePath?: string;\n files?: string[];\n}\n\nasync function generateUpstreamContext(\n manifest: MosaicManifest,\n mosaicTemplatePath: string,\n tag: string,\n appDir: string,\n files: string[],\n): Promise<string> {\n let content = `# Mosaic Upstream Context\n\n## App Info\n- **App name:** ${manifest.placeholders.__APP_NAME__ || \"unknown\"}\n- **Template:** ${manifest.templateType}\n- **Template version:** ${manifest.templateVersion}\n\n## Placeholder Mappings\n\nWhen generalizing app code back to template, replace these values with placeholder tokens:\n\n| Value | Placeholder |\n|-------|------------|\n${Object.entries(manifest.placeholders)\n .sort((a, b) => b[1].length - a[1].length) // longest first to avoid partial matches\n .map(([k, v]) => `| \\`${v}\\` | \\`${k}\\` |`)\n .join(\"\\n\")}\n\n## Files to Review\n\n`;\n\n for (const file of files) {\n const appFilePath = path.resolve(appDir, file);\n const templateRelPath = `${manifest.source.templatePath}/${file}`;\n\n const appContent = (await fs.pathExists(appFilePath))\n ? await fs.readFile(appFilePath, \"utf-8\")\n : null;\n const templateContent = getFileAtTag(\n mosaicTemplatePath,\n tag,\n templateRelPath,\n );\n\n content += `### ${file}\\n\\n`;\n\n if (!templateContent && appContent) {\n content += `**New file** (not in template at ${manifest.templateVersion})\\n\\n`;\n content += `\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n } else if (templateContent && !appContent) {\n content += `**Deleted** (exists in template but not in app)\\n\\n`;\n } else if (appContent && templateContent) {\n content += `**App version:**\\n\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n content += `**Template version (at ${manifest.templateVersion}):**\\n\\`\\`\\`\\n${templateContent}\\n\\`\\`\\`\\n\\n`;\n } else {\n content += `**Not found** (file does not exist in app or template)\\n\\n`;\n }\n }\n\n content += `## Instructions\n\n1. Review each file above\n2. Determine which changes are generalizable (useful for all apps) vs app-specific\n3. For generalizable changes: apply them to the template at \\`${manifest.source.templatePath}/\\`\n4. When applying, replace app-specific values with placeholders using the mapping table above (replace longest values first)\n5. After applying, bump the version in \\`packages/blueberry/template-versions.json\\`\n6. Run \\`pnpm template:tag\\` to create the new version tag\n7. Delete this file (\\`.mosaic-upstream-context.md\\`) when done\n`;\n\n return content;\n}\n\nexport async function upstreamCommand(options: UpstreamOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n const mosaicTemplatePath = resolveMosaicTemplatePath(options);\n\n if (!options.files || options.files.length === 0) {\n console.error(\n chalk.red(\"Specify files with --files <file1> <file2> ...\"),\n );\n console.log(\n chalk.dim(\n \" Example: create upstream --files src/config/getEnvConfig.ts\",\n ),\n );\n process.exit(1);\n }\n\n const tag = `template/${manifest.templateType}/${manifest.templateVersion}`;\n\n const context = await generateUpstreamContext(\n manifest,\n mosaicTemplatePath,\n tag,\n cwd,\n options.files,\n );\n const contextPath = path.join(cwd, \".mosaic-upstream-context.md\");\n await fs.writeFile(contextPath, context);\n\n console.log();\n console.log(chalk.bold(\"Upstream Context Generated\"));\n console.log();\n console.log(chalk.dim(\" Files:\"), options.files.join(\", \"));\n console.log(chalk.dim(\" Context file:\"), \".mosaic-upstream-context.md\");\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code in the mosaic repo\");\n console.log(\n chalk.dim(\" 2.\"),\n `Tell Claude: \"Read ${path.resolve(cwd, \".mosaic-upstream-context.md\")} and apply generalizable changes to the template\"`,\n );\n console.log(chalk.dim(\" 3.\"), \"Review Claude's changes to the template\");\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;AAeA,eAAe,wBACb,UACA,oBACA,KACA,QACA,OACiB;CACjB,IAAI,UAAU;;;kBAGE,SAAS,aAAa,gBAAgB,UAAU;kBAChD,SAAS,aAAa;0BACd,SAAS,gBAAgB;;;;;;;;EAQjD,OAAO,QAAQ,SAAS,aAAa,CACpC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,OAAO,CACzC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,MAAM,CAC1C,KAAK,KAAK,CAAC;;;;;AAMZ,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,QAAQ,KAAK;EAC9C,MAAM,kBAAkB,GAAG,SAAS,OAAO,aAAa,GAAG;EAE3D,MAAM,aAAc,MAAM,GAAG,WAAW,YAAY,GAChD,MAAM,GAAG,SAAS,aAAa,QAAQ,GACvC;EACJ,MAAM,kBAAkB,aACtB,oBACA,KACA,gBACD;AAED,aAAW,OAAO,KAAK;AAEvB,MAAI,CAAC,mBAAmB,YAAY;AAClC,cAAW,oCAAoC,SAAS,gBAAgB;AACxE,cAAW,WAAW,WAAW;aACxB,mBAAmB,CAAC,WAC7B,YAAW;WACF,cAAc,iBAAiB;AACxC,cAAW,6BAA6B,WAAW;AACnD,cAAW,0BAA0B,SAAS,gBAAgB,gBAAgB,gBAAgB;QAE9F,YAAW;;AAIf,YAAW;;;;gEAImD,SAAS,OAAO,aAAa;;;;;;AAO3F,QAAO;;AAGT,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;EACxC,MAAM,qBAAqB,0BAA0B,QAAQ;AAE7D,MAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,WAAW,GAAG;AAChD,WAAQ,MACN,MAAM,IAAI,iDAAiD,CAC5D;AACD,WAAQ,IACN,MAAM,IACJ,gEACD,CACF;AACD,WAAQ,KAAK,EAAE;;EAKjB,MAAM,UAAU,MAAM,wBACpB,UACA,oBACA,YALsB,SAAS,aAAa,GAAG,SAAS,mBAMxD,KACA,QAAQ,MACT;EACD,MAAM,cAAc,KAAK,KAAK,KAAK,8BAA8B;AACjE,QAAM,GAAG,UAAU,aAAa,QAAQ;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,6BAA6B,CAAC;AACrD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,WAAW,EAAE,QAAQ,MAAM,KAAK,KAAK,CAAC;AAC5D,UAAQ,IAAI,MAAM,IAAI,kBAAkB,EAAE,8BAA8B;AACxE,UAAQ,KAAK;AACb,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,sCAAsC;AACrE,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,sBAAsB,KAAK,QAAQ,KAAK,8BAA8B,CAAC,mDACxE;AACD,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,0CAA0C;AACzE,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
1
+ {"version":3,"file":"upstream-CAraZeSS.js","names":[],"sources":["../src/commands/upstream.ts"],"sourcesContent":["import path from \"node:path\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { getFileAtTag } from \"../utils/git-ops.js\";\nimport {\n readManifest,\n resolveMosaicTemplatePath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface UpstreamOptions {\n mosaicTemplatePath?: string;\n files?: string[];\n}\n\nasync function generateUpstreamContext(\n manifest: MosaicManifest,\n mosaicTemplatePath: string,\n tag: string,\n appDir: string,\n files: string[],\n): Promise<string> {\n let content = `# Mosaic Upstream Context\n\n## App Info\n- **App name:** ${manifest.placeholders.__APP_NAME__ || \"unknown\"}\n- **Template:** ${manifest.templateType}\n- **Template version:** ${manifest.templateVersion}\n\n## Placeholder Mappings\n\nWhen generalizing app code back to template, replace these values with placeholder tokens:\n\n| Value | Placeholder |\n|-------|------------|\n${Object.entries(manifest.placeholders)\n .sort((a, b) => b[1].length - a[1].length) // longest first to avoid partial matches\n .map(([k, v]) => `| \\`${v}\\` | \\`${k}\\` |`)\n .join(\"\\n\")}\n\n## Files to Review\n\n`;\n\n for (const file of files) {\n const appFilePath = path.resolve(appDir, file);\n const templateRelPath = `${manifest.source.templatePath}/${file}`;\n\n const appContent = (await fs.pathExists(appFilePath))\n ? await fs.readFile(appFilePath, \"utf-8\")\n : null;\n const templateContent = getFileAtTag(\n mosaicTemplatePath,\n tag,\n templateRelPath,\n );\n\n content += `### ${file}\\n\\n`;\n\n if (!templateContent && appContent) {\n content += `**New file** (not in template at ${manifest.templateVersion})\\n\\n`;\n content += `\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n } else if (templateContent && !appContent) {\n content += `**Deleted** (exists in template but not in app)\\n\\n`;\n } else if (appContent && templateContent) {\n content += `**App version:**\\n\\`\\`\\`\\n${appContent}\\n\\`\\`\\`\\n\\n`;\n content += `**Template version (at ${manifest.templateVersion}):**\\n\\`\\`\\`\\n${templateContent}\\n\\`\\`\\`\\n\\n`;\n } else {\n content += `**Not found** (file does not exist in app or template)\\n\\n`;\n }\n }\n\n content += `## Instructions\n\n1. Review each file above\n2. Determine which changes are generalizable (useful for all apps) vs app-specific\n3. For generalizable changes: apply them to the template at \\`${manifest.source.templatePath}/\\`\n4. When applying, replace app-specific values with placeholders using the mapping table above (replace longest values first)\n5. After applying, bump the version in \\`packages/blueberry/template-versions.json\\`\n6. Run \\`pnpm template:tag\\` to create the new version tag\n7. Delete this file (\\`.mosaic-upstream-context.md\\`) when done\n`;\n\n return content;\n}\n\nexport async function upstreamCommand(options: UpstreamOptions): Promise<void> {\n const cwd = process.cwd();\n\n try {\n const manifest = await readManifest(cwd);\n const mosaicTemplatePath = resolveMosaicTemplatePath(options);\n\n if (!options.files || options.files.length === 0) {\n console.error(\n chalk.red(\"Specify files with --files <file1> <file2> ...\"),\n );\n console.log(\n chalk.dim(\n \" Example: create upstream --files src/config/getEnvConfig.ts\",\n ),\n );\n process.exit(1);\n }\n\n const tag = `template/${manifest.templateType}/${manifest.templateVersion}`;\n\n const context = await generateUpstreamContext(\n manifest,\n mosaicTemplatePath,\n tag,\n cwd,\n options.files,\n );\n const contextPath = path.join(cwd, \".mosaic-upstream-context.md\");\n await fs.writeFile(contextPath, context);\n\n console.log();\n console.log(chalk.bold(\"Upstream Context Generated\"));\n console.log();\n console.log(chalk.dim(\" Files:\"), options.files.join(\", \"));\n console.log(chalk.dim(\" Context file:\"), \".mosaic-upstream-context.md\");\n console.log();\n console.log(\"Next steps:\");\n console.log(chalk.dim(\" 1.\"), \"Open Claude Code in the mosaic repo\");\n console.log(\n chalk.dim(\" 2.\"),\n `Tell Claude: \"Read ${path.resolve(cwd, \".mosaic-upstream-context.md\")} and apply generalizable changes to the template\"`,\n );\n console.log(chalk.dim(\" 3.\"), \"Review Claude's changes to the template\");\n console.log();\n } catch (error) {\n console.error(chalk.red((error as Error).message));\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;AAeA,eAAe,wBACb,UACA,oBACA,KACA,QACA,OACiB;CACjB,IAAI,UAAU;;;kBAGE,SAAS,aAAa,gBAAgB,UAAU;kBAChD,SAAS,aAAa;0BACd,SAAS,gBAAgB;;;;;;;;EAQjD,OAAO,QAAQ,SAAS,aAAa,CACpC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,OAAO,CACzC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,MAAM,CAC1C,KAAK,KAAK,CAAC;;;;;AAMZ,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,QAAQ,KAAK;EAC9C,MAAM,kBAAkB,GAAG,SAAS,OAAO,aAAa,GAAG;EAE3D,MAAM,aAAc,MAAM,GAAG,WAAW,YAAY,GAChD,MAAM,GAAG,SAAS,aAAa,QAAQ,GACvC;EACJ,MAAM,kBAAkB,aACtB,oBACA,KACA,gBACD;AAED,aAAW,OAAO,KAAK;AAEvB,MAAI,CAAC,mBAAmB,YAAY;AAClC,cAAW,oCAAoC,SAAS,gBAAgB;AACxE,cAAW,WAAW,WAAW;aACxB,mBAAmB,CAAC,WAC7B,YAAW;WACF,cAAc,iBAAiB;AACxC,cAAW,6BAA6B,WAAW;AACnD,cAAW,0BAA0B,SAAS,gBAAgB,gBAAgB,gBAAgB;QAE9F,YAAW;;AAIf,YAAW;;;;gEAImD,SAAS,OAAO,aAAa;;;;;;AAO3F,QAAO;;AAGT,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,MAAM,QAAQ,KAAK;AAEzB,KAAI;EACF,MAAM,WAAW,MAAM,aAAa,IAAI;EACxC,MAAM,qBAAqB,0BAA0B,QAAQ;AAE7D,MAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,WAAW,GAAG;AAChD,WAAQ,MACN,MAAM,IAAI,iDAAiD,CAC5D;AACD,WAAQ,IACN,MAAM,IACJ,gEACD,CACF;AACD,WAAQ,KAAK,EAAE;;EAKjB,MAAM,UAAU,MAAM,wBACpB,UACA,oBACA,YALsB,SAAS,aAAa,GAAG,SAAS,mBAMxD,KACA,QAAQ,MACT;EACD,MAAM,cAAc,KAAK,KAAK,KAAK,8BAA8B;AACjE,QAAM,GAAG,UAAU,aAAa,QAAQ;AAExC,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,KAAK,6BAA6B,CAAC;AACrD,UAAQ,KAAK;AACb,UAAQ,IAAI,MAAM,IAAI,WAAW,EAAE,QAAQ,MAAM,KAAK,KAAK,CAAC;AAC5D,UAAQ,IAAI,MAAM,IAAI,kBAAkB,EAAE,8BAA8B;AACxE,UAAQ,KAAK;AACb,UAAQ,IAAI,cAAc;AAC1B,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,sCAAsC;AACrE,UAAQ,IACN,MAAM,IAAI,OAAO,EACjB,sBAAsB,KAAK,QAAQ,KAAK,8BAA8B,CAAC,mDACxE;AACD,UAAQ,IAAI,MAAM,IAAI,OAAO,EAAE,0CAA0C;AACzE,UAAQ,KAAK;UACN,OAAO;AACd,UAAQ,MAAM,MAAM,IAAK,MAAgB,QAAQ,CAAC;AAClD,UAAQ,KAAK,EAAE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percepta/create",
3
- "version": "4.1.5",
3
+ "version": "4.1.6",
4
4
  "description": "Scaffold a new Mosaic package",
5
5
  "keywords": [
6
6
  "cli",
@@ -35,18 +35,6 @@ spec:
35
35
  displayName: "Ingress Domain"
36
36
  description: "Shared ingress domain for generated OS webapps."
37
37
  default: '{{ default "example.local" .ryvn.env.state.public_domain.name }}'
38
- - name: inngest_event_key
39
- type: string
40
- isSecret: true
41
- group: applications
42
- displayName: "Inngest Event Key"
43
- description: "Shared Inngest event key for generated OS webapps. Leave unset when the target Inngest installation does not require one."
44
- - name: inngest_signing_key
45
- type: string
46
- isSecret: true
47
- group: applications
48
- displayName: "Inngest Signing Key"
49
- description: "Shared Inngest signing key for generated OS webapps. Leave unset when the target Inngest installation does not require one."
50
38
  - name: langfuse_public_key
51
39
  type: string
52
40
  group: applications
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "__APP_NAME__",
2
+ "name": "@__CUSTOMER_SLUG__/__APP_NAME__",
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "description": "__APP_TITLE__",
@@ -0,0 +1 @@
1
+ export NPM_TOKEN="NPM_TOKEN_HERE"
@@ -4,7 +4,6 @@ on:
4
4
  pull_request: {}
5
5
 
6
6
  env:
7
- PNPM_VERSION: 10.x
8
7
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
9
8
 
10
9
  jobs:
@@ -18,8 +17,6 @@ jobs:
18
17
 
19
18
  - name: Setup PNPM
20
19
  uses: pnpm/action-setup@v4
21
- with:
22
- version: ${{ env.PNPM_VERSION }}
23
20
 
24
21
  - name: Setup Node.js
25
22
  uses: actions/setup-node@v4
@@ -1 +1 @@
1
- 24.4.1
1
+ 24.16.0
@@ -0,0 +1,3 @@
1
+ {
2
+ "recommendations": ["oxc.oxc-vscode"]
3
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "editor.codeActionsOnSave": {
3
+ "source.fixAll": "explicit"
4
+ },
5
+ "editor.defaultFormatter": "oxc.oxc-vscode",
6
+ "editor.formatOnSave": true,
7
+ "typescript.tsdk": "node_modules/typescript/lib",
8
+ "[javascript]": {
9
+ "editor.defaultFormatter": "oxc.oxc-vscode"
10
+ },
11
+ "[javascriptreact]": {
12
+ "editor.defaultFormatter": "oxc.oxc-vscode"
13
+ },
14
+ "[typescript]": {
15
+ "editor.defaultFormatter": "oxc.oxc-vscode"
16
+ },
17
+ "[typescriptreact]": {
18
+ "editor.defaultFormatter": "oxc.oxc-vscode"
19
+ },
20
+ "[json]": {
21
+ "editor.defaultFormatter": "oxc.oxc-vscode"
22
+ },
23
+ "[jsonc]": {
24
+ "editor.defaultFormatter": "oxc.oxc-vscode"
25
+ }
26
+ }
@@ -5,8 +5,8 @@ This workspace wires the customer-global Better Auth database schema from
5
5
  session validation and user / group table references instead of creating
6
6
  app-local auth tables.
7
7
 
8
- Import auth as `@__APP_NAME__/auth`, the database handle as
9
- `@__APP_NAME__/auth/db`, and table definitions as `@__APP_NAME__/auth/schema`
8
+ Import auth as `@__CUSTOMER_SLUG__/auth`, the database handle as
9
+ `@__CUSTOMER_SLUG__/auth/db`, and table definitions as `@__CUSTOMER_SLUG__/auth/schema`
10
10
  from app packages.
11
11
 
12
12
  The important identity invariant is:
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "@__APP_NAME__/auth",
2
+ "name": "@__CUSTOMER_SLUG__/auth",
3
3
  "version": "0.0.1",
4
4
  "private": true,
5
5
  "description": "Shared customer identity package.",
@@ -22,6 +22,7 @@
22
22
  "devDependencies": {
23
23
  "@percepta/build": "^1.0.0",
24
24
  "@types/node": "^24.1.0",
25
+ "@types/pg": "^8.15.4",
25
26
  "drizzle-kit": "^0.31.4",
26
27
  "tsx": "^4.20.3"
27
28
  }
@@ -1,8 +1,13 @@
1
1
  import { createAuthDatabase } from "@percepta/auth/drizzle";
2
+ import type { NodePgDatabase } from "drizzle-orm/node-postgres";
3
+ import type { Pool } from "pg";
2
4
  import { getAuthDatabaseConnectionString } from "../config/database";
3
5
  import * as schema from "./schema";
4
6
 
5
- export const { client, db } = createAuthDatabase({
7
+ export const {
8
+ client,
9
+ db,
10
+ }: { client: Pool; db: NodePgDatabase<typeof schema> } = createAuthDatabase({
6
11
  connectionString: getAuthDatabaseConnectionString(),
7
12
  schema,
8
13
  });
@@ -10,7 +10,6 @@ build/
10
10
 
11
11
  # IDE
12
12
  .idea/
13
- .vscode/
14
13
  *.swp
15
14
  *.swo
16
15
 
@@ -19,6 +18,7 @@ build/
19
18
  Thumbs.db
20
19
 
21
20
  # Env files
21
+ .envrc
22
22
  .env
23
23
  .env.local
24
24
  .env.*.local
@@ -6,14 +6,6 @@ packages:
6
6
  # user's pnpm config enforces minimumReleaseAge.
7
7
  minimumReleaseAgeExclude:
8
8
  - "@percepta/*"
9
- - "better-auth"
10
- - "@better-auth/*"
11
- - "oxfmt"
12
- - "@oxfmt/*"
13
- - "oxlint"
14
- - "@oxlint/*"
15
- - "oxlint-tsgolint"
16
- - "@oxlint-tsgolint/*"
17
9
 
18
10
  overrides:
19
11
  fast-xml-parser@<=5.5.6: 5.5.6
@@ -54,12 +54,13 @@ jobs:
54
54
  echo "isPreview=$isPreview" >> $GITHUB_OUTPUT
55
55
 
56
56
  - name: Build and Push
57
- uses: ryvn-technologies/ryvn-build-action@v1.2.0
57
+ uses: ryvn-technologies/ryvn-build-action@v2
58
58
  with:
59
59
  service_name: ${{ env.SERVICE_NAME }}
60
60
  version: ${{ steps.generate-tag.outputs.version }}
61
61
  build_only: ${{ !(github.ref == format('refs/heads/{0}', github.event.repository.default_branch) || steps.generate-tag.outputs.isPreview == 'true') }}
62
- build_args: NPM_TOKEN=${{ secrets.NPM_TOKEN }}
62
+ build_secrets: |
63
+ NPM_TOKEN=${{ secrets.NPM_TOKEN }}
63
64
  ryvn_client_id: ${{ secrets.RYVN_CLIENT_ID }}
64
65
  ryvn_client_secret: ${{ secrets.RYVN_CLIENT_SECRET }}
65
66
 
@@ -219,7 +219,7 @@ Client-side usage via `src/lib/trpc.ts`.
219
219
 
220
220
  ### Authentication
221
221
 
222
- Better Auth is configured in the customer monorepo's shared `@__REPO_NAME__/auth` package. The app imports it through `src/lib/auth/` for local session validation. `DATABASE_URL` is this app's database only; deployed auth should use `AUTH_DATABASE_URL` from the monorepo auth Secret.
222
+ Better Auth is configured in the customer monorepo's shared `@__CUSTOMER_SLUG__/auth` package. The app imports it through `src/lib/auth/` for local session validation. `DATABASE_URL` is this app's database only; deployed auth should use `AUTH_DATABASE_URL` from the monorepo auth Secret.
223
223
 
224
224
  - **Server-side**: `auth.api.getSession({ headers: await headers() })` — get session in server components or tRPC context
225
225
  - **Client-side**: `authClient.useSession()` — React hook from `src/lib/auth-client.ts`
@@ -1,34 +1,35 @@
1
1
  # Base image with PNPM:
2
- ARG NODE_VERSION=24.4.1
2
+ ARG NODE_VERSION=24.16.0
3
3
  FROM node:${NODE_VERSION}-slim AS base
4
- WORKDIR /app
5
- RUN npm install -g pnpm
4
+ WORKDIR /repo
5
+ ARG PNPM_VERSION=10.6.4
6
+ RUN npm install -g pnpm@${PNPM_VERSION}
6
7
 
7
8
  # Build stage:
8
9
  FROM base AS builder
9
- WORKDIR /repo
10
10
  ENV CI=true
11
11
 
12
- COPY . .
12
+ COPY .npmrc pnpm-lock.yaml pnpm-workspace.yaml ./
13
+ RUN --mount=type=secret,id=NPM_TOKEN \
14
+ NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN 2>/dev/null || true) \
15
+ pnpm fetch
13
16
 
14
- # Create .npmrc with build-time token for private packages:
15
- ARG NPM_TOKEN
16
- RUN echo "@percepta:registry=https://registry.npmjs.org/" > .npmrc && \
17
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" >> .npmrc
17
+ COPY . .
18
+ RUN --mount=type=secret,id=NPM_TOKEN \
19
+ NPM_TOKEN=$(cat /run/secrets/NPM_TOKEN 2>/dev/null || true) \
20
+ pnpm install --frozen-lockfile --offline
18
21
 
19
- # Add BASE_PATH as a build argument
22
+ # Build service and its workspace dependencies in topological order:
20
23
  ARG BASE_PATH
21
24
  ENV BASE_PATH=${BASE_PATH}
22
-
23
- RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile
24
-
25
- RUN NODE_ENV=production NODE_OPTIONS="--max-old-space-size=4096" pnpm --filter ./packages/__APP_NAME__ build
26
-
27
- # Remove .npmrc for security (contains auth token):
28
- RUN rm -f .npmrc
25
+ RUN --mount=type=secret,id=TURBO_TOKEN,env=TURBO_TOKEN,required=false \
26
+ --mount=type=secret,id=TURBO_TEAM,env=TURBO_TEAM,required=false \
27
+ NODE_ENV=production NODE_OPTIONS="--max-old-space-size=4096" \
28
+ pnpm turbo run build --filter=./packages/__APP_NAME__
29
29
 
30
30
  # Production stage - create the final image:
31
31
  FROM base AS production
32
+ WORKDIR /app
32
33
 
33
34
  # Install PostgreSQL client:
34
35
  USER root
@@ -41,7 +42,8 @@ RUN apt-get update \
41
42
  ENV NODE_ENV=production
42
43
  ENV NEXT_TELEMETRY_DISABLED=1
43
44
 
44
- # Copy BASE_PATH from the builder stage
45
+ # Carry BASE_PATH through to the runtime image:
46
+ ARG BASE_PATH
45
47
  ENV BASE_PATH=${BASE_PATH}
46
48
 
47
49
  # Copy built app from builder stage
@@ -59,7 +61,6 @@ COPY --from=builder /repo/packages/__APP_NAME__/node_modules ./packages/__APP_NA
59
61
  COPY --from=builder /repo/packages/__APP_NAME__/scripts ./packages/__APP_NAME__/scripts
60
62
  COPY --from=builder /repo/packages/__APP_NAME__/src ./packages/__APP_NAME__/src
61
63
 
62
-
63
64
  # Expose the port:
64
65
  EXPOSE 3000
65
66
 
@@ -149,7 +149,7 @@ logger.error(
149
149
 
150
150
  ## Authentication
151
151
 
152
- This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@__REPO_NAME__/auth`. The app still serves local development auth routes, but the users, sessions, accounts, groups, and group memberships live in the shared customer auth database. Deployed apps should receive that shared database through `AUTH_DATABASE_URL` from the monorepo auth Secret; `DATABASE_URL` is reserved for this app's own database.
152
+ This app consumes the customer monorepo's shared [Better Auth](https://better-auth.com) package, `@__CUSTOMER_SLUG__/auth`. The app still serves local development auth routes, but the users, sessions, accounts, groups, and group memberships live in the shared customer auth database. Deployed apps should receive that shared database through `AUTH_DATABASE_URL` from the monorepo auth Secret; `DATABASE_URL` is reserved for this app's own database.
153
153
 
154
154
  Required auth environment variables:
155
155