# Norml Wp Developer

> Norml WordPress Copilot Advanced: rebuild Squarespace captures and HTML mockups as editable WordPress in Local, with local-only onboarding before hosting. The CLI-only WordPress development skill for Claude Code, Codex, and Gemini CLI. Develop themes locally and deploy through SSH + Git + GitHub Actions. WP Local is the default local environment (DDEV / Lando / Valet recognized too). Per-project onboarding connects GitHub and SSH, verifies remote WP-CLI, chooses the CI/CD pattern, scaffolds .claude/ inside the theme repo, generates capabilities.md, and records a five-file architecture snapshot. The everyday workflow is edit locally, commit, push, stage, smoke test, promote to prod. **Does NOT make backups.** Every production write gates on a backup acknowledgement. Direct-on-server work is permitted but flagged and logged. Local-first is the default. Use when the user says "build a WordPress theme," "develop a WP site," "set up a new WP project," "init this WP project," "ship to staging," "deploy to productio

- Skill: `normlstudio/norml-wp-developer` (Agent Skill, multi-file: 50 files)
- Install (CLI): `npx skillmds@latest add normlstudio/norml-wp-developer`
- Raw SKILL.md: https://api.skillmd.com/api/skills/normlstudio/norml-wp-developer/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: normlstudio (https://skillmd.com/u/normlstudio)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/normlstudio/norml-wp-developer

---


# Norml WordPress Copilot Advanced

Installed skill: `norml-wp-developer`.

> **CLI only.** Run this skill in Claude Code, Codex, or Gemini CLI with
> filesystem and terminal access. It is not a Claude Desktop / Cowork upload
> skill. Desktop users should use Norml WordPress Copilot (`norml-wp-manager`),
> which operates through the WordPress REST API.

> **Local-only migration exception:** For Squarespace-to-WordPress or HTML-to-WordPress builds before hosting exists, start with [the migration recipe](development-guides/squarespace-to-wordpress.md). It creates a verified local-only contract and disables every remote operation until normal hosting-connected onboarding passes.

> **Requires hosting-connected onboarding for remote work.** Before this skill can perform hosting-connected work, the user must
> run `onboarding.md` once per project on their machine. If
> `~/.config/norml-wp-developer/projects/{slug}.json` is missing for the
> project they're asking about, hard-stop and point at `onboarding.md`.
> Never improvise the SSH connection by asking for host/port/passphrase in
> chat.

> ## ⛔ HARD WARNING — backups are not optional and not provided
>
> This skill does **not** make backups for you. The flows here — `rsync`
> over SSH, `wp db import`, theme deploys, `wp search-replace` — can lose
> data if a step fails or a wrong path is passed. You are expected to:
>
> - Keep host-side automatic backups on (most managed hosts have this on
>   by default; verify in your hosting panel).
> - Or take a manual backup before any destructive operation. A safe
>   manual backup is two things: a database dump and a `wp-content/`
>   archive. Both must live OFF the server you're deploying to.
>
> Every deploy command in this skill prints a backup-acknowledgement
> prompt — you confirm a backup exists before the command runs. The
> default answer is "no, abort." There is no autopilot here. **If you
> lose data, you lose data.** No skill can recover it.

> ## 🟡 Working directly on the production server is elevated risk
>
> The skill supports two modes:
>
> 1. **Local-first (recommended).** Develop locally against WP Local /
>    DDEV / similar. Commit to Git. Push. CI/CD ships to staging, you
>    smoke test, then promote to production. Mistakes stay local until
>    you explicitly promote them. **This is the pattern setup defaults
>    to.**
> 2. **Direct-on-server.** Edit theme files on the production server via
>    SSH. Faster for one-line hotfixes; risky because there's no
>    local copy to fall back to and the next CI/CD deploy can
>    overwrite your live edit if it isn't mirrored to Git. Permitted
>    but every direct-on-server write is logged as `[DIRECT-PROD]` in
>    the project changelog and triggers a warning that the change is
>    not in Git.
>
> Default to local-first. Reach for direct-on-server only when the
> developer has explicitly chosen it for a specific operation.

## When to use

For "migrate my Squarespace website", "turn this mirror into WordPress", or
"build this HTML mockup in WordPress", read [Squarespace to WordPress](development-guides/squarespace-to-wordpress.md) first.

Any request to **build, modify, or deploy theme code** for a WordPress
site. Typical asks:

- *"Set up a new WordPress project from scratch."*
- *"Initialize this WordPress theme repo — scaffold `.claude/`, set up
  Git, wire it to GitHub."*
