Every eds command, A to Z. The CLI is a dev tool - it ships nothing to your site. Run any command from anywhere inside your EDS project (it finds the root via fstab.yaml, head.html or scripts/, so Universal Editor projects without an fstab work too). Run eds on its own for an interactive menu.
Node.js 20 or newer. Installs the eds command globally.
$ npm i -g aem-eds-kit
It's a big tool, so here's the exact command path for each authoring model, start to published page. Steps 1 and 3-5 are shared; the content step (6) differs. Every step is interactive when you omit the flags.
npm i -g aem-eds-kiteds init --authoring da --org … --site …eds scaffold ue · eds scaffold helperseds block from-design (or create / migrate)eds model add · schema block · track blockeds template new home --blocks … --push → pushed to DAeds preview /… → eds publish /…npm i -g aem-eds-kiteds init --authoring gdrive --mountpoint <drive-folder>eds scaffold ue · eds scaffold helperseds block from-design (or create / migrate)eds model add · schema block · track blockeds template new home → import templates/home.html into a Google Doc; share the folder + install aem-code-synceds preview /… → eds publish /…npm i -g aem-eds-kiteds init --authoring sharepoint --mountpoint <sp-folder>eds scaffold ue · eds scaffold helperseds block from-design (or create / migrate)eds model add · schema block · track blockeds template new home → import templates/home.html into a Word doc; grant the folder + install aem-code-synceds preview /… → eds publish /…npm i -g aem-eds-kiteds init --authoring ueeds scaffold ue · eds scaffold helperseds block from-design (generates the UE model too)eds model add · schema block · track blockeds preview /… → eds publish /…Clone the current EDS boilerplate into a local folder. No GitHub auth needed, and nothing is created on GitHub (that's eds sandbox new). Pick Document Authoring / DA (adobe/aem-boilerplate), Universal Editor / crosswalk (adobe-rnd/aem-boilerplate-xwalk), the Adobe Commerce storefront (hlxsites/aem-boilerplate-commerce), or any repo or git URL.
By default the copy starts a fresh git history with one initial commit naming the boilerplate commit it came from. --keep-history keeps the boilerplate's history with its remote named upstream, so git pull upstream main brings in later boilerplate changes. It refuses a folder that isn't empty, then offers npm install and eds init, with Universal Editor preselected for the crosswalk boilerplate.
$ eds new # guided $ eds new my-site --yes # DA boilerplate, fresh history $ eds new my-site --ue --keep-history # crosswalk, upstream remote $ eds new shop --boilerplate commerce --install $ eds new my-site --remote git@github.com:me/my-site.git
| --boilerplate <repo> | da | ue | commerce, owner/repo on GitHub, or any git URL / path |
| --ue | The crosswalk / Universal Editor boilerplate |
| --ref <ref> | Branch or tag to clone |
| --keep-history | Keep the boilerplate's history (remote renamed to upstream) |
| --remote <url> | Set origin to your own repo |
| --install | Run npm install after cloning |
| --yes | Skip the guided prompts |
Adapt a freshly cloned EDS boilerplate to your setup in one interactive wizard. Pick the authoring model - Universal Editor (crosswalk), DA (da.live), Google Drive or SharePoint - and eds sets the project name (package.json + boilerplate placeholders), writes fstab.yaml only when the model needs it (UE/crosswalk doesn't), scaffolds the crosswalk config + paths.json for UE, optionally adds the CI workflow, then walks the external steps (AEM Code Sync app, content share, Sidekick) as a clickable checklist.
By default it also writes a local-dev .env for aem up (AEM_OPEN=/, AEM_PORT=3007, and AEM_PAGES_URL prefilled from your git remote) - never overwritten, always gitignored - and, for Universal Editor projects, an xwalk.json that enables UE multi-field support. Opt out with --no-env / --no-xwalk, or (re)generate either later with eds scaffold env / eds scaffold xwalk.
$ eds init # or non-interactive: $ eds init --name my-site --authoring da --org my-org --site my-site --yes
| --name <name> | Project name (kebab-case) |
| --authoring <model> | ue | da | gdrive | sharepoint (else detected from fstab.yaml / paths.json) |
| --mountpoint <url> | Content source URL (document models) |
| --org / --site | DA org / site (--authoring da) |
| --no-ci | Skip the GitHub Actions workflow |
| --no-env | Skip the local-dev .env (aem up config) |
| --no-xwalk | Skip xwalk.json (UE multi-field) |
| --yes | Skip prompts; apply with flags/defaults |
Spin up and tear down disposable EDS sandbox repos on GitHub - a rapid, throwaway environment in seconds, straight from an empty folder. Authorize GitHub once, no gh required: eds sandbox login runs the OAuth Device Flow (open github.com/login/device, enter the code) when an OAuth app is set (EDS_GITHUB_CLIENT_ID), otherwise it accepts a Personal Access Token (scopes repo, delete_repo); a GITHUB_TOKEN env var or an authenticated gh also work. The token is stored owner-only in ~/.eds/github.json; eds sandbox logout removes it.
sandbox new creates the repo from a boilerplate (Document Authoring or Universal Editor) via the GitHub API, clones it, tags it eds-sandbox, then guides you through the AEM Code Sync install page and eds init. Preview URL: https://main--<repo>--<owner>.aem.page/.
Safe by design. Sandboxes are throwaway test repos - stand one up to debug something or try an integration, then delete it. It works fine on your personal account. sandbox new never overwrites an existing repo: it checks first and refuses if <owner>/<name> already exists (and the GitHub template API never clobbers either). For least privilege, scope the token to just your sandboxes - see the Secure GitHub sandboxes guide (a fine-grained token for one repo, or a dedicated sandbox org via --org). list only ever shows eds-sandbox repos; rm deletes only the named repo, with confirmation.
$ eds sandbox login # authorize GitHub $ eds sandbox new my-test # guided wizard $ eds sandbox new my-test --ue --private --yes $ eds sandbox list $ eds sandbox rm my-test
| --ue | Use the crosswalk / Universal Editor boilerplate |
| --private | Create a private repo |
| --org <org> | Create under a GitHub org (else your account) |
| --yes | Skip the guided prompts |
Generate EDS page initial content - the EDS analog of an AEM template. In the wizard you compose the blocks in order (add them one by one, repeats allowed). Always writes templates/<name>.html, then routes by your authoring model (detected from fstab.yaml / markers, or --authoring): DA → push to da.live; Google Docs / SharePoint → importable doc + external steps; Universal Editor → writes templates/<name>.json (ordered components + default field values) and registers the blocks. --blocks hero,cards,cards keeps order and repeats.
$ eds template new homepage $ eds template new homepage --blocks hero,cards --push --org my-org --site my-site
| --blocks <list> | Comma-separated block ids to seed |
| --title / --description / --area | Page title, description, metadata label |
| --path <path> | Content path (default /templates/<name>) |
| --authoring <model> | Force da | gdrive | sharepoint | ue (else detected) |
| --push | Push to DA (da.live) |
| --org / --site | DA org / site (else from .edsrc.json) |
Turn a design - a Figma URL, a Framer / Google Stitch page (scraped with headless Chromium), or a local SVG/PNG export - into an EDS block. Run with no URL for a step-by-step wizard (design source, name, link, content source, UE model, screenshot, run mode). eds fetches the design once into a cached snapshot, then the agent generates from it - so retries and refine passes never re-fetch. You see a live, colour-coded log with per-phase timings; on success you get a menu to refine the block with another pass, open the live preview, or finish (preview is opt-in, never auto-launched).
The fetch adapter is chosen automatically: a local file (read from disk), a cached snapshot, the Figma REST API when a token is set (its own budget, separate from MCP's), or a single Figma MCP call (get_design_context) that the agent then caches. A Figma link is parsed into its file key + node id; other sources take the link as-is, so pass --name with them.
$ eds block from-design # or non-interactive: $ eds block from-design "https://figma.com/design/KEY/Name?node-id=42-100" --name hero # or from a local export (no network): $ eds block from-design ./hero.svg --name hero
| --name <name> | Block name (otherwise inferred from the file) |
| --source <type> | Content source: document | ue | cf | mixed |
| --widths <list> | Design frame widths for preview breakpoints (e.g. 390,768,1440) |
| --agent <type> | Force agent: claude | cursor | codex | ollama | omlx | local (any OpenAI-compatible server) | none. A local model gets the design embedded (or a PNG/JPG attached) and the CLI writes its files - Figma needs a token or an export, not MCP. |
| --model <name> | Model to run: a Claude alias / id, or a local model such as qwen2.5-coder:14b (else local.model, else a coder model on the server) |
| --dry-run | Build the prompt only; don't run an agent |
| --no-ue-model | Skip the Universal Editor model |
| --screenshot | Add a screenshot MCP call for a pixel check (opt-in; costs a call) |
| --mcp | Force the Figma MCP adapter even when a REST token is set |
| --refresh | Ignore the cached snapshot and re-fetch |
| --serve | Auto-start the live preview (non-interactive runs only) |
| --yes | Skip confirmation prompts |
Set a Figma Personal Access Token (scope file_content:read) via FIGMA_TOKEN or figma.token in config to use the REST path - its own budget, separate from MCP's. Both follow the plan that owns the file: 20 calls a month on Starter or with a View / Collab seat; with a Dev / Full seat on a paid plan, REST allows 10-20 per minute and MCP 200-600 a day. A REST 429 with a short Retry-After is waited out; a plan cap stops instead of spending MCP calls.
Scaffold an empty block - <name>.js, <name>.css, and a Universal Editor model _<name>.json (title + heading-level select + rich text) by default, so it's authorable in UE immediately.
$ eds block create hero-banner $ eds block create hero-banner --no-ue-model
List every block with its file status, UE model and Figma source. --json for tooling.
$ eds block list $ eds block list --json
Serve a live, breakpoint-switchable preview in your browser - nothing is written to your project (the harness is served from memory). A dropdown switches the block between widths inside an iframe, so its media queries fire against the chosen width. Edits to the block live-reload the browser. Breakpoints come from the design's own frame widths (recorded in the block's .eds-meta.json; frames backed by a real Figma frame are marked pixel-perfect), falling back to 375 / 768 / 1280 or your --widths. An "Open in Figma" link comes from the same metadata.
Run it with no name for a gallery of every content block, each in its own width-honest iframe. Routes are computed per request, so a block generated while the gallery is open appears on the next reload (header/footer are skipped - page chrome can't render standalone).
$ eds block preview # gallery of all blocks $ eds block preview hero $ eds block preview hero --widths 375,768,1280 --port 9000 --no-open
| --port <port> | Port to serve on (default 8777, falls back if taken) |
| --widths <list> | Comma-separated breakpoint widths |
| --no-open | Don't open the browser automatically |
Instrument a block's decorate() with dataLayer tracking on demand: a delegated block_click (links/buttons, label from data-track or the element text) and form_submit, pushed through trackEvent from scripts/analytics.js (created by eds integrate gtm). Adds the import, injects at the top of decorate, idempotent, keeps the real parameter name. Also offered after block from-design and migrate component.
$ eds track block hero $ eds track block hero --no-submit
| --no-click | Skip click tracking |
| --no-submit | Skip form-submit tracking |
Inject a JSON-LD (schema.org) structured-data builder into a block's decorate() for SEO / rich results. Pick a type - Article, FAQPage, BreadcrumbList, Product, Organization, LocalBusiness, Event, VideoObject, Recipe, HowTo - and it appends a <script type="application/ld+json"> to the head, built from the block's content (aliased to root). Idempotent; some fields are heuristic or TODO, so review and validate with Google's Rich Results test.
$ eds schema block faq --type FAQPage $ eds schema block article # interactive picker
| --type <type> | schema.org type (Article, FAQPage, Product, ...) |
| --yes | Skip prompts |
Light Universal Editor helpers - no Docker, no local AEM SDK, no local UE Service to stand up. eds ue check is an offline readiness report: it confirms the project is Universal-Editor-ready (component-definition.json + component-models.json + component-filters.json) and lists which blocks are actually editable - a block is editable when it has a model (_<name>.json or a component-models.json entry), so you catch a block that won't show up in the editor before you open it.
eds ue open [path] builds and opens the deep link into the hosted Universal Editor for your cloud author, pointing the editor canvas at your preview host (derived from the git remote as main--<repo>--<owner>.aem.page). Pass --org to skip the org picker, --ref for another branch, or --url to override the host.
$ eds ue check $ eds ue open /products $ eds ue open / --org my-org --ref dev $ eds ue open /blog --url main--site--owner.aem.page --no-open
| --org <slug> | IMS org slug (skips the org picker) |
| --ref <branch> | Branch/ref for the preview host (default: main) |
| --url <host> | Override the delivery host |
| --no-open | Print the link without opening the browser |
Compose reusable field groups (partials) into a block's Universal Editor model (_<block>.json) instead of hand-writing fields. Fields are added by name, so re-running never duplicates. Partials are built from typed field helpers (each sets the right valueType), and the same catalog is referenced in the from-design / migrate prompt so the agent reuses the standard groups. Built-in: heading, eyebrow, image, richtext, link, cta, cta-group, classes, embed, teaser, card.
$ eds model add hero teaser $ eds model add cards card cta-group $ eds model add hero # interactive picker
Start migrating a classic AEM component to EDS. Phase 1 is deterministic (no agent, no network): it parses the Touch UI dialog (_cq_dialog/.content.xml) and maps its Granite/Coral fields onto the 17 Universal Editor field types - selects keep their options, multifields become repeatable containers, tabs are preserved - then writes blocks/<name>/ with a UE model, a decorate() stub, and a scoped CSS file to fill from the component's clientlib or rendered page.
$ eds migrate component ./components/hero $ eds migrate component ./components/hero --name hero --yes
| --name <name> | Block name (otherwise inferred from the component) |
| --clientlib <dir> | Path to the component's clientlib (otherwise auto-detected) |
| --yes | Skip confirmation prompts |
It also pulls the component's clientlib CSS into the block (honours css.txt + #base=, flags LESS/SCSS). A starting point to review and scope under .<block>; the faithful rendered-CSS capture comes in a later phase.
Write (and merge into) the three UE config files - component-definition.json, component-models.json, component-filters.json - with the default-content components (text, image, button, title) and a field-reference block whose model demonstrates all 17 UE field types plus a multifield. Merge-by-id, so it never clobbers your config.
$ eds scaffold ue
Scaffold standard Block Collection blocks - hero, cards, columns, accordion, embed - each with JS/CSS and a distributed _<name>.json UE model (cards & accordion use the container/child-item multifield pattern), registered in the section filter. No names = all. Existing blocks are skipped.
$ eds scaffold blocks $ eds scaffold blocks hero cards
Generate a GitHub Actions workflow (.github/workflows/eds.yml) that gatekeeps every PR and push to main: it installs deps, runs the project's own lint/build if defined, then the eds audits - doctor, audit loading, audit security - which exit non-zero on problems. Deterministic, no agent. Won't overwrite an existing workflow.
$ eds scaffold ci
Write scripts/utils.js - tick the helpers you want in an interactive checkbox (dependencies pulled in automatically), or pass names. Available: toCamelCase, getPagePath / getLanguageRootPath, getSiteArea (top-level area like blog), getEnvironment, getContentTopic, isUEEdit / isUEPreview / isUE, and getMetadata. Self-contained; won't overwrite an existing file.
$ eds scaffold helpers
Write a local-dev .env for aem up: AEM_OPEN (path opened on start, default /), AEM_PORT (default 3007) and AEM_PAGES_URL (content origin, prefilled from your git remote as https://main--<repo>--<owner>.aem.page). Interactive prompts for each value, or pass flags. It never overwrites an existing .env and always gitignores it (it's local-only and may hold secrets). Also written by default during eds init (opt out with --no-env).
$ eds scaffold env $ eds scaffold env --port 3007 --open / --yes
| --open <path> | AEM_OPEN - path opened on start (default: /) |
| --port <port> | AEM_PORT - proxy port (default: 3007) |
| --pages-url <url> | AEM_PAGES_URL - content origin (default: from git remote) |
| --yes | Skip prompts; use flags/defaults |
Write xwalk.json at the project root, enabling Universal Editor multi-field support (public.xwalk.multi-field.enabled = true). Written by default during eds init for Universal Editor projects (opt out with --no-xwalk); use this to (re)generate it on demand. Never overwrites an existing file.
$ eds scaffold xwalk
Add a third-party service to scripts/delayed.js - the EDS-correct place for non-critical code (delayed phase, after LCP, zero Core Web Vitals impact). Chat widgets use the facade pattern (load on first interaction).
$ eds integrate # interactive $ eds integrate gtm
| gtm | Google Tag Manager |
| ga4 | Google Analytics 4 |
| chat | Chat widget (facade) |
| cookiebot | Cookie consent (Cookiebot) |
| custom | Any script URL |
Interactive wizard: detect installed AI agents, check whether Figma MCP is configured, show the install commands if not, and save your preference to ~/.eds/config.json.
$ eds figma setup
Audit the project for common issues: fstab.yaml valid, head.html present, every block has a .js and a UE model, no console.log, Figma-sync freshness, config present. --json supported.
$ eds doctor
Audit the three-phase loading strategy and LCP budget: nothing render-blocking in head.html, eager phase loads only the first section, styles.css within budget, no heavy libraries in blocks, block CSS scoped.
$ eds audit loading
Scan for vulnerabilities (npm audit) and exploit patterns: eval, dynamic innerHTML, document.write, javascript: URLs, hardcoded secrets / keys, mixed content, target="_blank" tabnabbing, postMessage('*'). Exits non-zero on critical/high - wire it into CI.
$ eds audit security $ eds audit security --json
Preview (staging) or publish (go live) pages through the AEM Admin API. Run with no paths for an interactive wizard (operation, org/site/ref, paths, confirmation). org, site and ref default from .edsrc.json.
$ eds preview # wizard $ eds preview /index /about $ eds publish /index --org my-org --site my-repo --ref main
| --verbose | Debug-level output |
| --quiet | Errors only |
| --json | Machine-readable JSON output (where supported) |
| --no-color | Disable colours (also respects NO_COLOR) |
| --version | Print the version |
| --help | Show help for any command |
Blocks can be driven by different content models - pick one in the from-design wizard or with --source:
| document | Default EDS - decorate() enhances the authored DOM |
| ue | Universal Editor authoring |
| cf | Content Fragment - async decorate() fetches a CF via GraphQL |
| mixed | Some fields authored inline (UE) + some from a Content Fragment, in one block |
The source and the UE model go together: pick ue, cf or mixed and the Universal Editor model is generated so authors can edit in context. With document (Google-Docs-style authoring) a UE model isn't used - add --no-ue-model if you don't want one.
Generated UE models use the full catalog of all 17 field component types: text, textarea, richtext, reference, aem-content, aem-content-fragment, aem-experience-fragment, aem-tag, select, multiselect, checkbox-group, radio-group, boolean, number, date-time, container (multifield), tab. Run eds scaffold ue for a live reference in your own project.