@cmssy/cli 0.22.3 → 1.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 (260) hide show
  1. package/README.md +49 -650
  2. package/dist/cli.js +706 -328
  3. package/package.json +26 -73
  4. package/templates/block/Component.module.css.tpl +12 -0
  5. package/templates/block/Component.tsx.tpl +15 -0
  6. package/templates/block/block.ts.tpl +11 -0
  7. package/templates/init/app/[[...path]]/page.tsx +19 -0
  8. package/templates/init/app/api/draft/route.ts +5 -0
  9. package/templates/init/blocks/hero/Hero.module.css +131 -0
  10. package/templates/init/blocks/hero/Hero.tsx +100 -0
  11. package/templates/init/blocks/hero/block.ts +21 -0
  12. package/templates/init/cmssy/blocks.ts +5 -0
  13. package/templates/init/cmssy/editor.tsx +10 -0
  14. package/templates/init/cmssy.config.ts +8 -0
  15. package/templates/init/env.example +4 -0
  16. package/templates/init/next.config.mjs +8 -0
  17. package/templates/init/proxy.ts +32 -0
  18. package/config.d.ts +0 -3
  19. package/config.js +0 -4
  20. package/dist/cli.d.ts +0 -3
  21. package/dist/cli.d.ts.map +0 -1
  22. package/dist/cli.js.map +0 -1
  23. package/dist/commands/add-source.d.ts +0 -13
  24. package/dist/commands/add-source.d.ts.map +0 -1
  25. package/dist/commands/add-source.js +0 -273
  26. package/dist/commands/add-source.js.map +0 -1
  27. package/dist/commands/build.d.ts +0 -7
  28. package/dist/commands/build.d.ts.map +0 -1
  29. package/dist/commands/build.js +0 -127
  30. package/dist/commands/build.js.map +0 -1
  31. package/dist/commands/codegen.d.ts +0 -7
  32. package/dist/commands/codegen.d.ts.map +0 -1
  33. package/dist/commands/codegen.js +0 -150
  34. package/dist/commands/codegen.js.map +0 -1
  35. package/dist/commands/configure.d.ts +0 -6
  36. package/dist/commands/configure.d.ts.map +0 -1
  37. package/dist/commands/configure.js +0 -39
  38. package/dist/commands/configure.js.map +0 -1
  39. package/dist/commands/create.d.ts +0 -18
  40. package/dist/commands/create.d.ts.map +0 -1
  41. package/dist/commands/create.js +0 -403
  42. package/dist/commands/create.js.map +0 -1
  43. package/dist/commands/dev.d.ts +0 -6
  44. package/dist/commands/dev.d.ts.map +0 -1
  45. package/dist/commands/dev.js +0 -144
  46. package/dist/commands/dev.js.map +0 -1
  47. package/dist/commands/doctor.d.ts +0 -2
  48. package/dist/commands/doctor.d.ts.map +0 -1
  49. package/dist/commands/doctor.js +0 -230
  50. package/dist/commands/doctor.js.map +0 -1
  51. package/dist/commands/init.d.ts +0 -6
  52. package/dist/commands/init.d.ts.map +0 -1
  53. package/dist/commands/init.js +0 -414
  54. package/dist/commands/init.js.map +0 -1
  55. package/dist/commands/lib.d.ts +0 -17
  56. package/dist/commands/lib.d.ts.map +0 -1
  57. package/dist/commands/lib.js +0 -132
  58. package/dist/commands/lib.js.map +0 -1
  59. package/dist/commands/link.d.ts +0 -8
  60. package/dist/commands/link.d.ts.map +0 -1
  61. package/dist/commands/link.js +0 -116
  62. package/dist/commands/link.js.map +0 -1
  63. package/dist/commands/migrate.d.ts +0 -2
  64. package/dist/commands/migrate.d.ts.map +0 -1
  65. package/dist/commands/migrate.js +0 -231
  66. package/dist/commands/migrate.js.map +0 -1
  67. package/dist/commands/package.d.ts +0 -14
  68. package/dist/commands/package.d.ts.map +0 -1
  69. package/dist/commands/package.js +0 -137
  70. package/dist/commands/package.js.map +0 -1
  71. package/dist/commands/publish-block-buildtime.d.ts +0 -8
  72. package/dist/commands/publish-block-buildtime.d.ts.map +0 -1
  73. package/dist/commands/publish-block-buildtime.js +0 -290
  74. package/dist/commands/publish-block-buildtime.js.map +0 -1
  75. package/dist/commands/publish-template.d.ts +0 -22
  76. package/dist/commands/publish-template.d.ts.map +0 -1
  77. package/dist/commands/publish-template.js +0 -345
  78. package/dist/commands/publish-template.js.map +0 -1
  79. package/dist/commands/publish.d.ts +0 -15
  80. package/dist/commands/publish.d.ts.map +0 -1
  81. package/dist/commands/publish.js +0 -1003
  82. package/dist/commands/publish.js.map +0 -1
  83. package/dist/commands/skills.d.ts +0 -11
  84. package/dist/commands/skills.d.ts.map +0 -1
  85. package/dist/commands/skills.js +0 -178
  86. package/dist/commands/skills.js.map +0 -1
  87. package/dist/commands/sync.d.ts +0 -6
  88. package/dist/commands/sync.d.ts.map +0 -1
  89. package/dist/commands/sync.js +0 -208
  90. package/dist/commands/sync.js.map +0 -1
  91. package/dist/commands/test.d.ts +0 -8
  92. package/dist/commands/test.d.ts.map +0 -1
  93. package/dist/commands/test.js +0 -60
  94. package/dist/commands/test.js.map +0 -1
  95. package/dist/commands/upload.d.ts +0 -11
  96. package/dist/commands/upload.d.ts.map +0 -1
  97. package/dist/commands/upload.js +0 -128
  98. package/dist/commands/upload.js.map +0 -1
  99. package/dist/commands/workspaces.d.ts +0 -2
  100. package/dist/commands/workspaces.d.ts.map +0 -1
  101. package/dist/commands/workspaces.js +0 -62
  102. package/dist/commands/workspaces.js.map +0 -1
  103. package/dist/dev-ui/app.js +0 -1284
  104. package/dist/dev-ui/index.html +0 -1511
  105. package/dist/dev-ui-react/App.tsx +0 -164
  106. package/dist/dev-ui-react/__tests__/previewData.test.ts +0 -193
  107. package/dist/dev-ui-react/components/BlocksList.tsx +0 -232
  108. package/dist/dev-ui-react/components/Editor.tsx +0 -469
  109. package/dist/dev-ui-react/components/Preview.tsx +0 -146
  110. package/dist/dev-ui-react/hooks/useBlocks.ts +0 -80
  111. package/dist/dev-ui-react/index.html +0 -13
  112. package/dist/dev-ui-react/main.tsx +0 -8
  113. package/dist/dev-ui-react/styles.css +0 -856
  114. package/dist/dev-ui-react/types.ts +0 -45
  115. package/dist/skills/claude/cmssy-block.md +0 -454
  116. package/dist/skills/claude/cmssy-mcp-content.md +0 -231
  117. package/dist/test-helpers/index.d.ts +0 -23
  118. package/dist/test-helpers/index.d.ts.map +0 -1
  119. package/dist/test-helpers/index.js +0 -50
  120. package/dist/test-helpers/index.js.map +0 -1
  121. package/dist/types/block-config.d.ts +0 -6
  122. package/dist/types/block-config.d.ts.map +0 -1
  123. package/dist/types/block-config.js +0 -12
  124. package/dist/types/block-config.js.map +0 -1
  125. package/dist/types/publish-job.d.ts +0 -61
  126. package/dist/types/publish-job.d.ts.map +0 -1
  127. package/dist/types/publish-job.js +0 -13
  128. package/dist/types/publish-job.js.map +0 -1
  129. package/dist/utils/api-error.d.ts +0 -10
  130. package/dist/utils/api-error.d.ts.map +0 -1
  131. package/dist/utils/api-error.js +0 -55
  132. package/dist/utils/api-error.js.map +0 -1
  133. package/dist/utils/block-config.d.ts +0 -87
  134. package/dist/utils/block-config.d.ts.map +0 -1
  135. package/dist/utils/block-config.js +0 -214
  136. package/dist/utils/block-config.js.map +0 -1
  137. package/dist/utils/blocks-meta-cache.d.ts +0 -28
  138. package/dist/utils/blocks-meta-cache.d.ts.map +0 -1
  139. package/dist/utils/blocks-meta-cache.js +0 -72
  140. package/dist/utils/blocks-meta-cache.js.map +0 -1
  141. package/dist/utils/builder.d.ts +0 -34
  142. package/dist/utils/builder.d.ts.map +0 -1
  143. package/dist/utils/builder.js +0 -140
  144. package/dist/utils/builder.js.map +0 -1
  145. package/dist/utils/cmssy-config.d.ts +0 -16
  146. package/dist/utils/cmssy-config.d.ts.map +0 -1
  147. package/dist/utils/cmssy-config.js +0 -19
  148. package/dist/utils/cmssy-config.js.map +0 -1
  149. package/dist/utils/config.d.ts +0 -9
  150. package/dist/utils/config.d.ts.map +0 -1
  151. package/dist/utils/config.js +0 -51
  152. package/dist/utils/config.js.map +0 -1
  153. package/dist/utils/dependency-check.d.ts +0 -21
  154. package/dist/utils/dependency-check.d.ts.map +0 -1
  155. package/dist/utils/dependency-check.js +0 -56
  156. package/dist/utils/dependency-check.js.map +0 -1
  157. package/dist/utils/dev-generator/api-routes/blocks.d.ts +0 -3
  158. package/dist/utils/dev-generator/api-routes/blocks.d.ts.map +0 -1
  159. package/dist/utils/dev-generator/api-routes/blocks.js +0 -194
  160. package/dist/utils/dev-generator/api-routes/blocks.js.map +0 -1
  161. package/dist/utils/dev-generator/api-routes/config.d.ts +0 -2
  162. package/dist/utils/dev-generator/api-routes/config.d.ts.map +0 -1
  163. package/dist/utils/dev-generator/api-routes/config.js +0 -107
  164. package/dist/utils/dev-generator/api-routes/config.js.map +0 -1
  165. package/dist/utils/dev-generator/api-routes/preview.d.ts +0 -2
  166. package/dist/utils/dev-generator/api-routes/preview.d.ts.map +0 -1
  167. package/dist/utils/dev-generator/api-routes/preview.js +0 -50
  168. package/dist/utils/dev-generator/api-routes/preview.js.map +0 -1
  169. package/dist/utils/dev-generator/api-routes/workspaces.d.ts +0 -2
  170. package/dist/utils/dev-generator/api-routes/workspaces.d.ts.map +0 -1
  171. package/dist/utils/dev-generator/api-routes/workspaces.js +0 -128
  172. package/dist/utils/dev-generator/api-routes/workspaces.js.map +0 -1
  173. package/dist/utils/dev-generator/helpers.d.ts +0 -23
  174. package/dist/utils/dev-generator/helpers.d.ts.map +0 -1
  175. package/dist/utils/dev-generator/helpers.js +0 -128
  176. package/dist/utils/dev-generator/helpers.js.map +0 -1
  177. package/dist/utils/dev-generator/home-page.d.ts +0 -2
  178. package/dist/utils/dev-generator/home-page.d.ts.map +0 -1
  179. package/dist/utils/dev-generator/home-page.js +0 -643
  180. package/dist/utils/dev-generator/home-page.js.map +0 -1
  181. package/dist/utils/dev-generator/index.d.ts +0 -11
  182. package/dist/utils/dev-generator/index.d.ts.map +0 -1
  183. package/dist/utils/dev-generator/index.js +0 -85
  184. package/dist/utils/dev-generator/index.js.map +0 -1
  185. package/dist/utils/dev-generator/layout.d.ts +0 -3
  186. package/dist/utils/dev-generator/layout.d.ts.map +0 -1
  187. package/dist/utils/dev-generator/layout.js +0 -63
  188. package/dist/utils/dev-generator/layout.js.map +0 -1
  189. package/dist/utils/dev-generator/next-config.d.ts +0 -3
  190. package/dist/utils/dev-generator/next-config.d.ts.map +0 -1
  191. package/dist/utils/dev-generator/next-config.js +0 -111
  192. package/dist/utils/dev-generator/next-config.js.map +0 -1
  193. package/dist/utils/dev-generator/preview-pages.d.ts +0 -3
  194. package/dist/utils/dev-generator/preview-pages.d.ts.map +0 -1
  195. package/dist/utils/dev-generator/preview-pages.js +0 -833
  196. package/dist/utils/dev-generator/preview-pages.js.map +0 -1
  197. package/dist/utils/dev-generator.d.ts +0 -12
  198. package/dist/utils/dev-generator.d.ts.map +0 -1
  199. package/dist/utils/dev-generator.js +0 -1752
  200. package/dist/utils/dev-generator.js.map +0 -1
  201. package/dist/utils/field-schema.d.ts +0 -13
  202. package/dist/utils/field-schema.d.ts.map +0 -1
  203. package/dist/utils/field-schema.js +0 -182
  204. package/dist/utils/field-schema.js.map +0 -1
  205. package/dist/utils/graphql.d.ts +0 -39
  206. package/dist/utils/graphql.d.ts.map +0 -1
  207. package/dist/utils/graphql.js +0 -138
  208. package/dist/utils/graphql.js.map +0 -1
  209. package/dist/utils/lib-manifest.d.ts +0 -7
  210. package/dist/utils/lib-manifest.d.ts.map +0 -1
  211. package/dist/utils/lib-manifest.js +0 -59
  212. package/dist/utils/lib-manifest.js.map +0 -1
  213. package/dist/utils/publish-helpers.d.ts +0 -43
  214. package/dist/utils/publish-helpers.d.ts.map +0 -1
  215. package/dist/utils/publish-helpers.js +0 -215
  216. package/dist/utils/publish-helpers.js.map +0 -1
  217. package/dist/utils/publish-helpers.test.d.ts +0 -2
  218. package/dist/utils/publish-helpers.test.d.ts.map +0 -1
  219. package/dist/utils/publish-helpers.test.js +0 -24
  220. package/dist/utils/publish-helpers.test.js.map +0 -1
  221. package/dist/utils/resolve-workspace.d.ts +0 -6
  222. package/dist/utils/resolve-workspace.d.ts.map +0 -1
  223. package/dist/utils/resolve-workspace.js +0 -32
  224. package/dist/utils/resolve-workspace.js.map +0 -1
  225. package/dist/utils/scanner.d.ts +0 -42
  226. package/dist/utils/scanner.d.ts.map +0 -1
  227. package/dist/utils/scanner.js +0 -156
  228. package/dist/utils/scanner.js.map +0 -1
  229. package/dist/utils/schema-diff.d.ts +0 -22
  230. package/dist/utils/schema-diff.d.ts.map +0 -1
  231. package/dist/utils/schema-diff.js +0 -107
  232. package/dist/utils/schema-diff.js.map +0 -1
  233. package/dist/utils/source-collector.d.ts +0 -19
  234. package/dist/utils/source-collector.d.ts.map +0 -1
  235. package/dist/utils/source-collector.js +0 -565
  236. package/dist/utils/source-collector.js.map +0 -1
  237. package/dist/utils/theme-builder.d.ts +0 -12
  238. package/dist/utils/theme-builder.d.ts.map +0 -1
  239. package/dist/utils/theme-builder.js +0 -117
  240. package/dist/utils/theme-builder.js.map +0 -1
  241. package/dist/utils/type-generator.d.ts +0 -9
  242. package/dist/utils/type-generator.d.ts.map +0 -1
  243. package/dist/utils/type-generator.js +0 -86
  244. package/dist/utils/type-generator.js.map +0 -1
  245. package/dist/utils/version.d.ts +0 -10
  246. package/dist/utils/version.d.ts.map +0 -1
  247. package/dist/utils/version.js +0 -30
  248. package/dist/utils/version.js.map +0 -1
  249. package/dist/utils/versions.d.ts.map +0 -1
  250. package/dist/utils/versions.js.map +0 -1
  251. package/src/dev-app/app/api/blocks/[name]/config/route.ts +0 -140
  252. package/src/dev-app/app/api/blocks/route.ts +0 -61
  253. package/src/dev-app/app/api/config/route.ts +0 -98
  254. package/src/dev-app/app/api/context/route.ts +0 -126
  255. package/src/dev-app/app/api/preview/[name]/route.ts +0 -137
  256. package/src/dev-app/app/api/workspaces/route.ts +0 -124
  257. package/src/dev-app/app/layout.tsx +0 -18
  258. package/src/dev-app/app/page.tsx +0 -1785
  259. package/test.d.ts +0 -1
  260. package/test.js +0 -2
