bsmnt 0.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 (98) hide show
  1. package/.changeset/2026-02-11-test-patch-bump.md +5 -0
  2. package/.changeset/README.md +10 -0
  3. package/.changeset/config.json +16 -0
  4. package/.cursor/rules/README.md +184 -0
  5. package/.cursor/rules/architecture.mdc +437 -0
  6. package/.cursor/rules/components.mdc +436 -0
  7. package/.cursor/rules/integrations.mdc +447 -0
  8. package/.cursor/rules/main.mdc +278 -0
  9. package/.cursor/rules/styling.mdc +433 -0
  10. package/.github/PULL_REQUEST_TEMPLATE.md +14 -0
  11. package/.github/workflows/.gitkeep +0 -0
  12. package/.github/workflows/ci.yml +37 -0
  13. package/.github/workflows/release.yml +54 -0
  14. package/.tldr/cache/call_graph.json +7 -0
  15. package/.tldr/languages.json +6 -0
  16. package/.tldr/status +1 -0
  17. package/.tldrignore +84 -0
  18. package/.vscode/extensions.json +20 -0
  19. package/.vscode/settings.json +98 -0
  20. package/CHANGELOG.md +13 -0
  21. package/CLAUDE.md +138 -0
  22. package/README.md +176 -0
  23. package/bin/index.js +262 -0
  24. package/biome.json +44 -0
  25. package/bun.lock +496 -0
  26. package/changelog/04-02-26.md +86 -0
  27. package/changelog/05-02-26.md +101 -0
  28. package/changelog/09-02-26.md +83 -0
  29. package/docs/fix-studio-hydration.md +46 -0
  30. package/docs/plans/2026-01-29-sanity-smart-merge-design.md +196 -0
  31. package/docs/plans/2026-01-29-sanity-smart-merge-implementation.md +695 -0
  32. package/docs/sanity-setup-steps.md +199 -0
  33. package/integrations/basehub/README.md +3 -0
  34. package/integrations/sanity/app/api/draft-mode/disable/route.ts +7 -0
  35. package/integrations/sanity/app/api/draft-mode/enable/route.ts +21 -0
  36. package/integrations/sanity/app/api/revalidate/route.ts +37 -0
  37. package/integrations/sanity/app/layout.tsx +111 -0
  38. package/integrations/sanity/app/sitemap.ts +80 -0
  39. package/integrations/sanity/app/studio/[[...tool]]/page.tsx +8 -0
  40. package/integrations/sanity/app/studio/layout.tsx +7 -0
  41. package/integrations/sanity/components/ui/sanity-image/index.tsx +37 -0
  42. package/integrations/sanity/lib/integrations/README.md +58 -0
  43. package/integrations/sanity/lib/integrations/check-integration.ts +62 -0
  44. package/integrations/sanity/lib/integrations/sanity/README.md +144 -0
  45. package/integrations/sanity/lib/integrations/sanity/client.ts +30 -0
  46. package/integrations/sanity/lib/integrations/sanity/components/disable-draft-mode.tsx +29 -0
  47. package/integrations/sanity/lib/integrations/sanity/components/rich-text.tsx +73 -0
  48. package/integrations/sanity/lib/integrations/sanity/env.ts +38 -0
  49. package/integrations/sanity/lib/integrations/sanity/live/index.tsx +34 -0
  50. package/integrations/sanity/lib/integrations/sanity/queries.ts +99 -0
  51. package/integrations/sanity/lib/integrations/sanity/sanity.cli.ts +20 -0
  52. package/integrations/sanity/lib/integrations/sanity/sanity.config.ts +94 -0
  53. package/integrations/sanity/lib/integrations/sanity/sanity.types.ts +337 -0
  54. package/integrations/sanity/lib/integrations/sanity/schema.json +1850 -0
  55. package/integrations/sanity/lib/integrations/sanity/schemas/article.ts +132 -0
  56. package/integrations/sanity/lib/integrations/sanity/schemas/example.ts +203 -0
  57. package/integrations/sanity/lib/integrations/sanity/schemas/index.ts +37 -0
  58. package/integrations/sanity/lib/integrations/sanity/schemas/link.ts +127 -0
  59. package/integrations/sanity/lib/integrations/sanity/schemas/metadata.ts +68 -0
  60. package/integrations/sanity/lib/integrations/sanity/schemas/navigation.ts +39 -0
  61. package/integrations/sanity/lib/integrations/sanity/schemas/page.ts +77 -0
  62. package/integrations/sanity/lib/integrations/sanity/schemas/richText.ts +59 -0
  63. package/integrations/sanity/lib/integrations/sanity/structure.ts +5 -0
  64. package/integrations/sanity/lib/integrations/sanity/utils/image.ts +11 -0
  65. package/integrations/sanity/lib/integrations/sanity/utils/link.ts +61 -0
  66. package/integrations/sanity/lib/scripts/copy-sanity-mcp.ts +23 -0
  67. package/integrations/sanity/lib/scripts/generate-page.ts +310 -0
  68. package/integrations/sanity/lib/utils/metadata.ts +190 -0
  69. package/layers/experiment/components/layout/header/index.tsx +58 -0
  70. package/layers/experiment/components/layout/navigation-menu.tsx +127 -0
  71. package/layers/experiment/lib/constants.ts +12 -0
  72. package/layers/webgl/app/page.tsx +10 -0
  73. package/layers/webgl/components/webgl/canvas/dynamic.tsx +34 -0
  74. package/layers/webgl/components/webgl/canvas/index.tsx +43 -0
  75. package/layers/webgl/components/webgl/components/scene/index.tsx +21 -0
  76. package/layers/webgpu/.gitkeep +0 -0
  77. package/package.json +44 -0
  78. package/plugins/README.md +21 -0
  79. package/plugins/no-anchor-element.grit +11 -0
  80. package/plugins/no-relative-parent-imports.grit +6 -0
  81. package/plugins/no-unnecessary-forwardref.grit +5 -0
  82. package/src/commands/add-integration.js +325 -0
  83. package/src/commands/create.js +415 -0
  84. package/src/commands/setup-sanity.js +426 -0
  85. package/src/commands/worktree.js +805 -0
  86. package/src/mergers/check-integration-merger.js +105 -0
  87. package/src/mergers/config.js +137 -0
  88. package/src/mergers/index.js +355 -0
  89. package/src/mergers/layout-merger.js +223 -0
  90. package/src/mergers/next-config-merger.js +63 -0
  91. package/src/mergers/sitemap-merger.js +121 -0
  92. package/tasks/prd-next-starter-dynamic-layers.md +184 -0
  93. package/tasks/prd.json +153 -0
  94. package/tasks/progress.txt +115 -0
  95. package/template-hooks/use-battery.ts +126 -0
  96. package/template-hooks/use-device-perf.ts +184 -0
  97. package/template-hooks/use-intersection-observer.ts +32 -0
  98. package/template-hooks/use-media.ts +33 -0
