Caretize CLI
@caretcms/caretize is the CLI that turns a shipped Astro site into an editable one. It scans .astro templates, proposes data-caret bindings (and editable() wraps for data arrays), verifies every change re-parses as valid Astro, and writes nothing unsafe.
Your template text stays the default content until an editor saves — caretize only marks what’s editable.
Two commands
Section titled “Two commands”npx @caretcms/caretize initFor projects that don’t have CaretCMS yet:
- Installs
@caretcms/core - Wires
caret()intoastro.config.mjs- Static Astro projects:
caret({ delivery: 'static' })— no SSR adapter - Server projects (
output: 'server'already): keeps or adds adapter wiring
- Static Astro projects:
- Scaffolds
.envwith a generatedCARET_SESSION_SECRET
Existing configs are only edited via verified pure insertions (shown as a diff you confirm, backed up first). When the config shape isn’t safe to touch automatically, caretize prints a snippet to paste instead.
After init, run caretize again to tag templates (or continue manually).
# preview only — no writesnpx @caretcms/caretize --dry-runnpx @caretcms/caretize --diff
# default: apply safe edits, then show a diff you can undonpx @caretcms/caretize
# approve each change before writingnpx @caretcms/caretize --reviewDefault workflow
Section titled “Default workflow”By default caretize optimistically applies high-confidence, safe candidates, then prints a before→after diff. If the result isn’t what you wanted:
npx @caretcms/caretize --restore # revert the most recent run from .caret/.caretize-bak/Use --review when you want to approve every binding interactively before anything hits disk.
What it tags
Section titled “What it tags”| Target | Result |
|---|---|
Pure-text leaf elements (h1–h6, p, li, a, button, …) | data-caret="collection::id::field" |
<img src="…"> | data-caret on src |
Static const items = [...] arrays in frontmatter | editable("collection::id::field", [...]) wrap |
Mixed inline markup (with --rich) | data-caret-rich for sanitizer-safe formatting |
Styled spans (with --rich-class) | data-caret-rich once those classes are listed in allowedClasses |
getCollection().map() loops (with --bind-collections) | Per-row data-caret on {item.data.field} leaves |
Dynamic routes (with --bind-routes) | Binds [slug].astro detail pages via getStaticPaths props |
Named-import loops, astro:assets <Image> / <Picture> | Tagged when patterns match (included in --all) |
Field names are derived from visible text ("Our Programs 🎨" → our_programs). Names are deterministic and won’t collide with existing bindings — re-runs are idempotent on already-tagged elements.
Opt-in tiers
Section titled “Opt-in tiers”Some patterns need explicit flags (or --all):
| Flag | What it unlocks |
|---|---|
--bind-collections | Collection list loops rendered inline |
--bind-routes | Dynamic collection-detail routes |
--rich | Mixed-content blocks → data-caret-rich |
--rich-class | Rich blocks whose inline children carry a CSS class (implies --rich) |
--min-confidence low | Lower-confidence candidates (included in --all) |
--all | Every tier above at once — review the diff afterward |
When --rich-class finds classes that would be stripped by the sanitizer, caretize prints an allowedClasses snippet to copy into caret():
caret({ allowedClasses: { span: ['gold'], strong: ['big'] },})See Inline editing → Rich text.
Interactive review (--review)
Section titled “Interactive review (--review)”With --review, each candidate is shown in context:
src/pages/index.astro <h1> "Launch faster with Acme" + data-caret="pages::home::launch_faster_with_acme" (high) [a]ccept [s]kip [e]dit [A]ll [S]kip-file [q]uit >Richer tiers skipped in the default pass are offered afterward in an [Y/n/customize] prompt (use --review to approve every tag up front).
Safety model
Section titled “Safety model”- Verified before write: every output must re-parse as valid Astro and be a pure insertion of the original bytes.
- All-or-nothing per run: if any file fails verification, the run aborts with nothing written.
- Backups: each written file is copied to
.caret/.caretize-bak/first. - Preflight: caretize checks that
@caretcms/coreis wired inastro.config.mjsand warns about static output withoutdelivery: 'static'.
Full option reference
Section titled “Full option reference”caretize [path] [options]caretize init
init wire CaretCMS (deps + astro.config + .env) path file or directory (default: src/) --review approve each change before writing --dry-run print the plan, write nothing --diff preview exact before→after, write nothing -y, --yes auto-accept at/above min-confidence --min-confidence <lvl> high (default) | medium | low --no-images skip <img> elements --no-props skip hoisting static component-prop strings --bind-collections bind getCollection().map() loops --bind-routes bind dynamic collection-detail routes --rich tag sanitizer-safe mixed-content blocks --rich-class tag styled-span rich blocks (needs allowedClasses) --all every opt-in tier + low confidence --scope <collection::id> override inferred scope --report <file> write JSON report --restore restore most recent backupAfter caretize
Section titled “After caretize”npm run dev- Sign in at
/admin— with noCARET_EDIT_PASSWORD, a temporary dev password prints in the terminal - Click tagged elements to edit; Studio lives at
/admin/cms - Static delivery: Publish, then
astro buildand deploy (optionally wiredelivery.publish.webhookUrl) - Server delivery: visitors see edits on the next request after save/publish