@percepta/create 4.1.7 → 4.1.9

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 (63) hide show
  1. package/dist/{git-ops-BD7JNnal.js → git-ops-BNpQnEc1.js} +1 -1
  2. package/dist/{git-ops-BD7JNnal.js.map → git-ops-BNpQnEc1.js.map} +1 -1
  3. package/dist/{github-D3YOEl91.js → github-BOp8VQCY.js} +1 -1
  4. package/dist/{github-D3YOEl91.js.map → github-BOp8VQCY.js.map} +1 -1
  5. package/dist/index.js +185 -28
  6. package/dist/index.js.map +1 -1
  7. package/dist/{init-BD3EyyLO.js → init-CsuO_mu2.js} +2 -4
  8. package/dist/{init-BD3EyyLO.js.map → init-CsuO_mu2.js.map} +1 -1
  9. package/dist/{register-app-DZg-Pmtd.js → register-app-B9vKTkoI.js} +89 -37
  10. package/dist/register-app-B9vKTkoI.js.map +1 -0
  11. package/dist/{register-os-blueprint-Cgq1rXzQ.js → register-os-blueprint-Gdyn0pN1.js} +3 -4
  12. package/dist/{register-os-blueprint-Cgq1rXzQ.js.map → register-os-blueprint-Gdyn0pN1.js.map} +1 -1
  13. package/dist/{status-K6raTwwu.js → status-BrK9v1yb.js} +3 -3
  14. package/dist/{status-K6raTwwu.js.map → status-BrK9v1yb.js.map} +1 -1
  15. package/dist/{sync-Bi958-2W.js → sync-DC5DhIBT.js} +3 -3
  16. package/dist/{sync-Bi958-2W.js.map → sync-DC5DhIBT.js.map} +1 -1
  17. package/dist/{upstream-CAraZeSS.js → upstream-PNL6DGtl.js} +3 -3
  18. package/dist/{upstream-CAraZeSS.js.map → upstream-PNL6DGtl.js.map} +1 -1
  19. package/package.json +3 -3
  20. package/template-versions.json +2 -2
  21. package/templates/infra/os.blueprint.yaml.template +13 -0
  22. package/templates/monorepo/auth/README.md +2 -2
  23. package/templates/monorepo/auth/package.json +1 -1
  24. package/templates/monorepo/auth/src/drizzle/migrations/meta/0000_snapshot.json +547 -0
  25. package/templates/monorepo/package.json.template +6 -6
  26. package/templates/monorepo/pnpm-workspace.yaml +9 -1
  27. package/templates/webapp/AGENTS.md +39 -17
  28. package/templates/webapp/README.md +58 -59
  29. package/templates/webapp/agent-skills/access-control.md +13 -12
  30. package/templates/webapp/agent-skills/database.md +12 -5
  31. package/templates/webapp/agent-skills/inngest.md +10 -8
  32. package/templates/webapp/agent-skills/langfuse.md +7 -5
  33. package/templates/webapp/agent-skills/oneshot.md +15 -13
  34. package/templates/webapp/next.config.ts +1 -1
  35. package/templates/webapp/package.json.template +6 -6
  36. package/templates/webapp/playwright.config.ts +1 -2
  37. package/templates/webapp/scripts/seed.ts +3 -3
  38. package/templates/webapp/src/app/(app)/page.tsx +5 -3
  39. package/templates/webapp/src/app/(auth)/layout.tsx +2 -2
  40. package/templates/webapp/src/app/(settings)/settings/page.tsx +21 -22
  41. package/templates/webapp/src/components/FaroProvider.tsx +1 -2
  42. package/templates/webapp/src/components/Header.tsx +2 -8
  43. package/templates/webapp/src/components/form/FormItem.tsx +1 -1
  44. package/templates/webapp/src/drizzle/db.ts +3 -1
  45. package/templates/webapp/src/drizzle/schema/index.ts +1 -1
  46. package/templates/webapp/src/drizzle/schema/utils/jsonbFromZod.ts +1 -1
  47. package/templates/webapp/src/instrumentation.ts +3 -6
  48. package/templates/webapp/src/lib/auth/index.ts +2 -2
  49. package/templates/webapp/src/lib/auth-client.ts +3 -2
  50. package/templates/webapp/src/lib/trpc.ts +1 -1
  51. package/templates/webapp/src/server/trpc.ts +1 -1
  52. package/templates/webapp/src/services/DatabaseService.ts +1 -1
  53. package/templates/webapp/src/services/access/AppAccessControl.ts +1 -3
  54. package/templates/webapp/src/services/inngest/AppWorkflowService.ts +1 -1
  55. package/templates/webapp/src/startup-checks.ts +1 -1
  56. package/templates/webapp/src/styles/globals.css +13 -2
  57. package/dist/manifest-By1SgOjC.js +0 -59
  58. package/dist/manifest-By1SgOjC.js.map +0 -1
  59. package/dist/register-app-DZg-Pmtd.js.map +0 -1
  60. package/dist/template-versions-CEIP9vhl.js +0 -35
  61. package/dist/template-versions-CEIP9vhl.js.map +0 -1
  62. package/dist/validate-dssldJAj.js +0 -14
  63. package/dist/validate-dssldJAj.js.map +0 -1
