# climaybe

Shopify CLI for **theme CI/CD** (GitHub Actions, branches, multi-store config) and light **app repo** setup. Same install works in theme or app repositories.

Built by [Electric Maybe](https://electricmaybe.com) — a Shopify-focused product and development studio.

**Commit linting and AI ruleset (optional in both flows):**

- **Conventional commit linting:** During `climaybe theme init` or `climaybe app init`, you can install [commitlint](https://commitlint.js.org/) and [Husky](https://typicode.github.io/husky) for [Conventional Commits](https://www.conventionalcommits.org/).
- **AI ruleset (rules + skills + subagents):** Opt in to Electric Maybe’s bundled rules, skills, and subagents (themes, JS, a11y, commits, changelog, Linear, **theme-translator** for locale sync, etc.). They install into a single `.config/ai/` source of truth and are bridged to the editors you pick (Cursor, Claude, Copilot, Windsurf, Cline, …). See [AI ruleset](#ai-ruleset).

## Command layout (Shopify CLI–style)

- **`climaybe theme <command>`** — canonical commands for theme repos (workflows, stores, branches).
- **Same commands at the top level** — `climaybe init` is the same as `climaybe theme init` (backward compatible).
- **`climaybe app init`** — app repos only: writes `project_type: "app"` in `climaybe.config.json`, optional commitlint + AI ruleset. Does **not** install theme GitHub Actions or store/branch setup.
- **`climaybe setup-commitlint`** and **`climaybe add-cursor`** — always at the top level (stack-agnostic).

Theme-only commands refuse to run when `climaybe.config.json` → `project_type` is **`app`**.

## Install

```bash
cd your-shopify-theme-repo   # or app repo
npm install -D climaybe
```

Run commands with `npx climaybe` (or add scripts to your `package.json`).

When a newer `climaybe` is available, the CLI can prompt at startup to update. Press **Enter** to accept the update (`npm install -g climaybe@latest`) or type `n` to skip.

## Quick Start (theme)

```bash
cd your-shopify-theme-repo
npm install -D climaybe
npx climaybe init
# equivalent: npx climaybe theme init
```

The interactive setup will ask for your store URL(s) and configure everything automatically.

## Quick Start (app)

```bash
cd your-shopify-app-repo
npm install -D climaybe
npx climaybe app init
```

Installs optional commitlint/Husky and the [AI ruleset](#ai-ruleset) (rules, skills, agents). Use [Shopify CLI](https://shopify.dev/docs/api/shopify-cli) for app development and deployment.

## Commands

### `climaybe init` / `climaybe theme init`

Interactive setup that configures your repo for CI/CD. Each question shows a one-line
hint (and a docs link where it helps) so you know what you're choosing. Defaults are
shown in the prompt; press Enter to accept.

1. Prompts for your store URL (e.g., `voldt-staging.myshopify.com`)
2. Extracts subdomain as alias, lets you override
3. Asks if you want to add more stores (two or more → multi-store mode)
4. Asks whether to enable **[preview + cleanup workflows](#preview-and-cleanup-workflows)** (default: yes)
5. Asks whether to enable **[build workflows](#build-and-lighthouse-workflows)** (default: yes)
6. If build is on, asks separately whether to run **[Lighthouse CI](#build-and-lighthouse-workflows)** on staging (default: yes)
7. Asks whether to install the **[theme dev kit](#theme-dev-kit)** (default: yes), and if so whether to add VS Code tasks
8. Asks whether to enable **commitlint + Husky** (enforce [conventional commits](https://www.conventionalcommits.org/) on `git commit`)
9. Asks whether to set up **[branch protection](#branch-protection)** (default: yes)
10. Asks whether to install the **[AI ruleset](#ai-ruleset)**, and if so which editors to bridge
11. Writes `climaybe.config.json`, scaffolds workflows, and creates branches/store directories
12. Optionally configures CI secrets (and can add a GitHub/GitLab remote if the folder has none)

### `climaybe app init`

Interactive setup for a **Shopify app** repository: optional commitlint + Husky, optional [AI ruleset](#ai-ruleset), and `project_type: "app"` in `package.json` `config`. No theme workflows, stores, or staging/live branches.

### `climaybe add-store` / `climaybe theme add-store`

Add a new store to an existing setup.

```bash
npx climaybe add-store
```

- Prompts for new store URL + alias
- Creates `staging-<alias>` and `live-<alias>` branches
- Creates `stores/<alias>/` directory structure
- If store count goes from 1 to 2+, automatically migrates from single to multi-store mode

### `climaybe switch <alias>` / `climaybe theme switch`

Switch your local dev environment to a specific store (multi-store only).

```bash
npx climaybe switch voldt-norway
```

Copies `stores/<alias>/` JSON files to the repo root so you can preview that store locally, and sets `default_store` in `climaybe.config.json` so `climaybe serve` / `shopify theme dev` targets that store. If your current git branch is `staging-<alias>` or `live-<alias>`, `climaybe serve` still passes that branch’s store to Shopify for `--store` (even if `default_store` differs), matching CI preview behavior.

### `climaybe serve` / `climaybe theme serve`

Runs Tailwind/scripts/schema watchers and `shopify theme dev` (see the theme dev kit section for flags). In **multi-store** mode, the first step asks **which store to serve** (interactive terminal); the default choice is the store matching `default_store` (same as after `climaybe switch`). If you pick a **different** store, the CLI saves the current root `config/`, `templates/`, and `sections/` JSON files into `stores/<previous-alias>/` (same paths as `climaybe sync`), then copies the selected store’s JSONs to the root and updates `default_store`, so work in progress is not lost. In **CI** or when **stdin is not a TTY**, there is no prompt: it uses `default_store` unless you set **`CLIMAYBE_SERVE_STORE=<alias>`** to a configured alias.

### `climaybe sync [alias]` / `climaybe theme sync`

Sync root JSON files back to a store directory (multi-store only).

```bash
npx climaybe sync voldt-norway
```

If no alias is given, syncs to the default store.

### `climaybe ensure-branches` / `climaybe theme ensure-branches`

Create missing branches from your current branch (usually `main`). In single-store mode, this creates `staging` only. In multi-store mode, this creates `staging` plus per-store branches (`staging-<alias>`, `live-<alias>`). Use when the repo only has `main` (e.g. after a fresh clone) so the configured sync flow can run.

When a GitHub remote and authenticated `gh` CLI are available, this command also reconciles branch protection to match current mode:
- single-store: protect `main` (PR required)
- multi-store: protect `live-<alias>` (PR required; bypass users: `shopify[bot]`, `github-actions[bot]`, `actions-user`)

```bash
npx climaybe ensure-branches
git push origin --all
```

### `climaybe update` / `climaybe theme update`

Refresh all climaybe-managed project files from your installed CLI version:
- GitHub workflows
- root dev-kit files (`.theme-check.yml`, `.shopifyignore`, `.prettierrc`, `.lighthouserc.js`, `.gitignore`)
- `package.json` managed deps (`climaybe`, `tailwindcss`)
- optional `.vscode/tasks.json` (if enabled)
- optional commitlint + Husky files (if enabled)
- optional AI ruleset in `.config/ai/` + editor bridges (if enabled)

```bash
npx climaybe update
```

`update-workflows` still works as a backward-compatible alias.

### `climaybe setup-commitlint`

Set up **only** commitlint + Husky (conventional commits enforced on `git commit`). Use this if you skipped it at init or want to add it later.

```bash
npx climaybe setup-commitlint
```

### `climaybe add-cursor`

Install the Electric Maybe **AI ruleset** (rules, skills, and subagents — including **theme-translator** for `theme/locales/`) into `.config/ai/`, then bridge it to the editors you select. Use this if you skipped it at init or want to refresh from the version of climaybe you have installed. See [AI ruleset](#ai-ruleset) for the layout and editor bridges.

```bash
npx climaybe add-cursor
```

The previous command name `add-cursor-skill` still works as an alias. Re-running replaces the bundled rules, skills, and subagent files (and refreshes editor bridges) with the copies shipped by your installed climaybe version (same idea as `update`).

## Configuration

The CLI writes config into `climaybe.config.json`:

```json
{
  "port": 9295,
  "default_store": "voldt-staging.myshopify.com",
  "preview_workflows": true,
  "build_workflows": true,
  "lighthouse_workflows": true,
  "commitlint": true,
  "cursor_skills": true,
  "ai_editors": ["cursor", "claude"],
  "stores": {
    "voldt-staging": "voldt-staging.myshopify.com",
    "voldt-norway": "voldt-norway.myshopify.com"
  }
}
```

`lighthouse_workflows` gates Lighthouse CI inside the build pipeline (it still only runs on `staging` with the right secrets). `ai_editors` records which editors are bridged to `.config/ai/`. Older configs without these keys keep working: Lighthouse defaults to on when build workflows exist.

Workflows read this config at runtime — no hardcoded values in YAML files.

## Branch Strategy

### Single-store

```
staging → main
```

- `staging` — development branch
- `main` — production branch
- branch protection: `main` requires PR (no direct pushes)

### Multi-store

```
staging → main → staging-<store> → live-<store>
```

- `staging` — development branch
- `main` — shared codebase (not live)
- `staging-<store>` — per-store staging with store-specific JSON data
- `live-<store>` — per-store production
- branch protection: each `live-<store>` requires PR; bypass users: `shopify[bot]`, `github-actions[bot]`, `actions-user`

Direct pushes to `staging-<store>` or `live-<store>` are automatically synced back to `main` (no PR; multistore-hotfix-to-main merges the branch into main).

### Branch protection

`init` (with the prompt accepted, default **yes**), `add-store`, and `ensure-branches` reconcile GitHub branch protection to match your mode:

- **single-store:** protect `main` — pull requests required, no direct pushes.
- **multi-store:** protect each `live-<alias>` — PRs required, with bypass for the automation users `shopify[bot]`, `github-actions[bot]`, and `actions-user`; `main` is left unprotected so the sync workflows can push to it.
- switching single → multi removes the old `main` protection and applies `live-<alias>` protection instead.

It is **best-effort**: it only runs when `origin` is GitHub and the `gh` CLI is installed and authenticated. Otherwise it skips cleanly and prints why — it never fails the command.

## Workflows

### Shared (both modes)

| Workflow | Purpose |
|----------|---------|
| `ai-changelog.yml` | Reusable workflow. Sends commits to Gemini API, returns classified changelog. |
| `version-bump.yml` | Reusable workflow. Bumps version in `settings_schema.json`, creates git tag. |

### Single-store

| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `release-pr-check.yml` | PR from `staging` to `main` | Finds latest tag on main, AI changelog to PR head, creates pre-release patch tag (e.g. v3.1.13) to lock state; posts changelog comment |
| `post-merge-tag.yml` | Push to `main` (merged PR) | Staging→main only: minor bump from latest tag (e.g. v3.1.13 → v3.2.0). No version in PR title |
| `nightly-hotfix.yml` | Cron 02:00 US Eastern | Collects commits since latest tag (incl. hotfix backports), ignores no-op/empty-tree commits, generates AI changelog, patch bump and tag |

### Multi-store (additional)

| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `main-to-staging-stores.yml` (main-to-staging-&lt;store&gt;) | Push to `main` | Merges main into each `staging-<alias>`; root JSONs ignored. Skips no-op sync when branch tree already matches main. For hotfix-backport: if source is `staging-<alias>`, that same staging branch is skipped; if source is `live-<alias>`, `staging-<alias>` is also synced. Skips only on pure store-sync. |
| `stores-to-root.yml` | Push to `staging-*` | From main merge: stores→root. From elsewhere (e.g. Shopify): root→stores |
| `pr-to-live.yml` | After stores-to-root | Opens PR from `staging-<alias>` to `live-<alias>` |
| `root-to-stores.yml` | Push to `live-*` | From main merge: stores→root. From elsewhere: root→stores (same as stores-to-root on staging-*) |
| `multistore-hotfix-to-main.yml` | Push to `staging-*` or `live-*` (and after root-to-stores) | Merges store branch into main (no PR). Skips when push is a merge from main (avoids loop) and skips no-op backports when source and main trees are identical |

### Preview and cleanup workflows

Optional package, enabled via the `climaybe init` prompt (`Enable preview + cleanup workflows?`; default: yes). These publish a per-PR preview theme, post preview/customize links, and clean the theme up when the PR closes.

| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `pr-update.yml` | PR opened/synchronize/reopened (base: main, staging, develop, staging-*, live-*) | Shares draft theme, renames with `-PR<number>`, comments preview + customize URLs. **Multi-store + source branch (`pull_request.head.ref`) not** `staging-<alias>` **or** `live-<alias>`**:** publishes to **every** configured store (matrix) and posts **all** links in one PR comment. For `staging-<alias>` / `live-<alias>` source branches, only that store is used. **Path filter:** theme paths only (`assets/`, `blocks/`, `config/`, `layout/`, `locales/`, `sections/`, `snippets/`, `templates/`, `_scripts/`, `_styles/`, `shopify.theme.toml`, `stores/**`). |
| `pr-close.yml` | PR closed (same branch set) | Deletes this PR’s preview themes using the **same store matrix rule** as `pr-update`; PR comment shows **total** deleted count across stores. |
| `cleanup-orphan-preview-themes.yml` | PR closed (same branch set) + weekly (Mon 06:00 UTC) + `workflow_dispatch` | Per store: deletes themes ending with `-PR<n>` when PR `#n` is **not** open (merged/closed without cleanup). Uses `gh pr list` (limit 1000 open PRs). |
| `reusable-publish-pr-preview-store.yml` | workflow_call | Share + rename + upload comment fragment for **one** store (matrix leg in `pr-update`). |
| `reusable-share-theme.yml` | workflow_call | Shares Shopify draft theme and returns `theme_id` (still available if you call it elsewhere) |
| `reusable-rename-theme.yml` | workflow_call | Renames shared theme to include `PR<number>` (still available for custom flows) |
| `reusable-comment-on-pr.yml` | workflow_call | Upserts one bot preview comment; aggregates `preview-fragment-*` artifacts when `use_preview_fragments` is true and removes older bot preview comments |
| `reusable-cleanup-themes.yml` | workflow_call | `cleanup_mode: by_pr` deletes names ending with `-PR{padded}`; `orphan_pr` deletes `-PR<n>` when PR `n` is not open. Optional `result_artifact_prefix` for matrix fan-in on `pr-close` |
| `reusable-extract-pr-number.yml` | workflow_call | Extracts padded/unpadded PR number outputs for naming and API-safe usage |

### Build and Lighthouse workflows

Optional package. `climaybe init` asks two separate questions: **build workflows** (bundle `_scripts` JS + compile Tailwind in CI; default: yes) and, if build is on, **Lighthouse CI** (performance + a11y budget on the `staging` branch; default: yes, stored as `lighthouse_workflows`). Lighthouse runs inside the build pipeline, so it requires build workflows.

When enabled, builds are **resilient**:
- If `_scripts/*.js` or `_styles/main.css` are missing, the build workflow **skips** those steps and continues.
- `init` may offer to create entrypoints; **default answer is No**.
- Script bundling preserves comments/spacing and emits bundles only for root entry files (files imported by other top-level `_scripts/*.js` are inlined, not emitted separately).
- Script bundles are written to `assets/*.js` (readable by default; use `climaybe build-scripts --minify` if you want minified output).
- `assets/*.js`, `assets/style.css`, and the injected `{% schema %}` blocks are **generated outputs** — edit the source in `_scripts/`, `_styles/`, and `_schemas/` instead, since the watcher/build regenerate (and overwrite) the outputs on save and in CI.
- **Orphan cleanup:** when `_scripts/` is in use, a full build (`climaybe build`, `climaybe build-scripts`, and the `serve` watcher) deletes any `assets/*.js` that no longer has a matching `_scripts/` source. Add new JS via a top-level `_scripts/<name>.js` entry, not by hand-editing `assets/`. Liquid-processed `*.js.liquid` assets and non-JS files are left untouched; targeted single-entry builds (`build-scripts <entry>`) skip pruning.
- Live minified `assets/*` changes are intentionally excluded from hotfix backports to `main` (no branch-specific `.gitignore` split required).

Build workflows install deps with `npm ci` and run `npx --no-install climaybe build-scripts` plus `npx --no-install climaybe build`, so CI uses lockfile-pinned versions (no `@latest` drift).

| Workflow | Trigger | What it does |
|----------|---------|-------------|
| `build-pipeline.yml` | Push to any branch (ignores docs-only/tooling-only paths; see CI/CD reference) | Runs reusable build; skips entirely on **`live-*`** when the pusher is a GitHub **`[bot]`** user; Lighthouse only on branch **`staging`**, when a build ran, and secrets allow |
| `reusable-build.yml` | workflow_call | Path-filtered `build-scripts` / Tailwind (`climaybe build`), then commits compiled assets when changed |
| `create-release.yml` | Push tag `v*`, or **workflow_run** after Post-Merge Tag / Nightly Hotfix Tag succeed on `main` | Builds release archive and creates GitHub Release notes from commits since the previous tag. It filters repetitive automation subjects (main→staging syncs, store/root sync chores, bot merge noise) before generating notes. If remaining subjects are low-signal and `GEMINI_API_KEY` exists, it uses Gemini to generate cleaner merchant-facing notes. Also covers tags created by workflows with `GITHUB_TOKEN`, which may not trigger tag-push workflows. |

### Theme dev kit

Optional local-development bundle, enabled during `climaybe init` (default: yes). It installs the config files that make local theme work and CI consistent:

| File | What it's for |
|------|---------------|
| `.theme-check.yml` | Theme Check defaults (`theme-check:recommended`); ignores `node_modules/` and generated `_styles/` output |
| `.shopifyignore` | Keeps dev-only files out of theme uploads (`_scripts`, `_styles`, `stores`, editor/AI config, docs, package files, …) |
| `.prettierrc` | Prettier with the Shopify Liquid plugin |
| `.lighthouserc.js` | Lighthouse CI config: local URL, start command, and score thresholds |
| `.vscode/tasks.json` *(optional)* | One background task that runs `climaybe serve` (added only if you opt in) |
| `.gitignore` | A managed block is merged in (editor dirs, `node_modules`, generated `assets/index.js` + `assets/style.css`, `.shopify`, `.vercel`) |
| `package.json` | Ensures the `climaybe` dependency and `tailwindcss` devDependency exist; fills default name/description/author if missing |
| `climaybe.config.json` | The local source-of-truth config (`port`, `default_store`, `dev_kit`, `vscode_tasks`, `project_type`) |

Existing files are reported before they're replaced. Install or refresh later with `climaybe add-dev-kit` (or `climaybe theme add-dev-kit`).

Local serve commands keep Theme Check disabled by default for faster startup. Enable it explicitly with
`climaybe serve --theme-check` or `climaybe serve:assets --theme-check`.

You can create optional build entrypoints later with `climaybe create-entrypoints`. The scaffolded `_scripts/main.js` and `_styles/main.css` include a commented sample import showing how to pull in other files (`import './foo.js';` and `@import "./foo.css";`).

### AI ruleset

Optional. Electric Maybe ships a bundle of editor rules, skills, and subagents (themes, JS, a11y, commits, changelog, Linear task creation, **theme-translator** for locale sync, …). To avoid duplicating prompt content per editor, it installs into **one** source of truth and bridges every editor to it:

```
your-repo/
├── .config/
│   └── ai/                <-- the only real files (rules/, skills/, agents/, rules.md)
├── .cursor               --> bridge to .config/ai            (Cursor)
├── .windsurf             --> bridge to .config/ai            (Windsurf)
├── .clinerules           --> bridge to .config/ai            (Cline / Roo Code)
├── AGENTS.md             --> bridge to .config/ai/rules.md   (generic / other editors)
├── CLAUDE.md             --> bridge to .config/ai/rules.md   (Claude Code)
└── .github/
    └── copilot-instructions.md --> bridge to .config/ai/rules.md  (Copilot / VS Code)
```

- `climaybe init` (and `add-cursor` / `app init`) asks **which editors** to bridge. Only the bridges you pick are created.
- Bridges are **symlinks** where the OS allows (relative symlinks on macOS/Linux, directory junctions on Windows), so there is zero duplication — edit a rule once under `.config/ai/` and every editor sees it. If a platform blocks symlinks, climaybe falls back to copying the file so the bridge still works.
- The chosen editors are recorded as `ai_editors` in `climaybe.config.json`. `.config/ai/` and the bridges are kept out of theme uploads via `.shopifyignore`.

Install or refresh anytime with `climaybe add-cursor` (alias `add-cursor-skill`).

### Section schema builder

Build Shopify section schemas dynamically from JavaScript or JSON files using `climaybe build-schemas`. Works directly in `sections/` — no separate source folder, no sync issues with the theme editor.

**How it works:** Add an inline-comment marker at the end of any `sections/*.liquid` or `blocks/*.liquid` file. Shopify treats `{% # ... %}` as a comment and ignores it. The builder finds the marker, resolves the schema from `_schemas/`, and writes the generated `{% schema %}...{% endschema %}` block below it. The marker is never removed, so rebuilds always work — even after Shopify theme editor edits.

```liquid
<section class="hero">{{ section.settings.title }}</section>

{% # schema 'hero-banner' %}
{% schema %}
{
  "name": "Hero Banner",
  "settings": [...]
}
{% endschema %}
```

On rebuild, only the generated `{% schema %}` block is replaced. Everything above the marker (including theme editor changes) is preserved.

**Supported patterns:**

- **Shared schemas** — one schema file reused across multiple sections
- **Partials** — `require()` shared settings arrays into multiple schemas
- **Common fieldsets** — spread partial arrays into settings (`...linkSettings`)
- **Looping fieldsets** — factory functions that generate repeated field groups
- **Section-specific overrides** — export a function receiving `(filename, inlineContent)` to customise per section
- **Inline JSON overrides** — add `{% # { "name": "Custom" } %}` below the marker

```bash
npx climaybe build-schemas              # generate schemas in sections/
npx climaybe build-schemas --dry-run    # preview without writing files
npx climaybe build-schemas --list       # list schema files and markers
```

Schemas also rebuild automatically during `climaybe serve` and `climaybe serve:assets` — the watcher monitors `_schemas/` for changes and rebuilds on save, tagged `[schema]` in green. `climaybe build` includes schemas alongside scripts and Tailwind.

Example `_schemas/hero-banner.js`:

```js
const createLinks = require('./partials/create-links');

module.exports = {
  name: 'Hero Banner',
  settings: [
    { label: 'Title', id: 'title', type: 'text' },
    ...createLinks(2)
  ]
};
```

**Full examples:** See **[Schema Builder Examples](docs/SCHEMA_BUILDER_EXAMPLES.md)** for working code covering every pattern.

You can install/update this later with:

`climaybe add-dev-kit` (or `climaybe theme add-dev-kit`)

## Versioning

- **Version format**: Always three-part (e.g. `v3.2.0`). No version in code or PR title; the system infers from tags.
- **No tags yet?** The system uses `theme_version` from `config/settings_schema.json` (`theme_info`), creates that tag on main (e.g. `v1.0.0`), and continues from there.
- **Staging → main**: On PR, a pre-release patch tag (e.g. v3.1.13) locks the current minor line; on merge, **minor** bump (e.g. v3.1.13 → v3.2.0).
- **Non-staging to main** (hotfix backports, direct commits): **Patch** bump only, via **nightly workflow** at 02:00 US Eastern (not at commit time). No-op/empty-tree commits are ignored.
- **Version bump runs only on main** (post-merge-tag and nightly-hotfix). Main-to-staging-stores merges main into each `staging-<alias>` on every push (version bumps and hotfixes). For hotfix-backport, only a `staging-<alias> -> main` source skips syncing back to the same staging branch; a `live-<alias> -> main` source still syncs into `staging-<alias>`.
- Version bumps update `config/settings_schema.json` and, when present, `package.json` `version`.
- **Safety**: The version-bump workflow fails if the new tag would not be **strictly higher** than the latest merged release tag (semver), so the release line cannot step backward.

**Full specification:** For detailed versioning rules, local dev flow, hotfix behavior, and alignment with the external CI/CD doc, see **[CI/CD Reference](docs/CI_CD_REFERENCE.md)**.

## File Sync Rules (Multi-store)

**Synced between root and `stores/<alias>/`:**
- `config/settings_data.json`
- `templates/*.json`
- `sections/*.json`

**NOT synced (travels via branch history):**
- `config/settings_schema.json`
- `locales/*.json`

## Recursive Trigger Prevention

- Hotfix sync merge commits (multistore-hotfix-to-main) contain `[hotfix-backport]` in the message
- Store sync commits contain `[stores-to-root]` or `[root-to-stores]`
- Version bump commits contain `chore(release): bump version`
- All workflows check for these flags and skip accordingly

## GitHub Secrets

Add the following secrets to your GitHub repository (or use **GitLab CI/CD variables** if you use GitLab). You can configure them during `climaybe init` via the GitHub or GitLab CLI.

| Secret | Required | Description |
|--------|----------|-------------|
| `GEMINI_API_KEY` | Optional | Google Gemini API key for AI-generated release notes fallback |
| `SHOPIFY_STORE_URL` | Set from config | Store URL is set automatically from the store domain(s) you add during init (no prompt). |
| `SHOPIFY_THEME_ACCESS_TOKEN` | Optional* | Theme access token for preview workflows (needed only when you want preview theme publish/cleanup to run). |
| `SHOP_ACCESS_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
| `LHCI_GITHUB_APP_TOKEN` | Optional* | Required only when optional build workflows are enabled (Lighthouse) |
| `SHOP_PASSWORD` | Optional | Used by Lighthouse action when your store requires password auth |

**Prompting behavior:** During `climaybe init` (or `add-store`), every GitHub/GitLab secret prompt is skippable. Add values later in CI settings if you prefer.

**Store URL:** During `climaybe init` (or `add-store`), store URL secret(s) are set from your configured store domain(s); theme tokens are optional prompts.

**Multi-store:** Per-store secrets `SHOPIFY_STORE_URL_<ALIAS>` and `SHOPIFY_THEME_ACCESS_TOKEN_<ALIAS>` — the URL is set from config; you must provide the theme token per store. `<ALIAS>` is uppercase with hyphens as underscores (e.g. `voldt-norway` → `SHOPIFY_STORE_URL_VOLDT_NORWAY`). **Preview:** if the PR source branch is **`staging-<alias>`** or **`live-<alias>`**, only that store is used. Otherwise, with **multiple** stores in `climaybe.config.json`, **pr-update** publishes the preview theme to **every** store (each must have secrets) and **pr-close** cleans that PR’s preview themes on **all** stores. For a **single** store in config, behavior matches the default-store case. Optional **cleanup-orphan-preview-themes** (weekly + manual) removes stale `-PR<n>` themes when PR `n` is no longer open.

## Directory Structure (Multi-store)

```
├── assets/
├── config/
├── layout/
├── locales/
├── sections/
├── snippets/
├── templates/
├── stores/
│   ├── voldt-staging/
│   │   ├── config/settings_data.json
│   │   ├── templates/*.json
│   │   └── sections/*.json
│   └── voldt-norway/
│       ├── config/settings_data.json
│       ├── templates/*.json
│       └── sections/*.json
├── _schemas/            (optional: JS/JSON schema definitions for build-schemas)
│   ├── hero-banner.js
│   └── partials/
│       └── link.js
├── package.json
└── .github/workflows/
```

## Releases and versioning

- **Branch:** Single default branch `main`. Feature branches open as PRs into `main`.
- **Versioning:** [SemVer](https://semver.org/). Versions are **bumped automatically** when PRs are merged to `main` using [conventional commits](https://www.conventionalcommits.org/): `fix:` → patch, `feat:` → minor, `BREAKING CHANGE` or `feat!:` → major.
- **Flow:** Merge to `main` → [Release version](.github/workflows/release-version.yml) runs semantic-release (bumps `package.json`, creates GitHub Release notes, publishes to npm, pushes tag). Optional: tag push runs [Verify release tag](.github/workflows/verify-release-tag.yml) for an extra test pass and tag vs `package.json` check (no publish). We publish to **npmjs.com** only (not GitHub Packages). Prefer [npm Trusted Publisher](https://docs.npmjs.com/trusted-publishers) (workflow file `release-version.yml`) so no long-lived `NPM_TOKEN` is needed for CI; see [CONTRIBUTING.md](CONTRIBUTING.md). Do not create tags manually; only the Release version workflow creates tags so that tag and package version stay in sync.
- **CI:** Every PR and push to `main` runs tests on Node 20 and 22 ([CI workflow](.github/workflows/ci.yml)).

See [CONTRIBUTING.md](CONTRIBUTING.md) for branch, PR, and conventional-commit details.

## License

MIT — Electric Maybe
