@percepta/create 4.1.6 → 4.1.8

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 (54) 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 +107 -15
  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-mNc1oYVK.js} +3 -5
  10. package/dist/{register-app-DZg-Pmtd.js.map → register-app-mNc1oYVK.js.map} +1 -1
  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/monorepo/auth/src/drizzle/migrations/meta/0000_snapshot.json +547 -0
  22. package/templates/monorepo/package.json.template +6 -6
  23. package/templates/monorepo/pnpm-workspace.yaml +1 -1
  24. package/templates/webapp/AGENTS.md +38 -16
  25. package/templates/webapp/README.md +56 -58
  26. package/templates/webapp/agent-skills/access-control.md +10 -9
  27. package/templates/webapp/agent-skills/database.md +10 -4
  28. package/templates/webapp/agent-skills/inngest.md +10 -8
  29. package/templates/webapp/agent-skills/langfuse.md +7 -5
  30. package/templates/webapp/agent-skills/oneshot.md +15 -13
  31. package/templates/webapp/package.json.template +5 -5
  32. package/templates/webapp/playwright.config.ts +1 -2
  33. package/templates/webapp/src/app/(app)/page.tsx +5 -3
  34. package/templates/webapp/src/app/(auth)/layout.tsx +2 -2
  35. package/templates/webapp/src/app/(settings)/settings/page.tsx +20 -21
  36. package/templates/webapp/src/components/FaroProvider.tsx +1 -2
  37. package/templates/webapp/src/components/Header.tsx +2 -8
  38. package/templates/webapp/src/components/form/FormItem.tsx +1 -1
  39. package/templates/webapp/src/drizzle/db.ts +3 -1
  40. package/templates/webapp/src/drizzle/schema/utils/jsonbFromZod.ts +1 -1
  41. package/templates/webapp/src/instrumentation.ts +3 -6
  42. package/templates/webapp/src/lib/auth-client.ts +3 -2
  43. package/templates/webapp/src/lib/trpc.ts +1 -1
  44. package/templates/webapp/src/server/trpc.ts +1 -1
  45. package/templates/webapp/src/services/DatabaseService.ts +1 -1
  46. package/templates/webapp/src/services/access/AppAccessControl.ts +1 -3
  47. package/templates/webapp/src/services/inngest/AppWorkflowService.ts +1 -1
  48. package/templates/webapp/src/styles/globals.css +13 -2
  49. package/dist/manifest-By1SgOjC.js +0 -59
  50. package/dist/manifest-By1SgOjC.js.map +0 -1
  51. package/dist/template-versions-CEIP9vhl.js +0 -35
  52. package/dist/template-versions-CEIP9vhl.js.map +0 -1
  53. package/dist/validate-dssldJAj.js +0 -14
  54. 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":";;AAEA,MAAM,sBAAsB;AAE5B,MAAa,mBAAmB;AAChC,MAAa,yBAAyB;AACtC,MAAa,oBAAoB;AA2CjC,eAAsB,+BAA+B,MASjB;AAClC,QAAO,oCAAoC;EACzC,MAAM,KAAK;EACX,YAAY,KAAK;EACjB,OAAO,CACL;GACE,aAAa,KAAK;GAClB,SAAS,KAAK;GACd,SAAS,KAAK;GACd,MAAM,KAAK;GACZ,CACF;EACD,QAAQ,KAAK;EACb,OAAO,KAAK;EACb,CAAC;;AAGJ,eAAsB,oCAAoC,MAMtB;CAClC,MAAM,UAAU,MAAM,KAAK,OAAO,aAAa,kBAAkB;AACjE,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,kBAAkB,iBAAiB,GAAG,kBAAkB,GAAG;AAG7E,KAAI,CAAE,MAAM,KAAK,OAAO,aAAa,KAAK,WAAW,CACnD,OAAM,KAAK,OAAO,aAAa,KAAK,YAAY,QAAQ;CAG1D,MAAM,sBAAsB,MAAM,KAAK,OAAO,oBAC5C,KAAK,WACN;AAED,MAAK,MAAM,QAAQ,KAAK,OAAO;EAC7B,MAAM,aAAa,MAAM,KAAK,OAAO,QAAQ,KAAK,MAAM,KAAK,WAAW;AACxE,MAAI,YAAY,YAAY,KAAK,QAC/B,OAAM,KAAK,OAAO,QAAQ;GACxB,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,SAAS,KAAK;GACd,SAAS,KAAK;GACd,KAAK,YAAY,OAAO,KAAK;GAC9B,CAAC;;AAYN,QAAO;EACL,iBARA,uBACC,MAAM,KAAK,OAAO,kBAAkB;GACnC,QAAQ,KAAK;GACb,OAAO,KAAK;GACZ,MAAM,KAAK;GACZ,CAAC,EAG0B;EAC5B,QAAQ,sBAAsB,eAAe;EAC9C;;AAGH,SAAgB,qBAAqB,OAA+B;AAClE,QAAO;EACL,MAAM,QAAQ,UAAU,KAAK;GAE3B,MAAM,WAAW,MAAM,cAKrB,OACA,OACA,UAAU,iBAAiB,YART,mBAAmB,SAQa,CAAC,OAAO,mBAAmB,IAAI,GAClF;AACD,OAAI,YAAY,KAAM,QAAO;AAC7B,OACE,OAAO,SAAS,QAAQ,YACxB,OAAO,SAAS,YAAY,YAC5B,SAAS,SAAS,OAElB,OAAM,IAAI,MACR,0CAA0C,SAAS,MAAM,iBAAiB,GAC3E;AAGH,UAAO;IACL,KAAK,SAAS;IACd,SAAS,OAAO,KACd,SAAS,QAAQ,QAAQ,OAAO,GAAG,EACnC,SACD,CAAC,SAAS,QAAQ;IACpB;;EAGH,MAAM,aAAa,QAAQ;GACzB,MAAM,WAAW,MAAM,cAEpB,OAAO,OAAO,UAAU,iBAAiB,iBAAiB,SAAS;AACtE,OAAI,YAAY,KAAM,QAAO;GAC7B,MAAM,MAAM,SAAS,QAAQ;AAC7B,OAAI,OAAO,QAAQ,SACjB,OAAM,IAAI,MAAM,sCAAsC,OAAO,GAAG;AAElE,UAAO;;EAGT,MAAM,aAAa,QAAQ,KAAK;AAC9B,SAAM,cACJ,OACA,QACA,UAAU,iBAAiB,YAC3B;IACE,KAAK,cAAc;IACnB;IACD,CACF;;EAGH,MAAM,QAAQ,EAAE,MAAM,UAAU,QAAQ,SAAS,SAAS,OAAO;AAE/D,SAAM,cACJ,OACA,OACA,UAAU,iBAAiB,YAJT,mBAAmB,SAIa,IAClD;IACE;IACA,SAAS,OAAO,KAAK,SAAS,QAAQ,CAAC,SAAS,SAAS;IACzD;IACA;IACD,CACF;;EAGH,MAAM,oBAAoB,QAAQ;GAShC,MAAM,eAAc,MAHG,cAErB,OAAO,OAAO,UAAU,iBAAiB,SAAS,IAPjC,gBAAgB;IACjC,MAAM;IACN,MAAM,GAAG,uBAAuB,GAAG;IACnC,OAAO;IACR,CAGyD,GAAG,IAC9B;AAC/B,OAAI,CAAC,YAAa,QAAO;AACzB,OACE,OAAO,YAAY,aAAa,YAChC,OAAO,YAAY,WAAW,SAE9B,OAAM,IAAI,MAAM,2CAA2C;AAE7D,UAAO;IACL,QAAQ,YAAY;IACpB,KAAK,YAAY;IAClB;;EAGH,MAAM,kBAAkB,EAAE,QAAQ,OAAO,QAAQ;GAC/C,MAAM,WAAW,MAAM,cAGpB,OAAO,QAAQ,UAAU,iBAAiB,SAAS;IACpD,MAAM;IACN;IACA,MAAM;IACN,uBAAuB;IACvB;IACD,CAAC;AACF,OACE,CAAC,YACD,OAAO,SAAS,aAAa,YAC7B,OAAO,SAAS,WAAW,SAE3B,OAAM,IAAI,MAAM,2CAA2C;AAE7D,UAAO;IACL,QAAQ,SAAS;IACjB,KAAK,SAAS;IACf;;EAEJ;;AAGH,SAAgB,qBAA6B;CAC3C,MAAM,WAAW,QAAQ,IAAI,gBAAgB,QAAQ,IAAI;AACzD,KAAI,SAAU,QAAO;AAErB,KAAI;EACF,MAAM,QAAQ,aAAa,MAAM,CAAC,QAAQ,QAAQ,EAAE;GAClD,UAAU;GACV,OAAO;IAAC;IAAU;IAAQ;IAAS;GACpC,CAAC,CAAC,MAAM;AACT,MAAI,MAAO,QAAO;SACZ;AAIR,OAAM,IAAI,MACR,iFACD;;AAGH,eAAe,cACb,OACA,QACA,MACA,MACmB;CACnB,MAAM,WAAW,MAAM,MAAM,GAAG,sBAAsB,QAAQ;EAC5D;EACA,SAAS;GACP,QAAQ;GACR,eAAe,UAAU;GACzB,gBAAgB;GAChB,wBAAwB;GACzB;EACD,MAAM,SAAS,KAAA,IAAY,KAAA,IAAY,KAAK,UAAU,KAAK;EAC5D,CAAC;AAEF,KAAI,SAAS,WAAW,IAAK,QAAO;AAEpC,KAAI,CAAC,SAAS,IAAI;EAChB,MAAM,eAAe,MAAM,SAAS,MAAM;AAC1C,QAAM,IAAI,MACR,UAAU,OAAO,GAAG,KAAK,eAAe,SAAS,OAAO,IAAI,eAC7D;;AAGH,KAAI,SAAS,WAAW,IAAK,QAAO;AACpC,QAAQ,MAAM,SAAS,MAAM;;AAG/B,SAAS,mBAAmB,UAA0B;AACpD,QAAO,SAAS,MAAM,IAAI,CAAC,IAAI,mBAAmB,CAAC,KAAK,IAAI"}
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);
@@ -485,6 +576,7 @@ const PACKAGE_MANAGER = "pnpm";
485
576
  const MAX_INSTALL_OUTPUT_CHARS = 64e3;