@@ -48,4 +48,4 @@ function getFileAtTag(repoPath, tag, filePath) {
48
48
  //#endregion
49
49
  export { getTemplateVersionFromTag as i, getLatestTemplateTag as n, getTemplateDiff as r, getFileAtTag as t };
50
50
 
51
- //# sourceMappingURL=git-ops-BD7JNnal.js.map
51
+ //# sourceMappingURL=git-ops-BNpQnEc1.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"git-ops-BD7JNnal.js","names":[],"sources":["../src/utils/git-ops.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\n// Git requires forward slashes for in-repo paths regardless of OS\nfunction toGitPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\nexport function getLatestTemplateTag(\n type: string,\n repoPath: string,\n): string | null {\n try {\n const tags = execFileSync(\n \"git\",\n [\"tag\", \"-l\", `template/${type}/*`, \"--sort=-v:refname\"],\n { cwd: repoPath, encoding: \"utf-8\" },\n ).trim();\n if (!tags) return null;\n return tags.split(\"\\n\")[0] ?? null;\n } catch {\n return null;\n }\n}\n\nexport function getTemplateVersionFromTag(tag: string): string {\n const parts = tag.split(\"/\");\n return parts[parts.length - 1] ?? \"\";\n}\n\nexport function getTemplateDiff(\n repoPath: string,\n templatePath: string,\n fromTag: string,\n toTag: string,\n): string {\n return execFileSync(\n \"git\",\n [\"diff\", `${fromTag}..${toTag}`, \"--\", toGitPath(templatePath)],\n {\n cwd: repoPath,\n encoding: \"utf-8\",\n },\n );\n}\n\nexport function getFileAtTag(\n repoPath: string,\n tag: string,\n filePath: string,\n): string | null {\n try {\n return execFileSync(\"git\", [\"show\", `${tag}:${toGitPath(filePath)}`], {\n cwd: repoPath,\n encoding: \"utf-8\",\n });\n } catch {\n return null;\n }\n}\n"],"mappings":";;AAGA,SAAS,UAAU,GAAmB;AACpC,QAAO,EAAE,QAAQ,OAAO,IAAI;;AAG9B,SAAgB,qBACd,MACA,UACe;AACf,KAAI;EACF,MAAM,OAAO,aACX,OACA;GAAC;GAAO;GAAM,YAAY,KAAK;GAAK;GAAoB,EACxD;GAAE,KAAK;GAAU,UAAU;GAAS,CACrC,CAAC,MAAM;AACR,MAAI,CAAC,KAAM,QAAO;AAClB,SAAO,KAAK,MAAM,KAAK,CAAC,MAAM;SACxB;AACN,SAAO;;;AAIX,SAAgB,0BAA0B,KAAqB;CAC7D,MAAM,QAAQ,IAAI,MAAM,IAAI;AAC5B,QAAO,MAAM,MAAM,SAAS,MAAM;;AAGpC,SAAgB,gBACd,UACA,cACA,SACA,OACQ;AACR,QAAO,aACL,OACA;EAAC;EAAQ,GAAG,QAAQ,IAAI;EAAS;EAAM,UAAU,aAAa;EAAC,EAC/D;EACE,KAAK;EACL,UAAU;EACX,CACF;;AAGH,SAAgB,aACd,UACA,KACA,UACe;AACf,KAAI;AACF,SAAO,aAAa,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,UAAU,SAAS,GAAG,EAAE;GACpE,KAAK;GACL,UAAU;GACX,CAAC;SACI;AACN,SAAO"}
1
+ {"version":3,"file":"git-ops-BNpQnEc1.js","names":[],"sources":["../src/utils/git-ops.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\n// Git requires forward slashes for in-repo paths regardless of OS\nfunction toGitPath(p: string): string {\n return p.replace(/\\\\/g, \"/\");\n}\n\nexport function getLatestTemplateTag(\n type: string,\n repoPath: string,\n): string | null {\n try {\n const tags = execFileSync(\n \"git\",\n [\"tag\", \"-l\", `template/${type}/*`, \"--sort=-v:refname\"],\n { cwd: repoPath, encoding: \"utf-8\" },\n ).trim();\n if (!tags) return null;\n return tags.split(\"\\n\")[0] ?? null;\n } catch {\n return null;\n }\n}\n\nexport function getTemplateVersionFromTag(tag: string): string {\n const parts = tag.split(\"/\");\n return parts[parts.length - 1] ?? \"\";\n}\n\nexport function getTemplateDiff(\n repoPath: string,\n templatePath: string,\n fromTag: string,\n toTag: string,\n): string {\n return execFileSync(\n \"git\",\n [\"diff\", `${fromTag}..${toTag}`, \"--\", toGitPath(templatePath)],\n {\n cwd: repoPath,\n encoding: \"utf-8\",\n },\n );\n}\n\nexport function getFileAtTag(\n repoPath: string,\n tag: string,\n filePath: string,\n): string | null {\n try {\n return execFileSync(\"git\", [\"show\", `${tag}:${toGitPath(filePath)}`], {\n cwd: repoPath,\n encoding: \"utf-8\",\n });\n } catch {\n return null;\n }\n}\n"],"mappings":";;AAGA,SAAS,UAAU,GAAmB;CACpC,OAAO,EAAE,QAAQ,OAAO,GAAG;AAC7B;AAEA,SAAgB,qBACd,MACA,UACe;CACf,IAAI;EACF,MAAM,OAAO,aACX,OACA;GAAC;GAAO;GAAM,YAAY,KAAK;GAAK;EAAmB,GACvD;GAAE,KAAK;GAAU,UAAU;EAAQ,CACrC,EAAE,KAAK;EACP,IAAI,CAAC,MAAM,OAAO;EAClB,OAAO,KAAK,MAAM,IAAI,EAAE,MAAM;CAChC,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,0BAA0B,KAAqB;CAC7D,MAAM,QAAQ,IAAI,MAAM,GAAG;CAC3B,OAAO,MAAM,MAAM,SAAS,MAAM;AACpC;AAEA,SAAgB,gBACd,UACA,cACA,SACA,OACQ;CACR,OAAO,aACL,OACA;EAAC;EAAQ,GAAG,QAAQ,IAAI;EAAS;EAAM,UAAU,YAAY;CAAC,GAC9D;EACE,KAAK;EACL,UAAU;CACZ,CACF;AACF;AAEA,SAAgB,aACd,UACA,KACA,UACe;CACf,IAAI;EACF,OAAO,aAAa,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,UAAU,QAAQ,GAAG,GAAG;GACpE,KAAK;GACL,UAAU;EACZ,CAAC;CACH,QAAQ;EACN,OAAO;CACT;AACF"}
@@ -144,4 +144,4 @@ function encodePathSegments(filePath) {
144
144
  //#endregion
145
145
  export { createOrUpdateInfraPullRequestFiles as a, createOrUpdateInfraPullRequest as i, INFRA_REPOSITORY as n, resolveGitHubToken as o, createInfraGitHubApi as r, INFRA_BASE_BRANCH as t };
146
146
 
147
- //# sourceMappingURL=github-D3YOEl91.js.map
147
+ //# sourceMappingURL=github-BOp8VQCY.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"github-D3YOEl91.js","names":[],"sources":["../src/commands/infra/github.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nconst GITHUB_API_BASE_URL = \"https://api.github.com\";\n\nexport const INFRA_REPOSITORY = \"Percepta-Core/infra\";\nexport const INFRA_REPOSITORY_OWNER = \"Percepta-Core\";\nexport const INFRA_BASE_BRANCH = \"main\";\n\nexport interface GitHubFile {\n sha: string;\n content: string;\n}\n\nexport interface GitHubPullRequest {\n number: number;\n url: string;\n}\n\nexport interface InfraGitHubApi {\n getFile(path: string, ref: string): Promise<GitHubFile | null>;\n getBranchSha(branch: string): Promise<string | null>;\n createBranch(branch: string, sha: string): Promise<void>;\n putFile(args: {\n path: string;\n branch: string;\n content: string;\n message: string;\n sha?: string;\n }): Promise<void>;\n findOpenPullRequest(branch: string): Promise<GitHubPullRequest | null>;\n createPullRequest(args: {\n branch: string;\n title: string;\n body: string;\n }): Promise<GitHubPullRequest>;\n}\n\nexport interface InfraPullRequestResult {\n pullRequestUrl: string;\n status: \"created_pr\" | \"updated_pr\";\n}\n\nexport interface InfraPullRequestFile {\n baseFileSha?: string;\n content: string;\n message: string;\n path: string;\n}\n\nexport async function createOrUpdateInfraPullRequest(args: {\n baseFileSha?: string;\n body: string;\n branchName: string;\n content: string;\n github: InfraGitHubApi;\n message: string;\n targetPath: string;\n title: string;\n}): Promise<InfraPullRequestResult> {\n return createOrUpdateInfraPullRequestFiles({\n body: args.body,\n branchName: args.branchName,\n files: [\n {\n baseFileSha: args.baseFileSha,\n content: args.content,\n message: args.message,\n path: args.targetPath,\n },\n ],\n github: args.github,\n title: args.title,\n });\n}\n\nexport async function createOrUpdateInfraPullRequestFiles(args: {\n body: string;\n branchName: string;\n files: InfraPullRequestFile[];\n github: InfraGitHubApi;\n title: string;\n}): Promise<InfraPullRequestResult> {\n const mainSha = await args.github.getBranchSha(INFRA_BASE_BRANCH);\n if (!mainSha) {\n throw new Error(`Could not find ${INFRA_REPOSITORY}@${INFRA_BASE_BRANCH}.`);\n }\n\n if (!(await args.github.getBranchSha(args.branchName))) {\n await args.github.createBranch(args.branchName, mainSha);\n }\n\n const existingPullRequest = await args.github.findOpenPullRequest(\n args.branchName,\n );\n\n for (const file of args.files) {\n const branchFile = await args.github.getFile(file.path, args.branchName);\n if (branchFile?.content !== file.content) {\n await args.github.putFile({\n path: file.path,\n branch: args.branchName,\n content: file.content,\n message: file.message,\n sha: branchFile?.sha ?? file.baseFileSha,\n });\n }\n }\n\n const pullRequest =\n existingPullRequest ??\n (await args.github.createPullRequest({\n branch: args.branchName,\n title: args.title,\n body: args.body,\n }));\n\n return {\n pullRequestUrl: pullRequest.url,\n status: existingPullRequest ? \"updated_pr\" : \"created_pr\",\n };\n}\n\nexport function createInfraGitHubApi(token: string): InfraGitHubApi {\n return {\n async getFile(filePath, ref) {\n const encodedPath = encodePathSegments(filePath);\n const response = await requestGitHub<{\n content?: unknown;\n sha?: unknown;\n type?: unknown;\n }>(\n token,\n \"GET\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`,\n );\n if (response == null) return null;\n if (\n typeof response.sha !== \"string\" ||\n typeof response.content !== \"string\" ||\n response.type !== \"file\"\n ) {\n throw new Error(\n `Unexpected GitHub content response for ${filePath} in ${INFRA_REPOSITORY}.`,\n );\n }\n\n return {\n sha: response.sha,\n content: Buffer.from(\n response.content.replace(/\\s/g, \"\"),\n \"base64\",\n ).toString(\"utf-8\"),\n };\n },\n\n async getBranchSha(branch) {\n const response = await requestGitHub<{\n object?: { sha?: unknown };\n }>(token, \"GET\", `/repos/${INFRA_REPOSITORY}/git/ref/heads/${branch}`);\n if (response == null) return null;\n const sha = response.object?.sha;\n if (typeof sha !== \"string\") {\n throw new Error(`Unexpected GitHub ref response for ${branch}.`);\n }\n return sha;\n },\n\n async createBranch(branch, sha) {\n await requestGitHub(\n token,\n \"POST\",\n `/repos/${INFRA_REPOSITORY}/git/refs`,\n {\n ref: `refs/heads/${branch}`,\n sha,\n },\n );\n },\n\n async putFile({ path: filePath, branch, content, message, sha }) {\n const encodedPath = encodePathSegments(filePath);\n await requestGitHub(\n token,\n \"PUT\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}`,\n {\n branch,\n content: Buffer.from(content, \"utf-8\").toString(\"base64\"),\n message,\n sha,\n },\n );\n },\n\n async findOpenPullRequest(branch) {\n const params = new URLSearchParams({\n base: INFRA_BASE_BRANCH,\n head: `${INFRA_REPOSITORY_OWNER}:${branch}`,\n state: \"open\",\n });\n const response = await requestGitHub<\n Array<{ html_url?: unknown; number?: unknown }>\n >(token, \"GET\", `/repos/${INFRA_REPOSITORY}/pulls?${params}`);\n const pullRequest = response?.[0];\n if (!pullRequest) return null;\n if (\n typeof pullRequest.html_url !== \"string\" ||\n typeof pullRequest.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: pullRequest.number,\n url: pullRequest.html_url,\n };\n },\n\n async createPullRequest({ branch, title, body }) {\n const response = await requestGitHub<{\n html_url?: unknown;\n number?: unknown;\n }>(token, \"POST\", `/repos/${INFRA_REPOSITORY}/pulls`, {\n base: INFRA_BASE_BRANCH,\n body,\n head: branch,\n maintainer_can_modify: true,\n title,\n });\n if (\n !response ||\n typeof response.html_url !== \"string\" ||\n typeof response.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: response.number,\n url: response.html_url,\n };\n },\n };\n}\n\nexport function resolveGitHubToken(): string {\n const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n if (envToken) return envToken;\n\n try {\n const token = execFileSync(\"gh\", [\"auth\", \"token\"], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n if (token) return token;\n } catch {\n // Fall through to the actionable error below.\n }\n\n throw new Error(\n \"GitHub auth is required. Set GITHUB_TOKEN or GH_TOKEN, or run `gh auth login`.\",\n );\n}\n\nasync function requestGitHub<T>(\n token: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T | null> {\n const response = await fetch(`${GITHUB_API_BASE_URL}${path}`, {\n method,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (response.status === 404) return null;\n\n if (!response.ok) {\n const responseText = await response.text();\n throw new Error(\n `GitHub ${method} ${path} failed with ${response.status}: ${responseText}`,\n );\n }\n\n if (response.status === 204) return null;\n return (await response.json()) as T;\n}\n\nfunction encodePathSegments(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"github-BOp8VQCY.js","names":[],"sources":["../src/commands/infra/github.ts"],"sourcesContent":["import { execFileSync } from \"node:child_process\";\n\nconst GITHUB_API_BASE_URL = \"https://api.github.com\";\n\nexport const INFRA_REPOSITORY = \"Percepta-Core/infra\";\nexport const INFRA_REPOSITORY_OWNER = \"Percepta-Core\";\nexport const INFRA_BASE_BRANCH = \"main\";\n\nexport interface GitHubFile {\n sha: string;\n content: string;\n}\n\nexport interface GitHubPullRequest {\n number: number;\n url: string;\n}\n\nexport interface InfraGitHubApi {\n getFile(path: string, ref: string): Promise<GitHubFile | null>;\n getBranchSha(branch: string): Promise<string | null>;\n createBranch(branch: string, sha: string): Promise<void>;\n putFile(args: {\n path: string;\n branch: string;\n content: string;\n message: string;\n sha?: string;\n }): Promise<void>;\n findOpenPullRequest(branch: string): Promise<GitHubPullRequest | null>;\n createPullRequest(args: {\n branch: string;\n title: string;\n body: string;\n }): Promise<GitHubPullRequest>;\n}\n\nexport interface InfraPullRequestResult {\n pullRequestUrl: string;\n status: \"created_pr\" | \"updated_pr\";\n}\n\nexport interface InfraPullRequestFile {\n baseFileSha?: string;\n content: string;\n message: string;\n path: string;\n}\n\nexport async function createOrUpdateInfraPullRequest(args: {\n baseFileSha?: string;\n body: string;\n branchName: string;\n content: string;\n github: InfraGitHubApi;\n message: string;\n targetPath: string;\n title: string;\n}): Promise<InfraPullRequestResult> {\n return createOrUpdateInfraPullRequestFiles({\n body: args.body,\n branchName: args.branchName,\n files: [\n {\n baseFileSha: args.baseFileSha,\n content: args.content,\n message: args.message,\n path: args.targetPath,\n },\n ],\n github: args.github,\n title: args.title,\n });\n}\n\nexport async function createOrUpdateInfraPullRequestFiles(args: {\n body: string;\n branchName: string;\n files: InfraPullRequestFile[];\n github: InfraGitHubApi;\n title: string;\n}): Promise<InfraPullRequestResult> {\n const mainSha = await args.github.getBranchSha(INFRA_BASE_BRANCH);\n if (!mainSha) {\n throw new Error(`Could not find ${INFRA_REPOSITORY}@${INFRA_BASE_BRANCH}.`);\n }\n\n if (!(await args.github.getBranchSha(args.branchName))) {\n await args.github.createBranch(args.branchName, mainSha);\n }\n\n const existingPullRequest = await args.github.findOpenPullRequest(\n args.branchName,\n );\n\n for (const file of args.files) {\n const branchFile = await args.github.getFile(file.path, args.branchName);\n if (branchFile?.content !== file.content) {\n await args.github.putFile({\n path: file.path,\n branch: args.branchName,\n content: file.content,\n message: file.message,\n sha: branchFile?.sha ?? file.baseFileSha,\n });\n }\n }\n\n const pullRequest =\n existingPullRequest ??\n (await args.github.createPullRequest({\n branch: args.branchName,\n title: args.title,\n body: args.body,\n }));\n\n return {\n pullRequestUrl: pullRequest.url,\n status: existingPullRequest ? \"updated_pr\" : \"created_pr\",\n };\n}\n\nexport function createInfraGitHubApi(token: string): InfraGitHubApi {\n return {\n async getFile(filePath, ref) {\n const encodedPath = encodePathSegments(filePath);\n const response = await requestGitHub<{\n content?: unknown;\n sha?: unknown;\n type?: unknown;\n }>(\n token,\n \"GET\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}?ref=${encodeURIComponent(ref)}`,\n );\n if (response == null) return null;\n if (\n typeof response.sha !== \"string\" ||\n typeof response.content !== \"string\" ||\n response.type !== \"file\"\n ) {\n throw new Error(\n `Unexpected GitHub content response for ${filePath} in ${INFRA_REPOSITORY}.`,\n );\n }\n\n return {\n sha: response.sha,\n content: Buffer.from(\n response.content.replace(/\\s/g, \"\"),\n \"base64\",\n ).toString(\"utf-8\"),\n };\n },\n\n async getBranchSha(branch) {\n const response = await requestGitHub<{\n object?: { sha?: unknown };\n }>(token, \"GET\", `/repos/${INFRA_REPOSITORY}/git/ref/heads/${branch}`);\n if (response == null) return null;\n const sha = response.object?.sha;\n if (typeof sha !== \"string\") {\n throw new Error(`Unexpected GitHub ref response for ${branch}.`);\n }\n return sha;\n },\n\n async createBranch(branch, sha) {\n await requestGitHub(\n token,\n \"POST\",\n `/repos/${INFRA_REPOSITORY}/git/refs`,\n {\n ref: `refs/heads/${branch}`,\n sha,\n },\n );\n },\n\n async putFile({ path: filePath, branch, content, message, sha }) {\n const encodedPath = encodePathSegments(filePath);\n await requestGitHub(\n token,\n \"PUT\",\n `/repos/${INFRA_REPOSITORY}/contents/${encodedPath}`,\n {\n branch,\n content: Buffer.from(content, \"utf-8\").toString(\"base64\"),\n message,\n sha,\n },\n );\n },\n\n async findOpenPullRequest(branch) {\n const params = new URLSearchParams({\n base: INFRA_BASE_BRANCH,\n head: `${INFRA_REPOSITORY_OWNER}:${branch}`,\n state: \"open\",\n });\n const response = await requestGitHub<\n Array<{ html_url?: unknown; number?: unknown }>\n >(token, \"GET\", `/repos/${INFRA_REPOSITORY}/pulls?${params}`);\n const pullRequest = response?.[0];\n if (!pullRequest) return null;\n if (\n typeof pullRequest.html_url !== \"string\" ||\n typeof pullRequest.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: pullRequest.number,\n url: pullRequest.html_url,\n };\n },\n\n async createPullRequest({ branch, title, body }) {\n const response = await requestGitHub<{\n html_url?: unknown;\n number?: unknown;\n }>(token, \"POST\", `/repos/${INFRA_REPOSITORY}/pulls`, {\n base: INFRA_BASE_BRANCH,\n body,\n head: branch,\n maintainer_can_modify: true,\n title,\n });\n if (\n !response ||\n typeof response.html_url !== \"string\" ||\n typeof response.number !== \"number\"\n ) {\n throw new Error(`Unexpected GitHub pull request response.`);\n }\n return {\n number: response.number,\n url: response.html_url,\n };\n },\n };\n}\n\nexport function resolveGitHubToken(): string {\n const envToken = process.env.GITHUB_TOKEN ?? process.env.GH_TOKEN;\n if (envToken) return envToken;\n\n try {\n const token = execFileSync(\"gh\", [\"auth\", \"token\"], {\n encoding: \"utf-8\",\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n }).trim();\n if (token) return token;\n } catch {\n // Fall through to the actionable error below.\n }\n\n throw new Error(\n \"GitHub auth is required. Set GITHUB_TOKEN or GH_TOKEN, or run `gh auth login`.\",\n );\n}\n\nasync function requestGitHub<T>(\n token: string,\n method: string,\n path: string,\n body?: unknown,\n): Promise<T | null> {\n const response = await fetch(`${GITHUB_API_BASE_URL}${path}`, {\n method,\n headers: {\n Accept: \"application/vnd.github+json\",\n Authorization: `Bearer ${token}`,\n \"Content-Type\": \"application/json\",\n \"X-GitHub-Api-Version\": \"2022-11-28\",\n },\n body: body === undefined ? undefined : JSON.stringify(body),\n });\n\n if (response.status === 404) return null;\n\n if (!response.ok) {\n const responseText = await response.text();\n throw new Error(\n `GitHub ${method} ${path} failed with ${response.status}: ${responseText}`,\n );\n }\n\n if (response.status === 204) return null;\n return (await response.json()) as T;\n}\n\nfunction encodePathSegments(filePath: string): string {\n return filePath.split(\"/\").map(encodeURIComponent).join(\"/\");\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,7 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { i as toTitleCase, n as toKebabCase, r as toSnakeCase, t as getTemplateVersion } from "./template-versions-CEIP9vhl.js";
3
- import { a as writeManifest, c as isValidMosaicDesignTheme, o as DEFAULT_MOSAIC_DESIGN_THEME, s as VALID_MOSAIC_DESIGN_THEMES, t as derivePlaceholders } from "./manifest-By1SgOjC.js";
4
- import { t as validateProjectName } from "./validate-dssldJAj.js";
5
2
  import { program } from "commander";
6
3
  import { execSync, spawn } from "node:child_process";
7
4
  import path from "node:path";
@@ -12,6 +9,21 @@ import { fileURLToPath } from "node:url";
12
9
  import { parse } from "yaml";
13
10
  import { randomBytes } from "node:crypto";
14
11
  import inquirer from "inquirer";
12
+ import validateNpmPackageName from "validate-npm-package-name";
13
+ //#region src/utils/case-converters.ts
14
+ /** Lowercase, hyphenated, npm-package-name-safe form: "My Cool App" → "my-cool-app". */
15
+ function toKebabCase(str) {
16
+ return str.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
17
+ }
18
+ /** Display form derived from a kebab-case name: "my-cool-app" → "My Cool App". */
19
+ function toTitleCase(str) {
20
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
21
+ }
22
+ /** Identifier form for env vars and DB names: "my-cool-app" → "my_cool_app". */
23
+ function toSnakeCase(str) {
24
+ return str.replace(/-/g, "_");
25
+ }
26
+ //#endregion
15
27
  //#region src/utils/copy-template.ts
16
28
  const __filename = fileURLToPath(import.meta.url);
17
29
  const __dirname = path.dirname(__filename);
@@ -45,7 +57,8 @@ function shouldSkip(src) {
45
57
  return false;
46
58
  }
47
59
  function getTemplateDir(templateType) {
48
- return path.resolve(__dirname, "../templates", templateType);
60
+ const candidates = [path.resolve(__dirname, "../templates", templateType), path.resolve(__dirname, "../../templates", templateType)];
61
+ return candidates.find((candidate) => fs.existsSync(candidate)) ?? candidates[0];
49
62
  }
50
63
  async function copyTemplate(targetDir, templateType) {
51
64
  const templateDir = getTemplateDir(templateType);
@@ -67,6 +80,17 @@ async function copyTemplate(targetDir, templateType) {
67
80
  }
68
81
  }
69
82
  //#endregion
83
+ //#region src/utils/design-theme.ts
84
+ const VALID_MOSAIC_DESIGN_THEMES = [
85
+ "paper",
86
+ "modern",
87
+ "dense"
88
+ ];
89
+ const DEFAULT_MOSAIC_DESIGN_THEME = "modern";
90
+ function isValidMosaicDesignTheme(value) {
91
+ return typeof value === "string" && VALID_MOSAIC_DESIGN_THEMES.includes(value);
92
+ }
93
+ //#endregion
70
94
  //#region src/utils/detect-monorepo.ts
71
95
  const NOT_FOUND = {
72
96
  found: false,
@@ -125,6 +149,49 @@ async function generateEnvLocal(packageDir) {
125
149
  }
126
150
  }
127
151
  //#endregion
152
+ //#region src/utils/manifest.ts
153
+ const MANIFEST_FILENAME = ".mosaic-template.json";
154
+ function getManifestPath(dir) {
155
+ return path.join(dir, MANIFEST_FILENAME);
156
+ }
157
+ async function readManifest(dir) {
158
+ const manifestPath = getManifestPath(dir);
159
+ if (!await fs.pathExists(manifestPath)) throw new Error(`No ${MANIFEST_FILENAME} found in ${dir}. Run 'create init' to create one.`);
160
+ const content = await fs.readFile(manifestPath, "utf-8");
161
+ try {
162
+ return JSON.parse(content);
163
+ } catch (error) {
164
+ throw new Error(`Invalid JSON in ${MANIFEST_FILENAME}: ${error.message}`);
165
+ }
166
+ }
167
+ async function writeManifest(dir, manifest) {
168
+ const manifestPath = getManifestPath(dir);
169
+ await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2) + "\n");
170
+ }
171
+ async function manifestExists(dir) {
172
+ return fs.pathExists(getManifestPath(dir));
173
+ }
174
+ function derivePlaceholders(appName, appTitle, repoName = appName, customerSlug = repoName, designTheme = DEFAULT_MOSAIC_DESIGN_THEME) {
175
+ const nameSnake = appName.replace(/-/g, "_");
176
+ const repoNameSnake = repoName.replace(/-/g, "_");
177
+ return {
178
+ __APP_NAME__: appName,
179
+ __APP_TITLE__: appTitle,
180
+ __DB_NAME__: nameSnake + "_db",
181
+ __APP_NAME_UPPER__: appName.toUpperCase(),
182
+ __APP_NAME_SNAKE__: nameSnake,
183
+ __REPO_NAME__: repoName,
184
+ __REPO_NAME_SNAKE__: repoNameSnake,
185
+ __CUSTOMER_SLUG__: customerSlug,
186
+ __MOSAIC_DESIGN_THEME__: designTheme
187
+ };
188
+ }
189
+ function resolveMosaicTemplatePath(options) {
190
+ if (options.mosaicTemplatePath) return path.resolve(options.mosaicTemplatePath);
191
+ if (process.env.MOSAIC_TEMPLATE_PATH) return path.resolve(process.env.MOSAIC_TEMPLATE_PATH);
192
+ throw new Error("Mosaic repo path required. Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH.");
193
+ }
194
+ //#endregion
128
195
  //#region src/utils/package-metadata.ts
