@percepta/create 4.1.9 → 4.1.11

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.
@@ -1,10 +1,10 @@
1
- import { c as readManifest, l as resolveMosaicTemplatePath } from "./index.js";
1
+ import { l as readManifest, u as resolveMosaicRepoPath } from "./index.js";
2
2
  import { t as getFileAtTag } from "./git-ops-BNpQnEc1.js";
3
3
  import path from "node:path";
4
4
  import chalk from "chalk";
5
5
  import fs from "fs-extra";
6
6
  //#region src/commands/upstream.ts
7
- async function generateUpstreamContext(manifest, mosaicTemplatePath, tag, appDir, files) {
7
+ async function generateUpstreamContext(manifest, mosaicRepoPath, tag, appDir, files) {
8
8
  let content = `# Mosaic Upstream Context
9
9
 
10
10
  ## App Info
@@ -27,7 +27,7 @@ ${Object.entries(manifest.placeholders).sort((a, b) => b[1].length - a[1].length
27
27
  const appFilePath = path.resolve(appDir, file);
28
28
  const templateRelPath = `${manifest.source.templatePath}/${file}`;
29
29
  const appContent = await fs.pathExists(appFilePath) ? await fs.readFile(appFilePath, "utf-8") : null;
30
- const templateContent = getFileAtTag(mosaicTemplatePath, tag, templateRelPath);
30
+ const templateContent = getFileAtTag(mosaicRepoPath, tag, templateRelPath);
31
31
  content += `### ${file}\n\n`;
32
32
  if (!templateContent && appContent) {
33
33
  content += `**New file** (not in template at ${manifest.templateVersion})\n\n`;
@@ -54,13 +54,13 @@ async function upstreamCommand(options) {
54
54
  const cwd = process.cwd();
55
55
  try {
56
56
  const manifest = await readManifest(cwd);
57
- const mosaicTemplatePath = resolveMosaicTemplatePath(options);
57
+ const mosaicRepoPath = resolveMosaicRepoPath(options);
58
58
  if (!options.files || options.files.length === 0) {
59
59
  console.error(chalk.red("Specify files with --files <file1> <file2> ..."));
60
60
  console.log(chalk.dim(" Example: create upstream --files src/config/getEnvConfig.ts"));
61
61
  process.exit(1);
62
62
  }
63
- const context = await generateUpstreamContext(manifest, mosaicTemplatePath, `template/${manifest.templateType}/${manifest.templateVersion}`, cwd, options.files);
63
+ const context = await generateUpstreamContext(manifest, mosaicRepoPath, `template/${manifest.templateType}/${manifest.templateVersion}`, cwd, options.files);
64
64
  const contextPath = path.join(cwd, ".mosaic-upstream-context.md");
65
65
  await fs.writeFile(contextPath, context);
66
66
  console.log();
@@ -82,4 +82,4 @@ async function upstreamCommand(options) {
82
82
  //#endregion
83
83
  export { upstreamCommand };
84
84
 
85
- //# sourceMappingURL=upstream-PNL6DGtl.js.map
85
+ //# sourceMappingURL=upstream-R8YDvUue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upstream-R8YDvUue.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 resolveMosaicRepoPath,\n type MosaicManifest,\n} from \"../utils/manifest.js\";\n\nexport interface UpstreamOptions {\n mosaicRepoPath?: string;\n files?: string[];\n}\n\nasync function generateUpstreamContext(\n manifest: MosaicManifest,\n mosaicRepoPath: 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(mosaicRepoPath, tag, templateRelPath);\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 mosaicRepoPath = resolveMosaicRepoPath(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 mosaicRepoPath,\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,gBACA,KACA,QACA,OACiB;CACjB,IAAI,UAAU;;;kBAGE,SAAS,aAAa,gBAAgB,UAAU;kBAChD,SAAS,aAAa;0BACd,SAAS,gBAAgB;;;;;;;;EAQjD,OAAO,QAAQ,SAAS,YAAY,EACnC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,MAAM,EACxC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,KAAK,EACzC,KAAK,IAAI,EAAE;;;;;CAMZ,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,QAAQ,IAAI;EAC7C,MAAM,kBAAkB,GAAG,SAAS,OAAO,aAAa,GAAG;EAE3D,MAAM,aAAc,MAAM,GAAG,WAAW,WAAW,IAC/C,MAAM,GAAG,SAAS,aAAa,OAAO,IACtC;EACJ,MAAM,kBAAkB,aAAa,gBAAgB,KAAK,eAAe;EAEzE,WAAW,OAAO,KAAK;EAEvB,IAAI,CAAC,mBAAmB,YAAY;GAClC,WAAW,oCAAoC,SAAS,gBAAgB;GACxE,WAAW,WAAW,WAAW;EACnC,OAAO,IAAI,mBAAmB,CAAC,YAC7B,WAAW;OACN,IAAI,cAAc,iBAAiB;GACxC,WAAW,6BAA6B,WAAW;GACnD,WAAW,0BAA0B,SAAS,gBAAgB,gBAAgB,gBAAgB;EAChG,OACE,WAAW;CAEf;CAEA,WAAW;;;;gEAImD,SAAS,OAAO,aAAa;;;;;;CAO3F,OAAO;AACT;AAEA,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EACvC,MAAM,iBAAiB,sBAAsB,OAAO;EAEpD,IAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,WAAW,GAAG;GAChD,QAAQ,MACN,MAAM,IAAI,gDAAgD,CAC5D;GACA,QAAQ,IACN,MAAM,IACJ,+DACF,CACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAIA,MAAM,UAAU,MAAM,wBACpB,UACA,gBACA,YALsB,SAAS,aAAa,GAAG,SAAS,mBAMxD,KACA,QAAQ,KACV;EACA,MAAM,cAAc,KAAK,KAAK,KAAK,6BAA6B;EAChE,MAAM,GAAG,UAAU,aAAa,OAAO;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,4BAA4B,CAAC;EACpD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,UAAU,GAAG,QAAQ,MAAM,KAAK,IAAI,CAAC;EAC3D,QAAQ,IAAI,MAAM,IAAI,iBAAiB,GAAG,6BAA6B;EACvE,QAAQ,IAAI;EACZ,QAAQ,IAAI,aAAa;EACzB,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,qCAAqC;EACpE,QAAQ,IACN,MAAM,IAAI,MAAM,GAChB,sBAAsB,KAAK,QAAQ,KAAK,6BAA6B,EAAE,kDACzE;EACA,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,yCAAyC;EACxE,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percepta/create",
3
- "version": "4.1.9",
3
+ "version": "4.1.11",
4
4
  "description": "Scaffold a new Mosaic package",
5
5
  "keywords": [
6
6
  "cli",
@@ -64,26 +64,26 @@ spec:
64
64
  type: string
65
65
  group: general
66
66
  displayName: "Auth Secret Name"
67
- description: "Optional Kubernetes Secret name for shared auth database credentials. Defaults to <environment>-auth-postgresql."
67
+ description: "Optional Kubernetes Secret name for shared auth database credentials. Defaults from the customer OS-scoped PostgreSQL Terraform name."
68
68
  default: ""
69
69
  - name: auth_username
70
70
  type: string
71
71
  group: general
72
72
  displayName: "Auth Username"
73
- description: "Optional shared auth database username. Defaults to <environment>_auth."
73
+ description: "Optional shared auth database username. Defaults from the customer OS-scoped PostgreSQL Terraform name."
74
74
  default: ""
75
75
  - name: aws_postgresql_cluster_name
76
76
  type: string
77
77
  group: aws_postgresql
78
78
  displayName: "AWS PostgreSQL Cluster Name"
79
- description: "Optional Aurora PostgreSQL cluster name. Defaults to <environment>-postgresql."
79
+ description: "Optional Aurora PostgreSQL cluster name. Defaults from the customer OS-scoped PostgreSQL Terraform name."
80
80
  default: ""
81
81
  condition: '{{ eq EnvironmentProviderType "aws" }}'
82
82
  - name: azure_postgresql_server_name
83
83
  type: string
84
84
  group: azure_postgresql
85
85
  displayName: "Azure PostgreSQL Server Name"
86
- description: "Optional Azure PostgreSQL flexible server name. Defaults to <environment>-postgresql."
86
+ description: "Optional Azure PostgreSQL flexible server name. Defaults from the customer OS-scoped PostgreSQL Terraform name."
87
87
  default: ""
88
88
  condition: '{{ eq EnvironmentProviderType "azure" }}'
89
89
  - name: azure_postgresql_private_dns_zone_id
@@ -124,10 +124,10 @@ spec:
124
124
 
125
125
  installations:
126
126
  - service: os-postgresql-terraform-aws
127
- name: os-postgresql-terraform
127
+ name: __CUSTOMER_SLUG__-os-postgresql-terraform
128
128
  condition: '{{ eq EnvironmentProviderType "aws" }}'
129
129
  config: |
130
- name: {{ EnvironmentName }}
130
+ name: __CUSTOMER_SLUG__-os-{{ EnvironmentName }}
131
131
  namespace: {{ EnvironmentNamespace }}
132
132
  {{ if ne (input "auth_secret_name") "" }}
133
133
  auth_secret_name: '{{ input "auth_secret_name" }}'
@@ -151,10 +151,10 @@ spec:
151
151
  {{ end }}
152
152
 
153
153
  - service: os-postgresql-terraform-azure
154
- name: os-postgresql-terraform
154
+ name: __CUSTOMER_SLUG__-os-postgresql-terraform
155
155
  condition: '{{ eq EnvironmentProviderType "azure" }}'
156
156
  config: |
157
- name: {{ EnvironmentName }}
157
+ name: __CUSTOMER_SLUG__-os-{{ EnvironmentName }}
158
158
  namespace: {{ EnvironmentNamespace }}
159
159
  {{ if ne (input "auth_secret_name") "" }}
160
160
  auth_secret_name: '{{ input "auth_secret_name" }}'
@@ -98,6 +98,19 @@ pnpm mosaic add library my-lib
98
98
 
99
99
  The customer slug and compatibility metadata live in `.mosaic-workspace.json`.
100
100
 
101
+ ## Syncing from Mosaic
102
+
103
+ Run sync from the monorepo root to pull both workspace-level template updates
104
+ and package-level template updates:
105
+
106
+ ```bash
107
+ pnpm mosaic sync
108
+ ```
109
+
110
+ By default, the command checks out `git@github.com:Percepta-Core/mosaic.git` at
111
+ `main` into a temporary local directory, generates `.mosaic-sync-context.md`
112
+ files for changed targets, and leaves the merge for your coding agent to apply.
113
+
101
114
  ## Registering the customer OS blueprint
102
115
 
103
116
  To register this customer monorepo's OS blueprint in `Percepta-Core/infra`,
@@ -1,19 +1,20 @@
1
- Sync downstream template changes into this app.
1
+ Sync downstream template changes into this app or its containing Mosaic monorepo.
2
2
 
3
3
  ## Steps
4
4
 
5
- 1. Read `.mosaic-template.json` in the current package directory to get the template type, current version, and placeholder mappings.
6
- 2. Determine the mosaic repo path: use `$ARGUMENTS` if provided, otherwise check the `MOSAIC_TEMPLATE_PATH` environment variable. If neither is available, ask the user for the path to their local mosaic repo checkout.
7
- 3. Run: `npx @percepta/create sync --mosaic-template-path <path>`
5
+ 1. Prefer running from the monorepo root so workspace-level template changes and package-level template changes are discovered together.
6
+ 2. Read `.mosaic-workspace.json` at the monorepo root and any package `.mosaic-template.json` files to understand template versions and placeholder mappings.
7
+ 3. Run: `pnpm mosaic sync`
8
+ - The CLI checks out `git@github.com:Percepta-Core/mosaic.git` at `main` into a temporary local directory.
8
9
  4. If the command reports "Already up to date" or "No template file changes", inform the user and stop.
9
- 5. Read the generated `.mosaic-sync-context.md` file.
10
- 6. Apply the template changes described in the context file to this app:
10
+ 5. Read every generated `.mosaic-sync-context.md` file.
11
+ 6. Apply the template changes described in the context files:
11
12
  - Replace placeholder tokens (e.g. `__APP_NAME__`) with actual values from the mapping table
12
- - Check `mosaic-template-notes.md` for intentional divergences — preserve them
13
+ - Check package `mosaic-template-notes.md` files for intentional divergences — preserve them
13
14
  - For files not modified locally, apply changes directly
14
15
  - For files modified locally, merge intelligently, preserving local customizations
15
16
  7. Run `pnpm install && pnpm build && pnpm lint` to verify the changes.
16
- 8. Update `.mosaic-template.json`: set `templateVersion` to the new version and update `templateCommit`.
17
- 9. If you made decisions about merge conflicts, add notes to `mosaic-template-notes.md`.
18
- 10. Delete `.mosaic-sync-context.md`.
17
+ 8. Update `.mosaic-workspace.json` and package `.mosaic-template.json` files as instructed by the sync contexts.
18
+ 9. If you made decisions about merge conflicts, add notes to the relevant `mosaic-template-notes.md`.
19
+ 10. Delete generated `.mosaic-sync-context.md` files.
19
20
  11. Summarize what changed and ask the user to review before committing.
@@ -4,8 +4,8 @@ Propose app improvements upstream to the mosaic template.
4
4
 
5
5
  1. Read `.mosaic-template.json` in the current package directory to get the template type, current version, and placeholder mappings.
6
6
  2. Determine which files to propose upstream: use `$ARGUMENTS` if provided (space-separated file paths), otherwise ask the user which files contain improvements they want to contribute back to the template.
7
- 3. Determine the mosaic repo path: check the `MOSAIC_TEMPLATE_PATH` environment variable. If not set, ask the user for the path to their local mosaic repo checkout.
8
- 4. Run: `npx @percepta/create upstream --mosaic-template-path <path> --files <file1> <file2> ...`
7
+ 3. Ask the user for the path to the local mosaic repo checkout they want to edit.
8
+ 4. Run: `npx @percepta/create upstream --mosaic-repo-path <path> --files <file1> <file2> ...`
9
9
  5. Read the generated `.mosaic-upstream-context.md` file.
10
10
  6. For each file listed in the context:
11
11
  - Compare the app version against the template version
@@ -276,9 +276,9 @@ use Ryvn releases.
276
276
 
277
277
  ## Template Sync
278
278
 
279
- This app tracks its template origin in `.mosaic-template.json`. Two Claude commands are available:
279
+ This app tracks its template origin in `.mosaic-template.json`. The monorepo root tracks workspace template compatibility in `.mosaic-workspace.json`. Two Claude commands are available:
280
280
 
281
- - **`/sync`** — pull downstream changes from the mosaic template into this app
281
+ - **`/sync`** — pull downstream changes from the Mosaic monorepo template and package templates
282
282
  - **`/upstream`** — propose app improvements back to the mosaic template
283
283
 
284
- Both commands use `@percepta/create` CLI under the hood. Check `mosaic-template-notes.md` for documented intentional divergences from the template.
284
+ Both commands use `@percepta/create` CLI under the hood. `sync` uses a temporary checkout of `git@github.com:Percepta-Core/mosaic.git` at `main`. Check `mosaic-template-notes.md` for documented intentional divergences from the template.
@@ -1 +0,0 @@
1
- {"version":3,"file":"register-app-B9vKTkoI.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 changed =\n addAppInput(document, inputs, renderInngestEventKeyInput()) || changed;\n changed =\n addAppInput(document, inputs, renderInngestSigningKeyInput()) || 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 schema_name: toSnakeCase(appName),\n });\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 renderInngestEventKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: inngestEventKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Inngest Event Key\",\n description: \"Shared Inngest event key for generated OS webapps.\",\n };\n}\n\nfunction renderInngestSigningKeyInput(): Record<string, unknown> & {\n name: string;\n} {\n return {\n name: inngestSigningKeyInputName(),\n type: \"string\",\n isSecret: true,\n group: \"applications\",\n displayName: \"Inngest Signing Key\",\n description: \"Shared Inngest signing key for generated OS webapps.\",\n };\n}\n\nfunction renderAppInstallationEnv(\n appName: string,\n): Array<Record<string, unknown>> {\n const appInternalEndpoint = `http://${appName}-web-server.{{ .ryvn.env.name }}.svc.cluster.local:3000/api/inngest`;\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: \"DATABASE_SCHEMA\",\n value: toSnakeCase(appName),\n },\n {\n key: \"INGRESS_DOMAIN\",\n valueFromInput: {\n name: ingressDomainInputName(),\n },\n },\n {\n key: \"APP_BASE_URL\",\n value: `https://${appName}.$(INGRESS_DOMAIN)`,\n },\n {\n key: \"BETTER_AUTH_URL\",\n value: `https://${appName}.$(INGRESS_DOMAIN)`,\n },\n {\n key: \"DEPLOYMENT_ENVIRONMENT\",\n value: \"{{ .ryvn.env.name }}\",\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 valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"inngest_base_url\",\n },\n },\n {\n key: \"INNGEST_EVENT_KEY\",\n isSecret: true,\n valueFromInput: {\n name: inngestEventKeyInputName(),\n },\n },\n {\n key: \"INNGEST_SIGNING_KEY\",\n isSecret: true,\n valueFromInput: {\n name: inngestSigningKeyInputName(),\n },\n },\n {\n key: \"INNGEST_APP_URL\",\n value: appInternalEndpoint,\n },\n {\n key: \"INNGEST_SERVE_HOST\",\n value: appInternalEndpoint,\n },\n {\n key: \"LANGFUSE_BASE_URL\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"langfuse_internal_url\",\n },\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 valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"otel_exporter_otlp_endpoint\",\n },\n },\n {\n key: \"SPICEDB_ENDPOINT\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_endpoint\",\n },\n },\n {\n key: \"SPICEDB_PRESHARED_KEY\",\n isSecret: true,\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_preshared_key\",\n },\n },\n {\n key: \"SPICEDB_INSECURE\",\n valueFromOutput: {\n blueprintInstallation: \"mosaic\",\n name: \"spicedb_insecure\",\n },\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 \"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 langfusePublicKeyInputName(): string {\n return \"langfuse_public_key\";\n}\n\nfunction langfuseSecretKeyInputName(): string {\n return \"langfuse_secret_key\";\n}\n\nfunction inngestEventKeyInputName(): string {\n return \"inngest_event_key\";\n}\n\nfunction inngestSigningKeyInputName(): string {\n return \"inngest_signing_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":"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"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"register-os-blueprint-Gdyn0pN1.js","names":[],"sources":["../src/commands/infra/register-os-blueprint.ts"],"sourcesContent":["import path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport chalk from \"chalk\";\nimport fs from \"fs-extra\";\nimport { toTitleCase } from \"../../utils/case-converters.js\";\nimport { detectMonorepo } from \"../../utils/detect-monorepo.js\";\nimport { readWorkspaceManifest } from \"../../utils/workspace-manifest.js\";\nimport {\n createOrUpdateInfraPullRequest,\n createInfraGitHubApi,\n INFRA_BASE_BRANCH,\n INFRA_REPOSITORY,\n type InfraGitHubApi,\n resolveGitHubToken,\n} from \"./github.js\";\n\nconst OS_BLUEPRINT_TEMPLATE = \"os.blueprint.yaml.template\";\n\nexport interface RegisterOsBlueprintResult {\n blueprintName: 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 targetPath: string;\n}\n\nexport async function registerOsBlueprint(\n args: {\n cwd?: string;\n github?: InfraGitHubApi;\n } = {},\n): Promise<RegisterOsBlueprintResult> {\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-${blueprintName}-blueprint`;\n const targetPath = [\n \"ryvn\",\n \"definitions\",\n customerSlug,\n \"blueprints\",\n `${blueprintName}.blueprint.yaml`,\n ].join(\"/\");\n const content = await renderOsBlueprint(customerSlug);\n\n const mainFile = await github.getFile(targetPath, INFRA_BASE_BRANCH);\n if (mainFile) {\n if (mainFile.content === content) {\n return {\n blueprintName,\n branchName,\n customerSlug,\n pullRequestUrl: null,\n repository: INFRA_REPOSITORY,\n status: \"already_registered\",\n targetPath,\n };\n }\n\n throw new Error(\n `${targetPath} already exists in ${INFRA_REPOSITORY}. Not overwriting an existing customer OS blueprint.`,\n );\n }\n\n const pullRequest = await createOrUpdateInfraPullRequest({\n branchName,\n content,\n github,\n message: `Register ${blueprintName} blueprint`,\n targetPath,\n title: `Register ${blueprintName} blueprint`,\n body: [\n `Registers the ${blueprintName} OS blueprint for ${customerSlug}.`,\n \"\",\n \"Generated by `mosaic infra register-os-blueprint`.\",\n ].join(\"\\n\"),\n });\n\n return {\n blueprintName,\n branchName,\n customerSlug,\n pullRequestUrl: pullRequest.pullRequestUrl,\n repository: INFRA_REPOSITORY,\n status: pullRequest.status,\n targetPath,\n };\n}\n\nexport async function registerOsBlueprintCommand(): Promise<void> {\n try {\n const result = await registerOsBlueprint();\n\n if (result.status === \"already_registered\") {\n console.log(\n chalk.green(\"✔\"),\n `${result.blueprintName} 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.blueprintName}:`,\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\nasync function renderOsBlueprint(customerSlug: string): Promise<string> {\n const template = await fs.readFile(getOsBlueprintTemplatePath(), \"utf-8\");\n const customerTitle = toTitleCase(customerSlug);\n\n return template\n .replaceAll(\"__CUSTOMER_SLUG__\", customerSlug)\n .replaceAll(\"__CUSTOMER_TITLE__\", customerTitle);\n}\n\nfunction getOsBlueprintTemplatePath(): string {\n const currentDir = path.dirname(fileURLToPath(import.meta.url));\n const candidates = [\n path.resolve(currentDir, \"../templates/infra\", OS_BLUEPRINT_TEMPLATE),\n path.resolve(currentDir, \"../../../templates/infra\", OS_BLUEPRINT_TEMPLATE),\n ];\n\n for (const candidate of candidates) {\n if (fs.existsSync(candidate)) return candidate;\n }\n\n throw new Error(\n `OS blueprint template not found. Checked: ${candidates.join(\", \")}`,\n );\n}\n"],"mappings":";;;;;;;AAgBA,MAAM,wBAAwB;AAY9B,eAAsB,oBACpB,OAGI,CAAC,GAC+B;CAEpC,MAAM,kBAAkB,MAAM,eADlB,KAAK,OAAO,QAAQ,IAAI,CACY;CAChD,IAAI,CAAC,gBAAgB,SAAS,CAAC,gBAAgB,SAC7C,MAAM,IAAI,MACR,sFACF;CAMF,MAAM,gBAAe,MAHW,sBAC9B,gBAAgB,OAClB,IACwC;CACxC,IAAI,CAAC,cACH,MAAM,IAAI,MACR,wGACF;CAGF,MAAM,SAAS,KAAK,UAAU,qBAAqB,mBAAmB,CAAC;CACvE,MAAM,gBAAgB,GAAG,aAAa;CACtC,MAAM,aAAa,sBAAsB,cAAc;CACvD,MAAM,aAAa;EACjB;EACA;EACA;EACA;EACA,GAAG,cAAc;CACnB,EAAE,KAAK,GAAG;CACV,MAAM,UAAU,MAAM,kBAAkB,YAAY;CAEpD,MAAM,WAAW,MAAM,OAAO,QAAQ,YAAY,iBAAiB;CACnE,IAAI,UAAU;EACZ,IAAI,SAAS,YAAY,SACvB,OAAO;GACL;GACA;GACA;GACA,gBAAgB;GAChB,YAAY;GACZ,QAAQ;GACR;EACF;EAGF,MAAM,IAAI,MACR,GAAG,WAAW,qBAAqB,iBAAiB,qDACtD;CACF;CAEA,MAAM,cAAc,MAAM,+BAA+B;EACvD;EACA;EACA;EACA,SAAS,YAAY,cAAc;EACnC;EACA,OAAO,YAAY,cAAc;EACjC,MAAM;GACJ,iBAAiB,cAAc,oBAAoB,aAAa;GAChE;GACA;EACF,EAAE,KAAK,IAAI;CACb,CAAC;CAED,OAAO;EACL;EACA;EACA;EACA,gBAAgB,YAAY;EAC5B,YAAY;EACZ,QAAQ,YAAY;EACpB;CACF;AACF;AAEA,eAAsB,6BAA4C;CAChE,IAAI;EACF,MAAM,SAAS,MAAM,oBAAoB;EAEzC,IAAI,OAAO,WAAW,sBAAsB;GAC1C,QAAQ,IACN,MAAM,MAAM,GAAG,GACf,GAAG,OAAO,cAAc,4BAA4B,OAAO,WAAW,MACtE,MAAM,KAAK,OAAO,UAAU,CAC9B;GACA;EACF;EAEA,MAAM,OACJ,OAAO,WAAW,eAAe,YAAY;EAC/C,QAAQ,IACN,MAAM,MAAM,GAAG,GACf,GAAG,KAAK,gBAAgB,OAAO,cAAc,IAC7C,MAAM,KAAK,OAAO,cAAc,CAClC;CACF,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAI,QAAQ,GAAI,MAAgB,OAAO;EAC3D,QAAQ,KAAK,CAAC;CAChB;AACF;AAEA,eAAe,kBAAkB,cAAuC;CACtE,MAAM,WAAW,MAAM,GAAG,SAAS,2BAA2B,GAAG,OAAO;CACxE,MAAM,gBAAgB,YAAY,YAAY;CAE9C,OAAO,SACJ,WAAW,qBAAqB,YAAY,EAC5C,WAAW,sBAAsB,aAAa;AACnD;AAEA,SAAS,6BAAqC;CAC5C,MAAM,aAAa,KAAK,QAAQ,cAAc,OAAO,KAAK,GAAG,CAAC;CAC9D,MAAM,aAAa,CACjB,KAAK,QAAQ,YAAY,sBAAsB,qBAAqB,GACpE,KAAK,QAAQ,YAAY,4BAA4B,qBAAqB,CAC5E;CAEA,KAAK,MAAM,aAAa,YACtB,IAAI,GAAG,WAAW,SAAS,GAAG,OAAO;CAGvC,MAAM,IAAI,MACR,6CAA6C,WAAW,KAAK,IAAI,GACnE;AACF"}
@@ -1,48 +0,0 @@
1
- import { c as readManifest } from "./index.js";
2
- import { i as getTemplateVersionFromTag, n as getLatestTemplateTag } from "./git-ops-BNpQnEc1.js";
3
- import path from "node:path";
4
- import chalk from "chalk";
5
- //#region src/commands/status.ts
6
- async function statusCommand(options) {
7
- const cwd = process.cwd();
8
- try {
9
- const manifest = await readManifest(cwd);
10
- console.log();
11
- console.log(chalk.bold("Mosaic Template Status"));
12
- console.log();
13
- console.log(chalk.dim(" Template type:"), manifest.templateType);
14
- console.log(chalk.dim(" Current version:"), manifest.templateVersion);
15
- console.log(chalk.dim(" Template commit:"), manifest.templateCommit);
16
- console.log(chalk.dim(" Created:"), manifest.createdAt);
17
- if (manifest.lastSyncedAt) console.log(chalk.dim(" Last synced:"), manifest.lastSyncedAt);
18
- const rawPath = options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;
19
- const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : void 0;
20
- if (mosaicTemplatePath) {
21
- const latestTag = getLatestTemplateTag(manifest.templateType, mosaicTemplatePath);
22
- if (latestTag) {
23
- const latestVersion = getTemplateVersionFromTag(latestTag);
24
- console.log(chalk.dim(" Latest version:"), latestVersion);
25
- console.log();
26
- if (latestVersion !== manifest.templateVersion) {
27
- console.log(chalk.yellow(` Update available: ${manifest.templateVersion} → ${latestVersion}`));
28
- console.log(chalk.dim(" Run:"), `create sync --mosaic-template-path ${mosaicTemplatePath}`);
29
- } else console.log(chalk.green(" Up to date"));
30
- } else {
31
- console.log();
32
- console.log(chalk.yellow(" No template tags found in mosaic repo."));
33
- console.log(chalk.dim(" Run:"), `cd ${mosaicTemplatePath} && pnpm template:tag`);
34
- }
35
- } else {
36
- console.log();
37
- console.log(chalk.dim(" Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates"));
38
- }
39
- console.log();
40
- } catch (error) {
41
- console.error(chalk.red(error.message));
42
- process.exit(1);
43
- }
44
- }
45
- //#endregion
46
- export { statusCommand };
47
-
48
- //# sourceMappingURL=status-BrK9v1yb.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"status-BrK9v1yb.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,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;EAChD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,kBAAkB,GAAG,SAAS,YAAY;EAChE,QAAQ,IAAI,MAAM,IAAI,oBAAoB,GAAG,SAAS,eAAe;EACrE,QAAQ,IAAI,MAAM,IAAI,oBAAoB,GAAG,SAAS,cAAc;EACpE,QAAQ,IAAI,MAAM,IAAI,YAAY,GAAG,SAAS,SAAS;EACvD,IAAI,SAAS,cACX,QAAQ,IAAI,MAAM,IAAI,gBAAgB,GAAG,SAAS,YAAY;EAGhE,MAAM,UACJ,QAAQ,sBAAsB,QAAQ,IAAI;EAC5C,MAAM,qBAAqB,UAAU,KAAK,QAAQ,OAAO,IAAI,KAAA;EAE7D,IAAI,oBAAoB;GACtB,MAAM,YAAY,qBAChB,SAAS,cACT,kBACF;GACA,IAAI,WAAW;IACb,MAAM,gBAAgB,0BAA0B,SAAS;IACzD,QAAQ,IAAI,MAAM,IAAI,mBAAmB,GAAG,aAAa;IACzD,QAAQ,IAAI;IAEZ,IAAI,kBAAkB,SAAS,iBAAiB;KAC9C,QAAQ,IACN,MAAM,OACJ,uBAAuB,SAAS,gBAAgB,KAAK,eACvD,CACF;KACA,QAAQ,IACN,MAAM,IAAI,QAAQ,GAClB,sCAAsC,oBACxC;IACF,OACE,QAAQ,IAAI,MAAM,MAAM,cAAc,CAAC;GAE3C,OAAO;IACL,QAAQ,IAAI;IACZ,QAAQ,IAAI,MAAM,OAAO,0CAA0C,CAAC;IACpE,QAAQ,IACN,MAAM,IAAI,QAAQ,GAClB,MAAM,mBAAmB,sBAC3B;GACF;EACF,OAAO;GACL,QAAQ,IAAI;GACZ,QAAQ,IACN,MAAM,IACJ,+EACF,CACF;EACF;EAEA,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
@@ -1,101 +0,0 @@
1
- import { c as readManifest, l as resolveMosaicTemplatePath } from "./index.js";
2
- import { i as getTemplateVersionFromTag, n as getLatestTemplateTag, r as getTemplateDiff } from "./git-ops-BNpQnEc1.js";
3
- import path from "node:path";
4
- import chalk from "chalk";
5
- import fs from "fs-extra";
6
- //#region src/commands/sync.ts
7
- function generateSyncContext(manifest, toVersion, diff, notes) {
8
- let content = `# Mosaic Sync Context
9
-
10
- ## App Info
11
- - **Template:** ${manifest.templateType}
12
- - **Current version:** ${manifest.templateVersion}
13
- - **Target version:** ${toVersion}
14
-
15
- ## Placeholder Mappings
16
-
17
- When applying template changes, replace these placeholder tokens with the actual values:
18
-
19
- | Placeholder | Value |
20
- |------------|-------|
21
- ${Object.entries(manifest.placeholders).map(([k, v]) => `| \`${k}\` | \`${v}\` |`).join("\n")}
22
-
23
- ## Template Changes (${manifest.templateVersion} → ${toVersion})
24
-
25
- \`\`\`diff
26
- ${diff}
27
- \`\`\`
28
- `;
29
- if (notes.trim()) content += `
30
- ## Divergence Notes (from mosaic-template-notes.md)
31
-
32
- ${notes}
33
- `;
34
- content += `
35
- ## Instructions
36
-
37
- 1. Apply the template changes above to this app
38
- 2. When you see placeholder tokens (e.g. \`__APP_NAME__\`), replace them with the actual values from the mapping table
39
- 3. Check the divergence notes — preserve intentional divergences
40
- 4. For files not modified locally: apply changes directly
41
- 5. For files modified locally: merge intelligently, preserving local customizations
42
- 6. After applying all changes, run: \`pnpm install && pnpm build && pnpm lint\`
43
- 7. Update \`.mosaic-template.json\`: set \`templateVersion\` to \`"${toVersion}"\` and update \`templateCommit\`
44
- 8. If you made decisions about merge conflicts, add notes to \`mosaic-template-notes.md\`
45
- 9. Delete this file (\`.mosaic-sync-context.md\`) when done
46
- `;
47
- return content;
48
- }
49
- async function syncCommand(options) {
50
- const cwd = process.cwd();
51
- try {
52
- const manifest = await readManifest(cwd);
53
- const mosaicTemplatePath = resolveMosaicTemplatePath(options);
54
- const fromTag = `template/${manifest.templateType}/${manifest.templateVersion}`;
55
- let toTag;
56
- if (options.to) toTag = `template/${manifest.templateType}/${options.to}`;
57
- else {
58
- const latest = getLatestTemplateTag(manifest.templateType, mosaicTemplatePath);
59
- if (!latest) {
60
- console.error(chalk.red("No template tags found. Run 'pnpm template:tag' in the mosaic repo first."));
61
- process.exit(1);
62
- }
63
- toTag = latest;
64
- }
65
- const toVersion = getTemplateVersionFromTag(toTag);
66
- if (toVersion === manifest.templateVersion) {
67
- console.log(chalk.green("Already up to date."));
68
- return;
69
- }
70
- const diff = getTemplateDiff(mosaicTemplatePath, manifest.source.templatePath, fromTag, toTag);
71
- if (!diff.trim()) {
72
- console.log(chalk.green("No template file changes between versions."));
73
- return;
74
- }
75
- const notesPath = path.join(cwd, "mosaic-template-notes.md");
76
- let notes = "";
77
- if (await fs.pathExists(notesPath)) notes = await fs.readFile(notesPath, "utf-8");
78
- const context = generateSyncContext(manifest, toVersion, diff, notes);
79
- const contextPath = path.join(cwd, ".mosaic-sync-context.md");
80
- await fs.writeFile(contextPath, context);
81
- console.log();
82
- console.log(chalk.bold("Sync Context Generated"));
83
- console.log();
84
- console.log(chalk.dim(" From:"), manifest.templateVersion);
85
- console.log(chalk.dim(" To:"), toVersion);
86
- console.log(chalk.dim(" Context file:"), ".mosaic-sync-context.md");
87
- console.log();
88
- console.log("Next steps:");
89
- console.log(chalk.dim(" 1."), "Open Claude Code in this directory");
90
- console.log(chalk.dim(" 2."), "Tell Claude: \"Read .mosaic-sync-context.md and apply the template changes\"");
91
- console.log(chalk.dim(" 3."), "Review Claude's changes, then delete .mosaic-sync-context.md");
92
- console.log();
93
- } catch (error) {
94
- console.error(chalk.red(error.message));
95
- process.exit(1);
96
- }
97
- }
98
- //#endregion
99
- export { syncCommand };
100
-
101
- //# sourceMappingURL=sync-DC5DhIBT.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"sync-DC5DhIBT.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,YAAY,EACnC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,KAAK,EACzC,KAAK,IAAI,EAAE;;uBAES,SAAS,gBAAgB,KAAK,UAAU;;;EAG7D,KAAK;;;CAIL,IAAI,MAAM,KAAK,GACb,WAAW;;;EAGb,MAAM;;CAIN,WAAW;;;;;;;;;qEASwD,UAAU;;;;CAK7E,OAAO;AACT;AAEA,eAAsB,YAAY,SAAqC;CACrE,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EACvC,MAAM,qBAAqB,0BAA0B,OAAO;EAE5D,MAAM,UAAU,YAAY,SAAS,aAAa,GAAG,SAAS;EAE9D,IAAI;EACJ,IAAI,QAAQ,IACV,QAAQ,YAAY,SAAS,aAAa,GAAG,QAAQ;OAChD;GACL,MAAM,SAAS,qBACb,SAAS,cACT,kBACF;GACA,IAAI,CAAC,QAAQ;IACX,QAAQ,MACN,MAAM,IACJ,2EACF,CACF;IACA,QAAQ,KAAK,CAAC;GAChB;GACA,QAAQ;EACV;EAEA,MAAM,YAAY,0BAA0B,KAAK;EAEjD,IAAI,cAAc,SAAS,iBAAiB;GAC1C,QAAQ,IAAI,MAAM,MAAM,qBAAqB,CAAC;GAC9C;EACF;EAEA,MAAM,OAAO,gBACX,oBACA,SAAS,OAAO,cAChB,SACA,KACF;EAEA,IAAI,CAAC,KAAK,KAAK,GAAG;GAChB,QAAQ,IAAI,MAAM,MAAM,4CAA4C,CAAC;GACrE;EACF;EAGA,MAAM,YAAY,KAAK,KAAK,KAAK,0BAA0B;EAC3D,IAAI,QAAQ;EACZ,IAAI,MAAM,GAAG,WAAW,SAAS,GAC/B,QAAQ,MAAM,GAAG,SAAS,WAAW,OAAO;EAG9C,MAAM,UAAU,oBAAoB,UAAU,WAAW,MAAM,KAAK;EACpE,MAAM,cAAc,KAAK,KAAK,KAAK,yBAAyB;EAC5D,MAAM,GAAG,UAAU,aAAa,OAAO;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,wBAAwB,CAAC;EAChD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,SAAS,GAAG,SAAS,eAAe;EAC1D,QAAQ,IAAI,MAAM,IAAI,OAAO,GAAG,SAAS;EACzC,QAAQ,IAAI,MAAM,IAAI,iBAAiB,GAAG,yBAAyB;EACnE,QAAQ,IAAI;EACZ,QAAQ,IAAI,aAAa;EACzB,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,oCAAoC;EACnE,QAAQ,IACN,MAAM,IAAI,MAAM,GAChB,8EACF;EACA,QAAQ,IACN,MAAM,IAAI,MAAM,GAChB,8DACF;EACA,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"upstream-PNL6DGtl.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,YAAY,EACnC,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,EAAE,GAAG,MAAM,EACxC,KAAK,CAAC,GAAG,OAAO,OAAO,EAAE,SAAS,EAAE,KAAK,EACzC,KAAK,IAAI,EAAE;;;;;CAMZ,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,cAAc,KAAK,QAAQ,QAAQ,IAAI;EAC7C,MAAM,kBAAkB,GAAG,SAAS,OAAO,aAAa,GAAG;EAE3D,MAAM,aAAc,MAAM,GAAG,WAAW,WAAW,IAC/C,MAAM,GAAG,SAAS,aAAa,OAAO,IACtC;EACJ,MAAM,kBAAkB,aACtB,oBACA,KACA,eACF;EAEA,WAAW,OAAO,KAAK;EAEvB,IAAI,CAAC,mBAAmB,YAAY;GAClC,WAAW,oCAAoC,SAAS,gBAAgB;GACxE,WAAW,WAAW,WAAW;EACnC,OAAO,IAAI,mBAAmB,CAAC,YAC7B,WAAW;OACN,IAAI,cAAc,iBAAiB;GACxC,WAAW,6BAA6B,WAAW;GACnD,WAAW,0BAA0B,SAAS,gBAAgB,gBAAgB,gBAAgB;EAChG,OACE,WAAW;CAEf;CAEA,WAAW;;;;gEAImD,SAAS,OAAO,aAAa;;;;;;CAO3F,OAAO;AACT;AAEA,eAAsB,gBAAgB,SAAyC;CAC7E,MAAM,MAAM,QAAQ,IAAI;CAExB,IAAI;EACF,MAAM,WAAW,MAAM,aAAa,GAAG;EACvC,MAAM,qBAAqB,0BAA0B,OAAO;EAE5D,IAAI,CAAC,QAAQ,SAAS,QAAQ,MAAM,WAAW,GAAG;GAChD,QAAQ,MACN,MAAM,IAAI,gDAAgD,CAC5D;GACA,QAAQ,IACN,MAAM,IACJ,+DACF,CACF;GACA,QAAQ,KAAK,CAAC;EAChB;EAIA,MAAM,UAAU,MAAM,wBACpB,UACA,oBACA,YALsB,SAAS,aAAa,GAAG,SAAS,mBAMxD,KACA,QAAQ,KACV;EACA,MAAM,cAAc,KAAK,KAAK,KAAK,6BAA6B;EAChE,MAAM,GAAG,UAAU,aAAa,OAAO;EAEvC,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,KAAK,4BAA4B,CAAC;EACpD,QAAQ,IAAI;EACZ,QAAQ,IAAI,MAAM,IAAI,UAAU,GAAG,QAAQ,MAAM,KAAK,IAAI,CAAC;EAC3D,QAAQ,IAAI,MAAM,IAAI,iBAAiB,GAAG,6BAA6B;EACvE,QAAQ,IAAI;EACZ,QAAQ,IAAI,aAAa;EACzB,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,qCAAqC;EACpE,QAAQ,IACN,MAAM,IAAI,MAAM,GAChB,sBAAsB,KAAK,QAAQ,KAAK,6BAA6B,EAAE,kDACzE;EACA,QAAQ,IAAI,MAAM,IAAI,MAAM,GAAG,yCAAyC;EACxE,QAAQ,IAAI;CACd,SAAS,OAAO;EACd,QAAQ,MAAM,MAAM,IAAK,MAAgB,OAAO,CAAC;EACjD,QAAQ,KAAK,CAAC;CAChB;AACF"}