486
577
  const MAX_INSTALL_OUTPUT_LINES = 80;
487
578
  var PackageManagerCommandError = class extends Error {
579
+ output;
488
580
  constructor(message, output) {
489
581
  super(message);
490
582
  this.output = output;
@@ -501,7 +593,7 @@ function runPackageManagerInstall(packageManager, cwd, args = ["install"]) {
501
593
  let output = "";
502
594
  const appendOutput = (chunk) => {
503
595
  output += chunk.toString();
504
- if (output.length > MAX_INSTALL_OUTPUT_CHARS) output = output.slice(-MAX_INSTALL_OUTPUT_CHARS);
596
+ if (output.length > MAX_INSTALL_OUTPUT_CHARS) output = output.slice(-64e3);
505
597
  };
506
598
  const child = spawn(packageManager, args, {
507
599
  cwd,
@@ -528,7 +620,7 @@ function printInstallFailureOutput(error) {
528
620
  if (!output) return;
529
621
  const lines = output.split(/\r?\n/);
530
622
  const omitted = Math.max(0, lines.length - MAX_INSTALL_OUTPUT_LINES);
531
- const visibleLines = lines.slice(-MAX_INSTALL_OUTPUT_LINES);
623
+ const visibleLines = lines.slice(-80);
532
624
  console.log();
533
625
  console.log(chalk.bold(`Last ${visibleLines.length} lines from pnpm install:`));
534
626
  if (omitted > 0) console.log(chalk.dim(`... omitted ${omitted} earlier lines ...`));
@@ -1207,31 +1299,31 @@ program.command("add").description("Add a Mosaic package to the current monorepo
1207
1299
  });
1208
1300
  const infra = program.command("infra").description("Manage Mosaic infra glue");
1209
1301
  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");
1302
+ const { registerOsBlueprintCommand } = await import("./register-os-blueprint-Gdyn0pN1.js");
1211
1303
  await registerOsBlueprintCommand();
1212
1304
  });
1213
1305
  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");
1306
+ const { registerAppCommand } = await import("./register-app-mNc1oYVK.js");
1215
1307
  await registerAppCommand(appName);
1216
1308
  });
1217
1309
  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");
1310
+ const { statusCommand } = await import("./status-BrK9v1yb.js");
1219
1311
  await statusCommand(options);
1220
1312
  });
1221
1313
  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");
1314
+ const { syncCommand } = await import("./sync-DC5DhIBT.js");
1223
1315
  await syncCommand(options);
1224
1316
  });
1225
1317
  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");
1318
+ const { upstreamCommand } = await import("./upstream-PNL6DGtl.js");
1227
1319
  await upstreamCommand(options);
1228
1320
  });
1229
1321
  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");
1322
+ const { initCommand } = await import("./init-CsuO_mu2.js");
1231
1323
  await initCommand(options);
1232
1324
  });
1233
1325
  program.parse();
1234
1326
  //#endregion
1235
- export { detectMonorepo as i, VALID_PROJECT_TYPES as n, isValidProjectType as r, readWorkspaceManifest as t };
1327
+ 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
1328
 
1237
1329
  //# sourceMappingURL=index.js.map