@cmssy/cli 0.12.1 → 0.14.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.
- package/README.md +212 -50
- package/dist/cli.js +32 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/codegen.d.ts.map +1 -1
- package/dist/commands/codegen.js +29 -12
- package/dist/commands/codegen.js.map +1 -1
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +178 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/skills/claude/cmssy-block.md +345 -0
- package/dist/skills/claude/cmssy-mcp-content.md +231 -0
- package/dist/test-helpers/index.d.ts +23 -0
- package/dist/test-helpers/index.d.ts.map +1 -0
- package/dist/test-helpers/index.js +50 -0
- package/dist/test-helpers/index.js.map +1 -0
- package/dist/utils/dev-generator/helpers.d.ts.map +1 -1
- package/dist/utils/dev-generator/helpers.js +28 -12
- package/dist/utils/dev-generator/helpers.js.map +1 -1
- package/dist/utils/dev-generator/layout.d.ts.map +1 -1
- package/dist/utils/dev-generator/layout.js +6 -2
- package/dist/utils/dev-generator/layout.js.map +1 -1
- package/dist/utils/dev-generator/next-config.d.ts.map +1 -1
- package/dist/utils/dev-generator/next-config.js +11 -4
- package/dist/utils/dev-generator/next-config.js.map +1 -1
- package/package.json +5 -2
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import fs from "fs-extra";
|
|
3
|
+
import inquirer from "inquirer";
|
|
4
|
+
import os from "os";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
8
|
+
const __dirname = path.dirname(__filename);
|
|
9
|
+
const SKILLS_ROOT = path.resolve(__dirname, "../skills");
|
|
10
|
+
const EDITOR_TARGETS = ["claude"];
|
|
11
|
+
const SKILLS = {
|
|
12
|
+
block: {
|
|
13
|
+
name: "block",
|
|
14
|
+
label: "Block dev (CLI + defineBlock/defineTemplate workflow)",
|
|
15
|
+
sourceBasename: "cmssy-block.md",
|
|
16
|
+
destDir: "cmssy-block",
|
|
17
|
+
examplePrompt: "scaffold a pricing block and publish as patch",
|
|
18
|
+
},
|
|
19
|
+
"mcp-content": {
|
|
20
|
+
name: "mcp-content",
|
|
21
|
+
label: "Content editing via @cmssy/mcp-server",
|
|
22
|
+
sourceBasename: "cmssy-mcp-content.md",
|
|
23
|
+
destDir: "cmssy-mcp-content",
|
|
24
|
+
examplePrompt: "add a new testimonials block to the homepage, in English and Polish",
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
function resolveEditorTarget(raw) {
|
|
28
|
+
const target = (raw ?? "claude").trim().toLowerCase();
|
|
29
|
+
if (!EDITOR_TARGETS.includes(target)) {
|
|
30
|
+
// JSON.stringify so whitespace-only / empty raw input is visible in
|
|
31
|
+
// the error (otherwise interpolating `" "` gives a blank message).
|
|
32
|
+
console.error(chalk.red(`✖ Unknown editor target: raw=${JSON.stringify(raw)}, normalized=${JSON.stringify(target)}`) + chalk.gray(`\n Supported: ${EDITOR_TARGETS.join(", ")}`));
|
|
33
|
+
process.exit(1);
|
|
34
|
+
}
|
|
35
|
+
return target;
|
|
36
|
+
}
|
|
37
|
+
function resolveSourcePath(editor, skill) {
|
|
38
|
+
return path.join(SKILLS_ROOT, editor, skill.sourceBasename);
|
|
39
|
+
}
|
|
40
|
+
function resolveInstallDir(editor, skill, opts) {
|
|
41
|
+
// Editor-specific layout. Today only `claude`, whose layout is:
|
|
42
|
+
// ~/.claude/skills/<destDir>/SKILL.md (global)
|
|
43
|
+
// <cwd>/.claude/skills/<destDir>/SKILL.md (local)
|
|
44
|
+
if (editor === "claude") {
|
|
45
|
+
const root = opts.local ? opts.cwd : os.homedir();
|
|
46
|
+
return path.join(root, ".claude", "skills", skill.destDir);
|
|
47
|
+
}
|
|
48
|
+
// Fallback (unreachable today, keeps TS happy for future targets)
|
|
49
|
+
throw new Error(`Unsupported editor target: ${editor}`);
|
|
50
|
+
}
|
|
51
|
+
function postInstallHint(editor, skill, installPath) {
|
|
52
|
+
if (editor === "claude") {
|
|
53
|
+
return [
|
|
54
|
+
chalk.gray("Next step:"),
|
|
55
|
+
chalk.white(" Restart Claude Code (or open a new session) so it picks up the skill."),
|
|
56
|
+
chalk.gray(" Then try something like:"),
|
|
57
|
+
chalk.white(` "${skill.examplePrompt}"`),
|
|
58
|
+
"",
|
|
59
|
+
chalk.gray(`Installed at: ${installPath}`),
|
|
60
|
+
].join("\n");
|
|
61
|
+
}
|
|
62
|
+
return chalk.gray(`Installed at: ${installPath}`);
|
|
63
|
+
}
|
|
64
|
+
async function installOne(skill, editor, options) {
|
|
65
|
+
const sourcePath = resolveSourcePath(editor, skill);
|
|
66
|
+
if (!(await fs.pathExists(sourcePath))) {
|
|
67
|
+
console.error(chalk.red(`✖ Skill source missing: ${sourcePath}`) +
|
|
68
|
+
chalk.gray("\n This is a packaging bug - please report it at https://github.com/cmssy-io/cmssy-cli/issues"));
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
const installDir = resolveInstallDir(editor, skill, {
|
|
72
|
+
local: !!options.local,
|
|
73
|
+
cwd: process.cwd(),
|
|
74
|
+
});
|
|
75
|
+
const installPath = path.join(installDir, "SKILL.md");
|
|
76
|
+
const exists = await fs.pathExists(installPath);
|
|
77
|
+
if (exists && !options.force) {
|
|
78
|
+
if (options.yes) {
|
|
79
|
+
console.error(chalk.red(`✖ ${installPath} already exists`) +
|
|
80
|
+
chalk.gray("\n Pass --force to overwrite."));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
const { overwrite } = await inquirer.prompt([
|
|
84
|
+
{
|
|
85
|
+
type: "confirm",
|
|
86
|
+
name: "overwrite",
|
|
87
|
+
message: `${installPath} already exists. Overwrite?`,
|
|
88
|
+
default: false,
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
if (!overwrite) {
|
|
92
|
+
console.log(chalk.yellow(`⚠ Skipped ${skill.name} - ${installPath} unchanged.`));
|
|
93
|
+
return "skipped";
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
await fs.ensureDir(installDir);
|
|
97
|
+
await fs.copy(sourcePath, installPath, { overwrite: true });
|
|
98
|
+
const scope = options.local ? "local (.claude/)" : "global (~/.claude/)";
|
|
99
|
+
console.log(chalk.green(`✔ Installed ${skill.label} skill`) + chalk.gray(` → ${scope}`));
|
|
100
|
+
console.log();
|
|
101
|
+
console.log(postInstallHint(editor, skill, installPath));
|
|
102
|
+
console.log();
|
|
103
|
+
return "installed";
|
|
104
|
+
}
|
|
105
|
+
export async function skillsInstallCommand(rawSkill, options) {
|
|
106
|
+
const editor = resolveEditorTarget(options.target);
|
|
107
|
+
// Normalize once - whitespace-only or empty input is treated as "no skill"
|
|
108
|
+
// so `cmssy skills install " "` behaves like `cmssy skills install`.
|
|
109
|
+
const skill = rawSkill?.trim().toLowerCase() || undefined;
|
|
110
|
+
// Friendly error if user passes an editor name where a skill name is expected
|
|
111
|
+
// (covers the pre-0.14.0 `cmssy skills install claude` shape).
|
|
112
|
+
if (skill && EDITOR_TARGETS.includes(skill)) {
|
|
113
|
+
console.error(chalk.red(`✖ "${rawSkill}" is an editor target, not a skill name.`) +
|
|
114
|
+
chalk.gray(`\n The first positional argument is now the skill name (e.g. "block", "mcp-content").`) +
|
|
115
|
+
chalk.gray(`\n Pass editor with --target, e.g.: cmssy skills install block --target ${skill}`));
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
if (options.all) {
|
|
119
|
+
if (skill) {
|
|
120
|
+
console.error(chalk.red(`✖ Cannot combine --all with a skill name.`) +
|
|
121
|
+
chalk.gray("\n Either pass a skill name or use --all, not both."));
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
for (const entry of Object.values(SKILLS)) {
|
|
125
|
+
await installOne(entry, editor, options);
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
let skillName;
|
|
130
|
+
if (skill) {
|
|
131
|
+
if (!Object.hasOwn(SKILLS, skill)) {
|
|
132
|
+
console.error(chalk.red(`✖ Unknown skill: ${rawSkill}`) +
|
|
133
|
+
chalk.gray(`\n Available skills: ${Object.keys(SKILLS).join(", ")}`) +
|
|
134
|
+
chalk.gray(`\n Run 'cmssy skills list' to see all skills.`));
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
skillName = skill;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
// No skill passed (or whitespace-only) → interactive prompt (unless -y)
|
|
141
|
+
if (options.yes) {
|
|
142
|
+
console.error(chalk.red(`✖ No skill specified.`) +
|
|
143
|
+
chalk.gray(`\n Pass a skill name (e.g. 'block', 'mcp-content') or use --all.`) +
|
|
144
|
+
chalk.gray(`\n Run 'cmssy skills list' to see all skills.`));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
const { chosen } = await inquirer.prompt([
|
|
148
|
+
{
|
|
149
|
+
type: "list",
|
|
150
|
+
name: "chosen",
|
|
151
|
+
message: "Which skill to install?",
|
|
152
|
+
choices: Object.values(SKILLS).map((s) => ({
|
|
153
|
+
name: `${s.name} - ${s.label}`,
|
|
154
|
+
value: s.name,
|
|
155
|
+
})),
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
skillName = chosen;
|
|
159
|
+
}
|
|
160
|
+
await installOne(SKILLS[skillName], editor, options);
|
|
161
|
+
}
|
|
162
|
+
export function skillsListCommand() {
|
|
163
|
+
console.log();
|
|
164
|
+
console.log(chalk.bold("Available skills:"));
|
|
165
|
+
console.log();
|
|
166
|
+
const padName = Math.max(...Object.values(SKILLS).map((s) => s.name.length));
|
|
167
|
+
for (const skill of Object.values(SKILLS)) {
|
|
168
|
+
console.log(` ${chalk.cyan(skill.name.padEnd(padName))} ${skill.label}`);
|
|
169
|
+
}
|
|
170
|
+
console.log();
|
|
171
|
+
console.log(chalk.gray(`Supported editor targets: ${EDITOR_TARGETS.join(", ")}`));
|
|
172
|
+
console.log();
|
|
173
|
+
console.log(chalk.gray("Install with:"));
|
|
174
|
+
console.log(chalk.white(" cmssy skills install <skill>"));
|
|
175
|
+
console.log(chalk.white(" cmssy skills install --all"));
|
|
176
|
+
console.log();
|
|
177
|
+
}
|
|
178
|
+
//# sourceMappingURL=skills.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,345 @@
|
|
|
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`, `create`, `dev`, `test`, `build`, `publish`, `sync`, `doctor`, `workspaces`.
|
|
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
|
+
- Node >= 18, npm, Next.js, React versions
|
|
19
|
+
- `cmssy.config.js` exists
|
|
20
|
+
- `.env` with `CMSSY_API_URL`, `CMSSY_API_TOKEN`, `CMSSY_WORKSPACE_ID`
|
|
21
|
+
- API reachable, token valid, workspace accessible
|
|
22
|
+
|
|
23
|
+
Run it before any non-trivial operation. If something fails, fix that first - don't proceed.
|
|
24
|
+
|
|
25
|
+
The project uses **pnpm**. Never run `npm install` - use `pnpm install`, `pnpm typecheck`, `pnpm lint`.
|
|
26
|
+
|
|
27
|
+
## 1. Project lifecycle (in order)
|
|
28
|
+
|
|
29
|
+
### 1.1 `cmssy init` - start a new project or bolt onto an existing one
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
cmssy init <name> # creates ./<name> via create-next-app + cmssy scaffolding
|
|
33
|
+
cmssy init -y <name> # skip prompts, use defaults
|
|
34
|
+
cmssy init # add cmssy to the current Next.js project
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Outputs: `cmssy.config.js`, `blocks/`, `templates/`, `styles/main.css`, `components/`, example block.
|
|
38
|
+
|
|
39
|
+
### 1.2 `cmssy link` - connect the project to a workspace
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
cmssy link # interactive: pick workspace, paste token
|
|
43
|
+
cmssy link --token cs_xxx --workspace <id|slug> # non-interactive, for CI or scripted setup
|
|
44
|
+
cmssy link --api-url https://api.cmssy.io/graphql
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Writes credentials and `CMSSY_WORKSPACE_ID` to `.env`. Tokens come from https://cmssy.io/settings/tokens. List existing workspaces with `cmssy workspaces`.
|
|
48
|
+
|
|
49
|
+
### 1.3 `cmssy workspaces` - show what this machine can publish to
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
cmssy workspaces
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Returns name, slug, ID, and role for every accessible workspace. Use this when the user is ambiguous about the publish target.
|
|
56
|
+
|
|
57
|
+
### 1.4 `cmssy create` - scaffold blocks or templates
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
cmssy create block <kebab-name> [-d "desc"] [-c <category>] [-t "tag1,tag2"] [-y]
|
|
61
|
+
cmssy create template <kebab-name> [-d "desc"] [-y]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Categories: `marketing`, `typography`, `media`, `layout`, `forms`, `navigation`, `other`.
|
|
65
|
+
|
|
66
|
+
Always use `-y` in automated flows unless the user wants to be prompted.
|
|
67
|
+
|
|
68
|
+
### 1.5 `cmssy dev` - local preview with HMR
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cmssy dev # port 3000
|
|
72
|
+
cmssy dev -p 8080 # custom port
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Watches `config.ts` and auto-regenerates `block.d.ts`. Use for visual verification after edits.
|
|
76
|
+
|
|
77
|
+
### 1.6 `cmssy test` - vitest runner
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
cmssy test # all blocks/templates
|
|
81
|
+
cmssy test --block hero faq # subset
|
|
82
|
+
cmssy test --watch
|
|
83
|
+
cmssy test --coverage
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Test files live at `blocks/*/src/**/*.{test,spec}.{ts,tsx}` (same for templates). If no tests exist yet, this is a no-op.
|
|
87
|
+
|
|
88
|
+
### 1.7 `cmssy build` - production bundle
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
cmssy build # everything
|
|
92
|
+
cmssy build --block hero pricing # subset
|
|
93
|
+
cmssy build --framework react # override
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Outputs `public/@<vendor>/blocks.<name>/<version>/{index.js,index.css,package.json}`. Run before publish if you want to preview the bundle.
|
|
97
|
+
|
|
98
|
+
### 1.8 `cmssy publish` - upload to workspace
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
cmssy publish <name> --patch # single, patch bump
|
|
102
|
+
cmssy publish --all --patch # everything
|
|
103
|
+
cmssy publish <name> -w <workspaceId> --patch # explicit workspace (otherwise .env)
|
|
104
|
+
cmssy publish <name> --patch --dry-run # preview only, upload nothing
|
|
105
|
+
cmssy publish <name> --patch --with-source # include source for AI Block Builder
|
|
106
|
+
cmssy publish <name> --patch --zip # package and upload ZIP instead of GraphQL
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Exactly one bump flag:** `--patch` | `--minor` | `--major` | `--no-bump`.
|
|
110
|
+
|
|
111
|
+
**Bump semantics:**
|
|
112
|
+
- `--patch` - bug fix, copy tweak, style-only change. No schema change.
|
|
113
|
+
- `--minor` - added optional fields, new variants. Non-breaking schema additions.
|
|
114
|
+
- `--major` - renamed/removed fields, changed field types, breaking component contract.
|
|
115
|
+
|
|
116
|
+
**Content preservation:** republishing preserves `defaultContent` and `schemaFields` on the workspace by default. Pass `--overwrite-content` only when the user explicitly wants to reset them from `config.ts` / `preview.json`.
|
|
117
|
+
|
|
118
|
+
**Breaking changes** trigger a confirmation prompt; pass `--force` only with explicit user approval.
|
|
119
|
+
|
|
120
|
+
### 1.9 `cmssy sync` - pull blocks down from a workspace
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
cmssy sync @cmssy/blocks.hero # one package from the design library
|
|
124
|
+
cmssy sync --workspace <id> # everything from a workspace
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use when cloning a project's block set to a new repo, or when a block has drifted from its published version.
|
|
128
|
+
|
|
129
|
+
## 2. Block anatomy (non-negotiable)
|
|
130
|
+
|
|
131
|
+
Every block lives in `blocks/<kebab-name>/`:
|
|
132
|
+
|
|
133
|
+
```
|
|
134
|
+
blocks/<name>/
|
|
135
|
+
├── config.ts # defineBlock({ name, description, category, tags, schema })
|
|
136
|
+
├── package.json # { "name": "@<project>/blocks.<name>", "version": "x.y.z" }
|
|
137
|
+
├── preview.json # sample content for cmssy dev
|
|
138
|
+
└── src/
|
|
139
|
+
├── index.tsx # export { default } from "./<Pascal>"; import "./index.css";
|
|
140
|
+
├── <Pascal>.tsx # the React component
|
|
141
|
+
├── index.css # @import "../../../styles/main.css";
|
|
142
|
+
└── block.d.ts # AUTO-GENERATED from config.ts - never hand-edit
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Naming (hard rules):**
|
|
146
|
+
- Directory + package `name` suffix: `kebab-case` (`blog-post-hero`).
|
|
147
|
+
- Component file and default export: `PascalCase` matching the directory (`BlogPostHero.tsx`).
|
|
148
|
+
- Package name: `@<projectName>/blocks.<kebab-name>` where `projectName` comes from `cmssy.config.js`.
|
|
149
|
+
- New blocks start at `"version": "1.0.0"`.
|
|
150
|
+
|
|
151
|
+
## 3. Writing `config.ts`
|
|
152
|
+
|
|
153
|
+
Import from `@cmssy/cli/config`. Available field types:
|
|
154
|
+
|
|
155
|
+
| type | Use for |
|
|
156
|
+
| --- | --- |
|
|
157
|
+
| `singleLine` | Short text (heading, label, badge). |
|
|
158
|
+
| `multiLine` | Paragraph without formatting. |
|
|
159
|
+
| `richText` | HTML body copy. |
|
|
160
|
+
| `link` | URL. |
|
|
161
|
+
| `media` | Image or video (returns URL string). |
|
|
162
|
+
| `boolean` | Toggle. |
|
|
163
|
+
| `numeric` | Number. |
|
|
164
|
+
| `date` | ISO date string. |
|
|
165
|
+
| `color` | Hex color picker. |
|
|
166
|
+
| `select` | Enum with `options: [{ label, value }]`. |
|
|
167
|
+
| `repeater` | Array of sub-objects with nested `schema`. |
|
|
168
|
+
| `form` | Reference to a form-builder form. |
|
|
169
|
+
| `pageSelector` | Pick a page from the workspace. |
|
|
170
|
+
|
|
171
|
+
**Field options:** `label` (required), `defaultValue`, `placeholder`, `required`, `group` (groups fields in the editor UI).
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { defineBlock, field } from "@cmssy/cli/config";
|
|
175
|
+
|
|
176
|
+
export default defineBlock({
|
|
177
|
+
name: "Feature Grid",
|
|
178
|
+
description: "Grid of feature cards with icon, title, description",
|
|
179
|
+
category: "marketing",
|
|
180
|
+
tags: ["marketing", "features", "grid"],
|
|
181
|
+
|
|
182
|
+
schema: {
|
|
183
|
+
heading: field({ type: "singleLine", label: "Heading", required: true }),
|
|
184
|
+
headingHighlight: field({ type: "singleLine", label: "Highlight" }),
|
|
185
|
+
description: field({ type: "multiLine", label: "Description" }),
|
|
186
|
+
features: field({
|
|
187
|
+
type: "repeater",
|
|
188
|
+
label: "Features",
|
|
189
|
+
schema: {
|
|
190
|
+
title: field({ type: "singleLine", label: "Title", required: true }),
|
|
191
|
+
description: field({ type: "multiLine", label: "Description" }),
|
|
192
|
+
icon: field({
|
|
193
|
+
type: "select",
|
|
194
|
+
label: "Icon",
|
|
195
|
+
options: [
|
|
196
|
+
{ label: "Zap", value: "ZapIcon" },
|
|
197
|
+
{ label: "Sparkles", value: "SparklesIcon" },
|
|
198
|
+
],
|
|
199
|
+
}),
|
|
200
|
+
color: field({ type: "color", label: "Accent color" }),
|
|
201
|
+
},
|
|
202
|
+
}),
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
After editing `config.ts`, `block.d.ts` regenerates on the next `cmssy dev` or `cmssy build`. Never hand-edit it.
|
|
208
|
+
|
|
209
|
+
## 4. Writing the component
|
|
210
|
+
|
|
211
|
+
Follow the design system in the repo's `DESIGN.md`:
|
|
212
|
+
|
|
213
|
+
- Import `Container` from `../../../components/container`.
|
|
214
|
+
- Section wrapper: `<section className="py-24">` or `"py-24 bg-slate-50/50 dark:bg-slate-900/50"` for alternating tint.
|
|
215
|
+
- Destructure `content` with defaults: `const { heading = "Default" } = content;`.
|
|
216
|
+
- Gradient-clip highlight phrases: `bg-linear-to-r from-violet-600 to-purple-600 bg-clip-text text-transparent`.
|
|
217
|
+
- Primary CTAs: gradient pill button with `shadow-lg shadow-violet-500/25`. Secondary: outline with `border-input`.
|
|
218
|
+
- Icons: `lucide-react`, default stroke width.
|
|
219
|
+
- Rich text: `dangerouslySetInnerHTML` inside a `prose` wrapper.
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
import { Container } from "../../../components/container";
|
|
223
|
+
import { BlockContent } from "./block";
|
|
224
|
+
|
|
225
|
+
export default function FeatureGrid({ content }: { content: BlockContent }) {
|
|
226
|
+
const { heading = "", features = [] } = content;
|
|
227
|
+
return (
|
|
228
|
+
<section className="py-24">
|
|
229
|
+
<Container>{/* ... */}</Container>
|
|
230
|
+
</section>
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Blocks that read platform data (auth, i18n) accept a second prop:
|
|
236
|
+
|
|
237
|
+
```tsx
|
|
238
|
+
export default function Header({ content, context }: {
|
|
239
|
+
content: BlockContent;
|
|
240
|
+
context?: PlatformContext;
|
|
241
|
+
}) { ... }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Components that use hooks or browser APIs: `"use client";` at the top.
|
|
245
|
+
|
|
246
|
+
## 5. `preview.json`
|
|
247
|
+
|
|
248
|
+
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.
|
|
249
|
+
|
|
250
|
+
## 6. Templates
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
templates/<name>/
|
|
254
|
+
├── config.ts # defineTemplate({ name, description, category, tags, pages })
|
|
255
|
+
├── package.json
|
|
256
|
+
├── pages.json # layoutPositions (header/footer) + block references
|
|
257
|
+
├── preview.json
|
|
258
|
+
└── src/ # optional root component
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
```ts
|
|
262
|
+
import { defineTemplate, field } from "@cmssy/cli/config";
|
|
263
|
+
|
|
264
|
+
export default defineTemplate({
|
|
265
|
+
name: "Marketing Site",
|
|
266
|
+
description: "...",
|
|
267
|
+
category: "website",
|
|
268
|
+
tags: [...],
|
|
269
|
+
pages: [
|
|
270
|
+
{
|
|
271
|
+
name: "Home",
|
|
272
|
+
slug: "/",
|
|
273
|
+
blocks: [
|
|
274
|
+
{ type: "hero", content: { heading: "...", ... } },
|
|
275
|
+
{ type: "features", content: { ... } },
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
],
|
|
279
|
+
});
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Publish templates the same way as blocks: `cmssy publish <template-name> --patch`.
|
|
283
|
+
|
|
284
|
+
## 7. Standard workflows
|
|
285
|
+
|
|
286
|
+
### Brand-new project
|
|
287
|
+
|
|
288
|
+
```bash
|
|
289
|
+
cmssy init my-blocks -y
|
|
290
|
+
cd my-blocks
|
|
291
|
+
cmssy link # interactive - pick workspace
|
|
292
|
+
cmssy doctor # confirm green
|
|
293
|
+
cmssy dev
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Add a new block end-to-end
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
cmssy create block testimonials -c marketing -t "marketing,social-proof" -y
|
|
300
|
+
# edit blocks/testimonials/config.ts, Testimonials.tsx, preview.json
|
|
301
|
+
cmssy dev # visual check
|
|
302
|
+
pnpm typecheck && pnpm lint
|
|
303
|
+
cmssy test --block testimonials # if tests exist
|
|
304
|
+
cmssy publish testimonials --patch --dry-run
|
|
305
|
+
cmssy publish testimonials --patch
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Extend an existing block with an optional field
|
|
309
|
+
|
|
310
|
+
1. Add `field({...})` to `config.ts`. Don't set `required: true` - that's breaking.
|
|
311
|
+
2. Update the component to read and render it with a default.
|
|
312
|
+
3. Update `preview.json`.
|
|
313
|
+
4. `cmssy publish <block> --minor`.
|
|
314
|
+
|
|
315
|
+
### Copy-only fix
|
|
316
|
+
|
|
317
|
+
1. Edit default strings or JSX in the component.
|
|
318
|
+
2. `cmssy publish <block> --patch`.
|
|
319
|
+
|
|
320
|
+
### Pull a block from the design library
|
|
321
|
+
|
|
322
|
+
```bash
|
|
323
|
+
cmssy sync @cmssy/blocks.hero
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## 8. Safety rules (always)
|
|
327
|
+
|
|
328
|
+
- Run `cmssy doctor` before publishing.
|
|
329
|
+
- Never fabricate a workspace ID. Use `cmssy workspaces` and confirm with the user.
|
|
330
|
+
- Never pass `--force` without explicit user approval (skips breaking-change confirmation).
|
|
331
|
+
- Never pass `--overwrite-content` without explicit user approval (wipes editor content).
|
|
332
|
+
- Use `--dry-run` first when unsure what will change.
|
|
333
|
+
- Confirm with the user before publishing from a branch other than `develop` or `main`.
|
|
334
|
+
- Commit local changes before publishing - publish uploads the working tree.
|
|
335
|
+
- For init and link, never write a token or workspace ID into `.env` without user confirmation.
|
|
336
|
+
|
|
337
|
+
## 9. Troubleshooting
|
|
338
|
+
|
|
339
|
+
| Symptom | Likely cause | Fix |
|
|
340
|
+
| --- | --- | --- |
|
|
341
|
+
| `doctor` says token invalid | Revoked/expired token | `cmssy link` again with a fresh token from https://cmssy.io/settings/tokens |
|
|
342
|
+
| Publish says "workspace not accessible" | Wrong `CMSSY_WORKSPACE_ID` or no role | `cmssy workspaces`, update `.env` |
|
|
343
|
+
| Schema diff prompts blocking publish | Breaking field change | Bump `--major`, or revert to non-breaking, or `--force` with user approval |
|
|
344
|
+
| `block.d.ts` shows wrong fields | Stale generation | Restart `cmssy dev`, or run `cmssy build` to regenerate |
|
|
345
|
+
| Dev server shows a blank block | `preview.json` missing keys | Match keys to `config.ts` field names |
|