CLI reference
The nera CLI (@nera-static/nera)
One command scaffolds, builds, previews, updates and validates a site. Scaffold a new one with:
npx @nera-static/nera new <project-name>
nera new . scaffolds into the current, empty folder instead, and
--theme <name> starts the site from a theme. --starter <name> starts it
from a ready-to-edit starter kit instead (blank, blog, business or
portfolio; not together with --theme), and --name "<site name>" and
--lang <code> set name and lang in config/app.yaml:
npx @nera-static/nera new my-blog --starter blog --name "My Blog" --lang de
--no-install skips the npm install at the end; run it yourself later.
On a terminal, without --starter or --yes, nera new asks what you are
building, the site name and the language; with --yes, or without a terminal,
it never asks. Starter kits below has the details.
A scaffolded site depends on @nera-static/nera, so run these from inside it
with npx, e.g. npx nera validate. The scaffold also wires build, dev and
serve to npm scripts, e.g. npm run dev:
| Command | What it does |
|---|---|
nera build |
Build pages/ → public/; with --check, check the result too |
nera dev |
Build + live-reload preview on :3000 |
nera serve |
Serve public/ without rebuilding |
nera update |
Update the site's Nera packages (npm update) and raise their ^ ranges in package.json to the installed versions, then create or refresh AGENTS.md and CLAUDE.md |
nera validate |
Check layouts, includes and YAML before publishing |
nera check |
Check the built public/ for accessibility, privacy and legal-notice problems |
nera publish |
Publish the site on GitHub Pages: sign in with GitHub, create the repository, push and print the live URL |
Publishing with nera publish. nera publish takes a site from its
folder to a live URL through GitHub: it signs you in with the "Nera Static"
GitHub App, creates the repository (or reuses the one in
config/publish.yaml), generates .github/workflows/deploy.yml, pushes and
follows the Actions run. The host is GitHub Pages (--target github-pages,
the default): it switches Pages on with the GitHub Actions build type, sets
base_path in config/app.yaml for a project page (/<repo>; none for a
<user>.github.io repository or a custom domain) and prints the live URL.
--domain <host> sets a custom domain in the Pages settings, checks its DNS
and prints the records to set when they are missing or wrong. The other flags
are --repo <name> (default: the folder name), --private (GitHub Pages
serves a private repository only on a paid GitHub plan), --yes, --json
and --sign-out. Netlify follows in a later release. To host somewhere else,
build the site and upload the contents of public/ to any static host, as
Deployment describes.
Starter kits
A starter kit is a whole ready-to-edit site: sample pages, config, optional
site-level theme/ overrides, assets and the theme and plugins it depends on.
nera new --starter copies its pages/, config/, theme/, assets/,
plugins/ and .neraignore over the base template, adds its dependencies
to the site's package.json, applies --name and --lang, installs, and
prints the ## First steps section of the starter's README (nothing, when it
has none). After that the site owns the files: a starter has no update path,
though the theme it depends on still updates through npm.
--name "<site name>" and --lang <code> set name: and lang: in
config/app.yaml, with or without a starter. A new language gets a copy of
the current language's translations: entry, ready to translate, so no page
shows a raw translation key.
--starter takes:
- a starter name:
blank,blog,businessorportfolio, short for@nera-static/starter-blankand so on; - a package name:
@acme/starter-shop, or unscopednera-starter-shop; - a GitHub URL:
https://github.com/owner/repo(optionally.git,#ref), orgithub:owner/repo#ref; - a local path:
./my-starter,../my-starteror an absolute path.
Packages and GitHub repositories are downloaded with
npm pack --ignore-scripts, so no script of the starter runs. A starter is a
package whose package.json has a nera.starter field
({ "label": "…", "description": "…" }); anything else is refused before
anything is written. A starter that sets theme: in its config/app.yaml
leaves out the scaffold's starter templates, as --theme does.
Run nera new my-site on a terminal and it asks instead:
What are you building?
1) Empty — the base template, no starter
2) …
Choose 1–2 [1]:
Site name [My Nera Site]:
Language [en]:
The list comes from the npm registry (packages with the keyword
nera-starter, official @nera-static/ ones first), or from a short list
bundled with the CLI when the registry cannot be reached. Enter accepts the
default. Every question has a flag (--starter, --name, --lang); with
--yes, or without a terminal (scripts, CI, AI agents), nothing is asked and
nera new scaffolds the base template.
The official starters:
| Starter | What you get |
|---|---|
blank |
One accessible layout, no theme |
blog |
A blog on @nera-static/theme-blog with tags, an archive, an Atom feed and a sitemap |
business |
A small-business site on @nera-static/theme-business with services, opening hours and a contact form |
portfolio |
A designer's portfolio on @nera-static/theme-business with projects in image galleries, an about page and a contact form |
Checking the built site
nera validate reads your sources. nera check reads what the build produced —
the page a visitor gets, with its layout, navigation, footer, scripts,
stylesheets and fonts — and looks for problems a parser can find there:
nera build --check # build, then check — the one command for CI
nera check # check an existing public/ without rebuilding
Run nera build first; without public/, nera check stops with "run
nera build first". A problem in a template is reported once, with the number
of pages it appears on.
Every finding is a warning by default, so the command exits 0. It exits
1 only when a rule you promoted to error fires — that is how you make it a
CI gate.
These are hints, not legal advice. A clean run is not proof of compliance: automated checks find only part of the accessibility problems (about a third of WCAG failures). Colour contrast, visible focus and whether a law applies to your site are not checked. Every report ends with that reminder.
| rule | default | basis |
|---|---|---|
a11y-html-lang |
warning | WCAG 3.1.1 |
a11y-title |
warning | WCAG 2.4.2 |
a11y-h1 |
warning | WCAG 1.3.1 |
a11y-heading-skip |
warning | WCAG 1.3.1 |
a11y-img-alt |
warning | WCAG 1.1.1 |
a11y-form-label |
warning | WCAG 1.3.1, 4.1.2 |
a11y-link-name |
warning | WCAG 2.4.4, 4.1.2 |
a11y-main |
warning | WCAG 1.3.1 |
a11y-skip-link |
warning | WCAG 2.4.1 |
a11y-nav-name |
warning | WCAG 1.3.1 |
a11y-duplicate-id |
warning | WCAG 4.1.2 |
a11y-viewport-zoom |
warning | WCAG 1.4.4 |
a11y-link-lang |
off (opt-in) | WCAG 3.1.2 |
a11y-target-blank |
off (opt-in) | WCAG 3.2.5 (AAA) |
a11y-reduced-motion |
off (opt-in) | WCAG 2.3.3 (AAA) |
privacy-third-party |
warning | Art. 6 DSGVO |
privacy-consent-leak |
warning | § 25 TDDDG |
privacy-insecure |
warning | Art. 32 DSGVO |
privacy-storage |
off (opt-in) | § 25 TDDDG |
legal-imprint-link |
warning | § 5 DDG |
legal-privacy-link |
warning | Art. 13 DSGVO |
legal-outdated-law |
warning | DDG, TDDDG, MStV |
Configure the checks in config/validate.yaml (optional — without it the
defaults above apply):
rules:
a11y-img-alt: error # promote: the check fails
a11y-target-blank: warning # enable an opt-in rule
a11y-skip-link: off # disable
legal:
imprint: # per language, the page every page links to
de: /de/impressum.html
en: /en/imprint.html
privacy:
de: /de/datenschutz.html
en: /en/privacy.html
privacy:
allowed_hosts: # third-party hosts you have accounted for
- cdn.example.org
To silence a rule on one page, list it in the page's frontmatter as
validate_ignore: [a11y-h1].
To silence a rule on whole files or folders, list them under ignore. This
works for nera validate too — for example for layout-missing on content
fragments that another page pulls in, or on drafts, which have no layout on
purpose:
ignore:
layout-missing:
- pages/*/references # a folder covers everything below it
- pages/de/blog/drafts # * stands for one folder name
Paths are relative to the site root. Requires @nera-static/validate 1.3.0,
which npm update brings into an existing site.
Your own host. To tell your resources from third-party ones,
privacy-third-party reads origin from config/app.yaml, else app_origin
from config/canonical-links.yaml (with and without www.). With neither set,
only relative URLs count as your own.
Consent-gated resources. A script or embed in the markup of
@nera-static/plugin-consent (type="text/plain" with data-src, or
data-consent-src instead of src) loads nothing on page view, so
privacy-third-party does not report it. An element marked data-consent
that still loads is what privacy-consent-leak reports. The tutorial
Add a consent banner — or none shows both.
Imprint and privacy links in German and English only. Without legal
config, the links are found by their text — Impressum, Imprint, Legal
notice, Datenschutz, Privacy, Data protection — and only on German and
English pages. For any other language, name the pages under
legal.imprint.<lang> and legal.privacy.<lang>.
The full rule descriptions are in the
@nera-static/validate README.
Instructions for AI assistants
Every scaffolded site carries an AGENTS.md — the file Codex, Cursor, Copilot
and other assistants read first — and a CLAUDE.md holding the single line
@AGENTS.md, so Claude Code loads the same text. AGENTS.md ends with a
Notes for this site section for your own instructions, below the marker
line <!-- nera:site-notes … -->. Keep that line in place: the part above it
is Nera's, the part below it is yours.
From @nera-static/nera 1.7.0 on, nera update keeps both files current, with
no extra flag. After updating the packages (and after nera update --migrate)
it:
- creates
AGENTS.mdandCLAUDE.mdwhen they are missing, so an older site gets them too; - replaces only the part of
AGENTS.mdabove the marker line with the text from the installed@nera-static/nera; the marker line and your notes below it stay exactly as they are; - leaves an
AGENTS.mdwithout the marker line alone and prints a hint — copy the marker line from the template directly above your own notes to opt in; - never touches an existing
CLAUDE.md.
A file is written only when its content changes, so running nera update again
changes nothing.
Migrating an older (cloned) site
Sites created before the CLI were git clones that vendored the engine under
src/. Inside such a site, run nera update --migrate — it adds the
@nera-static/nera dependency, rewrites the scripts, removes the vendored
src/ engine, and installs, leaving your pages/, config/ and theme/
untouched.
Plugin publish commands
Each template-shipping plugin exposes a bin to copy its Pug into
theme/views/vendor/, e.g.:
npx nera-navigation
npx nera-tags
npx nera-search --force # overwrite existing published templates