- *"Build a hero block with ACF fields."*
- *"Add a new ACF block to the existing theme."*
- *"Ship this to staging."*
- *"Promote staging to production."*
- *"Hot-fix a typo on production — direct on the server."*
- *"Pull production database down to my local."*
- *"What's the CI/CD pattern for this project?"*

## Skip if

- The work is **content management on a deployed site** (posts, ACF
  values, media, users, settings). → use `norml-wp-manager`. That skill
  is REST-API-only, doesn't require SSH, and is safer to hand to
  non-developers.
- The work is **fixing the WordPress install itself** (broken database,
  corrupted core files, hosting migration). → escalate to the hosting
  provider's support or use a dedicated migration skill.
- The work is **security testing / penetration testing**. → use a
  dedicated security skill.

## Three-phase mental model

Every interaction with this skill falls into one of three phases. Each
phase has its own rules + safety posture.

### 1. Setup (one-time per project)

Done once per project. Outputs:

- `~/.config/norml-wp-developer/projects/{slug}.json` — per-project
  connection config (no secrets).
- SSH key (existing or generated) registered with `ssh-agent`.
- SSH passphrase (if any) in macOS Keychain / Windows Credential Manager.
- Local WordPress environment detected (WP Local default; DDEV / Lando /
  Valet / Local Lightning recognized as alternates).
- Git repo initialized in the theme folder, first commit made.
- GitHub remote configured and read-only connectivity verified.
- `.claude/` scaffolded inside the theme repo with `CLAUDE.md`,
  `capabilities.md`, `architecture.md`, `ci-cd.md`, rolling changelog,
  and generated architecture docs.
- CI/CD pattern chosen + `.github/workflows/` scaffolded.
- Required first-time read-only architecture scrape via WP-CLI over SSH.

See `development-guides/wp-local-setup.md` for the local environment details and
`onboarding.md` for the step-by-step.

### 2. Develop (the everyday workflow)

After setup, building is the bulk of the work. Rules vary by project mode:

- **New / Sage-based project (Norml-authored):** Roots Sage + Acorn +
  Blade + Tailwind v4 + Vite + Alpine.js + ACF Pro. Read
  `development-guides/dev-conventions.md` → Mode A, which routes to
  `conventions/sage-stack.md`, `component-system.md`,
  `css-architecture.md`, and `acf-blocks.md`.
- **Inherited project (non-Sage, handed to us):** respect the existing
  architecture. Apply the framework-agnostic core principles
  (section/component/partial separation, ACF block registration,
  block previews, 3-layer CSS model) inside the existing theme.
  Read `development-guides/dev-conventions.md` → Mode B, which routes to
  `conventions/inherited-projects.md` (+ `css-architecture.md` and
  `acf-blocks.md` for the cross-cutting specifics).

Git workflow + commit conventions are the same for both:
GitHub Flow + Conventional Commits. See `development-guides/git-workflow.md`.

### 3. Deploy (the gated, backup-required workflow)

The deploy phase runs:

- `npm run build` (or yarn / pnpm equivalent) → produces production
  assets.
- `git push` → triggers CI/CD if configured.
- Or `rsync -avz --delete` over SSH for direct deploys (no CI/CD).
- `wp cache flush` + `wp acorn view:cache` (Sage projects) on the
  server after deploy.
- Smoke test (HEAD on the homepage, follow-up GET on a CPT archive).
- For staging→prod promotions: re-run the staging deploy steps against
  the production server.

Every deploy gates on the **backup acknowledgement prompt** described in
the HARD WARNING block above. Without acknowledgement, the deploy
aborts. See `development-guides/ci-cd/patterns.md` for the three supported
patterns and their command sequences, `development-guides/qa-gates.md` for the
pre-deploy and post-deploy QA checkpoints (staging-based for
`full-pipeline`, local-based for the prod-direct patterns), and
`development-guides/ci-cd/backup-strategies.md` for the per-provider backup +
restore detail behind the acknowledgement.

## Pre-flight (every session, before the first dev/deploy operation)

If this is an explicitly selected local-only build, use the local pre-flight in
[the migration recipe](development-guides/squarespace-to-wordpress.md) instead.
The following full pre-flight remains mandatory for any hosting-connected operation.

1. **Resolve the project slug** from the user's request, the working
   directory, or by asking. Slugs are kebab-cased.

2. **Read `~/.config/norml-wp-developer/projects/{slug}.json`.**
   - If missing → hard-stop. Tell the user the project isn't
     configured and point them at `onboarding.md`. Do NOT improvise SSH
     details in chat.

