Docs

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.

Install

Node.js 20 or newer. Installs the eds command globally.

$ npm i -g aem-eds-kit

From zero to a page - pick your authoring model

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.

DAda.live
  1. 1npm i -g aem-eds-kit
  2. 2eds init --authoring da --org … --site …
  3. 3eds scaffold ue · eds scaffold helpers
  4. 4eds block from-design (or create / migrate)
  5. 5eds model add · schema block · track block
  6. 6eds template new home --blocks … --push → pushed to DA
  7. 7eds preview /… → eds publish /…
Google Docs
  1. 1npm i -g aem-eds-kit
  2. 2eds init --authoring gdrive --mountpoint <drive-folder>
  3. 3eds scaffold ue · eds scaffold helpers
  4. 4eds block from-design (or create / migrate)
  5. 5eds model add · schema block · track block
  6. 6eds template new home → import templates/home.html into a Google Doc; share the folder + install aem-code-sync
  7. 7eds preview /… → eds publish /…
SharePoint
  1. 1npm i -g aem-eds-kit
  2. 2eds init --authoring sharepoint --mountpoint <sp-folder>
  3. 3eds scaffold ue · eds scaffold helpers
  4. 4eds block from-design (or create / migrate)
  5. 5eds model add · schema block · track block
  6. 6eds template new home → import templates/home.html into a Word doc; grant the folder + install aem-code-sync
  7. 7eds preview /… → eds publish /…
Universal Editorcrosswalk
  1. 1npm i -g aem-eds-kit
  2. 2eds init --authoring ue
  3. 3eds scaffold ue · eds scaffold helpers
  4. 4eds block from-design (generates the UE model too)
  5. 5eds model add · schema block · track block
  6. 6author in the Universal Editor (AEM); connect the repo to AEM. (eds UE templates coming)
  7. 7eds preview /… → eds publish /…

eds new [dir]

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
--ueThe crosswalk / Universal Editor boilerplate
--ref <ref>Branch or tag to clone
--keep-historyKeep the boilerplate's history (remote renamed to upstream)
--remote <url>Set origin to your own repo
--installRun npm install after cloning
--yesSkip the guided prompts

eds init

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 / --siteDA org / site (--authoring da)
--no-ciSkip the GitHub Actions workflow
--no-envSkip the local-dev .env (aem up config)
--no-xwalkSkip xwalk.json (UE multi-field)
--yesSkip prompts; apply with flags/defaults

eds sandbox login | new | list | rm

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
--ueUse the crosswalk / Universal Editor boilerplate
--privateCreate a private repo
--org <org>Create under a GitHub org (else your account)
--yesSkip the guided prompts

eds template new <name>

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 / --areaPage title, description, metadata label
--path <path>Content path (default /templates/<name>)
--authoring <model>Force da | gdrive | sharepoint | ue (else detected)
--pushPush to DA (da.live)
--org / --siteDA org / site (else from .edsrc.json)

eds block from-design [design-url]

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-runBuild the prompt only; don't run an agent
--no-ue-modelSkip the Universal Editor model
--screenshotAdd a screenshot MCP call for a pixel check (opt-in; costs a call)
--mcpForce the Figma MCP adapter even when a REST token is set
--refreshIgnore the cached snapshot and re-fetch
--serveAuto-start the live preview (non-interactive runs only)
--yesSkip 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.

eds block create <name>

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

eds block list

List every block with its file status, UE model and Figma source. --json for tooling.

$ eds block list
$ eds block list --json

eds block preview [name]

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-openDon't open the browser automatically

eds track block <name>

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-clickSkip click tracking
--no-submitSkip form-submit tracking

eds schema block <name>

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, ...)
--yesSkip prompts

eds ue check | open

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-openPrint the link without opening the browser

eds model add <block> [partials...]

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

eds migrate component [dir]

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

eds scaffold ue

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

eds scaffold blocks [names...]

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

eds scaffold ci

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

eds scaffold helpers

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

eds scaffold env

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)
--yesSkip prompts; use flags/defaults

eds scaffold xwalk

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

eds integrate [type]

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
gtmGoogle Tag Manager
ga4Google Analytics 4
chatChat widget (facade)
cookiebotCookie consent (Cookiebot)
customAny script URL

eds figma setup

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

eds doctor

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

eds audit loading

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

eds audit security

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

eds preview / publish [paths...]

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

Global flags

--verboseDebug-level output
--quietErrors only
--jsonMachine-readable JSON output (where supported)
--no-colorDisable colours (also respects NO_COLOR)
--versionPrint the version
--helpShow help for any command

Content sources

Blocks can be driven by different content models - pick one in the from-design wizard or with --source:

documentDefault EDS - decorate() enhances the authored DOM
ueUniversal Editor authoring
cfContent Fragment - async decorate() fetches a CF via GraphQL
mixedSome 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.

Universal Editor field types

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.