129
196
  const FALLBACK_METADATA = {
130
197
  name: "@percepta/create",
@@ -143,6 +210,16 @@ function readCreatePackageMetadata() {
143
210
  return FALLBACK_METADATA;
144
211
  }
145
212
  //#endregion
213
+ //#region src/utils/validate.ts
214
+ function validateProjectName(name) {
215
+ const result = validateNpmPackageName(name);
216
+ if (!result.validForNewPackages) return {
217
+ valid: false,
218
+ error: [...result.errors || [], ...result.warnings || []][0] || "Invalid package name"
219
+ };
220
+ return { valid: true };
221
+ }
222
+ //#endregion
146
223
  //#region src/utils/prompts.ts
147
224
  const VALID_PROJECT_TYPES = [
148
225
  "monorepo",
@@ -441,6 +518,21 @@ async function replacePlaceholders(targetDir, config) {
441
518
  return stats;
442
519
  }
443
520
  //#endregion
521
+ //#region src/utils/template-versions.ts
522
+ const FALLBACK_TEMPLATE_VERSION = "1.0.0";
523
+ function readTemplateVersions() {
524
+ const currentDir = path.dirname(fileURLToPath(import.meta.url));
525
+ const candidates = [path.resolve(currentDir, "../template-versions.json"), path.resolve(currentDir, "../../template-versions.json")];
526
+ for (const versionsPath of candidates) try {
527
+ const content = fs.readFileSync(versionsPath, "utf-8");
528
+ return JSON.parse(content);
529
+ } catch {}
530
+ return {};
531
+ }
532
+ function getTemplateVersion(templateType) {
533
+ return readTemplateVersions()[templateType] ?? FALLBACK_TEMPLATE_VERSION;
534
+ }
535
+ //#endregion
444
536
  //#region src/utils/workspace-manifest.ts
445
537
  const WORKSPACE_MANIFEST_FILENAME = ".mosaic-workspace.json";
446
538
  function getWorkspaceManifestPath(rootDir) {
@@ -473,8 +565,7 @@ async function readWorkspaceManifest(rootDir) {
473
565
  }
474
566
  async function writeWorkspaceManifest(rootDir, manifest) {
475
567
  const manifestPath = getWorkspaceManifestPath(rootDir);
476
- await fs.writeJson(manifestPath, manifest, { spaces: 2 });
477
- await fs.appendFile(manifestPath, "\n");
568
+ await fs.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
478
569
  }
479
570
  function getCompatibleTemplateVersion(manifest, templateType) {
480
571
  return manifest?.compatibleTemplates[templateType] ?? getTemplateVersion(templateType);
@@ -482,11 +573,14 @@ function getCompatibleTemplateVersion(manifest, templateType) {
482
573
  //#endregion
483
574
  //#region src/commands/create.ts
484
575
  const PACKAGE_MANAGER = "pnpm";
485
- const MAX_INSTALL_OUTPUT_CHARS = 64e3;
486
- const MAX_INSTALL_OUTPUT_LINES = 80;
576
+ const MAX_PACKAGE_MANAGER_OUTPUT_CHARS = 64e3;
577
+ const MAX_PACKAGE_MANAGER_OUTPUT_LINES = 80;
487
578
  var PackageManagerCommandError = class extends Error {
488
- constructor(message, output) {
579
+ command;
580
+ output;
581
+ constructor(message, command, output) {
489
582
  super(message);
583
+ this.command = command;
490
584
  this.output = output;
491
585
  this.name = "PackageManagerCommandError";
492
586
  }
@@ -495,13 +589,14 @@ var PackageManagerCommandError = class extends Error {
495
589
  function shPath(p) {
496
590
  return p.split(path.sep).join("/");
497
591
  }
498
- /** Non-blocking install so ora can animate (execSync would block timers). */
499
- function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
592
+ /** Non-blocking package-manager command so ora can animate. */
593
+ function runPackageManagerCommand(packageManager, cwd, args) {
500
594
  return new Promise((resolve, reject) => {
501
595
  let output = "";
596
+ const command = `${packageManager} ${args.join(" ")}`;
502
597
  const appendOutput = (chunk) => {
503
598
  output += chunk.toString();
504
- if (output.length > MAX_INSTALL_OUTPUT_CHARS) output = output.slice(-MAX_INSTALL_OUTPUT_CHARS);
599
+ if (output.length > MAX_PACKAGE_MANAGER_OUTPUT_CHARS) output = output.slice(-64e3);
505
600
  };
506
601
  const child = spawn(packageManager, args, {
507
602
  cwd,
@@ -514,23 +609,27 @@ function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
514
609
  child.stdout?.on("data", appendOutput);
515
610
  child.stderr?.on("data", appendOutput);
516
611
  child.on("error", (error) => {
517
- reject(new PackageManagerCommandError(`${packageManager} ${args.join(" ")} failed: ${error.message}`, output));
612
+ reject(new PackageManagerCommandError(`${command} failed: ${error.message}`, command, output));
518
613
  });
519
614
  child.on("close", (code) => {
520
615
  if (code === 0) resolve();
521
- else reject(new PackageManagerCommandError(`${packageManager} ${args.join(" ")} exited with code ${code ?? "unknown"}`, output));
616
+ else reject(new PackageManagerCommandError(`${command} exited with code ${code ?? "unknown"}`, command, output));
522
617
  });
523
618
  });
524
619
  }
525
- function printInstallFailureOutput(error) {
620
+ /** Non-blocking install so ora can animate (execSync would block timers). */
621
+ function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
622
+ return runPackageManagerCommand(packageManager, cwd, args);
623
+ }
624
+ function printPackageManagerFailureOutput(error) {
526
625
  if (!(error instanceof PackageManagerCommandError)) return;
527
626
  const output = error.output.trim();
528
627
  if (!output) return;
529
628
  const lines = output.split(/\r?\n/);
530
- const omitted = Math.max(0, lines.length - MAX_INSTALL_OUTPUT_LINES);
531
- const visibleLines = lines.slice(-MAX_INSTALL_OUTPUT_LINES);
629
+ const omitted = Math.max(0, lines.length - MAX_PACKAGE_MANAGER_OUTPUT_LINES);
630
+ const visibleLines = lines.slice(-80);
532
631
  console.log();
533
- console.log(chalk.bold(`Last ${visibleLines.length} lines from pnpm install:`));
632
+ console.log(chalk.bold(`Last ${visibleLines.length} lines from ${error.command}:`));
534
633
  if (omitted > 0) console.log(chalk.dim(`... omitted ${omitted} earlier lines ...`));
535
634
  console.log(visibleLines.join("\n"));
536
635
  }
@@ -788,7 +887,51 @@ async function installAtMonorepoRoot(monorepoRoot, installDeps) {
788
887
  return true;
789
888
  } catch (error) {
790
889
  spinner.warn(`Failed to install dependencies. Run '${PACKAGE_MANAGER} install' from monorepo root.`);
791
- printInstallFailureOutput(error);
890
+ printPackageManagerFailureOutput(error);
891
+ return false;
892
+ }
893
+ }
894
+ async function collectGeneratedFormatTargets(args) {
895
+ const targets = [path.relative(args.monorepoRoot, args.packageDir) || "."];
896
+ if (args.projectType === "webapp") {
897
+ const workflowPath = path.join(args.monorepoRoot, ".github", "workflows", `${path.basename(args.packageDir)}-ryvn-release.yaml`);
898
+ if (await fs.pathExists(workflowPath)) targets.push(path.relative(args.monorepoRoot, workflowPath));
899
+ }
900
+ return targets.map(shPath);
901
+ }
902
+ async function runGeneratedProjectChecks(args) {
903
+ if (!args.installSucceeded) return false;
904
+ const formatArgs = args.scope === "workspace" || !args.packageDir ? ["run", "format"] : [
905
+ "exec",
906
+ "oxfmt",
907
+ ...await collectGeneratedFormatTargets({
908
+ monorepoRoot: args.monorepoRoot,
909
+ packageDir: args.packageDir,
910
+ projectType: args.projectType
911
+ })
912
+ ];
913
+ const lintArgs = args.scope === "workspace" || !args.packageDir ? ["run", "lint"] : [
914
+ "exec",
915
+ "oxlint",
916
+ shPath(path.relative(args.monorepoRoot, args.packageDir) || ".")
917
+ ];
918
+ const formatSpinner = ora("Formatting generated files...").start();
919
+ try {
920
+ await runPackageManagerCommand(PACKAGE_MANAGER, args.monorepoRoot, formatArgs);
921
+ formatSpinner.succeed("Formatted generated files");
922
+ } catch (error) {
923
+ formatSpinner.warn("Generated formatting failed");
924
+ printPackageManagerFailureOutput(error);
925
+ return false;
926
+ }
927
+ const lintSpinner = ora("Linting generated files...").start();
928
+ try {
929
+ await runPackageManagerCommand(PACKAGE_MANAGER, args.monorepoRoot, lintArgs);
930
+ lintSpinner.succeed("Linted generated files");
931
+ return true;
932
+ } catch (error) {
933
+ lintSpinner.warn("Generated lint failed");
934
+ printPackageManagerFailureOutput(error);
792
935
  return false;
793
936
  }
794
937
  }
@@ -976,10 +1119,17 @@ async function createProject(options) {
976
1119
  });
977
1120
  await warnIfMissingRootNpmrc(monorepoRoot);
978
1121
  const installSucceeded = await installAtMonorepoRoot(monorepoRoot, answers.installDeps);
1122
+ const checksSucceeded = await runGeneratedProjectChecks({
1123
+ monorepoRoot,
1124
+ packageDir,
1125
+ projectType: answers.projectType,
1126
+ scope: "generated-package",
1127
+ installSucceeded
1128
+ });
979
1129
  console.log();
980
1130
  console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(path.relative(monorepoRoot, packageDir)));
981
1131
  console.log();
982
- if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
1132
+ if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded && checksSucceeded)) return;
983
1133
  printNextStepsExisting(answers, packageDir, !installSucceeded);
984
1134
  } else {
985
1135
  const isBareMonorepo = answers.projectType === "monorepo";
@@ -1023,12 +1173,19 @@ async function createProject(options) {
1023
1173
  templateCommit: getTemplateCommitSource(newWorkspaceManifest)
1024
1174
  });
1025
1175
  const installSucceeded = await installAtMonorepoRoot(monorepoRoot, answers.installDeps);
1176
+ const checksSucceeded = await runGeneratedProjectChecks({
1177
+ monorepoRoot,
1178
+ packageDir,
1179
+ projectType: answers.projectType,
1180
+ scope: "workspace",
1181
+ installSucceeded
1182
+ });
1026
1183
  initGitRepo(monorepoRoot);
1027
1184
  console.log();
1028
1185
  console.log(chalk.green("✔"), chalk.bold(isBareMonorepo ? `Created ${typeLabel} at` : "Created monorepo at"), chalk.cyan(monorepoRoot));
1029
1186
  if (!isBareMonorepo) console.log(chalk.green("✔"), chalk.bold(`Created ${typeLabel} at`), chalk.cyan(`packages/${answers.name}/`));
1030
1187
  console.log();
1031
- if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded)) return;
1188
+ if (await maybeAutoRunWebapp(packageDir, monorepoRoot, answers.projectType, installSucceeded && checksSucceeded)) return;
1032
1189
  printNextStepsNew(answers, monorepoRoot, !installSucceeded);
1033
1190
  }
1034
1191
  }
