@percepta/create 3.0.0

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 (138) hide show
  1. package/README.md +93 -0
  2. package/dist/chunk-GEVZERMP.js +108 -0
  3. package/dist/chunk-R4FWPE4A.js +49 -0
  4. package/dist/chunk-WMJT7CB5.js +57 -0
  5. package/dist/index.d.ts +1 -0
  6. package/dist/index.js +974 -0
  7. package/dist/init-Z4VGBHAK.js +96 -0
  8. package/dist/status-MITGDLTT.js +76 -0
  9. package/dist/sync-J4SFZHDX.js +136 -0
  10. package/dist/upstream-AQI7P4EU.js +144 -0
  11. package/package.json +58 -0
  12. package/template-versions.json +4 -0
  13. package/templates/library/README.md +30 -0
  14. package/templates/library/eslint.config.js +10 -0
  15. package/templates/library/gitignore.template +18 -0
  16. package/templates/library/package.json.template +29 -0
  17. package/templates/library/src/index.ts +9 -0
  18. package/templates/library/tsconfig.json +19 -0
  19. package/templates/monorepo/README.md +41 -0
  20. package/templates/monorepo/eslint.config.js +10 -0
  21. package/templates/monorepo/gitignore.template +31 -0
  22. package/templates/monorepo/npmrc.template +4 -0
  23. package/templates/monorepo/package.json.template +25 -0
  24. package/templates/monorepo/packages/.gitkeep +0 -0
  25. package/templates/monorepo/pnpm-workspace.yaml +2 -0
  26. package/templates/monorepo/tsconfig.json +16 -0
  27. package/templates/webapp/.claude/commands/sync.md +19 -0
  28. package/templates/webapp/.claude/commands/upstream.md +17 -0
  29. package/templates/webapp/.dockerignore +59 -0
  30. package/templates/webapp/.gitattributes +1 -0
  31. package/templates/webapp/.github/workflows/__APP_NAME__-ryvn-release.yaml +114 -0
  32. package/templates/webapp/.github/workflows/__APP_NAME__-terraform.yml +28 -0
  33. package/templates/webapp/.github/workflows/ci.yml +149 -0
  34. package/templates/webapp/.node-version +2 -0
  35. package/templates/webapp/.prettierrc.mjs +5 -0
  36. package/templates/webapp/AGENTS.md +240 -0
  37. package/templates/webapp/Dockerfile +64 -0
  38. package/templates/webapp/README.md +200 -0
  39. package/templates/webapp/agent-skills/database.md +140 -0
  40. package/templates/webapp/agent-skills/deploy.md +94 -0
  41. package/templates/webapp/agent-skills/inngest.md +147 -0
  42. package/templates/webapp/agent-skills/langfuse.md +117 -0
  43. package/templates/webapp/agent-skills/oneshot.md +216 -0
  44. package/templates/webapp/agent-skills/ryvn.md +25 -0
  45. package/templates/webapp/deploy/README.md +39 -0
  46. package/templates/webapp/deploy/ryvn/__APP_NAME__.service.yaml +11 -0
  47. package/templates/webapp/deploy/ryvn/environments/percepta-test/installations/__APP_NAME__.env.percepta-test.serviceinstallation.yaml +121 -0
  48. package/templates/webapp/docker-compose.yml +19 -0
  49. package/templates/webapp/drizzle.config.ts +30 -0
  50. package/templates/webapp/env.example.template +44 -0
  51. package/templates/webapp/eslint.config.mjs +52 -0
  52. package/templates/webapp/gitignore.template +53 -0
  53. package/templates/webapp/next.config.ts +8 -0
  54. package/templates/webapp/npmrc.template +4 -0
  55. package/templates/webapp/package.json.template +122 -0
  56. package/templates/webapp/postcss.config.mjs +5 -0
  57. package/templates/webapp/scripts/create-user.ts +47 -0
  58. package/templates/webapp/scripts/migrate.ts +18 -0
  59. package/templates/webapp/scripts/seed.ts +62 -0
  60. package/templates/webapp/scripts/setup-database.ts +57 -0
  61. package/templates/webapp/scripts/setup-readonly-user.ts +193 -0
  62. package/templates/webapp/scripts/start.sh +52 -0
  63. package/templates/webapp/src/app/(app)/layout.tsx +21 -0
  64. package/templates/webapp/src/app/(app)/page.tsx +30 -0
  65. package/templates/webapp/src/app/(auth)/auth/signin/CredentialsSignInForm.tsx +103 -0
  66. package/templates/webapp/src/app/(auth)/auth/signin/page.tsx +30 -0
  67. package/templates/webapp/src/app/(auth)/layout.tsx +15 -0
  68. package/templates/webapp/src/app/api/auth/[...all]/route.ts +4 -0
  69. package/templates/webapp/src/app/api/healthz/route.ts +10 -0
  70. package/templates/webapp/src/app/api/inngest/route.ts +31 -0
  71. package/templates/webapp/src/app/api/readyz/route.ts +31 -0
  72. package/templates/webapp/src/app/api/trpc/[trpc]/route.ts +21 -0
  73. package/templates/webapp/src/app/favicon.ico +0 -0
  74. package/templates/webapp/src/app/global-error.tsx +27 -0
  75. package/templates/webapp/src/app/layout.tsx +18 -0
  76. package/templates/webapp/src/components/FaroProvider.tsx +37 -0
  77. package/templates/webapp/src/components/Header.tsx +70 -0
  78. package/templates/webapp/src/components/Providers.tsx +45 -0
  79. package/templates/webapp/src/components/form/FormItem.tsx +82 -0
  80. package/templates/webapp/src/config/clientEnvConfig.ts +11 -0
  81. package/templates/webapp/src/config/getEnvConfig.ts +62 -0
  82. package/templates/webapp/src/config/isDev.ts +7 -0
  83. package/templates/webapp/src/drizzle/db.ts +28 -0
  84. package/templates/webapp/src/drizzle/migrations/0000_eager_grandmaster.sql +57 -0
  85. package/templates/webapp/src/drizzle/migrations/meta/0000_snapshot.json +376 -0
  86. package/templates/webapp/src/drizzle/migrations/meta/_journal.json +13 -0
  87. package/templates/webapp/src/drizzle/schema/auth/accounts.ts +33 -0
  88. package/templates/webapp/src/drizzle/schema/auth/sessions.ts +25 -0
  89. package/templates/webapp/src/drizzle/schema/auth/users.ts +38 -0
  90. package/templates/webapp/src/drizzle/schema/auth/verifications.ts +19 -0
  91. package/templates/webapp/src/drizzle/schema/index.ts +4 -0
  92. package/templates/webapp/src/drizzle/schema/utils/jsonbFromZod.ts +25 -0
  93. package/templates/webapp/src/instrumentation.ts +35 -0
  94. package/templates/webapp/src/lib/auth/index.ts +85 -0
  95. package/templates/webapp/src/lib/auth-client.ts +6 -0
  96. package/templates/webapp/src/lib/trpc.ts +15 -0
  97. package/templates/webapp/src/server/api/root.ts +5 -0
  98. package/templates/webapp/src/server/trpc.ts +61 -0
  99. package/templates/webapp/src/services/AuthContextService.ts +63 -0
  100. package/templates/webapp/src/services/DatabaseService.ts +54 -0
  101. package/templates/webapp/src/services/inngest/InngestFunctionCollection.ts +5 -0
  102. package/templates/webapp/src/services/inngest/InngestService.ts +71 -0
  103. package/templates/webapp/src/services/inngest/events/AppEvents.ts +34 -0
  104. package/templates/webapp/src/services/inngest/events/payloads/ExampleEventPayload.ts +14 -0
  105. package/templates/webapp/src/services/langfuse/LangfuseService.ts +80 -0
  106. package/templates/webapp/src/services/logger/AppLogger.ts +61 -0
  107. package/templates/webapp/src/services/logger/withRequestContext.ts +27 -0
  108. package/templates/webapp/src/services/observability/initFaro.ts +22 -0
  109. package/templates/webapp/src/startup-checks.ts +32 -0
  110. package/templates/webapp/src/styles/globals.css +27 -0
  111. package/templates/webapp/src/utils/__tests__/cn.test.ts +20 -0
  112. package/templates/webapp/src/utils/cn.ts +6 -0
  113. package/templates/webapp/src/utils/syncInngestApp.ts +62 -0
  114. package/templates/webapp/terraform/README.md +147 -0
  115. package/templates/webapp/terraform/deploy.sh +97 -0
  116. package/templates/webapp/terraform/main.tf +101 -0
  117. package/templates/webapp/terraform/modules/cloudtrail/main.tf +27 -0
  118. package/templates/webapp/terraform/modules/cloudtrail/outputs.tf +10 -0
  119. package/templates/webapp/terraform/modules/cloudtrail/variables.tf +15 -0
  120. package/templates/webapp/terraform/modules/networking/main.tf +118 -0
  121. package/templates/webapp/terraform/modules/networking/outputs.tf +38 -0
  122. package/templates/webapp/terraform/modules/networking/variables.tf +24 -0
  123. package/templates/webapp/terraform/modules/rds/main.tf +227 -0
  124. package/templates/webapp/terraform/modules/rds/outputs.tf +73 -0
  125. package/templates/webapp/terraform/modules/rds/variables.tf +61 -0
  126. package/templates/webapp/terraform/modules/s3-logging/main.tf +148 -0
  127. package/templates/webapp/terraform/modules/s3-logging/outputs.tf +10 -0
  128. package/templates/webapp/terraform/modules/s3-logging/variables.tf +16 -0
  129. package/templates/webapp/terraform/modules/secrets/main.tf +39 -0
  130. package/templates/webapp/terraform/modules/secrets/outputs.tf +9 -0
  131. package/templates/webapp/terraform/modules/secrets/variables.tf +51 -0
  132. package/templates/webapp/terraform/outputs.tf +102 -0
  133. package/templates/webapp/terraform/providers.tf +32 -0
  134. package/templates/webapp/terraform/terraform.tfvars.example +65 -0
  135. package/templates/webapp/terraform/variables.tf +129 -0
  136. package/templates/webapp/tsconfig.json +14 -0
  137. package/templates/webapp/vitest.config.ts +9 -0
  138. package/templates/webapp/vitest.setup.ts +5 -0
