Skip to content

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.

Terminal window
npx @caretcms/caretize init

For projects that don’t have CaretCMS yet:

  1. Installs @caretcms/core
  2. Wires caret() into astro.config.mjs
    • Static Astro projects: caret({ delivery: 'static' }) — no SSR adapter
    • Server projects (output: 'server' already): keeps or adds adapter wiring
  3. Scaffolds .env with a generated CARET_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).

By default caretize optimistically applies high-confidence, safe candidates, then prints a before→after diff. If the result isn’t what you wanted:

Terminal window
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.

Target Result
Pure-text leaf elements (h1h6, 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.

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.

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).

  • 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/core is wired in astro.config.mjs and warns about static output without delivery: 'static'.
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 backup
  1. npm run dev
  2. Sign in at /admin — with no CARET_EDIT_PASSWORD, a temporary dev password prints in the terminal
  3. Click tagged elements to edit; Studio lives at /admin/cms
  4. Static delivery: Publish, then astro build and deploy (optionally wire delivery.publish.webhookUrl)
  5. Server delivery: visitors see edits on the next request after save/publish