@@ -1,45 +0,0 @@
1
- export interface Block {
2
- type: 'block' | 'template';
3
- name: string;
4
- displayName: string;
5
- description?: string;
6
- category?: string;
7
- tags?: string[];
8
- version: string;
9
- hasConfig?: boolean;
10
- schema?: Record<string, FieldConfig>;
11
- pages?: TemplatePage[];
12
- layoutPositions?: LayoutPosition[];
13
- }
14
-
15
- export interface FieldConfig {
16
- type: string;
17
- label: string;
18
- required?: boolean;
19
- placeholder?: string;
20
- defaultValue?: unknown;
21
- helpText?: string;
22
- options?: Array<{ label: string; value: string }>;
23
- schema?: Record<string, FieldConfig>;
24
- minItems?: number;
25
- maxItems?: number;
26
- }
27
-
28
- export interface TemplatePage {
29
- name: string;
30
- slug: string;
31
- blocksCount: number;
32
- }
33
-
34
- export interface LayoutPosition {
35
- position: 'header' | 'footer' | 'sidebar_left' | 'sidebar_right' | 'before_content' | 'after_content';
36
- type: string;
37
- content?: Record<string, unknown>;
38
- }
39
-
40
- export interface Filters {
41
- search: string;
42
- type: 'all' | 'block' | 'template';
43
- category: string;
44
- tags: string[];
45
- }
@@ -1,454 +0,0 @@
1
- ---
2
- name: cmssy-block
3
- description: "Run the full cmssy CLI lifecycle - init, link, scaffold/edit blocks and templates, dev/build/test, publish, sync. Trigger when the user asks to: initialize a cmssy project, link to a workspace, create/add a new block or template, add a field to a block's schema, edit config.ts, run cmssy dev/build/test/doctor, publish a block, bump a block version, sync a block from the design library, or work with defineBlock/defineTemplate. Trigger on words: cmssy, blok, block, template, publish, opublikuj, workspace, link, sync, defineBlock, defineTemplate, scaffold, zescafoldwac."
4
- ---
5
-
6
- # cmssy-block
7
-
8
- Operates the full cmssy CLI workflow for blocks and templates in a project like `cmssy-marketing`. Covers every command the CLI exposes: `init`, `link`, `workspaces`, `create`, `dev`, `test`, `build`, `publish-block`, `publish-template`, `sync`, `lib`, `skills`, `doctor`. (`configure`, `migrate`, `codegen` exist but are hidden/legacy - don't use them.)
9
-
10
- ## 0. Orientation
11
-
12
- ```bash
13
- cmssy --version # CLI must be installed globally
14
- cmssy doctor # all-in-one health check
15
- ```
16
-
17
- `cmssy doctor` is the canonical precheck. It verifies:
18
-
19
- - Node >= 18, npm, Next.js, React versions
20
- - `cmssy.config.js` exists
21
- - `.env` with `CMSSY_API_URL`, `CMSSY_API_TOKEN`, `CMSSY_WORKSPACE_ID`
22
- - API reachable, token valid, workspace accessible
23
-
24
- Run it before any non-trivial operation. If something fails, fix that first - don't proceed.
25
-
26
- The project uses **pnpm**. Never run `npm install` - use `pnpm install`, `pnpm typecheck`, `pnpm lint`.
27
-
28
- ## 1. Project lifecycle (in order)
29
-
30
- ### 1.1 `cmssy init` - start a new project or bolt onto an existing one
31
-
32
- ```bash
33
- cmssy init <name> # creates ./<name> via create-next-app + cmssy scaffolding
34
- cmssy init -y <name> # skip prompts, use defaults
35
- cmssy init # add cmssy to the current Next.js project
36
- ```
37
-
38
- Outputs: `cmssy.config.js`, `blocks/`, `templates/`, `styles/main.css`, `components/`, example block.
39
-
40
- ### 1.2 `cmssy link` - connect the project to a workspace
41
-
42
- ```bash
43
- cmssy link # interactive: pick workspace, paste token
44
- cmssy link --token cs_xxx --workspace <id|slug> # non-interactive, for CI or scripted setup
45
- cmssy link --api-url https://api.cmssy.io/graphql
46
- ```
47
-
48
- Writes credentials and `CMSSY_WORKSPACE_ID` to `.env`. Tokens come from https://cmssy.io/settings/tokens. List existing workspaces with `cmssy workspaces`.
49
-
50
- ### 1.3 `cmssy workspaces` - show what this machine can publish to
51
-
52
- ```bash
53
- cmssy workspaces
54
- ```
55
-
56
- Returns name, slug, ID, and role for every accessible workspace. Use this when the user is ambiguous about the publish target.
57
-
58
- ### 1.4 `cmssy create` - scaffold blocks or templates
59
-
60
- ```bash
61
- cmssy create block <kebab-name> [-d "desc"] [-c <category>] [-t "tag1,tag2"] [-y]
62
- cmssy create template <kebab-name> [-d "desc"] [-y]
63
- ```
64
-
65
- Categories: `marketing`, `typography`, `media`, `layout`, `forms`, `navigation`, `other`.
66
-
67
- Always use `-y` in automated flows unless the user wants to be prompted.
68
-
69
- ### 1.5 `cmssy dev` - local preview with HMR
70
-
71
- ```bash
72
- cmssy dev # port 3000
73
- cmssy dev -p 8080 # custom port
74
- ```
75
-
76
- Watches `config.ts` and auto-regenerates `block.d.ts`. Use for visual verification after edits.
77
-
78
- ### 1.6 `cmssy test` - vitest runner
79
-
80
- ```bash
81
- cmssy test # all blocks/templates
82
- cmssy test --block hero faq # subset
83
- cmssy test --watch
84
- cmssy test --coverage
85
- ```
86
-
87
- Test files live at `blocks/*/src/**/*.{test,spec}.{ts,tsx}` (same for templates). If no tests exist yet, this is a no-op.
88
-
89
- ### 1.7 `cmssy build` - production bundle
90
-
91
- ```bash
92
- cmssy build # everything
93
- cmssy build --block hero pricing # subset
94
- cmssy build --framework react # override
95
- ```
96
-
97
- Outputs `public/@<vendor>/blocks.<name>/<version>/{index.js,index.css,package.json}`. Run before `cmssy publish-block` if you want to preview the bundle locally - publishing itself bundles again in the sandbox.
98
-
99
- ### 1.8 `cmssy publish-block` - upload to workspace via the sandbox build pipeline
100
-
101
- ```bash
102
- cmssy publish-block <name> # publish to workspace from .env
103
- cmssy publish-block <name> -w <workspaceId> # explicit workspace
104
- cmssy publish-block <name> --dry-run # collect files + print plan, no upload
105
- cmssy publish-block <name> --entry src/main.tsx # override default src/index.tsx entry
106
- ```
107
-
108
- **Flow:** CLI collects the source files and POSTs them base64-encoded via the `publishBlock` GraphQL mutation. The backend packs the bundle into `tar.gz`, uploads it to Vercel Blob, and enqueues an Inngest job that builds in a Vercel Sandbox and writes artifacts back to Blob. CLI polls `publishJobStatus` until status is `completed` or `failed`.
109
-
110
- **Limits:** 200 files / 10 MB total per block. Polling: 1.5 s interval, 10 min cap, gives up after 5 consecutive errors.
111
-
112
- **SSR smoke test (can fail an otherwise-valid publish):** after bundling, the sandbox renders your block with `renderToString` using `defaultContent` derived from `config.ts`. When `defaultContent` is non-empty the test **demands non-empty HTML** - a block that renders nothing on its default content fails with `SSR_FAILURE: renderToString returned empty output despite realistic content`, and no artifacts are stored. The two recurring causes are an early `return null` on an empty repeater (see §4) and a repeater with no top-level `defaultValue` (see §3). Fix the block, bump the version, republish.
113
-
114
- **Content preservation:** republishing preserves the workspace's `defaultContent` overrides by default. There is no `--overwrite-content` flag - that knob lives on `cmssy publish-template` only.
115
-
116
- The legacy `cmssy publish` command + its flags (`--patch`, `--minor`, `--major`, `--zip`, `--with-source`, `--force`, `--all`) were removed in CMS-606. Versioning is handled by the build pipeline; there is no per-publish bump prompt anymore.
117
-
118
- ### 1.9 `cmssy sync` - pull blocks down from a workspace
119
-
120
- ```bash
121
- cmssy sync @cmssy/blocks.hero # one package from the design library
122
- cmssy sync --workspace <id> # everything from a workspace
123
- ```
124
-
125
- Use when cloning a project's block set to a new repo, or when a block has drifted from its published version.
126
-
127
- ### 1.10 `cmssy lib` - manage the workspace npm dependency manifest
128
-
129
- ```bash
130
- cmssy lib install <pkg...> # install locally + push manifest to workspace
131
- cmssy lib install lodash zod@^4
132
- cmssy lib install <pkg> --skip-install # only sync from package.json, no local install
133
- cmssy lib install <pkg> --dry-run # print manifest, don't push
134
- cmssy lib sync # push current package.json deps to the manifest
135
- ```
136
-
137
- When a block imports an npm package, the sandbox build needs it in the workspace's dependency manifest. `lib install` adds the dep locally and registers it so the build pipeline can resolve it. `--package-manager <npm|pnpm|yarn|bun>` forces the PM; both subcommands take `-w` and `--dry-run`.
138
-
139
- ### 1.11 `cmssy skills` - install AI-assistant skills
140
-
141
- ```bash
142
- cmssy skills list # show available skills
143
- cmssy skills install block # this skill (CLI + block dev workflow)
144
- cmssy skills install mcp-content # content editing via @cmssy/mcp-server
145
- cmssy skills install --all # all skills
146
- cmssy skills install block --local # into ./.claude/skills (default: ~/.claude/skills)
147
- cmssy skills install block --force # overwrite existing
148
- ```
149
-
150
- Positional arg is the skill name (`block`, `mcp-content`); use `--target` for the editor (default `claude`).
151
-
152
- ## 2. Block anatomy (non-negotiable)
153
-
154
- Every block lives in `blocks/<kebab-name>/`:
155
-
156
- ```
157
- blocks/<name>/
158
- ├── config.ts # defineBlock({ name, description, category, tags, schema })
159
- ├── package.json # { "name": "@<project>/blocks.<name>", "version": "x.y.z" }
160
- ├── preview.json # sample content for cmssy dev
161
- └── src/
162
- ├── index.tsx # export { default } from "./<Pascal>"; import "./index.css";
163
- ├── <Pascal>.tsx # the React component
164
- ├── index.css # @import "../../../styles/main.css";
165
- └── block.d.ts # AUTO-GENERATED from config.ts - never hand-edit
166
- ```
167
-
168
- **Naming (hard rules):**
169
-
170
- - Directory + package `name` suffix: `kebab-case` (`blog-post-hero`).
171
- - Component file and default export: `PascalCase` matching the directory (`BlogPostHero.tsx`).
172
- - Package name: `@<projectName>/blocks.<kebab-name>` where `projectName` comes from `cmssy.config.js`.
173
- - New blocks start at `"version": "1.0.0"`.
174
-
175
- ## 3. Writing `config.ts`
176
-
177
- Import from `@cmssy/cli/config`. Available field types:
178
-
179
- | type | Use for |
180
- | -------------- | ---------------------------------------------------- |
181
- | `singleLine` | Short text (heading, label, badge). |
182
- | `multiLine` | Paragraph without formatting. |
183
- | `richText` | HTML body copy. |
184
- | `link` | URL. |
185
- | `media` | Image or video (returns URL string). |
186
- | `boolean` | Toggle. |
187
- | `numeric` | Number. |
188
- | `date` | ISO date string. |
189
- | `color` | Hex color picker. |
190
- | `select` | Enum with `options: [{ label, value }]`. |
191
- | `multiselect` | Multiple values from `options` (returns `string[]`). |
192
- | `repeater` | Array of sub-objects with nested `schema`. |
193
- | `form` | Reference to a form-builder form. |
194
- | `pageSelector` | Pick a page (`multiple: true` for several). |
195
-
196
- **Common field options** (all forwarded to the backend on publish): `label` (required), `required`, `placeholder`, `defaultValue`, `helperText` (helper text under the field; `helpText` is a deprecated alias), `group` (groups fields in the editor UI), `showWhen` (conditional visibility - `{ field, equals | notEquals | notEmpty | isEmpty }`), `validation` (`{ minLength, maxLength, min, max, pattern, message }`).
197
-
198
- **Per-type options:** `options: [{ label, value }]` (select/multiselect), `minItems` / `maxItems` / `schema` (repeater), `multiple` (pageSelector). Media's `acceptedTypes` / `accept` / `maxSize` are **not** forwarded by the publish pipeline - don't rely on them.
199
-
200
- ```ts
201
- import { defineBlock, field } from "@cmssy/cli/config";
202
-
203
- export default defineBlock({
204
- name: "Feature Grid",
205
- description: "Grid of feature cards with icon, title, description",
206
- category: "marketing",
207
- tags: ["marketing", "features", "grid"],
208
-
209
- schema: {
210
- heading: field({ type: "singleLine", label: "Heading", required: true }),
211
- headingHighlight: field({ type: "singleLine", label: "Highlight" }),
212
- description: field({ type: "multiLine", label: "Description" }),
213
- features: field({
214
- type: "repeater",
215
- label: "Features",
216
- schema: {
217
- title: field({ type: "singleLine", label: "Title", required: true }),
218
- description: field({ type: "multiLine", label: "Description" }),
219
- icon: field({
220
- type: "select",
221
- label: "Icon",
222
- options: [
223
- { label: "Zap", value: "ZapIcon" },
224
- { label: "Sparkles", value: "SparklesIcon" },
225
- ],
226
- }),
227
- color: field({ type: "color", label: "Accent color" }),
228
- },
229
- }),
230
- },
231
- });
232
- ```
233
-
234
- **Repeaters and the SSR smoke test (critical):** `defaultContent` is built by `extractDefaultContent`, which only reads a field's **top-level** `defaultValue`. For a `repeater` it does **not** synthesize a sample row from the nested fields' `defaultValue`s - a repeater without its own top-level `defaultValue` becomes an empty array `[]`. If your component renders nothing when that array is empty (e.g. `if (items.length === 0) return null`), publish fails the SSR smoke test. Seed the repeater with a top-level `defaultValue` holding at least one realistic row:
235
-
236
- ```ts
237
- items: field({
238
- type: "repeater",
239
- label: "Items",
240
- defaultValue: [{ title: "Example", description: "Sample row" }],
241
- schema: {
242
- title: field({ type: "singleLine", label: "Title", required: true }),
243
- description: field({ type: "multiLine", label: "Description" }),
244
- },
245
- }),
246
- ```
247
-
248
- After editing `config.ts`, `block.d.ts` regenerates on the next `cmssy dev` or `cmssy build`. Never hand-edit it.
249
-
250
- ### Block-level config keys (`defineBlock`)
251
-
252
- Beyond `name`, `description`, `category`, `tags`, `schema`, `defineBlock` accepts:
253
-
254
- | key | Type | Purpose |
255
- | ----------------- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
256
- | `longDescription` | `string` | Long copy for the design-library listing. |
257
- | `layoutPosition` | `"header" \| "footer" \| "sidebar_left" \| "sidebar_right" \| "top" \| "bottom"` | Makes this a **layout block** rendered into that slot instead of the content area. |
258
- | `useClient` | `boolean` (default `false`) | `false` = server-rendered HTML only (no JS shipped). `true` = hydrate on the client (hooks, browser APIs, **and client animations like framer-motion `whileInView` - which render `opacity:0` invisible without it**). |
259
- | `requires` | `BlockRequires` | Capability metadata: `{ auth?, language?, workspace?, modules?, permissions?, features? }`. |
260
-
261
- `requires` modules: `pim`, `crm`, `forms`, `analytics`, `newsletter`, `ecommerce`. `requires` features: `ai-generation`, `ai-translation`, `a-b-testing`. **`requires` is metadata** (editor/library filtering + auth-aware rendering hints) - it does **not** gate the runtime `context`, which is always populated. Always guard for missing data in the component regardless of what you declare.
262
-
263
- (`groups` appears in some legacy block configs but is **not** forwarded by the publish pipeline - use the per-field `group` string for editor grouping instead.)
264
-
265
- ### Layout blocks (header / footer / sidebar)
266
-
267
- A layout block is an ordinary block with a `layoutPosition`. It renders into a template slot via `LayoutPositionRenderer` and is excluded from the content-block drawer. Header/footer/nav blocks are almost always interactive, so set `useClient: true` and declare what they read:
268
-
269
- ```ts
270
- export default defineBlock({
271
- name: "Header Navigation",
272
- description: "Responsive header with logo, nav, and CTA",
273
- category: "layout",
274
- layoutPosition: "header",
275
- useClient: true,
276
- requires: { auth: true, language: true },
277
- schema: {
278
- logo: field({ type: "media", label: "Logo" }),
279
- links: field({
280
- type: "repeater",
281
- label: "Nav links",
282
- defaultValue: [{ label: "Home", href: "/" }],
283
- schema: {
284
- label: field({ type: "singleLine", label: "Label", required: true }),
285
- href: field({ type: "link", label: "URL", required: true }),
286
- },
287
- }),
288
- },
289
- });
290
- ```
291
-
292
- The same SSR-smoke and repeater-seeding rules apply (§3 above, §1.8). Publish with `cmssy publish-block <name>` like any other block.
293
-
294
- ## 4. Writing the component
295
-
296
- Follow the design system in the repo's `DESIGN.md`:
297
-
298
- - Import `Container` from `../../../components/container`.
299
- - Section wrapper: `<section className="py-24">` or `"py-24 bg-slate-50/50 dark:bg-slate-900/50"` for alternating tint.
300
- - Destructure `content` with defaults: `const { heading = "Default" } = content;`.
301
- - Gradient-clip highlight phrases: `bg-linear-to-r from-violet-600 to-purple-600 bg-clip-text text-transparent`.
302
- - Primary CTAs: gradient pill button with `shadow-lg shadow-violet-500/25`. Secondary: outline with `border-input`.
303
- - Icons: `lucide-react`, default stroke width.
304
- - Rich text: `dangerouslySetInnerHTML` inside a `prose` wrapper.
305
-
306
- ```tsx
307
- import { Container } from "../../../components/container";
308
- import { BlockContent } from "./block";
309
-
310
- export default function FeatureGrid({ content }: { content: BlockContent }) {
311
- const { heading = "", features = [] } = content;
312
- return (
313
- <section className="py-24">
314
- <Container>{/* ... */}</Container>
315
- </section>
316
- );
317
- }
318
- ```
319
-
320
- Blocks that read platform data accept a second `context` prop. The SSR render path populates it in full (and `requires` does **not** gate which fields you get), but type it **optional** (`context?: PlatformContext`) - that is the convention across all blocks, since non-SSR paths (editor CSR preview, fallbacks) may not pass it. Always guard every field defensively:
321
-
322
- ```tsx
323
- export default function Header({ content, context }: {
324
- content: BlockContent;
325
- context?: PlatformContext;
326
- }) { ... }
327
- ```
328
-
329
- `PlatformContext` top-level keys: `locale` (always present - `{ current, default, enabled }`), `auth`, `workspace`, `site`, `branding`, `primaryDomain`, `pages`, `media`, `members`, `forms`, `formDefinitions`, `models`, `cart` / `canUseCart`, `isPreview`, `isDraftMode`, and `graphql(query, variables?)` for public queries. Collection sources (`pages`, `media`, `forms`, ...) are injected from the block's `dataDeclarations` and auto-detected references (e.g. a `formId` in content), so they may be empty if not declared.
330
-
331
- **Two different "client" knobs - don't confuse them:**
332
-
333
- - `useClient: true` in **`config.ts`** opts the block into client hydration. Without it the block is server-rendered only and ships no JS, so `useState`/`onClick`/effects won't run in production no matter what the component file says.
334
- - `"use client";` at the top of the **component file** is the Next-style compile pragma for files that use hooks or browser APIs.
335
-
336
- An interactive block needs **both**: `useClient: true` in config and `"use client";` in the component.
337
-
338
- **Entrance/scroll animations gate VISIBILITY, not just interactivity - they REQUIRE `useClient: true`.** If your block (or any component it imports, e.g. a shared `Reveal`) uses framer-motion `whileInView` / `initial={{ opacity: 0 }}` / IntersectionObserver to fade content in, the server-rendered HTML carries `opacity:0`. With `useClient: false` there is no hydration, so the JS that animates opacity to 1 never runs and **the content is permanently invisible in production** - not merely non-interactive. This is a trap because the **editor canvas renders blocks pure-CSR**, so the animation always plays there and the block looks fine while editing; the bug only appears on the public/preview SSR path (symptom: "structure and background show but the text is missing"). Any block with reveal-on-scroll/entrance animation must set `useClient: true` and be republished. Don't "fix" it by rewriting the animation to CSS - the platform hydrates client blocks on purpose; `useClient: true` is the intended path.
339
-
340
- **Empty-state guards vs the SSR smoke test:** an early `return null` (or rendering nothing) when a repeater is empty is a legitimate pattern, but it makes the block fail the publish-time SSR smoke test unless that repeater is seeded with a top-level `defaultValue` (see §3). Either seed the repeater, or render a non-empty wrapper (heading/section) even with zero rows.
341
-
342
- ## 5. `preview.json`
343
-
344
- Sample content for `cmssy dev`. Keys must match `config.ts` field names. Repeater fields take arrays of objects. Make it realistic - preview is what reviewers see.
345
-
346
- ## 6. Templates
347
-
348
- ```
349
- templates/<name>/
350
- ├── config.ts # defineTemplate({ name, description, category, tags, pages })
351
- ├── package.json
352
- ├── pages.json # layoutPositions (header/footer) + block references
353
- ├── preview.json
354
- └── src/ # optional root component
355
- ```
356
-
357
- ```ts
358
- import { defineTemplate, field } from "@cmssy/cli/config";
359
-
360
- export default defineTemplate({
361
- name: "Marketing Site",
362
- description: "...",
363
- category: "website",
364
- tags: [...],
365
- pages: [
366
- {
367
- name: "Home",
368
- slug: "/",
369
- blocks: [
370
- { type: "hero", content: { heading: "...", ... } },
371
- { type: "features", content: { ... } },
372
- ],
373
- },
374
- ],
375
- });
376
- ```
377
-
378
- Publish templates via the dedicated command: `cmssy publish-template <template-name>`. Templates are declarative (no sandbox build) - the CLI accepts `--patch` / `--minor` / `--major` / `--no-bump` and `--overwrite-content` here.
379
-
380
- ## 7. Standard workflows
381
-
382
- ### Brand-new project
383
-
384
- ```bash
385
- cmssy init my-blocks -y
386
- cd my-blocks
387
- cmssy link # interactive - pick workspace
388
- cmssy doctor # confirm green
389
- cmssy dev
390
- ```
391
-
392
- ### Add a new block end-to-end
393
-
394
- ```bash
395
- cmssy create block testimonials -c marketing -t "marketing,social-proof" -y
396
- # edit blocks/testimonials/config.ts, Testimonials.tsx, preview.json
397
- cmssy dev # visual check
398
- pnpm typecheck && pnpm lint
399
- cmssy test --block testimonials # if tests exist
400
- cmssy publish-block testimonials --dry-run
401
- cmssy publish-block testimonials
402
- ```
403
-
404
- ### Add a layout block (header / footer / sidebar)
405
-
406
- ```bash
407
- cmssy create block site-header -c layout -y
408
- # in config.ts add: layoutPosition: "header", useClient: true, requires: { auth, language }
409
- # seed any repeater (nav links) with a top-level defaultValue
410
- cmssy dev
411
- pnpm typecheck && pnpm lint
412
- cmssy publish-block site-header
413
- ```
414
-
415
- `cmssy create` does not set `layoutPosition` - add it (and `useClient`) to `config.ts` by hand. See §3 "Layout blocks".
416
-
417
- ### Extend an existing block with an optional field
418
-
419
- 1. Add `field({...})` to `config.ts`. Don't set `required: true` - that's breaking.
420
- 2. Update the component to read and render it with a default.
421
- 3. Update `preview.json`.
422
- 4. `cmssy publish-block <block>`.
423
-
424
- ### Copy-only fix
425
-
426
- 1. Edit default strings or JSX in the component.
427
- 2. `cmssy publish-block <block>`.
428
-
429
- ### Pull a block from the design library
430
-
431
- ```bash
432
- cmssy sync @cmssy/blocks.hero
433
- ```
434
-
435
- ## 8. Safety rules (always)
436
-
437
- - Run `cmssy doctor` before publishing.
438
- - Never fabricate a workspace ID. Use `cmssy workspaces` and confirm with the user.
439
- - Never pass `cmssy publish-template --overwrite-content` without explicit user approval (wipes editor content on the affected pages).
440
- - Use `--dry-run` first when unsure what will change.
441
- - Confirm with the user before publishing from a branch other than `develop` or `main`.
442
- - Commit local changes before publishing - publish uploads the working tree.
443
- - For init and link, never write a token or workspace ID into `.env` without user confirmation.
444
-
445
- ## 9. Troubleshooting
446
-
447
- | Symptom | Likely cause | Fix |
448
- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
449
- | `doctor` says token invalid | Revoked/expired token | `cmssy link` again with a fresh token from https://cmssy.io/settings/tokens |
450
- | Publish says "workspace not accessible" | Wrong `CMSSY_WORKSPACE_ID` or no role | `cmssy workspaces`, update `.env` |
451
- | Sandbox build fails with bundling error | Missing import or > 10 MB source tree | Inspect the polled `publishJobStatus` log; trim deps or fix the import path |
452
- | `block.d.ts` shows wrong fields | Stale generation | Restart `cmssy dev`, or run `cmssy build` to regenerate |
453
- | Dev server shows a blank block | `preview.json` missing keys | Match keys to `config.ts` field names |
454
- | Publish fails `SSR_FAILURE` / "empty output despite realistic content" | Block renders nothing on its `defaultContent` - usually `return null` on an empty repeater that has no top-level `defaultValue` | Seed the repeater's top-level `defaultValue` with ≥1 row (§3), or render a non-empty wrapper (§4); bump version; republish |