@@ -1207,31 +1364,31 @@ program.command("add").description("Add a Mosaic package to the current monorepo
1207
1364
  });
1208
1365
  const infra = program.command("infra").description("Manage Mosaic infra glue");
1209
1366
  infra.command("register-os-blueprint").description("Register this customer monorepo's OS blueprint in infra").action(async () => {
1210
- const { registerOsBlueprintCommand } = await import("./register-os-blueprint-Cgq1rXzQ.js");
1367
+ const { registerOsBlueprintCommand } = await import("./register-os-blueprint-Gdyn0pN1.js");
1211
1368
  await registerOsBlueprintCommand();
1212
1369
  });
1213
1370
  infra.command("register-app").description("Register a webapp database in this customer OS blueprint").argument("<app>", "Webapp package name").action(async (appName) => {
1214
- const { registerAppCommand } = await import("./register-app-DZg-Pmtd.js");
1371
+ const { registerAppCommand } = await import("./register-app-B9vKTkoI.js");
1215
1372
  await registerAppCommand(appName);
1216
1373
  });
1217
1374
  program.command("status").description("Show template sync status for current app").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").action(async (options) => {
1218
- const { statusCommand } = await import("./status-K6raTwwu.js");
1375
+ const { statusCommand } = await import("./status-BrK9v1yb.js");
1219
1376
  await statusCommand(options);
