@sleekcms/sync 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.
package/README.md ADDED
@@ -0,0 +1,476 @@
1
+ # SleekCMS CLI
2
+
3
+ **Build complete websites with AI. Edit them locally. Deploy instantly.**
4
+
5
+ The SleekCMS CLI brings your CMS to your code editor — with full AI-agent support baked in. Spin up an entirely new site by describing it in plain English, or drop into any existing site and edit templates, content, and styles the way you'd expect: locally, with real files, in your editor of choice.
6
+
7
+ Every save auto-syncs. Every change goes live. No build steps. No Git hooks. No infrastructure to manage.
8
+
9
+ ---
10
+
11
+ ## Why developers love it
12
+
13
+ - **AI generates your entire site from a description** — models, templates, layouts, styles, and content, all wired up and ready to go.
14
+ - **AI-generated sites are plain files** — EJS templates, JSON content, CSS, JS. You own every line. Open in VS Code, Cursor, or any editor.
15
+ - **Any AI can maintain it** — the CLI injects `AGENT.md`, `CLAUDE.md`, and `.vscode/copilot-instructions.md` into your workspace. GitHub Copilot, Claude, Cursor — they all read the same site reference and understand your structure from day one.
16
+ - **Live sync, always** — save a file, it syncs. Open the SleekCMS dashboard and your preview updates in real time.
17
+ - **One command to start** — no installs required.
18
+
19
+ ---
20
+
21
+ ## Quickstart
22
+
23
+ ```bash
24
+ npx @sleekcms/sync --token <YOUR_AUTH_TOKEN>
25
+ ```
26
+
27
+ That's it. The CLI fetches your site, opens an editor prompt, and starts watching for changes. Grab a token from your [SleekCMS dashboard](https://app.sleekcms.com).
28
+
29
+ ---
30
+
31
+ ## Installation
32
+
33
+ ### Run without installing (recommended)
34
+
35
+ ```bash
36
+ npx @sleekcms/sync --token <YOUR_AUTH_TOKEN>
37
+ ```
38
+
39
+ ### Install globally
40
+
41
+ ```bash
42
+ npm install -g @sleekcms/sync
43
+ sleekcms --token <YOUR_AUTH_TOKEN>
44
+ ```
45
+
46
+ ---
47
+
48
+ ## Usage
49
+
50
+ ```bash
51
+ npx @sleekcms/sync [OPTIONS]
52
+ ```
53
+
54
+ | Option | Alias | Description | Default |
55
+ |--------------------|-------|--------------------------------------------------------|---------------|
56
+ | `--token <token>` | `-t` | Your SleekCMS CLI auth token (required) | — |
57
+ | `--path <path>` | `-p` | Parent directory for the local workspace | `~/.sleekcms` |
58
+ | `--help` | `-h` | Show help | — |
59
+
60
+ > The actual workspace folder is created at `<path>/<site-name>-<site-id>/`.
61
+
62
+ ```bash
63
+ # Basic
64
+ npx @sleekcms/sync --token abc123-xxxx
65
+
66
+ # Custom workspace folder
67
+ npx @sleekcms/sync -t abc123-xxxx -p ~/Sites
68
+ ```
69
+
70
+ Once running, press `r` to re-fetch all files or `x` / `Ctrl+C` to exit.
71
+
72
+ ---
73
+
74
+ ## Building a site with AI
75
+
76
+ This is where SleekCMS CLI becomes something different. Instead of hand-crafting every template, you describe the site you want and let an AI agent build it.
77
+
78
+ ### How it works
79
+
80
+ When the CLI starts for the first time, it writes three files into your local workspace:
81
+
82
+ | File | Picked up by |
83
+ |---|---|
84
+ | `AGENT.md` | Copilot (agent mode), any generic agent |
85
+ | `CLAUDE.md` | Claude / Claude Code |
86
+ | `.vscode/copilot-instructions.md` | GitHub Copilot in VS Code |
87
+
88
+ These files contain the complete SleekCMS site-building reference — file naming conventions, model syntax, template helpers, content format, and all the rules an AI needs to produce valid, working code.
89
+
90
+ ### Generate a complete site
91
+
92
+ Open the workspace in Cursor, VS Code with GitHub Copilot, or Claude Code, then describe your site:
93
+
94
+ ```
95
+ Build a portfolio site with:
96
+ - A home page with a hero section, featured projects, and a contact form
97
+ - A blog with individual post pages
98
+ - A shared header and footer
99
+ - Tailwind CSS styling
100
+ - SEO meta tags on every page
101
+ ```
102
+
103
+ The AI creates the right files in the right places:
104
+
105
+ ```
106
+ src/models/pages/home.model
107
+ src/models/pages/blog[].model
108
+ src/models/entries/header.model
109
+ src/models/entries/footer.model
110
+ src/models/blocks/hero.model
111
+ src/views/pages/home.ejs
112
+ src/views/pages/blog[].ejs
113
+ src/views/entries/header.ejs
114
+ src/views/entries/footer.ejs
115
+ src/views/blocks/hero.ejs
116
+ src/views/layouts/main.ejs
117
+ src/public/css/tailwind.css
118
+ src/content/pages/home.json
119
+ src/content/pages/blog[]/my-first-post.json
120
+ src/content/entries/header.json
121
+ src/content/entries/footer.json
122
+ ```
123
+
124
+ As soon as those files hit disk, the watcher picks them up and syncs each one to SleekCMS. Your site is live before the AI finishes explaining what it built.
125
+
126
+ ### AI-generated sites are fully editable and maintainable — automatically
127
+
128
+ There's no lock-in, no proprietary format, no "AI black box". Every file the AI writes is the same real file you'd write by hand:
129
+
130
+ - **Templates** are standard EJS — open them in any editor, tweak any line.
131
+ - **Models** are plain JSON-like schema files — add a field, save, done.
132
+ - **Content** is readable JSON — edit values directly without touching the CMS dashboard.
133
+
134
+ And because `AGENT.md` / `CLAUDE.md` live in the workspace, any AI session — today or months from now — picks up the same full site context. Ask it to add a dark mode toggle, refactor the blog layout, or generate ten more blog posts. It already knows your site.
135
+
136
+ ---
137
+
138
+ ## How sync works
139
+
140
+ The CLI runs a local watcher after the initial fetch. Every time you save a file:
141
+
142
+ 1. The watcher detects the change (debounced to batch rapid edits).
143
+ 2. The changed file is classified — template, model, content record, CSS, or JS.
144
+ 3. It's pushed to the SleekCMS API in the correct order: models first, then templates, then content.
145
+ 4. The SleekCMS server rebuilds and redeploys the affected pages.
146
+
147
+ On first run, the CLI pulls the full site state from the server. After that, a local `.cache/` folder tracks server-known state so only real diffs are pushed — no redundant API calls.
148
+
149
+ ```
150
+ Your editor → file save → watcher → SleekCMS API → rebuild → live site
151
+ ```
152
+
153
+ ### Watch mode commands
154
+
155
+ | Key | Action |
156
+ |---|---|
157
+ | `r` | Re-fetch all files from server |
158
+ | `x` or `Ctrl+C` | Exit and clean up local workspace |
159
+
160
+ ---
161
+
162
+ ## Previewing your site
163
+
164
+ Every change you sync is immediately reflected in SleekCMS. To preview:
165
+
166
+ 1. Open the [SleekCMS dashboard](https://app.sleekcms.com).
167
+ 2. Navigate to your site.
168
+ 3. Click **Preview** — you'll see the live build with your latest changes.
169
+
170
+ No manual deploy. No waiting. SleekCMS rebuilds on every sync and the preview reflects the current state of your local workspace in real time.
171
+
172
+ ---
173
+
174
+ ## Local workspace structure
175
+
176
+ ```
177
+ <site-name>/
178
+ ├── AGENT.md # AI agent reference (auto-generated)
179
+ ├── CLAUDE.md # Claude reference (auto-generated)
180
+ ├── .vscode/
181
+ │ ├── copilot-instructions.md # GitHub Copilot context (auto-generated)
182
+ │ └── settings.json # Editor settings
183
+
184
+ └── src/
185
+ ├── models/
186
+ │ ├── pages/<key>.model # Page content schema
187
+ │ ├── entries/<key>.model # Entry content schema
188
+ │ └── blocks/<key>.model # Block content schema
189
+
190
+ ├── views/
191
+ │ ├── pages/<key>.ejs # Page templates
192
+ │ ├── entries/<key>.ejs # Entry templates
193
+ │ ├── blocks/<key>.ejs # Block templates
194
+ │ └── layouts/<name>.ejs # Layout wrappers
195
+
196
+ ├── content/
197
+ │ ├── pages/<key>.json # Single page content
198
+ │ ├── pages/<key>/<slug>.json # Collection page content (one file per item)
199
+ │ ├── entries/<key>.json # Single entry content
200
+ │ ├── entries/<key>[].json # Collection entry content (array)
201
+ │ └── images.json # Reusable site-level images (handle → shortcut)
202
+
203
+ └── public/
204
+ ├── css/<name>.css # Stylesheets
205
+ └── js/<name>.js # Scripts
206
+ ```
207
+
208
+ ### File naming
209
+
210
+ Keys are lowercase, dash-separated. For pages, `_` in the key maps to `/` in the URL. A `[]` suffix marks a collection.
211
+
212
+ | Key | URL |
213
+ |---|---|
214
+ | `_index` | `/` |
215
+ | `about` | `/about` |
216
+ | `blog[]` | `/blog/<slug>` |
217
+ | `docs_getting-started` | `/docs/getting-started` |
218
+
219
+ ---
220
+
221
+ ## Content models
222
+
223
+ Models describe the shape of your content. They're JSON-like files with unquoted keys and type names as values.
224
+
225
+ ```
226
+ {
227
+ title: text,
228
+ image: image,
229
+ body: richtext,
230
+ published: boolean
231
+ }
232
+ ```
233
+
234
+ **Nested groups:**
235
+ ```
236
+ {
237
+ hero: {
238
+ heading: text,
239
+ background: image
240
+ }
241
+ }
242
+ ```
243
+
244
+ **Repeatable lists:**
245
+ ```
246
+ {
247
+ features: [
248
+ {
249
+ title: text,
250
+ icon: image
251
+ }
252
+ ]
253
+ }
254
+ ```
255
+
256
+ **Block and entry references:**
257
+ ```
258
+ {
259
+ cta: block(cta),
260
+ author: entry(authors),
261
+ tags: [entry(tags)]
262
+ }
263
+ ```
264
+
265
+ ### Field types
266
+
267
+ | Type | Value |
268
+ |---|---|
269
+ | `text` | String |
270
+ | `paragraph` | Multiline string |
271
+ | `richtext` | HTML string |
272
+ | `markdown` | Markdown string |
273
+ | `number` | Number |
274
+ | `boolean` | `true` / `false` |
275
+ | `date` | `YYYY-MM-DD` |
276
+ | `image` | `{ url, alt }` |
277
+ | `video` | `{ url, embed }` |
278
+ | `link` | URL string |
279
+ | `json` | Object or array |
280
+ | `block(key)` | Embedded block object |
281
+ | `entry(key)` | Entry object or slug reference |
282
+
283
+ ---
284
+
285
+ ## EJS template syntax
286
+
287
+ | Tag | Purpose |
288
+ |---|---|
289
+ | `<%= expr %>` | Output with HTML escaping |
290
+ | `<%- expr %>` | Output raw HTML |
291
+ | `<% code %>` | Execute JS (loops, conditionals) |
292
+
293
+ ### Template context
294
+
295
+ | Variable | Description |
296
+ |---|---|
297
+ | `item` | The current page, entry, or block record |
298
+ | `pages` | All page records |
299
+ | `entries` | All entries keyed by handle |
300
+ | `main` | Rendered page output (layouts only) |
301
+
302
+ ### Helper functions
303
+
304
+ **Content access**
305
+
306
+ | Function | Description |
307
+ |---|---|
308
+ | `getPage(path)` | Page by exact path |
309
+ | `getPages(path, opts?)` | Pages where path starts with prefix; pass `{ collection: true }` for collection pages only |
310
+ | `getEntry(handle)` | Entry by handle |
311
+ | `getImage(handle)` | Site-level image by handle (from `images.json`) |
312
+ | `getSlugs(path)` | Slugs under a collection path |
313
+ | `path(page)` | Relative URL path of a page (e.g. `/blog/my-post`) |
314
+ | `url(pathOrPage?)` | Site origin, or a full absolute URL when given a path string or page object |
315
+
316
+ **Rendering**
317
+
318
+ | Function | Description |
319
+ |---|---|
320
+ | `render(val)` | Render a block or entry through its template |
321
+ | `marked(md)` | Convert markdown to HTML |
322
+
323
+ **Images**
324
+
325
+ | Function | Description |
326
+ |---|---|
327
+ | `img(image, attr)` | `<img>` element |
328
+ | `src(image, attr)` | Optimized image URL |
329
+ | `picture(image, attr)` | `<picture>` with dark/light variants |
330
+ | `svg(image, attr?)` | Inline SVG |
331
+
332
+ `attr` can be a `"WxH"` string or `{ w, h, class, style, fit }` object.
333
+
334
+ **Head injection** (deduplicated automatically)
335
+
336
+ | Function | Description |
337
+ |---|---|
338
+ | `title(text)` | Set `<title>` |
339
+ | `meta(attrs)` | Add `<meta>` tag |
340
+ | `link(value)` | Add `<link>` (CSS URL, font, etc.) |
341
+ | `style(css)` | Inline `<style>` block |
342
+ | `script(value)` | External or inline `<script>` |
343
+
344
+ > **Tailwind:** Creating `src/public/css/tailwind.css` enables Tailwind automatically — it's compiled and injected for you. Do **not** add it via `link()`.
345
+
346
+ ---
347
+
348
+ ## Example templates
349
+
350
+ **Layout (`src/views/layouts/main.ejs`)**
351
+
352
+ ```ejs
353
+ <!DOCTYPE html>
354
+ <html lang="en">
355
+ <head>
356
+ <meta charset="UTF-8">
357
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
358
+ </head>
359
+ <body>
360
+ <%- render(getEntry('header')) %>
361
+ <main><%- main %></main>
362
+ <%- render(getEntry('footer')) %>
363
+ </body>
364
+ </html>
365
+ ```
366
+
367
+ **Page (`src/views/pages/blog[].ejs`)**
368
+
369
+ ```ejs
370
+ <% title(item.title + ' | My Blog') %>
371
+ <% link('/css/styles.css') %>
372
+
373
+ <article>
374
+ <h1><%= item.title %></h1>
375
+ <%- img(item.cover, '1200x600') %>
376
+ <%- marked(item.body) %>
377
+ </article>
378
+ ```
379
+
380
+ **Blog index listing**
381
+
382
+ ```ejs
383
+ <% for (const post of getPages('/blog', { collection: true })) { %>
384
+ <a href="<%- path(post) %>">
385
+ <%- img(post.cover, '400x250') %>
386
+ <h3><%= post.title %></h3>
387
+ </a>
388
+ <% } %>
389
+ ```
390
+
391
+ **Forms** (no backend needed)
392
+
393
+ ```html
394
+ <form data-sleekcms="contact">
395
+ <input name="name" type="text" required>
396
+ <input name="email" type="email" required>
397
+ <textarea name="message"></textarea>
398
+ <button type="submit">Send</button>
399
+ </form>
400
+ ```
401
+
402
+ Any form with `data-sleekcms="<name>"` captures and stores submissions in the CMS dashboard automatically.
403
+
404
+ ---
405
+
406
+ ## Content files
407
+
408
+ Content lives in `src/content/` as plain JSON. Edit any content file and save — it syncs like any other file.
409
+
410
+ ## Images
411
+
412
+ Images are part of content, not a separate asset pipeline. Drop a shortcut string into any `image` field and the sync engine resolves it to a full `{ url, alt }` object on save:
413
+
414
+ ```json
415
+ {
416
+ "title": "About Us",
417
+ "image": "pexels:team meeting",
418
+ "hero": {
419
+ "heading": "Hello",
420
+ "background": "unsplash:mountain sunrise|Sunrise over the mountains"
421
+ }
422
+ }
423
+ ```
424
+
425
+ Supported sources: `unsplash`, `pexels`, `pixabay`, `iconify`, `url`, `cms`. Append `|<alt text>` to set the image's `alt`.
426
+
427
+ ### Reusable images (`src/content/images.json`)
428
+
429
+ For images used in more than one place — logos, recurring icons, shared hero art — declare them once as `handle → shortcut`:
430
+
431
+ ```json
432
+ {
433
+ "logo": "url:https://cdn.example.com/logo.svg",
434
+ "hero": "pexels:team meeting|Our team",
435
+ "favicon": "iconify:mdi:rocket"
436
+ }
437
+ ```
438
+
439
+ Then reference any of them from any `image` field with `"cms:<handle>"` (e.g. `"cms:logo"`). Templates can also fetch the resolved object directly via `getImage('logo')`.
440
+
441
+ ### Images in markdown
442
+
443
+ Inside `markdown` fields, the same shortcuts work in standard image syntax. Append `|<alt>` for alt text and a `<width>x<height>` token to set the rendered dimensions (defaults to `600x400`):
444
+
445
+ ```markdown
446
+ ![doctor](pexels:doctor|Smiling doctor 800x600)
447
+ ```
448
+
449
+ ---
450
+
451
+ ## Prerequisites
452
+
453
+ - **Node.js** v16 or later
454
+ - A **SleekCMS account** with a CLI auth token ([get one here](https://app.sleekcms.com))
455
+
456
+ ---
457
+
458
+ ## Troubleshooting
459
+
460
+ | Problem | Fix |
461
+ |---|---|
462
+ | Authentication error | Verify your token in the SleekCMS dashboard |
463
+ | No files downloaded | Check your token and `--env` setting |
464
+ | Changes not syncing | Check terminal output; changes are debounced by 5 seconds |
465
+ | Wrong workspace opened | Each token maps to a specific workspace folder |
466
+ | Workspace disappeared | Expected — the local workspace is wiped on `Ctrl+C` / `x`. Your edits are already on the server; re-run the CLI to pull a fresh copy. |
467
+
468
+ ---
469
+
470
+ ## License
471
+
472
+ ISC — [Yusuf Bhabhrawala](https://sleekcms.com)
473
+
474
+ ---
475
+
476
+ [SleekCMS](https://sleekcms.com) · [Documentation](https://docs.sleekcms.com) · [Dashboard](https://app.sleekcms.com)