@@ -0,0 +1,325 @@
1
+ import { execSync } from "node:child_process";
2
+ import path from "node:path";
3
+ import fs from "fs-extra";
4
+ import ora from "ora";
5
+ import pc from "picocolors";
6
+ import prompts from "prompts";
7
+
8
+ /**
9
+ * Validates that the current directory is a valid Next.js project
10
+ */
11
+ async function validateProject(targetDir) {
12
+ const packageJsonPath = path.join(targetDir, "package.json");
13
+
14
+ // Check for package.json
15
+ if (!fs.existsSync(packageJsonPath)) {
16
+ return {
17
+ valid: false,
18
+ error: "No package.json found in current directory",
19
+ };
20
+ }
21
+
22
+ const pkg = await fs.readJson(packageJsonPath);
23
+
24
+ // Check for Next.js dependency
25
+ const hasNext = pkg.dependencies?.next || pkg.devDependencies?.next;
26
+ if (!hasNext) {
27
+ return {
28
+ valid: false,
29
+ error:
30
+ "This doesn't appear to be a Next.js project (next not found in dependencies)",
31
+ };
32
+ }
33
+
34
+ // Check for app directory (App Router) - supports both root and src/ structures
35
+ const appDir = path.join(targetDir, "app");
36
+ const srcAppDir = path.join(targetDir, "src/app");
37
+ const hasAppDir = fs.existsSync(appDir) || fs.existsSync(srcAppDir);
38
+
39
+ if (!hasAppDir) {
40
+ return {
41
+ valid: false,
42
+ error:
43
+ "No app/ or src/app/ directory found. This CLI supports Next.js App Router projects only.",
44
+ };
45
+ }
46
+
47
+ // Detect which structure is used
48
+ const usesSrcPrefix = fs.existsSync(srcAppDir);
49
+
50
+ return { valid: true, pkg, usesSrcPrefix };
51
+ }
52
+
53
+ /**
54
+ * Check if an integration is already installed
55
+ */
56
+ async function checkExistingIntegration(targetDir, integration, usesSrcPrefix) {
57
+ const integrationPaths = {
58
+ sanity: "lib/integrations/sanity",
59
+ basehub: "lib/integrations/basehub",
60
+ };
61
+
62
+ const basePath = integrationPaths[integration];
63
+ const prefix = usesSrcPrefix ? "src/" : "";
64
+ const integrationPath = path.join(targetDir, prefix + basePath);
65
+ return fs.existsSync(integrationPath);
66
+ }
67
+
68
+ /**
69
+ * Add CMS depeπzckage.json
70
+ */
71
+ async function addDependencies(targetDir, integration) {
72
+ const pkgPath = path.join(targetDir, "package.json");
73
+ const pkg = await fs.readJson(pkgPath);
74
+
75
+ if (integration === "sanity") {
76
+ pkg.dependencies = {
77
+ ...pkg.dependencies,
78
+ "@sanity/asset-utils": "^2.3.0",
79
+ "@sanity/image-url": "^2.0.3",
80
+ "@sanity/visual-editing": "^5.1.1",
81
+ "@sanity/vision": "^5.7.0",
82
+ "next-sanity": "^12.0.14",
83
+ };
84
+
85
+ pkg.devDependencies = {
86
+ ...pkg.devDependencies,
87
+ sanity: "^5.6.0",
88
+ };
89
+
90
+ pkg.scripts = {
91
+ ...pkg.scripts,
92
+ "sanity:extract":
93
+ "cd lib/integrations/sanity && bun --env-file ../../../.env.local sanity schema extract",
94
+ "sanity:typegen":
95
+ "cd lib/integrations/sanity && bun --env-file ../../../.env.local sanity typegen generate && bun biome check --write --unsafe",
96
+ };
97
+ } else if (integration === "basehub") {
98
+ pkg.dependencies = {
99
+ ...pkg.dependencies,
100
+ basehub: "^3.0.0",
101
+ };
102
+ }
103
+
104
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
105
+ }
106
+
107
+ /**
108
+ * Adds an integration to the current working directory project
109
+ */
110
+ export async function addIntegration(options = {}) {
111
+ const targetDir = process.cwd();
112
+
113
+ // 1. Validate current directory is a Next.js project
114
+ const validationSpinner = ora("Validating project...").start();
115
+ const validation = await validateProject(targetDir);
116
+
117
+ if (!validation.valid) {
118
+ validationSpinner.fail(pc.red(validation.error));
119
+ console.log(
120
+ pc.yellow(
121
+ "\n💡 Make sure you're in a Next.js App Router project directory.\n",
122
+ ),
123
+ );
124
+ return;
125
+ }
126
+ const projectStructure = validation.usesSrcPrefix ? "src/" : "root";
127
+ validationSpinner.succeed(
128
+ pc.green(
129
+ `Found Next.js project: ${pc.cyan(validation.pkg.name || "unnamed")} (${projectStructure} structure)`,
130
+ ),
131
+ );
132
+
133
+ // 2. Select integration (or use flag)
134
+ let integration = options.cms;
135
+
136
+ if (!integration || integration === "none") {
137
+ const { selectedIntegration } = await prompts({
138
+ type: "select",
139
+ name: "selectedIntegration",
140
+ message: "Which integration would you like to add?",
141
+ choices: [
142
+ {
143
+ title: "Sanity CMS",
144
+ description: "Headless CMS with real-time collaboration",
145
+ value: "sanity",
146
+ },
147
+ {
148
+ title: "BaseHub",
149
+ description: "AI-native headless CMS",
150
+ value: "basehub",
151
+ },
152
+ ],
153
+ });
154
+
155
+ if (!selectedIntegration) {
156
+ console.log(pc.yellow("\n⚠ Operation cancelled.\n"));
157
+ return;
158
+ }
159
+
160
+ integration = selectedIntegration;
161
+ }
162
+
163
+ // 3. Check if integration already exists
164
+ const alreadyExists = await checkExistingIntegration(
165
+ targetDir,
166
+ integration,
167
+ validation.usesSrcPrefix,
168
+ );
169
+ if (alreadyExists) {
170
+ const { overwrite } = await prompts({
171
+ type: "confirm",
172
+ name: "overwrite",
173
+ message: `${pc.yellow(integration)} integration already exists. Overwrite?`,
174
+ initial: false,
175
+ });
176
+
177
+ if (!overwrite) {
178
+ console.log(pc.yellow("⚠ Operation cancelled."));
179
+ return;
180
+ }
181
+ }
182
+
183
+ // 4. Inject integration files using existing merger system
184
+ const integrationSpinner = ora(
185
+ `Adding ${pc.cyan(integration)} integration...`,
186
+ ).start();
187
+
188
+ try {
189
+ const { injectIntegration, formatMergeResults } = await import(
190
+ "../mergers/index.js"
191
+ );
192
+ const results = await injectIntegration(
193
+ targetDir,
194
+ integration,
195
+ integrationSpinner,
196
+ );
197
+
198
+ const hasErrors = results.failed.length > 0;
199
+ const resultLines = formatMergeResults(results);
200
+
201
+ if (hasErrors) {
202
+ integrationSpinner.warn(
203
+ pc.yellow(`${integration} integration completed with warnings:`),
204
+ );
205
+ } else {
206
+ integrationSpinner.succeed(pc.green(`${integration} integration added:`));
207
+ }
208
+
209
+ // Display detailed results
210
+ for (const line of resultLines) {
211
+ console.log(pc.dim(line));
212
+ }
213
+ } catch (e) {
214
+ integrationSpinner.fail(
215
+ pc.red(`Failed to inject ${integration} integration: ${e.message}`),
216
+ );
217
+ return;
218
+ }
219
+
220
+ // 5. Add dependencies to package.json
221
+ const depsSpinner = ora("Adding dependencies to package.json...").start();
222
+ try {
223
+ await addDependencies(targetDir, integration);
224
+ depsSpinner.succeed(pc.green("Dependencies added to package.json"));
225
+ } catch (e) {
226
+ depsSpinner.warn(pc.yellow(`Failed to update package.json: ${e.message}`));
227
+ }
228
+
229
+ // 6. Register Sanity MCP Server + Agent Toolkit
230
+ if (integration === "sanity") {
231
+ // MCP registration (Claude Code only)
232
+ const claudeDir = path.join(targetDir, ".claude");
233
+ if (fs.existsSync(claudeDir)) {
234
+ const mcpSpinner = ora("Registering Sanity MCP server...").start();
235
+ try {
236
+ execSync(
237
+ "claude mcp add Sanity -t http https://mcp.sanity.io --scope user",
238
+ {
239
+ stdio: "pipe",
240
+ },
241
+ );
242
+ mcpSpinner.succeed(pc.green("Sanity MCP server registered."));
243
+ } catch (e) {
244
+ const stderr = e.stderr?.toString() || "";
245
+ if (stderr.includes("already exists")) {
246
+ mcpSpinner.succeed(pc.green("Sanity MCP server already registered."));
247
+ } else {
248
+ mcpSpinner.warn(
249
+ pc.yellow(
250
+ "Could not register Sanity MCP server. Run manually: claude mcp add Sanity -t http https://mcp.sanity.io --scope user",
251
+ ),
252
+ );
253
+ }
254
+ }
255
+ }
256
+
257
+ // Agent toolkit skills
258
+ const agentArg = fs.existsSync(path.join(targetDir, ".claude"))
259
+ ? "claude-code"
260
+ : fs.existsSync(path.join(targetDir, ".opencode"))
261
+ ? "opencode"
262
+ : null;
263
+
264
+ if (agentArg) {
265
+ const skillsSpinner = ora("Installing Sanity agent toolkit...").start();
266
+ try {
267
+ execSync(`bunx skills add sanity-io/agent-toolkit -a ${agentArg} -y`, {
268
+ cwd: targetDir,
269
+ stdio: "ignore",
270
+ });
271
+ skillsSpinner.succeed(pc.green("Sanity agent toolkit installed."));
272
+ } catch (_e) {
273
+ skillsSpinner.warn(
274
+ pc.yellow(
275
+ "Could not install Sanity agent toolkit. Add manually: bunx skills add sanity-io/agent-toolkit",
276
+ ),
277
+ );
278
+ }
279
+ }
280
+ }
281
+
282
+ // 7. Automated Sanity Project Setup
283
+ let sanitySetupDone = false;
284
+ if (integration === "sanity") {
285
+ try {
286
+ const { setupSanityProject } = await import("./setup-sanity.js");
287
+ sanitySetupDone = await setupSanityProject(targetDir);
288
+ } catch (e) {
289
+ console.log(pc.yellow(`\n Sanity auto-setup skipped: ${e.message}`));
290
+ console.log(pc.dim(" You can set up Sanity manually later.\n"));
291
+ }
292
+ }
293
+
294
+ // 8. Success message with next steps
295
+ console.log(
296
+ `\n${pc.bold(pc.green("✅ Success!"))} ${pc.cyan(integration)} integration added to your project.`,
297
+ );
298
+ console.log(`\n${pc.bold("Next steps:")}`);
299
+ console.log(pc.cyan(` 1. Run: bun install`));
300
+
301
+ if (integration === "sanity") {
302
+ if (sanitySetupDone) {
303
+ console.log(pc.cyan(` 2. Run: bun sanity:typegen`));
304
+ console.log(pc.cyan(` 3. Access Sanity Studio at: /studio`));
305
+ } else {
306
+ console.log(
307
+ pc.cyan(` 2. Create a Sanity project at https://sanity.io/manage`),
308
+ );
309
+ console.log(pc.cyan(` 3. Add your Sanity credentials to .env.local:`));
310
+ console.log(pc.dim(` NEXT_PUBLIC_SANITY_PROJECT_ID=your-project-id`));
311
+ console.log(pc.dim(` NEXT_PUBLIC_SANITY_DATASET=production`));
312
+ console.log(pc.dim(` SANITY_API_READ_TOKEN=your-read-token`));
313
+ console.log(pc.cyan(` 4. Run: bun sanity:typegen`));
314
+ console.log(pc.cyan(` 5. Access Sanity Studio at: /studio`));
315
+ }
316
+ } else if (integration === "basehub") {
317
+ console.log(
318
+ pc.cyan(` 2. Create a BaseHub project at https://basehub.com`),
319
+ );
320
+ console.log(pc.cyan(` 3. Add your BaseHub token to .env.local:`));
321
+ console.log(pc.dim(` BASEHUB_TOKEN=your-token`));
322
+ }
323
+
324
+ console.log();
325
+ }