Skip to content

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.

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.

TargetResult
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 frontmattereditable("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):

FlagWhat it unlocks
--bind-collectionsCollection list loops rendered inline
--bind-routesDynamic collection-detail routes
--richMixed-content blocks → data-caret-rich
--rich-classRich blocks whose inline children carry a CSS class (implies --rich)
--min-confidence lowLower-confidence candidates (included in --all)
--allEvery 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