@cmssy/cli 0.13.0 → 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 +199 -58
- package/dist/cli.js +19 -10
- 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 +4 -1
- package/dist/commands/skills.d.ts.map +1 -1
- package/dist/commands/skills.js +132 -29
- package/dist/commands/skills.js.map +1 -1
- 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 +4 -1
package/dist/commands/skills.js
CHANGED
|
@@ -7,43 +7,72 @@ import { fileURLToPath } from "url";
|
|
|
7
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
8
8
|
const __dirname = path.dirname(__filename);
|
|
9
9
|
const SKILLS_ROOT = path.resolve(__dirname, "../skills");
|
|
10
|
+
const EDITOR_TARGETS = ["claude"];
|
|
10
11
|
const SKILLS = {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
label: "
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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 [
|
|
20
54
|
chalk.gray("Next step:"),
|
|
21
55
|
chalk.white(" Restart Claude Code (or open a new session) so it picks up the skill."),
|
|
22
|
-
chalk.gray(" Then
|
|
23
|
-
chalk.white(
|
|
56
|
+
chalk.gray(" Then try something like:"),
|
|
57
|
+
chalk.white(` "${skill.examplePrompt}"`),
|
|
24
58
|
"",
|
|
25
59
|
chalk.gray(`Installed at: ${installPath}`),
|
|
26
|
-
].join("\n")
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
export async function skillsInstallCommand(rawTarget, options) {
|
|
30
|
-
const target = (rawTarget ?? "claude").trim().toLowerCase();
|
|
31
|
-
if (!Object.hasOwn(SKILLS, target)) {
|
|
32
|
-
console.error(chalk.red(`✖ Unknown skill target: ${rawTarget}`) +
|
|
33
|
-
chalk.gray(`\n Supported: ${Object.keys(SKILLS).join(", ")}`));
|
|
34
|
-
process.exit(1);
|
|
60
|
+
].join("\n");
|
|
35
61
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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}`) +
|
|
39
68
|
chalk.gray("\n This is a packaging bug - please report it at https://github.com/cmssy-io/cmssy-cli/issues"));
|
|
40
69
|
process.exit(1);
|
|
41
70
|
}
|
|
42
|
-
const installDir =
|
|
71
|
+
const installDir = resolveInstallDir(editor, skill, {
|
|
43
72
|
local: !!options.local,
|
|
44
73
|
cwd: process.cwd(),
|
|
45
74
|
});
|
|
46
|
-
const installPath = path.join(installDir,
|
|
75
|
+
const installPath = path.join(installDir, "SKILL.md");
|
|
47
76
|
const exists = await fs.pathExists(installPath);
|
|
48
77
|
if (exists && !options.force) {
|
|
49
78
|
if (options.yes) {
|
|
@@ -60,16 +89,90 @@ export async function skillsInstallCommand(rawTarget, options) {
|
|
|
60
89
|
},
|
|
61
90
|
]);
|
|
62
91
|
if (!overwrite) {
|
|
63
|
-
console.log(chalk.yellow(
|
|
64
|
-
return;
|
|
92
|
+
console.log(chalk.yellow(`⚠ Skipped ${skill.name} - ${installPath} unchanged.`));
|
|
93
|
+
return "skipped";
|
|
65
94
|
}
|
|
66
95
|
}
|
|
67
96
|
await fs.ensureDir(installDir);
|
|
68
|
-
await fs.copy(
|
|
97
|
+
await fs.copy(sourcePath, installPath, { overwrite: true });
|
|
69
98
|
const scope = options.local ? "local (.claude/)" : "global (~/.claude/)";
|
|
70
99
|
console.log(chalk.green(`✔ Installed ${skill.label} skill`) + chalk.gray(` → ${scope}`));
|
|
71
100
|
console.log();
|
|
72
|
-
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"));
|
|
73
176
|
console.log();
|
|
74
177
|
}
|
|
75
178
|
//# sourceMappingURL=skills.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,QAAQ,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/commands/skills.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cmssy-mcp-content
|
|
3
|
+
description: "Rules for managing CMS content via cmssy MCP tools. Use when creating/editing pages, blocks, translations, forms, or layout blocks through MCP. Trigger on words: cmssy MCP, page, block content, translation, form, header, footer, layout, publish page, add_block_to_page, update_block_content, create_page, list_pages, i18n, language."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# cmssy-mcp-content
|
|
7
|
+
|
|
8
|
+
Rules and patterns for working with site content through cmssy MCP tools.
|
|
9
|
+
|
|
10
|
+
## Prerequisites
|
|
11
|
+
|
|
12
|
+
This skill assumes the `@cmssy/mcp-server` is configured as an MCP server in your Claude Code settings. If the tools below are unavailable, install and configure it:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @cmssy/mcp-server
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Then register it in `~/.claude.json` (or your project `.mcp.json`) with your workspace token and workspace ID. See https://www.npmjs.com/package/@cmssy/mcp-server for the exact config snippet.
|
|
19
|
+
|
|
20
|
+
If the MCP tools (`list_pages`, `update_block_content`, etc.) aren't available in your session, this skill won't work - fall back to the Cmssy editor UI.
|
|
21
|
+
|
|
22
|
+
## When to use this skill
|
|
23
|
+
|
|
24
|
+
- Creating or editing pages via MCP
|
|
25
|
+
- Adding/updating block content or translations
|
|
26
|
+
- Working with layout blocks (header, footer)
|
|
27
|
+
- Managing page settings, SEO, or publishing
|
|
28
|
+
- Creating or managing forms and form submissions
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Rule 0: Discover the workspace's languages BEFORE editing
|
|
33
|
+
|
|
34
|
+
**Cmssy is multi-tenant and language-agnostic.** Every workspace picks its own set of languages — could be `en`, `pl`, `de`, `es`, `fr`, `zh`, `ja`, `ar`, `uk`, anything. **Never assume** a workspace uses `en`/`pl` — they are examples in this doc, not a fixed set.
|
|
35
|
+
|
|
36
|
+
Before any content edit:
|
|
37
|
+
|
|
38
|
+
1. Call `get_site_config` and read **`enabledLanguages`** (array of ISO 639-1 codes) and **`defaultLanguage`**
|
|
39
|
+
2. Use exactly those codes as keys in `content` - and every one of them when adding translations
|
|
40
|
+
3. If the workspace has only one language, content is still keyed by that language code (e.g. `content: { zh: {...} }`), never flat
|
|
41
|
+
|
|
42
|
+
When this rule is violated, you either silently drop a language or write under the wrong key. Both corrupt content.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Rule 1: Always use language-keyed content
|
|
47
|
+
|
|
48
|
+
All block content MUST be keyed by the workspace's enabled language codes (see Rule 0). Examples below show `en` + `pl` purely as an illustration - substitute whatever `enabledLanguages` returns for the actual workspace.
|
|
49
|
+
|
|
50
|
+
```jsonc
|
|
51
|
+
// CORRECT - language-keyed, includes EVERY language the workspace enabled
|
|
52
|
+
// (here en + pl; your workspace might return e.g. ["en","de","zh"] instead)
|
|
53
|
+
{
|
|
54
|
+
"en": { "heading": "Welcome", "subheading": "Hello world" },
|
|
55
|
+
"pl": { "heading": "Witaj", "subheading": "Witaj świecie" }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// WRONG - flat content, no language keys
|
|
59
|
+
{
|
|
60
|
+
"heading": "Welcome",
|
|
61
|
+
"subheading": "Hello world"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// WRONG - adding one language without including the others the workspace enabled
|
|
65
|
+
// If enabledLanguages is ["en","pl","de"], you MUST send all three, not just one
|
|
66
|
+
{
|
|
67
|
+
"pl": { "heading": "Witaj" }
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Critical edge case**: if existing content is flat (no language keys - legacy), you MUST restructure it by providing a key for EVERY enabled language in a single update - the default language gets the existing content, the others get the new translation (or a placeholder copy if the user didn't provide one).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Rule 2: Layout blocks (header/footer) follow the same i18n rules
|
|
76
|
+
|
|
77
|
+
Layout blocks (header, footer) live in `layoutBlocks` on a page, not in `blocks`. They use the same `update_block_content` tool and the same language-keyed content structure - with the exact same language codes from `enabledLanguages`.
|
|
78
|
+
|
|
79
|
+
### Header translatable fields
|
|
80
|
+
|
|
81
|
+
- `navigation[].label` - top-level nav labels
|
|
82
|
+
- `navigation[].children[].label` - submenu item labels
|
|
83
|
+
- `navigation[].children[].description` - submenu item descriptions
|
|
84
|
+
- `ctaLabel`, `secondaryCtaLabel` - button labels
|
|
85
|
+
- `announcementText` - announcement bar text
|
|
86
|
+
- `logoutButtonText` - logout button
|
|
87
|
+
|
|
88
|
+
### Footer translatable fields
|
|
89
|
+
|
|
90
|
+
- `tagline` - site tagline
|
|
91
|
+
- `linkColumns[].title` - column headings
|
|
92
|
+
- `linkColumns[].links[].name` - link labels
|
|
93
|
+
- `copyrightText` - copyright notice
|
|
94
|
+
|
|
95
|
+
### Non-translatable fields (identical value across all languages)
|
|
96
|
+
|
|
97
|
+
- URLs (`url`, `href`, `ctaUrl`, `announcementLink`)
|
|
98
|
+
- Icons (`icon`)
|
|
99
|
+
- Booleans (`showCta`, `sticky`, `transparent`, `showSocial`)
|
|
100
|
+
- Style values (`announcementBg`, `announcementTextColor`, `logoSize`)
|
|
101
|
+
- `logo` (image URL)
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## Rule 3: Workflow for content updates
|
|
106
|
+
|
|
107
|
+
1. **Read first**: Always `get_page` or `get_site_config` before editing
|
|
108
|
+
2. **Read `enabledLanguages`**: Language codes vary per workspace - never hardcode
|
|
109
|
+
3. **Inspect existing content shape**: Is it flat or language-keyed?
|
|
110
|
+
4. **Write every enabled language**: When adding translations, include ALL codes returned by `enabledLanguages`, not just the ones the user mentioned
|
|
111
|
+
5. **Verify**: After update, confirm the response has a key for each enabled language
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## Rule 4: Page blocks vs Layout blocks
|
|
116
|
+
|
|
117
|
+
| | Page blocks | Layout blocks |
|
|
118
|
+
| -------------- | ---------------------------------- | ---------------------------------- |
|
|
119
|
+
| Location | `blocks[]` | `layoutBlocks[]` |
|
|
120
|
+
| Scope | Single page | Inherited across pages |
|
|
121
|
+
| Content | `content[<lang>]` per enabled lang | `content[<lang>]` per enabled lang |
|
|
122
|
+
| Types | hero, features, faq, cta, etc. | header, footer |
|
|
123
|
+
| Position field | No | Yes (`header`, `footer`) |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Rule 5: Publishing workflow
|
|
128
|
+
|
|
129
|
+
- `update_block_content` creates unpublished changes (draft)
|
|
130
|
+
- Use `publish_page` to make changes live
|
|
131
|
+
- Use `revert_to_published` to discard draft changes
|
|
132
|
+
- Always inform the user about unpublished state after edits
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Rule 6: Available MCP tools quick reference
|
|
137
|
+
|
|
138
|
+
| Tool | Use for |
|
|
139
|
+
| --------------------------------------- | ----------------------------------------------- |
|
|
140
|
+
| `get_site_config` | **Languages**, site name, features |
|
|
141
|
+
| `get_workspace_info` | Plan, limits, usage |
|
|
142
|
+
| `list_pages` | Browse/search pages |
|
|
143
|
+
| `get_page` | Full page with blocks and content |
|
|
144
|
+
| `create_page` | New page |
|
|
145
|
+
| `update_page_settings` | SEO, displayName, description |
|
|
146
|
+
| `update_block_content` | Edit block content (page or layout) |
|
|
147
|
+
| `add_block_to_page` | Add new block |
|
|
148
|
+
| `remove_block_from_page` | Remove block |
|
|
149
|
+
| `update_page_blocks` | Reorder blocks |
|
|
150
|
+
| `update_page_layout` | Change layout blocks |
|
|
151
|
+
| `publish_page` / `unpublish_page` | Publishing |
|
|
152
|
+
| `list_block_types` / `get_block_schema` | Discover available blocks |
|
|
153
|
+
| `list_media` | Browse uploaded media |
|
|
154
|
+
| `list_forms` | Browse forms (filter by status) |
|
|
155
|
+
| `get_form` | Full form with fields, settings, i18n |
|
|
156
|
+
| `create_form` | New form with fields and settings |
|
|
157
|
+
| `update_form` | Edit form name, fields, settings, status |
|
|
158
|
+
| `delete_form` | Delete form and all submissions |
|
|
159
|
+
| `list_form_submissions` | Browse submissions (filter by form/status) |
|
|
160
|
+
| `get_form_submission` | Full submission details |
|
|
161
|
+
| `update_form_submission_status` | Change status (pending/processed/spam/archived) |
|
|
162
|
+
| `delete_form_submission` | Delete a submission |
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Rule 7: Forms - field types and settings
|
|
167
|
+
|
|
168
|
+
### Available field types (15)
|
|
169
|
+
|
|
170
|
+
`text`, `email`, `password`, `textarea`, `number`, `phone`, `url`, `date`, `datetime`, `select`, `multiselect`, `checkbox`, `radio`, `file`, `hidden`
|
|
171
|
+
|
|
172
|
+
### Action types
|
|
173
|
+
|
|
174
|
+
- `contact` (default) - standard contact form
|
|
175
|
+
- `login` - authentication form
|
|
176
|
+
- `register` - user registration
|
|
177
|
+
- `newsletter` - email subscription
|
|
178
|
+
- `custom` - custom webhook/processing
|
|
179
|
+
|
|
180
|
+
### Form i18n fields (language-keyed, same rules as blocks)
|
|
181
|
+
|
|
182
|
+
All the following fields are keyed by the workspace's `enabledLanguages` codes - whatever the site uses (`en`, `de`, `zh`, …):
|
|
183
|
+
|
|
184
|
+
- `fields[].label`, `fields[].placeholder`, `fields[].helpText`
|
|
185
|
+
- `fields[].options[].label`
|
|
186
|
+
- `settings.submitButtonLabel`, `settings.successMessage`, `settings.errorMessage`
|
|
187
|
+
|
|
188
|
+
### Form statuses
|
|
189
|
+
|
|
190
|
+
- `draft` - not publicly accessible
|
|
191
|
+
- `published` - live and accepting submissions
|
|
192
|
+
- `archived` - disabled
|
|
193
|
+
|
|
194
|
+
### Creating a form example
|
|
195
|
+
|
|
196
|
+
Below: `en` + `pl` shown for illustration. For a workspace with `enabledLanguages: ["en","de","zh"]` you'd send keys `en`, `de`, `zh` instead - always match whatever `get_site_config` returns.
|
|
197
|
+
|
|
198
|
+
```json
|
|
199
|
+
{
|
|
200
|
+
"name": "Contact Form",
|
|
201
|
+
"slug": "contact",
|
|
202
|
+
"fields": [
|
|
203
|
+
{
|
|
204
|
+
"id": "name-field",
|
|
205
|
+
"name": "name",
|
|
206
|
+
"fieldType": "text",
|
|
207
|
+
"label": { "en": "Name", "pl": "Imię" },
|
|
208
|
+
"validation": { "required": true }
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"id": "email-field",
|
|
212
|
+
"name": "email",
|
|
213
|
+
"fieldType": "email",
|
|
214
|
+
"label": { "en": "Email", "pl": "Email" },
|
|
215
|
+
"validation": { "required": true }
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
"id": "message-field",
|
|
219
|
+
"name": "message",
|
|
220
|
+
"fieldType": "textarea",
|
|
221
|
+
"label": { "en": "Message", "pl": "Wiadomość" },
|
|
222
|
+
"validation": { "required": true, "minLength": 10 }
|
|
223
|
+
}
|
|
224
|
+
],
|
|
225
|
+
"settings": {
|
|
226
|
+
"actionType": "contact",
|
|
227
|
+
"submitButtonLabel": { "en": "Send", "pl": "Wyślij" },
|
|
228
|
+
"successMessage": { "en": "Thank you!", "pl": "Dziękujemy!" }
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for Cmssy blocks.
|
|
3
|
+
* Usage: import { renderBlock } from "@cmssy/cli/test";
|
|
4
|
+
*/
|
|
5
|
+
interface RenderBlockOptions {
|
|
6
|
+
content: Record<string, unknown>;
|
|
7
|
+
context?: Record<string, unknown>;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Render a block component with content and optional context.
|
|
11
|
+
* Wraps @testing-library/react render with block-specific props.
|
|
12
|
+
*/
|
|
13
|
+
export declare function renderBlock(Component: any, options: RenderBlockOptions): Promise<any>;
|
|
14
|
+
/**
|
|
15
|
+
* Validate that preview data has required fields present.
|
|
16
|
+
* Does not check value types - only presence of required fields.
|
|
17
|
+
*/
|
|
18
|
+
export declare function validatePreviewData(schema: Record<string, any>, data: Record<string, unknown>): {
|
|
19
|
+
valid: boolean;
|
|
20
|
+
errors: string[];
|
|
21
|
+
};
|
|
22
|
+
export {};
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test-helpers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAOD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,kBAAkB,gBAyB5E;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC3B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAatC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers for Cmssy blocks.
|
|
3
|
+
* Usage: import { renderBlock } from "@cmssy/cli/test";
|
|
4
|
+
*/
|
|
5
|
+
const DEFAULT_CONTEXT = {
|
|
6
|
+
locale: { current: "en", default: "en", enabled: ["en"] },
|
|
7
|
+
isPreview: true,
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* Render a block component with content and optional context.
|
|
11
|
+
* Wraps @testing-library/react render with block-specific props.
|
|
12
|
+
*/
|
|
13
|
+
export async function renderBlock(Component, options) {
|
|
14
|
+
let render;
|
|
15
|
+
try {
|
|
16
|
+
const rtl = await import("@testing-library/react");
|
|
17
|
+
render = rtl.render;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
throw new Error("Missing @testing-library/react. Install it:\n npm install -D @testing-library/react");
|
|
21
|
+
}
|
|
22
|
+
let React;
|
|
23
|
+
try {
|
|
24
|
+
React = await import("react");
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new Error("Missing react. Install it:\n npm install react");
|
|
28
|
+
}
|
|
29
|
+
const context = { ...DEFAULT_CONTEXT, ...options.context };
|
|
30
|
+
const element = React.createElement(Component, {
|
|
31
|
+
content: options.content,
|
|
32
|
+
context,
|
|
33
|
+
});
|
|
34
|
+
return render(element);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate that preview data has required fields present.
|
|
38
|
+
* Does not check value types - only presence of required fields.
|
|
39
|
+
*/
|
|
40
|
+
export function validatePreviewData(schema, data) {
|
|
41
|
+
const errors = [];
|
|
42
|
+
for (const [key, field] of Object.entries(schema)) {
|
|
43
|
+
if (field.required &&
|
|
44
|
+
(data[key] === undefined || data[key] === null || data[key] === "")) {
|
|
45
|
+
errors.push(`Required field "${key}" is missing or empty`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return { valid: errors.length === 0, errors };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/test-helpers/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,MAAM,eAAe,GAAG;IACtB,MAAM,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE;IACzD,SAAS,EAAE,IAAI;CAChB,CAAC;AAEF;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAc,EAAE,OAA2B;IAC3E,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QACnD,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sFAAsF,CACvF,CAAC;IACJ,CAAC;IAED,IAAI,KAAU,CAAC;IACf,IAAI,CAAC;QACH,KAAK,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,OAAO,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3D,MAAM,OAAO,GAAG,KAAK,CAAC,aAAa,CAAC,SAAS,EAAE;QAC7C,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,OAAO;KACR,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAA2B,EAC3B,IAA6B;IAE7B,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAClD,IACE,KAAK,CAAC,QAAQ;YACd,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,EACnE,CAAC;YACD,MAAM,CAAC,IAAI,CAAC,mBAAmB,GAAG,uBAAuB,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;AAChD,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/dev-generator/helpers.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWlE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/dev-generator/helpers.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAWlE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAwE5B;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG;IACrE,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IACrC,KAAK,EAAE,GAAG,EAAE,CAAC;CACd,CA+BA;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKhD"}
|
|
@@ -28,6 +28,9 @@ export function loadTemplateConfigSync(templateDir, projectRoot) {
|
|
|
28
28
|
const configPath = path.join(templateDir, "config.ts");
|
|
29
29
|
if (!fs.existsSync(configPath))
|
|
30
30
|
return null;
|
|
31
|
+
const cacheDir = path.join(projectRoot, ".cmssy", "cache");
|
|
32
|
+
let mockConfigPath = null;
|
|
33
|
+
let tempPath = null;
|
|
31
34
|
try {
|
|
32
35
|
const cliPath = path.dirname(path.dirname(new URL(import.meta.url).pathname));
|
|
33
36
|
const possibleTsxPaths = [
|
|
@@ -38,13 +41,18 @@ export function loadTemplateConfigSync(templateDir, projectRoot) {
|
|
|
38
41
|
let tsxBinary = possibleTsxPaths.find((p) => fs.existsSync(p));
|
|
39
42
|
if (!tsxBinary)
|
|
40
43
|
tsxBinary = "npx -y tsx";
|
|
41
|
-
const cacheDir = path.join(projectRoot, ".cmssy", "cache");
|
|
42
44
|
fs.ensureDirSync(cacheDir);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
mockConfigPath = path.join(cacheDir, "cmssy-cli-config.mjs");
|
|
46
|
+
// Mirror the mock exports in src/utils/block-config.ts - cover every
|
|
47
|
+
// export a user config might import, otherwise the dynamic import
|
|
48
|
+
// throws and preview silently falls back to null.
|
|
49
|
+
fs.writeFileSync(mockConfigPath, "export const defineBlock = (config) => config;\n" +
|
|
50
|
+
"export const defineTemplate = (config) => config;\n" +
|
|
51
|
+
"export const defineTheme = (config) => config;\n" +
|
|
52
|
+
"export const field = (config) => config;\n");
|
|
45
53
|
const configContent = fs.readFileSync(configPath, "utf-8");
|
|
46
54
|
const modified = configContent.replace(/from\s+['"](?:@?cmssy-?(?:\/cli)?\/config|cmssy-cli\/config)['"]/g, `from '${mockConfigPath.replace(/\\/g, "/")}'`);
|
|
47
|
-
|
|
55
|
+
tempPath = path.join(cacheDir, `temp-template-config-${Date.now()}.ts`);
|
|
48
56
|
fs.writeFileSync(tempPath, modified);
|
|
49
57
|
const evalCode = `import cfg from '${tempPath.replace(/\\/g, "/")}'; console.log(JSON.stringify(cfg.default || cfg));`;
|
|
50
58
|
const cmd = tsxBinary.includes("npx")
|
|
@@ -55,20 +63,28 @@ export function loadTemplateConfigSync(templateDir, projectRoot) {
|
|
|
55
63
|
cwd: projectRoot,
|
|
56
64
|
stdio: ["pipe", "pipe", "pipe"],
|
|
57
65
|
});
|
|
58
|
-
try {
|
|
59
|
-
fs.removeSync(tempPath);
|
|
60
|
-
}
|
|
61
|
-
catch { }
|
|
62
|
-
try {
|
|
63
|
-
fs.removeSync(mockConfigPath);
|
|
64
|
-
}
|
|
65
|
-
catch { }
|
|
66
66
|
const lines = output.trim().split("\n");
|
|
67
67
|
return JSON.parse(lines[lines.length - 1]);
|
|
68
68
|
}
|
|
69
69
|
catch {
|
|
70
70
|
return null;
|
|
71
71
|
}
|
|
72
|
+
finally {
|
|
73
|
+
// Always clean up temp files - a throw in execSync / JSON.parse used
|
|
74
|
+
// to leave them lingering in .cmssy/cache which could affect later runs.
|
|
75
|
+
if (tempPath) {
|
|
76
|
+
try {
|
|
77
|
+
fs.removeSync(tempPath);
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
}
|
|
81
|
+
if (mockConfigPath) {
|
|
82
|
+
try {
|
|
83
|
+
fs.removeSync(mockConfigPath);
|
|
84
|
+
}
|
|
85
|
+
catch { }
|
|
86
|
+
}
|
|
87
|
+
}
|
|
72
88
|
}
|
|
73
89
|
/**
|
|
74
90
|
* Convert template config (from config.ts defineTemplate) to pages.json format.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/utils/dev-generator/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AAExB;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,SAAiB;IACxD,IAAI,MAAM,GAAG,SAAS,CAAC;IACvB,IAAI,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC;IACpC,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACjC,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC/B,CAAC;SAAM,IAAI,MAAM,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC3C,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,sBAAsB,CACpC,WAAmB,EACnB,WAAmB;IAEnB,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IAE5C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAC1B,IAAI,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,CAChD,CAAC;QACF,MAAM,gBAAgB,GAAG;YACvB,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC;YACvD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC;YAC7D,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC;SACtD,CAAC;QACF,IAAI,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/D,IAAI,CAAC,SAAS;YAAE,SAAS,GAAG,YAAY,CAAC;QAEzC,
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../../src/utils/dev-generator/helpers.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/utils/dev-generator/layout.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/utils/dev-generator/layout.ts"],"names":[],"mappings":"AAGA,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,QAyBjD;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAsCtE"}
|