@@ -0,0 +1,96 @@
1
+ import {
2
+ VALID_PROJECT_TYPES,
3
+ isValidProjectType
4
+ } from "./chunk-GEVZERMP.js";
5
+ import {
6
+ derivePlaceholders,
7
+ manifestExists,
8
+ writeManifest
9
+ } from "./chunk-WMJT7CB5.js";
10
+
11
+ // src/commands/init.ts
12
+ import path from "path";
13
+ import fs from "fs-extra";
14
+ import chalk from "chalk";
15
+ import inquirer from "inquirer";
16
+ async function initCommand(options) {
17
+ const cwd = process.cwd();
18
+ if (await manifestExists(cwd)) {
19
+ console.error(
20
+ chalk.red(".mosaic-template.json already exists in this directory.")
21
+ );
22
+ process.exit(1);
23
+ }
24
+ const pkgPath = path.join(cwd, "package.json");
25
+ let appName = path.basename(cwd);
26
+ if (await fs.pathExists(pkgPath)) {
27
+ const pkg = JSON.parse(await fs.readFile(pkgPath, "utf-8"));
28
+ appName = pkg.name?.replace(/^@[^/]+\//, "") || appName;
29
+ }
30
+ let templateType = options.type;
31
+ if (templateType && !isValidProjectType(templateType)) {
32
+ console.error(
33
+ chalk.red(
34
+ `Invalid template type "${templateType}". Valid types: ${VALID_PROJECT_TYPES.join(", ")}`
35
+ )
36
+ );
37
+ process.exit(1);
38
+ }
39
+ if (!templateType) {
40
+ const answer = await inquirer.prompt([
41
+ {
42
+ type: "list",
43
+ name: "type",
44
+ message: "Template type:",
45
+ choices: ["webapp", "library"]
46
+ }
47
+ ]);
48
+ templateType = answer.type;
49
+ }
50
+ const templateVersion = options.templateVersion || "1.0.0";
51
+ const appTitle = appName.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
52
+ const manifest = {
53
+ templateType,
54
+ templateVersion,
55
+ templateCommit: "unknown",
56
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
57
+ placeholders: derivePlaceholders(appName, appTitle),
58
+ source: {
59
+ templatePath: `packages/create-mosaic-module/templates/${templateType}`
60
+ }
61
+ };
62
+ await writeManifest(cwd, manifest);
63
+ const notesPath = path.join(cwd, "mosaic-template-notes.md");
64
+ if (!await fs.pathExists(notesPath)) {
65
+ await fs.writeFile(
66
+ notesPath,
67
+ `# Mosaic Divergence Notes
68
+
69
+ Document intentional differences from the ${templateType} template here.
70
+ Claude reads this file during sync to preserve your customizations.
71
+
72
+ ## Intentional Divergences
73
+
74
+ `
75
+ );
76
+ }
77
+ console.log();
78
+ console.log(
79
+ chalk.green("\u2714"),
80
+ chalk.bold("Initialized .mosaic-template.json")
81
+ );
82
+ console.log();
83
+ console.log(chalk.dim(" Template:"), templateType);
84
+ console.log(chalk.dim(" Version:"), templateVersion);
85
+ console.log(chalk.dim(" App name:"), appName);
86
+ console.log();
87
+ console.log(
88
+ chalk.dim(
89
+ "Review .mosaic-template.json and mosaic-template-notes.md, then commit them."
90
+ )
91
+ );
92
+ console.log();
93
+ }
94
+ export {
95
+ initCommand
96
+ };
@@ -0,0 +1,76 @@
1
+ import {
2
+ getLatestTemplateTag,
3
+ getTemplateVersionFromTag
4
+ } from "./chunk-R4FWPE4A.js";
5
+ import {
6
+ readManifest
7
+ } from "./chunk-WMJT7CB5.js";
8
+
9
+ // src/commands/status.ts
10
+ import path from "path";
11
+ import chalk from "chalk";
12
+ async function statusCommand(options) {
13
+ const cwd = process.cwd();
14
+ try {
15
+ const manifest = await readManifest(cwd);
16
+ console.log();
17
+ console.log(chalk.bold("Mosaic Template Status"));
18
+ console.log();
19
+ console.log(chalk.dim(" Template type:"), manifest.templateType);
20
+ console.log(chalk.dim(" Current version:"), manifest.templateVersion);
21
+ console.log(chalk.dim(" Template commit:"), manifest.templateCommit);
22
+ console.log(chalk.dim(" Created:"), manifest.createdAt);
23
+ if (manifest.lastSyncedAt) {
24
+ console.log(chalk.dim(" Last synced:"), manifest.lastSyncedAt);
25
+ }
26
+ const rawPath = options.mosaicTemplatePath || process.env.MOSAIC_TEMPLATE_PATH;
27
+ const mosaicTemplatePath = rawPath ? path.resolve(rawPath) : void 0;
28
+ if (mosaicTemplatePath) {
29
+ const latestTag = getLatestTemplateTag(
30
+ manifest.templateType,
31
+ mosaicTemplatePath
32
+ );
33
+ if (latestTag) {
34
+ const latestVersion = getTemplateVersionFromTag(latestTag);
35
+ console.log(chalk.dim(" Latest version:"), latestVersion);
36
+ console.log();
37
+ if (latestVersion !== manifest.templateVersion) {
38
+ console.log(
39
+ chalk.yellow(
40
+ ` Update available: ${manifest.templateVersion} \u2192 ${latestVersion}`
41
+ )
42
+ );
43
+ console.log(
44
+ chalk.dim(" Run:"),
45
+ `create sync --mosaic-template-path ${mosaicTemplatePath}`
46
+ );
47
+ } else {
48
+ console.log(chalk.green(" Up to date"));
49
+ }
50
+ } else {
51
+ console.log();
52
+ console.log(
53
+ chalk.yellow(" No template tags found in mosaic repo.")
54
+ );
55
+ console.log(
56
+ chalk.dim(" Run:"),
57
+ `cd ${mosaicTemplatePath} && pnpm template:tag`
58
+ );
59
+ }
60
+ } else {
61
+ console.log();
62
+ console.log(
63
+ chalk.dim(
64
+ " Use --mosaic-template-path or set MOSAIC_TEMPLATE_PATH to check for updates"
65
+ )
66
+ );
67
+ }
68
+ console.log();
69
+ } catch (error) {
70
+ console.error(chalk.red(error.message));
71
+ process.exit(1);
72
+ }
73
+ }
74
+ export {
75
+ statusCommand
76
+ };
@@ -0,0 +1,136 @@
1
+ import {
2
+ getLatestTemplateTag,
3
+ getTemplateDiff,
4
+ getTemplateVersionFromTag
5
+ } from "./chunk-R4FWPE4A.js";
6
+ import {
7
+ readManifest,
8
+ resolveMosaicTemplatePath
9
+ } from "./chunk-WMJT7CB5.js";
10
+
11
+ // src/commands/sync.ts
12
+ import path from "path";
13
+ import fs from "fs-extra";
14
+ import chalk from "chalk";
15
+ function generateSyncContext(manifest, toVersion, diff, notes) {
16
+ let content = `# Mosaic Sync Context
17
+
18
+ ## App Info
19
+ - **Template:** ${manifest.templateType}
20
+ - **Current version:** ${manifest.templateVersion}
21
+ - **Target version:** ${toVersion}
22
+
23
+ ## Placeholder Mappings
24
+
25
+ When applying template changes, replace these placeholder tokens with the actual values:
26
+
27
+ | Placeholder | Value |
28
+ |------------|-------|
29
+ ${Object.entries(manifest.placeholders).map(([k, v]) => `| \`${k}\` | \`${v}\` |`).join("\n")}
30
+
31
+ ## Template Changes (${manifest.templateVersion} \u2192 ${toVersion})
32
+
33
+ \`\`\`diff
34
+ ${diff}
35
+ \`\`\`
36
+ `;
37
+ if (notes.trim()) {
38
+ content += `
39
+ ## Divergence Notes (from mosaic-template-notes.md)
40
+
41
+ ${notes}
42
+ `;
43
+ }
44
+ content += `
45
+ ## Instructions
46
+
47
+ 1. Apply the template changes above to this app
48
+ 2. When you see placeholder tokens (e.g. \`__APP_NAME__\`), replace them with the actual values from the mapping table
49
+ 3. Check the divergence notes \u2014 preserve intentional divergences
50
+ 4. For files not modified locally: apply changes directly
51
+ 5. For files modified locally: merge intelligently, preserving local customizations
52
+ 6. After applying all changes, run: \`pnpm install && pnpm build && pnpm lint\`
53
+ 7. Update \`.mosaic-template.json\`: set \`templateVersion\` to \`"${toVersion}"\` and update \`templateCommit\`
54
+ 8. If you made decisions about merge conflicts, add notes to \`mosaic-template-notes.md\`
55
+ 9. Delete this file (\`.mosaic-sync-context.md\`) when done
56
+ `;
57
+ return content;
58
+ }
59
+ async function syncCommand(options) {
60
+ const cwd = process.cwd();
61
+ try {
62
+ const manifest = await readManifest(cwd);
63
+ const mosaicTemplatePath = resolveMosaicTemplatePath(options);
64
+ const fromTag = `template/${manifest.templateType}/${manifest.templateVersion}`;
65
+ let toTag;
66
+ if (options.to) {
67
+ toTag = `template/${manifest.templateType}/${options.to}`;
68
+ } else {
69
+ const latest = getLatestTemplateTag(
70
+ manifest.templateType,
71
+ mosaicTemplatePath
72
+ );
73
+ if (!latest) {
74
+ console.error(
75
+ chalk.red(
76
+ "No template tags found. Run 'pnpm template:tag' in the mosaic repo first."
77
+ )
78
+ );
79
+ process.exit(1);
80
+ }
81
+ toTag = latest;
82
+ }
83
+ const toVersion = getTemplateVersionFromTag(toTag);
84
+ if (toVersion === manifest.templateVersion) {
85
+ console.log(chalk.green("Already up to date."));
86
+ return;
87
+ }
88
+ const diff = getTemplateDiff(
89
+ mosaicTemplatePath,
90
+ manifest.source.templatePath,
91
+ fromTag,
92
+ toTag
93
+ );
94
+ if (!diff.trim()) {
95
+ console.log(
96
+ chalk.green("No template file changes between versions.")
97
+ );
98
+ return;
99
+ }
100
+ const notesPath = path.join(cwd, "mosaic-template-notes.md");
101
+ let notes = "";
102
+ if (await fs.pathExists(notesPath)) {
103
+ notes = await fs.readFile(notesPath, "utf-8");
104
+ }
105
+ const context = generateSyncContext(manifest, toVersion, diff, notes);
106
+ const contextPath = path.join(cwd, ".mosaic-sync-context.md");
107
+ await fs.writeFile(contextPath, context);
108
+ console.log();
109
+ console.log(chalk.bold("Sync Context Generated"));
110
+ console.log();
111
+ console.log(chalk.dim(" From:"), manifest.templateVersion);
112
+ console.log(chalk.dim(" To:"), toVersion);
113
+ console.log(chalk.dim(" Context file:"), ".mosaic-sync-context.md");
114
+ console.log();
115
+ console.log("Next steps:");
116
+ console.log(
117
+ chalk.dim(" 1."),
118
+ "Open Claude Code in this directory"
119
+ );
120
+ console.log(
121
+ chalk.dim(" 2."),
122
+ 'Tell Claude: "Read .mosaic-sync-context.md and apply the template changes"'
123
+ );
124
+ console.log(
125
+ chalk.dim(" 3."),
126
+ "Review Claude's changes, then delete .mosaic-sync-context.md"
127
+ );
128
+ console.log();
129
+ } catch (error) {
130
+ console.error(chalk.red(error.message));
131
+ process.exit(1);
132
+ }
133
+ }
134
+ export {
135
+ syncCommand
136
+ };
@@ -0,0 +1,144 @@
1
+ import {
2
+ getFileAtTag
3
+ } from "./chunk-R4FWPE4A.js";
4
+ import {
5
+ readManifest,
6
+ resolveMosaicTemplatePath
7
+ } from "./chunk-WMJT7CB5.js";
8
+
9
+ // src/commands/upstream.ts
10
+ import path from "path";
11
+ import fs from "fs-extra";
12
+ import chalk from "chalk";
13
+ async function generateUpstreamContext(manifest, mosaicTemplatePath, tag, appDir, files) {
14
+ let content = `# Mosaic Upstream Context
15
+
16
+ ## App Info
17
+ - **App name:** ${manifest.placeholders.__APP_NAME__ || "unknown"}
18
+ - **Template:** ${manifest.templateType}
19
+ - **Template version:** ${manifest.templateVersion}
20
+
21
+ ## Placeholder Mappings
22
+
23
+ When generalizing app code back to template, replace these values with placeholder tokens:
24
+
25
+ | Value | Placeholder |
26
+ |-------|------------|
27
+ ${Object.entries(manifest.placeholders).sort((a, b) => b[1].length - a[1].length).map(([k, v]) => `| \`${v}\` | \`${k}\` |`).join("\n")}
28
+
29
+ ## Files to Review
30
+
31
+ `;
32
+ for (const file of files) {
33
+ const appFilePath = path.resolve(appDir, file);
34
+ const templateRelPath = `${manifest.source.templatePath}/${file}`;
35
+ const appContent = await fs.pathExists(appFilePath) ? await fs.readFile(appFilePath, "utf-8") : null;
36
+ const templateContent = getFileAtTag(
37
+ mosaicTemplatePath,
38
+ tag,
39
+ templateRelPath
40
+ );
41
+ content += `### ${file}
42
+
43
+ `;
44
+ if (!templateContent && appContent) {
45
+ content += `**New file** (not in template at ${manifest.templateVersion})
46
+
47
+ `;
48
+ content += `\`\`\`
49
+ ${appContent}
50
+ \`\`\`
51
+
52
+ `;
53
+ } else if (templateContent && !appContent) {
54
+ content += `**Deleted** (exists in template but not in app)
55
+
56
+ `;
57
+ } else if (appContent && templateContent) {
58
+ content += `**App version:**
59
+ \`\`\`
60
+ ${appContent}
61
+ \`\`\`
62
+
63
+ `;
64
+ content += `**Template version (at ${manifest.templateVersion}):**
65
+ \`\`\`
66
+ ${templateContent}
67
+ \`\`\`
68
+
69
+ `;
70
+ } else {
71
+ content += `**Not found** (file does not exist in app or template)
72
+
73
+ `;
74
+ }
75
+ }
76
+ content += `## Instructions
77
+
78
+ 1. Review each file above
79
+ 2. Determine which changes are generalizable (useful for all apps) vs app-specific
80
+ 3. For generalizable changes: apply them to the template at \`${manifest.source.templatePath}/\`
81
+ 4. When applying, replace app-specific values with placeholders using the mapping table above (replace longest values first)
82
+ 5. After applying, bump the version in \`packages/create-mosaic-module/template-versions.json\`
83
+ 6. Run \`pnpm template:tag\` to create the new version tag
84
+ 7. Delete this file (\`.mosaic-upstream-context.md\`) when done
85
+ `;
86
+ return content;
87
+ }
88
+ async function upstreamCommand(options) {
89
+ const cwd = process.cwd();
90
+ try {
91
+ const manifest = await readManifest(cwd);
92
+ const mosaicTemplatePath = resolveMosaicTemplatePath(options);
93
+ if (!options.files || options.files.length === 0) {
94
+ console.error(
95
+ chalk.red("Specify files with --files <file1> <file2> ...")
96
+ );
97
+ console.log(
98
+ chalk.dim(
99
+ " Example: create upstream --files src/config/getEnvConfig.ts"
100
+ )
101
+ );
102
+ process.exit(1);
103
+ }
104
+ const tag = `template/${manifest.templateType}/${manifest.templateVersion}`;
105
+ const context = await generateUpstreamContext(
106
+ manifest,
107
+ mosaicTemplatePath,
108
+ tag,
109
+ cwd,
110
+ options.files
111
+ );
112
+ const contextPath = path.join(cwd, ".mosaic-upstream-context.md");
113
+ await fs.writeFile(contextPath, context);
114
+ console.log();
115
+ console.log(chalk.bold("Upstream Context Generated"));
116
+ console.log();
117
+ console.log(chalk.dim(" Files:"), options.files.join(", "));
118
+ console.log(
119
+ chalk.dim(" Context file:"),
120
+ ".mosaic-upstream-context.md"
121
+ );
122
+ console.log();
123
+ console.log("Next steps:");
124
+ console.log(
125
+ chalk.dim(" 1."),
126
+ "Open Claude Code in the mosaic repo"
127
+ );
128
+ console.log(
129
+ chalk.dim(" 2."),
130
+ `Tell Claude: "Read ${path.resolve(cwd, ".mosaic-upstream-context.md")} and apply generalizable changes to the template"`
131
+ );
132
+ console.log(
133
+ chalk.dim(" 3."),
134
+ "Review Claude's changes to the template"
135
+ );
136
+ console.log();
137
+ } catch (error) {
138
+ console.error(chalk.red(error.message));
139
+ process.exit(1);
140
+ }
141
+ }
142
+ export {
143
+ upstreamCommand
144
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@percepta/create",
3
+ "version": "3.0.0",
4
+ "description": "Scaffold a new Mosaic package",
5
+ "type": "module",
6
+ "bin": {
7
+ "create": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "templates",
12
+ "template-versions.json"
13
+ ],
14
+ "dependencies": {
15
+ "chalk": "^5.4.1",
16
+ "commander": "^13.1.0",
17
+ "fs-extra": "^11.3.0",
18
+ "inquirer": "^12.5.0",
19
+ "ora": "^8.2.0",
20
+ "validate-npm-package-name": "^6.0.0",
21
+ "yaml": "^2.7.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/fs-extra": "^11.0.4",
25
+ "@types/node": "^24.1.0",
26
+ "@types/validate-npm-package-name": "^4.0.2",
27
+ "tsup": "^8.4.0",
28
+ "typescript": "^5.7.3",
29
+ "vitest": "^4.0.0",
30
+ "@percepta/build": "0.4.0"
31
+ },
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "publishConfig": {
36
+ "access": "public"
37
+ },
38
+ "keywords": [
39
+ "create",
40
+ "template",
41
+ "nextjs",
42
+ "mosaic",
43
+ "percepta",
44
+ "scaffold",
45
+ "cli"
46
+ ],
47
+ "license": "MIT",
48
+ "scripts": {
49
+ "build": "tsup src/index.ts --format esm --dts --clean",
50
+ "dev": "tsup src/index.ts --format esm --watch",
51
+ "typecheck": "tsc --noEmit",
52
+ "sync-template": "tsx scripts/sync-template.ts",
53
+ "template:tag": "tsx scripts/template-tag.ts",
54
+ "test": "vitest run",
55
+ "test:watch": "vitest",
56
+ "test:template": "bash scripts/test-template.sh"
57
+ }
58
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "webapp": "1.0.0",
3
+ "library": "1.0.0"
4
+ }
@@ -0,0 +1,30 @@
1
+ # __APP_TITLE__
2
+
3
+ A TypeScript library package.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ pnpm add __APP_NAME__
9
+ ```
10
+
11
+ ## Development
12
+
13
+ ```bash
14
+ # Build the library
15
+ pnpm build
16
+
17
+ # Watch mode for development
18
+ pnpm dev
19
+
20
+ # Run linting
21
+ pnpm lint
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```typescript
27
+ import { hello } from "__APP_NAME__";
28
+
29
+ console.log(hello("World"));
30
+ ```
@@ -0,0 +1,10 @@
1
+ import eslint from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ ignores: ["node_modules/**", "dist/**"],
9
+ }
10
+ );
@@ -0,0 +1,18 @@
1
+ # Dependencies
2
+ node_modules/
3
+
4
+ # Build outputs
5
+ dist/
6
+
7
+ # IDE
8
+ .idea/
9
+ .vscode/
10
+ *.swp
11
+ *.swo
12
+
13
+ # OS
14
+ .DS_Store
15
+ Thumbs.db
16
+
17
+ # Logs
18
+ *.log
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "__APP_NAME__",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "description": "__APP_TITLE__",
6
+ "sideEffects": false,
7
+ "type": "module",
8
+ "main": "dist/index.js",
9
+ "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist/**/*"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsup src/index.ts --format esm --dts --clean",
15
+ "dev": "tsup src/index.ts --format esm --dts --watch",
16
+ "clean": "rimraf dist",
17
+ "lint": "eslint .",
18
+ "lint:fix": "eslint . --fix",
19
+ "test": "echo \"No tests configured yet\""
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^24.1.0",
23
+ "eslint": "^9.18.0",
24
+ "rimraf": "^5.0.5",
25
+ "tsup": "^8.4.0",
26
+ "typescript": "^5.8.3",
27
+ "typescript-eslint": "^8.33.0"
28
+ }
29
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * __APP_TITLE__
3
+ *
4
+ * Add your library exports here.
5
+ */
6
+
7
+ export function hello(name: string): string {
8
+ return `Hello, ${name}!`;
9
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "lib": ["ES2022"],
5
+ "module": "NodeNext",
6
+ "moduleResolution": "NodeNext",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "forceConsistentCasingInFileNames": true,
11
+ "declaration": true,
12
+ "declarationMap": true,
13
+ "sourceMap": true,
14
+ "outDir": "dist",
15
+ "rootDir": "src"
16
+ },
17
+ "include": ["src/**/*"],
18
+ "exclude": ["node_modules", "dist"]
19
+ }
@@ -0,0 +1,41 @@
1
+ # __APP_TITLE__
2
+
3
+ A monorepo powered by [pnpm workspaces](https://pnpm.io/workspaces).
4
+
5
+ ## Getting Started
6
+
7
+ ```bash
8
+ # Install dependencies
9
+ pnpm install
10
+
11
+ # Run development mode for all packages
12
+ pnpm dev
13
+
14
+ # Build all packages
15
+ pnpm build
16
+
17
+ # Run tests
18
+ pnpm test
19
+
20
+ # Lint all packages
21
+ pnpm lint
22
+ ```
23
+
24
+ ## Structure
25
+
26
+ ```
27
+ packages/
28
+ └── your-package/ # Add your packages here
29
+ ```
30
+
31
+ ## Adding a new package
32
+
33
+ Create a new directory in `packages/` with its own `package.json`:
34
+
35
+ ```bash
36
+ mkdir packages/my-package
37
+ cd packages/my-package
38
+ pnpm init
39
+ ```
40
+
41
+ Then run `pnpm install` from the root to link the workspace.
@@ -0,0 +1,10 @@
1
+ import eslint from "@eslint/js";
2
+ import tseslint from "typescript-eslint";
3
+
4
+ export default tseslint.config(
5
+ eslint.configs.recommended,
6
+ ...tseslint.configs.recommended,
7
+ {
8
+ ignores: ["**/node_modules/**", "**/dist/**"],
9
+ }
10
+ );