Caretize CLI
@caretcms/caretize scans
.astro templates and proposes data-caret bindings. It can also add
editable() wrappers to data arrays. Before writing, it confirms that each
changed template still parses as valid Astro.
Template text remains the default until an editor saves an override. Caretize only marks which elements are 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 | Additional changes allowed |
|---|---|
--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