3. **Read the project's `.claude/CLAUDE.md` and `.claude/ci-cd.md`**
   from the theme repo path in the project JSON.
   - If `.claude/` is missing → tell the user the project isn't
     initialized and offer to run init (scripts/init-claude.sh /
     init-claude.ps1) to scaffold it. Don't proceed with dev work
     against an un-initialized repo.

4. **Read `.claude/capabilities.md` and `.claude/architecture.md`.**
   - `capabilities.md` is the live operating contract for GitHub, SSH,
     WP-CLI, local development, staging, and production.
   - `architecture.md` is the index into the latest generated site and theme
     snapshot under `.claude/docs/`.
   - If either is missing or still says the first scan is pending, run
     `scripts/scrape-architecture.sh {slug}` (or the PowerShell mirror) before
     development.

5. **Read the project changelog** at
   `{theme_root}/.claude/changelog/daily.md` (or `changelog.md` if the
   project skipped the rolling-three-tier setup). Surface the most
   recent entries — they tell you what was just done.

6. **Identify the project mode** from `.claude/CLAUDE.md`:
   - `mode: sage` → New / Norml-authored. Apply Sage conventions.
   - `mode: inherited` → Respect the existing theme. Apply
     framework-agnostic principles.

7. **Resolve the SSH alias** to the production server from the project
   JSON. Confirm the alias exists in `~/.ssh/config` (the setup script
   writes it; if it's missing, re-run setup).

8. **Identify the deploy pattern** from `.claude/ci-cd.md`:
   - `full-pipeline` — Git push → CI/CD → staging → manual promote → prod
   - `prod-direct-with-git` — Git push → server pulls from Git → prod
   - `prod-direct-no-ci` — rsync from local → prod (no automated deployment pipeline; source still committed and pushed to GitHub)

You now have everything needed to operate. **Never** ask for SSH
passphrases, GitHub tokens, or server passwords in chat. If anything's
missing, point back at onboarding.

## Project knowledge layer — inside the theme repo

Every project carries its own `.claude/` directory **inside the theme
repository** (not in the user's project folder, not on Drive — inside
the repo, so it travels with the code).

```
{theme_root}/.claude/
├── CLAUDE.md                 # Project overview, mode (sage/inherited), pointers
├── capabilities.md           # Generated operating contract for this project
├── architecture.md           # Generated architecture summary + docs index
├── ci-cd.md                  # Per-project deploy contract (pattern, envs, hooks)
├── changelog/
│   ├── README.md             # The rolling-three-tier protocol
│   ├── daily.md              # Today's raw entries
│   ├── weekly.md             # Compressed week
│   └── changelog.md          # Long-term history
├── docs/                     # 5-file read-only architecture scrape
│   ├── 01-infrastructure.md
│   ├── 02-application.md
│   ├── 03-theme-architecture.md
│   ├── 04-content-structure.md
│   └── 05-issues.md
└── skills/                   # Project-specific skills (empty by default)
```

This is the **canonical knowledge layer for this project**. Both the
human developer and Claude read from it at every session. It is **in
git, committed with the theme code, travels everywhere the repo
travels.**

### Why `.claude/` lives in the theme repo (not in `~/.config/` or on
Drive)

| Concern | Where it lives | Why |
|---|---|---|
| Connection details (host, port, SSH key path, Git remote URL) | `~/.config/norml-wp-developer/projects/{slug}.json` | Per-machine, private. Doesn't travel with the code. |
| Project knowledge (architecture, conventions, decisions, changelog) | `{theme_root}/.claude/` | Lives with the code, travels in git, available to anyone who clones the repo. |
| Secrets (SSH passphrase, GitHub PAT) | macOS Keychain / Windows Credential Manager / ssh-agent | OS-managed. Claude never sees the value. |

This split is non-negotiable. **Never write a secret into
`{theme_root}/.claude/` — those files are committed to git.** Never write
deploy state or build artifacts into `~/.config/` either — they're
per-machine config, not knowledge.

## Write-back rules — how the skill compounds knowledge per-project

The point of `.claude/` is that **every session leaves it richer**. Three
write-back triggers:

### `daily.md` — close-of-session changelog (always)

Append an entry to `{theme_root}/.claude/changelog/daily.md` before
ending any session that:

- Wrote theme code (component, block, template, function)
- Ran a deploy to staging or production
- Performed a direct-on-server edit
- Pulled production DB / uploads to local
- Made a non-trivial decision the next session would benefit from
  knowing

Tags:

