@electriccitizen/bolt
v0.8.1
Published
CLI tool for Drupal 11+ site testing and operations
Readme
Bolt
CLI tool for automated Drupal 11+ site testing, updates, and PR creation. Built by Electric Citizen.
Install once per site ("Bolt Certified"), pull updates via npm without re-initializing.
Principles
These guide every design decision in Bolt:
- Non-invasive. You can remove Bolt from any project with no consequence. Bolt doesn't modify your site's code, config, or database beyond its own tracked test content (which it cleans up). Uninstalling the module and removing the Composer entry is a clean exit.
- Transparent. Bolt tells you what it's doing at every step. Progress output, clear error messages, and no hidden side effects. If Bolt changes something, it says so.
- Careful. When a destructive, ambiguous, or unexpected situation arises, Bolt warns you and asks before proceeding. It won't overwrite your database, force-push your code, or modify files outside its scope without explicit confirmation.
- Idempotent. Every command is safe to re-run.
bolt initskips steps already done.bolt suppressregenerates cleanly.bolt testleaves the site in the state it found it. - Automation-friendly. Predictable exit codes (0/1/2), structured output formats (JSON, markdown), no interactive prompts in test/CI paths. Humans and scripts get the same reliability.
- Site-agnostic. Bolt adapts to what it finds. It reads your site's structure and tests accordingly — no hardcoded content types, no assumptions about your setup. Works on any Drupal 11+ site.
Requirements
- Node.js 20+
- DDEV
- A Drupal 11+ site running in DDEV
Installation
# npm global install (recommended)
npm install -g @electriccitizen/bolt
npx playwright install chromium
# Verify
bolt --version
bolt doctorDevelopment install (for contributors):
git clone [email protected]:electriccitizen/bolt.git ~/projects/bolt
cd ~/projects/bolt
./scripts/setup.shQuick Start
# From your Drupal project directory (on main branch):
bolt init # installs module, creates config (run on main!)
bolt doctor # verify environment + coverage report
bolt test # full test suite
# Capture known issues as baseline
bolt suppress # creates .boltrc.yml
bolt test # now only NEW regressions cause failures
# Check site status and available updates
bolt status # git state, updates, AI risk assessment
# Before running updates, sync local environment
bolt refresh --db # pull latest code + database
bolt update --dry-run --all # preview updates with risk assessment
bolt update --all # apply updates (each tested + rolled back on failure)
bolt pr --all # create PRs for reviewCommands
bolt test
Run tests against a Drupal site. Auto-discovers what to test based on site structure.
bolt test [options]
--url <url> Site URL (auto-detected from DDEV if omitted)
--mode <mode> full | read-only | admin-only (default: full)
--plugins <list> Comma-separated plugin names to run
--output <format> text | json | markdown (default: text, auto-json when piped)
--report <path> Write report to file
--exit-code Non-zero exit on failure (for CI)
--fail-on <severity> Threshold: critical | major | minor (default: major)
--headed Show browser window
--screenshots <dir> Save failure screenshots
--vr-baseline <dir> Visual regression baseline directory
--vr-threshold <n> VR diff threshold percentage (default: 0.1)
--content-types <list> Limit to specific content typesExit codes: 0 = pass, 1 = failure at or above threshold, 2 = error.
Execution modes:
full— generate test content, authenticate, run all plugins, cleanupread-only— no auth, no content changes, only anonymous-safe pluginsadmin-only— authenticate but don't generate content
Auto-JSON: When stdout is piped (non-TTY), bolt outputs JSON automatically.
Examples:
bolt test --screenshots=./screenshots # full suite with failure screenshots
bolt test --mode=read-only # anonymous-only tests
bolt test --plugins=accessibility # single plugin
bolt test --content-types=page,blog # specific content types
bolt test --exit-code --fail-on=major # CI mode
bolt test --output=markdown --report=report.md # markdown report for a PRbolt suppress
Capture current test failures as a suppression baseline. Creates .boltrc.yml.
bolt suppress [options]
--url <url> Site URL (auto-detected from DDEV)
--dry-run Preview without writing file
--mode <mode> Execution mode (default: full)Suppressed results appear in output but don't affect exit codes. Only new regressions cause failures. Safe to re-run — always captures the full current state.
bolt refresh
Sync local environment to match production before running updates.
bolt refresh [options]
--db Pull fresh database (prompted if omitted)
--skip-db Skip database pull entirely
--source <env> Database source environment (default: live)
--yes Skip confirmation prompts
--branch <name> Base branch to checkout (default: main)What it does:
- Checks for uncommitted changes (warns, prompts to continue)
- Checks out base branch and pulls latest
- Starts DDEV if not running
- Runs
composer install(sync to lockfile) - Optionally pulls database from hosting provider
- Runs
drush cr,drush updb,drush cim,drush cr
Each step logs its duration. Exits early on failure.
Examples:
bolt refresh --db # full refresh with database
bolt refresh --skip-db # code-only refresh
bolt refresh --db --yes # non-interactive (for scripting)
bolt refresh --branch=develop --db # refresh from a different branchbolt update
Run Drupal module/core updates with per-module testing and rollback.
bolt update [options]
--module <name> Update a specific module (Composer package name)
--all Update all outdated packages
--security-only Only security updates
--dry-run Show what would be updated without doing it
--skip-refresh Skip the pre-update refresh step
--skip-test Skip the baseline test
--no-ai Disable AI reasoning (use scripted fallback)
--yes Skip confirmation promptsWhat it does:
- Pre-flight: refreshes local state, runs baseline test, captures VR baselines, gets outdated packages
- Per-module loop: creates branch, updates via Composer, runs
drush updb, runs full test suite + VR comparison - If tests pass: exports config, commits on update branch
- If tests fail: rolls back changes, deletes branch, continues to next module
- Produces summary report with per-module results
Visual regression: Before any updates begin, bolt captures screenshots of all representative pages. After each module update, it compares against those baselines. Module updates should produce zero visual changes — any difference triggers a test failure and rollback. VR runs automatically during updates even if skipped in .bolt.yml.
Each update gets its own branch (update/<package>-<version>) off the base branch for clean per-module PRs.
Exit codes: 0 = at least one update succeeded, 1 = all failed, 2 = error.
Examples:
bolt update --dry-run --all # see what would be updated
bolt update --module=drupal/token # update a single module
bolt update --all --security-only # only security updates
bolt update --all --yes # non-interactive, all updates
bolt update --all --skip-refresh # skip the git pull / composer install stepbolt pr
Create GitHub PRs from update branches produced by bolt update. Requires GitHub CLI.
bolt pr [options]
--base <branch> Base branch for PR (default: main)
--reviewers <list> Comma-separated GitHub usernames
--draft Create as draft PR
--all Create PRs for all update/* branches
--no-ai Disable AI-generated PR descriptions
--yes Skip confirmation promptsWhat it does:
- Warns if uncommitted changes exist (PRs only include committed state)
- Finds update branches (current branch or
--allfor allupdate/*branches) - Confirms before pushing (lists branches that will be pushed)
- Parses commit messages for update details (package, versions, test results)
- Pushes branch to remote
- Creates PR with structured body via
gh pr create - Checks for existing PRs to avoid duplicates
Examples:
bolt update --module=drupal/token # creates update/drupal-token-1.15.0
bolt pr --reviewers=broeker # creates PR for current branch
bolt update --all # creates multiple update branches
bolt pr --all --draft # creates draft PRs for all of thembolt analyze
AI-powered ticket analysis — produces an investigation plan from a Jira ticket or description.
bolt analyze [options]
--ticket <key> Jira ticket key (e.g. PROJ-123) — pulls via Jira MCP
--description <text> Bug description (manual, no Jira needed)
--url <url> Site URL (auto-detected from DDEV if omitted)
--output <format> text | json | markdown (default: text)
--report <path> Write analysis to fileRequires AI — there is no --no-ai fallback for this command, since analysis IS the AI.
Input methods:
--ticket PROJ-123— pulls from Jira via Atlassian MCP (configured per user)--description "..."— manual text inputcat ticket.txt | bolt analyze— stdin
Output includes: problem summary, likely root causes (ranked), investigation steps with specific commands, risk assessment, suggested bolt tests to run after fix, and a confidence assessment for whether the issue can be fixed autonomously.
Examples:
bolt analyze --ticket EC-42 # Jira integration
bolt analyze --description "Media upload broken" # manual input
bolt analyze --ticket EC-42 --output markdown --report analysis.mdbolt fix
Autonomous ticket resolution — analyze, fix, test, and create a PR.
bolt fix [options]
--ticket <key> Jira ticket key (runs analysis internally)
--description <text> Bug description (manual, no Jira needed)
--analysis <path> Path to saved bolt analyze report (JSON)
--context <text> Additional context (answers to questions from analyze)
--interactive Launch interactive Claude session (converse instead of autonomous)
--dry-run Show planned changes without modifying files
--skip-test Skip post-fix test run
--skip-pr Skip PR creation
--branch <name> Branch name (default: fix/<ticket-key>)
--yes Skip confirmation promptsRequires AI — no --no-ai fallback.
Two modes:
- Autonomous (default): Claude runs unattended with full tool access (ddev, drush, file editing). The confidence model from
bolt analyzegates entry — if it says "needs info" or "needs human", bolt exits with instructions. - Interactive (
--interactive): Launches a live Claude session with the ticket analysis, site profile, and Drupal knowledge pre-loaded. You converse with Claude to guide the fix.
Examples:
# Full autonomous flow
bolt fix --ticket EC-42 # analyze → fix → test → PR
# Provide answers to questions from analyze
bolt fix --ticket EC-42 --context "Chrome only, content_editor role"
# Interactive — converse with Claude
bolt fix --ticket EC-42 --interactive
# Dry run — see what would change
bolt fix --description "footer logo broken" --dry-runbolt init
Connect bolt to a client site. Run from the site's project root.
bolt init [options]
--skip-composer Skip Composer setup
--skip-module Skip module enable
--upgrade Update bolt-inspect module via Composer
--yes Skip confirmation promptsWhat it does:
- Warns if NOT on
main/masterbranch (init belongs on main so all branches inherit setup) - Installs
electriccitizen/bolt-inspectvia Composer (from Packagist) - Enables
bolt_inspectDrupal module - Creates
.bolt.ymlconfig template - Adds bolt artifacts to
.gitignore
Other developers on the same project just run composer install — the module is in composer.json like any other dependency. No additional setup needed beyond installing the bolt CLI globally.
Idempotent — safe to re-run.
bolt status
AI-enhanced site status overview — git state, available updates with risk assessment, recommended update order.
bolt status [options]
--no-ai Disable AI risk assessment
--output <format> text | json (default: text)Example output:
Git:
✓ Branch: main
✓ Working tree clean
Site:
✓ DDEV running: https://acme.ddev.site
✓ Drupal 11.1.5, PHP 8.3
✓ 6 content types, 25 paragraph bundles
Available updates:
✗ 1 security update(s):
drupal/core: 11.1.3 → 11.1.5 [SECURITY]
! 3 other update(s):
drupal/webform: 6.2-beta8 → 6.2-beta9 (patch)
drupal/token: 1.14.0 → 1.15.0 (minor/major)
drupal/paragraphs: 1.17.0 → 1.18.0 (minor/major)
AI assessment:
! Risk: medium — Security fix plus minor version bumps
Recommended update order:
1. drupal/core — Security fix, apply first
2. drupal/paragraphs — Dependency of multiple content types
3. drupal/webform — Patch update, low risk
4. drupal/token — Leaf dependency, safe last
Next steps:
→ Run bolt update --security-only first
→ Run bolt test after core update before proceedingbolt doctor
Validate the environment.
bolt doctorChecks: bolt version, Node.js, Playwright, DDEV, module status, CLI/module version compatibility, .bolt.yml validity, full test coverage report (content types, fields, paragraph bundles, plugins).
Test Plugins
| Plugin | What it tests | Mode |
|--------|--------------|------|
| structural-smoke | Create + render nodes for every content type via Drush | full |
| browser-smoke | Visit URLs, check HTTP status, JS errors, broken images | all |
| accessibility | axe-core WCAG 2.0 AA scan on all representative URLs | all |
| visual-regression | Screenshot comparison across 3 viewports per URL | all |
| field-interaction | Fill and submit admin forms for each content type | full, admin |
| media-browser | Upload media via library interface | full, admin |
| wysiwyg | CKEditor 5 toolbar, formatting, media embed | full, admin |
| linkit | LinkIt autocomplete in CKEditor | full, admin |
Plugins skip gracefully when their required modules aren't installed.
Configuration
.bolt.yml
Per-site test configuration, created by bolt init.
adapter: ddev
plugins:
skip:
- visual-regression
config:
accessibility:
rules_to_skip: [color-contrast]
visual-regression:
threshold: 0.5
mask:
- selector: ".block-current-date"
custom_routes:
- /products
- /about
refresh:
base_branch: main
db:
source: pantheon # pantheon | file (or any ddev pull provider)
pantheon_site: mysite # Terminus site name
pantheon_env: live # Environment to pull from
# source: file
# file_path: /path/to/dump.sql.gz
update:
ignore:
- drupal/core-* # Update core manually
priority:
- drupal/core-recommended
pr:
base_branch: main
default_reviewers:
- broeker
draft: false
remote: origin.boltrc.yml
Per-site suppression rules, created by bolt suppress.
suppress:
js_errors:
- pattern: "Siteimprove"
reason: "Analytics script, fails locally"
urls:
- path: "/admin/orphan"
reason: "Known 403, will be removed"
accessibility:
- rule: "color-contrast"
reason: "Design fix scheduled Q2"
plugins:
- name: "visual-regression"
reason: "Baselines not captured yet"Severity Levels
| Severity | Meaning | Example |
|----------|---------|---------|
| critical | Site broken | CKEditor toolbar missing, node creation fails |
| major | Significant functionality broken | Media upload fails, form validation errors |
| minor | Cosmetic or edge-case issue | Broken image on one page, minor a11y violation |
| info | Informational | Test passed, baseline captured |
--fail-on controls which severities trigger non-zero exit. Default is major.
Upgrading
# Update bolt CLI
npm update -g @electriccitizen/bolt
# Update the Drupal module on each site
cd ~/projects/client-acme
bolt init --upgrade
bolt doctor # verify versions matchbolt init --upgrade runs composer update electriccitizen/bolt-inspect and clears caches — no config changes, no re-init.
Roadmap
See docs/PLAN-MVP.md for the post-MVP roadmap.
Development
See docs/CONTRIBUTING.md for architecture, dev setup, and how to add plugins.
License
Proprietary - Electric Citizen