1220
1377
  });
1221
1378
  program.command("sync").description("Generate downstream sync context (template → app)").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").option("--to <version>", "Target template version (default: latest)").action(async (options) => {
1222
- const { syncCommand } = await import("./sync-Bi958-2W.js");
1379
+ const { syncCommand } = await import("./sync-DC5DhIBT.js");
1223
1380
  await syncCommand(options);
1224
1381
  });
1225
1382
  program.command("upstream").description("Generate upstream context (app → template)").option("--mosaic-template-path <path>", "Path to local mosaic repo checkout").option("--files <patterns...>", "Specific files to propose upstream").action(async (options) => {
1226
- const { upstreamCommand } = await import("./upstream-CAraZeSS.js");
1383
+ const { upstreamCommand } = await import("./upstream-PNL6DGtl.js");
1227
1384
  await upstreamCommand(options);
1228
1385
  });
1229
1386
  program.command("init").description("Add .mosaic-template.json to an existing app").option("-t, --type <type>", "Template type (e.g., webapp, library)").option("--template-version <version>", "Template version to set").action(async (options) => {
1230
- const { initCommand } = await import("./init-BD3EyyLO.js");
1387
+ const { initCommand } = await import("./init-CsuO_mu2.js");
1231
1388
  await initCommand(options);
1232
1389
  });
1233
1390
  program.parse();
1234
1391
  //#endregion
1235
- export { detectMonorepo as i, VALID_PROJECT_TYPES as n, isValidProjectType as r, readWorkspaceManifest as t };
1392
+ export { validateProjectName as a, readManifest as c, detectMonorepo as d, toKebabCase as f, isValidProjectType as i, resolveMosaicTemplatePath as l, toTitleCase as m, getTemplateVersion as n, derivePlaceholders as o, toSnakeCase as p, VALID_PROJECT_TYPES as r, manifestExists as s, readWorkspaceManifest as t, writeManifest as u };
1236
1393
 
1237
1394
  //# sourceMappingURL=index.js.map