- `[CODE]` — theme code change
- `[BUILD]` — Vite / Composer / NPM build ran
- `[DEPLOY-STAGING]` — push or deploy to staging
- `[DEPLOY-PROD]` — promote to prod (gated by backup acknowledgement)
- `[DIRECT-PROD]` — edit made directly on the production server (high
  risk, flag the un-mirrored-to-git status)
- `[PULL-DB]` — production DB pulled to local
- `[PULL-UPLOADS]` — production uploads pulled to local
- `[DECISION]` — architectural choice worth recording
- `[LEARNED]` — non-obvious fact about this project

The daily file compresses to `weekly.md` weekly and to `changelog.md`
when the calendar quarter rolls over — see
`{theme_root}/.claude/changelog/README.md` for the protocol the init
flow scaffolds.

### `.claude/CLAUDE.md` — project-level decisions

When the user makes a project-level decision ("from now on this site
uses BEM for component class names," "we always set `show_in_rest:
true` on ACF field groups"), update the relevant section of
`CLAUDE.md`. This file is the durable answer to "how do we work on
this project."

### `.claude/capabilities.md` + architecture docs — rescan only

The visible capability contract, architecture index, and 5-file snapshot are
generated by `scripts/scrape-architecture.sh` (or `.ps1`) from local theme files,
Git/GitHub state, and read-only WP-CLI commands over SSH. They are overwritten on
rescan. Anything that should survive a rescan goes in `CLAUDE.md` or `daily.md`,
never hand-edited into generated files.

## Safety classification — every operation buckets

Three buckets:

- **Safe** — read-only or low-risk single-item operations. Run
  immediately. Examples: `git status`, `git log`, `wp post list` on
  local, reading any file under the theme repo, listing the active
  plugin set.

- **Confirm** — anything that writes to the local repo, to staging, or
  changes git state. Show the exact command, show the diff if relevant,
  ask for "yes" before running. Examples: `git commit`, `git push`,
  `git rebase`, `rsync` to staging, `wp plugin install` on local,
  database imports.

- **Confirm + backup** — anything that writes to the production server.
  Show the exact command, **show the backup-acknowledgement prompt**,
  refuse without an explicit "I have a backup from {date}" reply.
  Examples: `rsync` to production, `wp db import` on production,
  `wp search-replace` on production, deleting plugin/theme files on
  production, direct file edits over SSH on production.

The backup-acknowledgement prompt is mandatory for every production
write. There is no "skip the prompt" flag. If the project's
`.claude/ci-cd.md` declares the host has automatic backups (and we
trust the host), the prompt may be answered with "yes, host backups
verified" — but only if `ci-cd.md` says so, and only after the user
explicitly types that phrase.

## Output style

- Default to terse. The user wants the result.
- When you ran a command, show the command first (one line, monospaced).
- For `Confirm + backup` operations, the backup prompt is mandatory and
  shown verbatim. Don't paraphrase it.
- For diffs, prefer `git diff --stat` for summary + the full diff on
  request rather than dumping 500 lines unprompted.

## Error handling

- **SSH refused / timeout** → don't retry. Suggest the user run
  `scripts/test-ssh.sh` and check their `~/.ssh/config` alias.
- **`wp: command not found` on the remote** → WP-CLI not installed on
  the server. Stop, tell the user, suggest contacting their host.
- **`Composer detected ... platform_check.php`** → server PHP < theme's
  Composer floor. Stop, point at the `composer.json` `require.php`
  line, tell the user to either bump the server PHP or lower the
  theme's floor.
- **Git conflicts** → don't auto-resolve. Show the conflicting files,
  let the user pick the side. Conventional Commit rules in
  `development-guides/git-workflow.md`.
- **Build failures** → show the full Vite / Composer / NPM error, stop,
  do not deploy with a failing build.
- **Deploy failures mid-flight** → stop immediately. Tell the user the
  exact step that failed. Do not auto-retry — partial deploys are the
  most common cause of broken production.

In all cases, **fail loudly and stop**. No improvised destructive
recovery.

## Knowledge baseline — what the skill reads

| File | When to read |
|---|---|
| `development-guides/dev-conventions.md` | At the start of any session that touches theme code. The **router** — routes to the right `conventions/` files by project mode, plus the global rules (Conventional Commits, don't-commit artifacts/secrets, ACF JSON sync caveat). |
| `development-guides/conventions/sage-stack.md` | Sage / new projects, before building. The Sage + Acorn + Blade + Tailwind v4 + Vite + Alpine + ACF Pro foundation. |
| `development-guides/conventions/component-system.md` | Before building a section / component / block in a Sage project. Anatomy + core component catalog + the Alpine pattern + layout. |
| `development-guides/conventions/css-architecture.md` | Before writing ANY CSS, **either mode**. The 3-layer model + the forbidden per-page-stylesheet rule. |
| `development-guides/conventions/acf-blocks.md` | Before registering an ACF block or its preview, **either mode**. Registration + previews + ACF JSON sync. |
| `development-guides/conventions/inherited-projects.md` | FIRST, whenever you open a non-Sage / inherited theme. Respect-existing decision flow + framework-agnostic principles. |
| `development-guides/git-workflow.md` | Before any `git commit`, `git push`, branch creation, or PR. GitHub Flow + Conventional Commits. |
| `development-guides/ci-cd/patterns.md` | Before any deploy or any CI/CD configuration task. The three supported patterns and the per-project `ci-cd.md` contract. |
| `development-guides/ci-cd/database-strategies.md` | When content / the database moves (or must not move) between environments. |
| `development-guides/ci-cd/backup-strategies.md` | Before any production write — backup provider, verification, restore runbook, acknowledgement tier. |
| `development-guides/qa-gates.md` | Before merging, before deploying, and right after deploying. The pre-merge / pre-deploy / post-deploy checkpoints. |
| `development-guides/safety-rules.md` | At session start. The full Safe / Confirm / Confirm+backup classification with examples. |
| `development-guides/wp-local-setup.md` | During onboarding, or if the local environment is unclear. WP Local default, DDEV / Lando / Valet / Local Lightning as alternates. |
| `{theme_root}/.claude/CLAUDE.md` | At session start. Project mode (sage / inherited), live URLs, deploy hints. |
| `{theme_root}/.claude/capabilities.md` | At session start and before any operation. Verified GitHub, SSH, WP-CLI, local, staging, and production boundary. |
| `{theme_root}/.claude/architecture.md` | At session start. Latest architecture snapshot summary and read order. |
| `{theme_root}/.claude/ci-cd.md` | Before any deploy. Per-project pattern, deploy commands, pre/post hooks. |
| `{theme_root}/.claude/changelog/daily.md` | At session start. The last ~20 entries — what the last sessions touched. |

## Hard rules

1. **Never deploy without an acknowledged backup.** The backup-
   acknowledgement prompt is mandatory for every production write.
2. **Never write secrets into committed files.** SSH keys, GitHub
   PATs, database passwords belong in the OS secret store or
   `ssh-agent` — never in `.claude/`, never in `.env` checked into
   git, never in commit messages.
3. **Never silently overwrite production.** Every prod write shows the
   exact command and waits for explicit "yes."
4. **Never introduce a new framework on an inherited project** without
   explicit, in-session approval. Match the existing architecture.
5. **Always log production writes** as `[DEPLOY-PROD]` or
   `[DIRECT-PROD]` in `daily.md` before closing the response.
6. **Stop on any unexpected error.** No improvised recovery.
7. **`.claude/` is committed to git.** Never write secrets to it.
   `~/.config/norml-wp-developer/projects/*.json` is per-machine and
   never committed.
8. **Local-first is the default.** Direct-on-server is permitted but
   explicit per-operation. Never default to it.

## Related skills

**Companion (install both for a full WordPress workflow):**

- `norml-wp-manager` — REST-API-only content management on a deployed
  site. Use it for posts, pages, ACF values, media. This skill
  (`norml-wp-developer`) is for theme code; `norml-wp-manager` is for
  content. They share no state.

## What lives where (recap)

| What | Where | Why |
|---|---|---|
| The skill itself | Runtime-specific skills directory | Claude Code, Codex, or Gemini CLI discovers the stable `norml-wp-developer` slug. |
| **Per-project connection config** | **`~/.config/norml-wp-developer/projects/{slug}.json`** | Per-machine. Host, SSH alias, theme repo path, remote URL, deploy pattern. **No secrets.** |
| **Per-project knowledge** | **`{theme_root}/.claude/`** | Lives in the GitHub repo. Travels with the code. CLAUDE.md, capabilities.md, architecture.md, ci-cd.md, changelog, architecture docs. |
| SSH key | `~/.ssh/{key}` | Owned by the user; ssh-agent holds the passphrase. |
| SSH passphrase | macOS Keychain (via `ssh-add --apple-use-keychain`) / Windows ssh-agent (DPAPI-backed) | OS-managed. Claude never reads it. |

The split is intentional: `~/.config/` is per-machine connection state;
`{theme_root}/.claude/` is portable per-project knowledge; the OS
secret store is for secrets.

