# z2w-starter-kit

**Version:** 0.32.1 | **Framework:** 2.19.0

> Node CLI to scaffold new Z2W projects from canonical Templates + AGENTS.md.

**Brand:** Zero2Webmaster

---

## Overview

`z2w-starter-kit` is a command-line scaffolder for [Zero2Webmaster](https://zero2webmaster.com) projects. It reads canonical content from the master Templates folder — the AGENTS body is discovered by glob (`AGENTS_v*.md` for general projects, `AGENTS_WP_v*.md` for WordPress) plus the matching `SETUP_GUIDE_*` and `Resources/*`, so an upstream framework-version bump flows through with no code change. It parses a pasted project brief OR walks an 8-question fallback wizard, applies brand presets, layers the current Z2W standards block (portable-stack, Fathom analytics, Uptime Kuma, Neon-careful, Agent Coordination, SEO baseline, email-service, moderation, text-size), substitutes placeholders, and writes a complete project skeleton atomically into the target path.

It is the Node/npm sibling of the `instantiate-z2w-project` Claude Code skill — the same orchestration logic, packaged so any developer can run it without Claude Code installed. When standards drift, both must be updated together.

```bash
npx @zero2webmaster/starter-kit init
```

## Quick start

```bash
# Run without installing
npx @zero2webmaster/starter-kit init

# Or install globally
npm install -g @zero2webmaster/starter-kit
z2w-starter-kit init

# Or run from a local clone during development
git clone https://github.com/zero2webmaster/z2w-starter-kit
cd z2w-starter-kit
npm install
npm run build
node dist/cli.js init
```

The CLI's primary surface is **brief-first**: paste a project-instantiation brief, the CLI parses it deterministically, shows a numbered confirmation screen with per-line edit, then scaffolds. Type `skip` at the brief prompt or pass `--wizard` to fall back to the 8-question sequential wizard.

Project types supported: `wp-plugin`, `cf-worker`, `cf-pages`, `nextjs`, `python-service`, `library`, `protocol-repo`.

Brand presets: `save-the-frogs`, `bansuri-bliss`, `zero2webmaster`, `custom`.

## CLI flags

| Flag | Purpose |
|---|---|
| `--wizard` | Skip brief-first mode; go straight to the 8-question fallback wizard |
| `--input <source>` | Non-interactive mode. `--input <path>` reads `ScaffoldInputs` JSON from a file; `--input -` reads from stdin. Bypasses brief / confirm / wizard entirely. For CI use |
| `--dry-run` | Print the file list + contents that would be written, without touching disk |
| `--target-path <path>` | Override the target path from inputs |
| `--templates-path <path>` | Point at a Templates folder other than `~/Desktop/Zero2Webmaster/AI/Templates/`. Strict — only that exact folder is consulted |
| `--promote-standards` | Run the interactive (a) inline / (b) promote-into-Templates dialog per applicable standard. Promoted standards are captured in `STANDARDS_PROMOTIONS.md` alongside the scaffold (the Templates-folder write itself is a v0.3.x concern) |
| `--allow-non-empty` | Write into a non-empty target directory. Off by default — the CLI refuses to scaffold over existing content unless explicitly opted in |
| `--no-color` | Suppress ANSI color in the final summary output. Honored regardless of terminal capability, per POSIX `NO_COLOR` convention |

## Architecture

This is a pure CLI tool. No web UI, no database, no scheduled jobs, no deployment target. Side-effectful commands (`git init`, `gh repo create`, `npm install`, `composer install`, symlink setup) are surfaced as next-actions in the final summary block — the CLI does NOT run them on the user's behalf in v0.1.x.

- **Layer 1 (Directive):** SOPs in `directives/` (system architecture, scaffolder contracts, brand presets)
- **Layer 2 (Orchestration):** Node CLI (`src/cli.ts`) parses args, parses the brief or runs the wizard, decides what to write
- **Layer 3 (Execution):** Filesystem operations via `fs-extra` (atomic per-file writes via `.tmp` + rename); git/`gh` shell-outs run by the user post-scaffold, not by this CLI

For non-WP web apps (`nextjs`, `cf-pages` with `hasWebUi`), the CLI emits the full 7-item SEO baseline at instantiation: SSR/SSG via the framework's Metadata API, per-page metadata, one `<h1>` + semantic HTML, sitemap + robots with preview-noindex gating, OG image placeholder, Organization JSON-LD, Fathom Analytics wiring via the canonical env var (`NEXT_PUBLIC_FATHOM_SITE_ID` for Next.js, `PUBLIC_FATHOM_SITE_ID` for Vite/SvelteKit/Astro, inline `<script>` for cf-pages static), and a `SEO-CHECKLIST.md` pre-launch audit.

## Templates resolution

The CLI resolves canonical template content (AGENTS bodies + Resources/) from the **local Templates folder only**:

1. **`--templates-path <path>`** if set — strict use of that exact location.
2. **Local Templates folder** at `~/Desktop/Zero2Webmaster/AI/Templates/`.
3. Otherwise, hard fail with a clear `--templates-path <path>` hint.

The CLI reads the folder fresh on every run (nothing is cached or embedded), so an upstream Templates edit is picked up the next time you run `init`.

> **Note:** the v0.2.0 hosted-mirror fallback (`templates.z2w.us` + a 24h local cache) was **retired 2026-06-23** to protect the framework IP — the public mirror was taken down. The CLI is therefore back to requiring a local Templates folder (or `--templates-path`), the same as v0.1.x. **No license-gated content channel is planned** — the framework stays in-house rather than being licensed out (decided 2026-07-30).

## Relationship to the Claude Code skill

The Claude Code skill at `~/.claude/skills/instantiate-z2w-project/SKILL.md` and this CLI share a contract: same inputs, same outputs, same brand presets, same standards block. The skill is the in-Claude-Code path; this CLI is the standalone path. The canonical Templates folder remains the source for AGENTS file bodies and `Resources/` boilerplate — both the skill and this CLI read Templates files fresh on every run; neither embeds copies.

## Status

- **v0.27.0 (current)** answers `org-hq`'s two scaffold defects, ten days after they were filed. The headline: **`app/robots.ts` was asking the wrong question.** Its gate required a production deployment AND a real custom domain — and an internal admin console satisfies **both** while still being wrong to index, so `org-hq` served `Allow: /` on a branded public subdomain for a month with every check it had passing, because none of them asked. Indexability now takes a third condition, `hasPublicSurface`, baked in at scaffold time rather than read from the environment, because it is a fact about what an app IS and not about where a copy of it is running. **It fails CLOSED** — omitted reads as "no public surface" — which is the opposite default from every other optional input here and is deliberate: a public site wrongly withheld is one field and a redeploy with nothing having escaped, while an internal console wrongly published is silent, slow to undo, and has already happened by the time anyone notices. The wizard and brief-first flows ask, and the CLI's final summary now **states the posture out loud in both directions**, since a silent default is how the original defect survived. `cf-pages` is fixed in the same release — it published `Allow: /` unconditionally — because shipping the fix only in the type that was reported would have left its sibling quietly broken. The second defect: the `*.vercel.app` alias redirect now **excludes `/api/*`**, whose SEO rationale never applied and whose 308 breaks every server-to-server caller that does not follow redirects. **And re-measuring the report changed it:** the companion claim — that adding the exclusion would silently stop redirecting `/`, because a `:path` param cannot match the empty string — **did not reproduce.** On Next.js 15.5.23 a CUSTOM param pattern supplies its own `.*`, which matches empty, so the wildcard alone still redirected the root; the explicit root rule ships anyway as insurance against a path-to-regexp detail that is not a documented guarantee, described as insurance rather than as a repair. Verified per tier 1 on two real scaffolds from the built `dist/` — `npm install`, a real `next build`, and `curl` with forged `Host` headers against both the shipped form and a control build with the root rule removed. **1241 of 1241 passing.**
- **v0.26.0** releases two things. First, the **per-service Uptime Kuma health keyword**, built on 2026-08-21 but never released or written down until now: every deploy target's health document serves `{"status":"ok","service":"<slug>","keyword":"<slug>-health-ok"}`, and the emitted standards block tells the reader to match the full keyword. **The old portfolio-wide `"status":"ok"` was worthless as a check** — byte-identical across every Z2W service, so a monitor with a mistyped URL, one copied from a sibling project, or one left pointing at a decommissioned host still went green, which is the exact failure a keyword exists to catch; a portfolio audit found **12 of 23 monitors** keyed on it. The value is DERIVED from the slug via `healthKeyword()`, which is the load-bearing half — a shared constant would rebuild the same trap with extra steps. Second, a fix in `rollup-integrations.mjs`: **a comma in an author's prose is not an edge separator.** Stripping parentheticals before splitting handled the `(why…)` note form and did nothing for the canonical form's `— why` note, which runs to end of line, so `- consumes: contact-registry — people, tags, entitlements.` was about to write **`tags`** into the Project Registry as a dependency of `commerce-engine`, and `audit-engine`'s trailing sentence was about to write **`live-status`**. Both had been PRINTED on every run under `ⓘ not registry slugs (external, or prose noise — check)` — a line that reports and does not filter, so **a reported artifact that is still written is not a caught one**. A comma-part is now an edge only when it is a known registry slug or carries a `:qualifier`, with the first part exempt because that is where the author names the edge and the only position a legitimate non-registry backend occupies. The same work uncovered the inverse defect — a MISS: prose left attached to the last inline item swallowed its slug, so `z2w-observability-bridge`'s section yielded **only `Vercel`**, an external its own sentence had just disclaimed, while its real edge went unread. **1233 of 1233 passing.**
- **v0.25.0** adds `scripts/reconcile-production-url.mjs`, which reconciles the Project Registry's `production_url` column against the Vercel API — and **measuring the real API showed the 2026-08-17 design sketch was wrong in three separate ways.** The sketch said "read `domains[]`, filter out `*.vercel.app`, never take `domains[0]`." Measured across all 23 Zero2Webmaster Vercel projects: **position carries no information at all** (`z2w-social`'s correct domain is the first survivor, `org-hq`'s is the second), so no positional rule can exist; **filtering the aliases out blanks five Live rows** whose only public surface *is* a `.vercel.app` alias, so aliases are a lower-priority tier rather than noise; and **multiple custom domains are the common case, not an edge** — 6 of 19 resolvable projects have more than one, `file-server` has four. The reconciler therefore returns a *verdict object* rather than a URL, so "there is one" and "I picked one of four" cannot print the same way, and it writes only into a NULL — a recorded value that disagrees is reported as drift and never overwritten, because it may be a human's deliberate choice. **It found a real one on its first live run:** the registry's `z2w-social` URL 308-redirects to `frogspace.savethefrogs.com`. Live redirect evidence is used only to *eliminate* a candidate that points at another candidate — both `leaderboard` domains 307 to `/bansuri`, a tenant path, so "follow redirects and take the final URL" would have written a tenant-scoped URL as a project's production URL. A coverage ledger accounts for every row asserting a public surface, and a completeness check makes an unexplained row loud instead of absent. Also re-syncs the canonical Agent Coordination block to **v0.1.27**, which brings the previously-red conformance test back to green — **1199 of 1199 passing.**
- **v0.24.0** makes contrast a measured property of a PAIR rather than a taste call about a swatch. Every `nextjs` scaffold now emits `lib/a11y/contrast.ts` and `test/contrast.test.ts`, a table of **SURFACE x TEXT-ROLE x THRESHOLD** rows measured against the stylesheet the project actually ships — thirteen pairs across six surfaces, with `--brand-ink` measured four separate times because it is painted on four different backgrounds. The candidate came from `static-sites` and its lesson is one line: *contrast is per-surface, not per-palette* — a validator that checks each swatch against white passes cleanly while shipping unreadable text, because the swatch is rendered on the tint panel three components down. **Writing that check found two real defects in this scaffold's own CSS, both invisible to every brand preset.** The keyboard focus ring was painted in `--brand-primary`, which measures 2.03:1 on white in Z2W green and 1.40:1 in Z2W gold — so focusing a field made its boundary *less* visible than at rest, in Zero2Webmaster's and Save The Frogs' own colors. And `readableInkOn()` picked the better of near-black and white without checking that the winner cleared anything, which put a 4.41:1 glyph on any mid-tone brand fill. Fixed with a derived `--brand-focus` (same hue, darkened only as far as 3:1 requires) and an escalation to pure black, which provably clears 4.5:1 on every possible background. **Verified by running the scaffold's own vitest, plus four canaries that each reproduce a defect on demand** — including the one that matters most: an ink that stays green on white and fails at 4.06:1 on the code tint.
- **v0.23.0** makes every scaffolded web app defend its own head. `audit-engine` measured 26 reachable Z2W surfaces on 2026-08-20: **22 carried none of the five baseline security headers**, while `leaderboard` carried five of five — so this was a lift from a passing in-house implementation rather than a design exercise. Every `nextjs` scaffold now emits `lib/security-headers.mjs`, applied to every route by a new `async headers()` in **both** `next.config.mjs` variants, and `cf-pages` goes from two of five to five of five. **The CSP is assembled from the origins the scaffold actually loads**, each gated on the input that ships the feature: Fathom unconditionally, Turnstile in `script-src` *and* `frame-src` when the project takes cards (the widget is an iframe, so allowing only the script leaves an uncompletable card path), Sentry in `connect-src` alone when observability is on. **It ships enforcing rather than Report-Only** — a considered deviation from the retrofit-oriented rule, reasoned in the emitted comments, because a scaffold has no live traffic to break and a header that blocks nothing is just a note asking someone to finish later. Alongside it, `lib/json-ld.ts` closes the `JSON.stringify`-into-a-`<script>` sink: not because the scaffold's own JSON-LD was exploitable (it holds a brand name and an env URL) but because the Pro-SEO extension's *documented example* fed a post title — database content — straight into it, which is the exact stored XSS found live in `z2w-social`. **A scaffold's examples are copied more often than its code is read.** Verified on the artifact: real `next build`, all six headers `curl`-confirmed on a home page, a child page and an API route, and headless Chrome reporting zero CSP violations — with a positive control proving the detector fires.
- **v0.22.0** reshapes every emitted `.env.example` / `.env.local` so the line you came to fill in is the one you see. Kerry, 2026-08-16: the `# ── Database (Neon Postgres) ─────…` ruler *"is usually too long and therefore doesn't look like anything useful"*, and eighteen lines of explanation sat between the header and `DATABASE_URL=`. Headers are now a plain `# Database (Neon Postgres)`, and each block leads with its assignments and carries its prose directly underneath. **Per block rather than per file, which was Kerry's call between two readings of his own note** — collecting every variable at the top of the file would put the pooled-connection warning a screen away from the person about to paste the wrong connection string, and that warning only works where it is read. **Not a trim: every explanatory sentence survives, reworded and moved**, and there is a control test that unwraps the comment prose and asserts the load-bearing warnings are all still present, because a layout change is exactly where a paragraph goes missing unnoticed. Verified by loading the emitted file with Node's real dotenv parser — all 13 variables read back — not by grepping the template.
- **v0.21.0** makes the external audience gate say what actually went wrong. Its refusal named exactly one cause — *"a heading was probably renamed upstream"* — for a state that has two, and `project-creator`, the only consumer that resolves Templates from a version-**pinned** package rather than from Kerry's disk, hits the other one every time: at AGENTS v2.15.0 the `## Keeping This Framework Current` section simply did not exist yet, so every outside-brand scaffold refused in their production while they hunted a rename that never happened. Each gated section now declares the Templates version that first shipped it, the gate derives a minimum from those declarations, and the error distinguishes **version skew** (upgrade Templates) from **unexplained** (fix the section list). The throw is deliberately not softened. Same release: the standards decision log is now gate-aware — a standard whose whole section the gate removed reports `removed-by-audience-gate` instead of `inserted-inline`, because the old log let a preview UI tell an outside customer it had "Skill Vault awareness" in a file that no longer mentioned it; and a new `privateReferences` finding separates a surviving line that *instructs* the reader into the private Skill Vault from one that merely points at a missing section — one hit at Templates v2.18.0 and zero at v2.19.0, which removed that sentence upstream during the same session, demoting the finding into the milder dangling-reference class rather than making the check inert. No emitted file changes by a byte.
- **v0.20.0** emits `lib/csv-export.ts` and an enforcing `test/csv-export.test.ts` into **every** Next.js scaffold, with no capability flag. A CSV cell beginning `=`, `+`, `-` or `@` is evaluated as a formula by Excel and Google Sheets, so an unguarded export is code execution on the machine of whoever opens it — arriving through a file the app itself told them to download. A survey of 53 Z2W repos found 1 pass and 12 fail, and all 12 grew their export months after the project was scaffolded, which is why this one ships unconditionally rather than behind a flag: "this app will export CSV" is not knowable at scaffold time the way "this app takes cards" is. The emitted test greps for any file serving `text/csv` that skips the helper, so the guard survives the next person who adds an export. Same release: `scripts/rollup-integrations.mjs` now reads markdown tables and `**We CONSUME:**` headings, recovering 18 real dependency edges the ecosystem map had been missing, with none lost.
- **v0.19.1** emits a `:disabled` rule for **buttons** in `app/globals.css`, so a disabled button still looks disabled after the author gives buttons a brand fill. The scaffold ships no button styling of its own, so today the browser's grey rendering still applies — the trap opens on the author's first `background`/`color`, which overrides that grey and leaves `:disabled` a state with no appearance: the button looks pressable, the click does nothing, and the user concludes the app is broken. A sibling Z2W app shipped exactly that across ~50 screens behind a clean typecheck, a green build and 1,051 passing tests, because it is a rendered-pixel property no unit test holds. Includes the `:hover` cancel people forget — a `filter: brightness()` hover happily re-brightens a dimmed button at the moment the user is deciding to click. Verified on real pixels via CDP with `:hover` forced, and canary-verified by stripping the rule from a live scaffold.
- **v0.19.0** emits the **PWA baseline** for `nextjs` projects with a web UI — `public/manifest.json`, `public/icon.svg`, `public/sw.js` and `components/ServiceWorker.tsx`, wired into `app/layout.tsx` — so a scaffolded app installs on a phone. Three decisions run against the original design note and are documented in place: `app/icon.svg` is NOT emitted (a static icon file in that segment would silently retire the generated `app/icon.tsx` favicon), `theme_color` comes from the brand primary so it agrees with the layout's `viewport.themeColor`, and the glyph is a path rather than text because no font is guaranteed where the OS rasterises an icon. The service worker is network-first, never caches `/api/`, and evicts only its own cache prefix — that last clause fixed a real defect found by **executing** the emitted worker in tests rather than grepping it: an origin-wide sweep meant one Z2W app's deploy wiped a neighbouring app's offline cache under `/path` routing.
- **v0.18.4** points the writer's cross-cutting placeholder check at EVERY emitted file. It had run against `CLAUDE.md` alone since Step 6 — one of the ~42 files a Next.js scaffold emits — so `sentry.shared.ts` shipped a literal `{{project_name}}`, a key that has never existed in the substitution map. Found by dogfooding a real scaffold, not by the suite. A per-project-type sweep now covers all eight types.
- **v0.18.3** makes every emitted `.gitignore` refuse a downloaded cloud service-account key. The Google Cloud console names a private key `<project-id>-<12 hex>.json`, which matched nothing in the scaffolded ignore file and, unlike a stray `.env.production`, carries no hint in its filename that it is a credential. Found when a live Gmail delegation key for two SAVE THE FROGS! mailboxes landed in a repo this CLI had emitted.
- **v0.18.2** bumps the emitted Agent Coordination fingerprint to **v0.1.26**, matching what `z2w-agent-coordination` currently publishes. Every scaffold emitted while the constant said `v0.1.25` carries a stale fingerprint, which is precisely the drift the fingerprint exists to detect.
- **v0.18.1** returns the audience gate's findings as **data** (`ScaffoldResult.audienceGate`) rather than only printing them. v0.18.0 wrote its dangling-reference warning to the terminal, but `previewScaffold()` discards its output stream by design — and that is the entry point a web UI uses — so the report had no receiver on the one path where an external audience actually occurs. No emitted file changes by a byte.
- **v0.18.0** implements **Kerry's 2026-08-14 ruling — Option A: "ship the artifact, not the doctrine."** A new `audience` input (`internal` | `external`, defaulting to `internal`) gates what an outside customer's scaffold contains. The emitted **code is untouched** — that is the value a customer is buying — but the ~40 KB prose corpus explaining which internal rulings produced it is not emitted: `Standards block`, `Z2W Portfolio Standards`, `Capture Learnings`, `Keeping This Framework Current`, `Common Name & Terminology Glossary` and `Agent Coordination` are dropped as **whole `##` sections**, and surviving `[[skill]]` Vault pointers are unwrapped to plain text. Measured on a real customer scaffold: **CLAUDE.md 85,702 → 44,848 B, a 47.7% reduction, 0 Vault pointers, 0 bulletin-repo mentions**; the internal control is byte-for-byte unchanged. **Whole sections, never edited sentences** — omitting a fingerprinted block makes no identity claim, whereas editing one ships a modified body under an unmodified fingerprint and an emitter mints that lie forever. The gate **refuses to emit** when a section it must drop cannot be found (a renamed heading would otherwise re-leak it silently), and **reports** surviving back-references to dropped sections rather than editing the canonical body to fix them.
  **Also fixes a defect that was wrong under every option and had nothing to do with the ruling:** `buildCustomBrandSpec()` hardcoded `author: DEFAULT_AUTHOR` and `CustomBrandInputs` had no author field, so **every outside-customer scaffold emitted `© 2026 Dr. Kerry Kriger` into the customer's own `README.md` and page footer** — the inverse of an IP leak, a false ownership claim on someone else's repo, live on the `project-creator` path. A custom brand now supplies its own `author`/`authorUrl` and falls back to its **own brand name**, the README's hardcoded `, Zero2Webmaster` is empty for custom brands, and the typography rule no longer cites a "Z2W standing organization instruction" at somebody else's organisation. The `marketing.md` / `BACKUPS.md` instructions to mirror status into the **private** Project Registry are omitted for external audiences. Tests **983 → 1,009**.
- **v0.17.2** closes the gap v0.17.1 left open and stated: the file selection behind that denominator was still an inline chain of filters in `main()`, so **nothing could execute it** without a live database and a full run — the same untestable-inline shape v0.16.2 had just fixed for `--strict`, in the same file, for a second decision. `selectBulletinFiles()` and `describeSelection()` are now pure exported functions with 13 tests, canary-verified by replanting the v0.17.0 bug. Two things came out of the extraction: the selection now **partitions** its input (four disjoint buckets that always sum to the entry count, so a file cannot leave the denominator without changing a number somebody prints), which surfaced two exclusion classes the report had never mentioned; and `main()` now **refuses a run that selected zero files** instead of printing `scanned: 0 / rows that WOULD change: 0` — a summary byte-identical to a healthy, fully-in-sync run. That is this script's own founding failure, relocated one layer up from the parse into the file selection. Real-corpus figure unchanged at **52**, so the refactor is behaviour-preserving.
- **v0.17.1** stops `scripts/rollup-integrations.mjs` counting its own `*-archive.md` spill files in the "Bulletin files scanned" denominator (72 -> 52), and STATES the exclusion rather than silently shrinking the number.
- **v0.17.0** makes the **Cloud-Run-vs-Fly.io host choice a standard** for `python-service` / `node-service` scaffolds, so a long-running service's platform is decided by one test — *does it need a persistent volume?* — rather than per project by whoever is in the chair. Cloud Run for stateless request/response that scales to zero with the database elsewhere; Fly.io for stateful always-on needing a disk that survives a restart, which Cloud Run structurally cannot hold. It is a ruling rather than a preference because `z2w-multi-lingual-api` costed it on a live service (~13 req/hr inside a $2/month budget, because Cloud Run genuinely idles at zero), and it carries the half a new project would not think to ask for: **do not re-litigate this as a migration** — every real incident there traced to Neon's CU-hour cap or to secret hygiene, both external to *both* platforms. Deliberately NOT inserted for `cf-worker` / `cf-pages` / `nextjs`, whose host the project type already settles.
- **v0.16.2** fixes `--strict` so it judges only what the invocation actually reconciled, unblocking `z2w-agent-coordination`'s Project Registry reconciler cron.
- **v0.15.1** ships `scripts/rollup-integrations.mjs` **in the npm tarball**, which is the one thing standing between the Project Registry reconciler and a schedule. `z2w-agent-coordination` accepted this project's proposal to host it on a cron and built the workflow, then found neither delivery route worked: a checkout of this private repo needs a PAT that could read every private repo in the org, and although the package *is* published, the tarball shipped **no `scripts/` at all** — `files` listed `dist/**` + README + LICENSE and the reconciler matched none of those globs. *"It is published"* and *"the script is reachable"* were two different facts. A runner can now `npm i @zero2webmaster/starter-kit@latest` and get it with **zero credentials**. Guarded by `src/package-contents.test.ts`, which runs `npm pack --dry-run --json` and asserts on the **artifact** rather than the glob — nothing here could previously tell a shipped file from an unshipped one.
- **v0.15.0** deletes the embedded Agent Coordination block in favour of a **pointer**, because this CLI is an *emitter* and a stale copy here is minted into every new Z2W project. `z2w-agent-coordination` measured currency across the ecosystem on 2026-08-07 and found **0 of 37 files at canonical** — 34 stale, 3 frozen paraphrases — and named "delete the copy, keep a pointer" its preferred remedy; the block had grown 4,274 B → 34,515 B in ten weeks, so every bump re-asked ~21 projects for a bigger paste. Scaffolded `CLAUDE.md` files now carry heading + fingerprint + session-start steps 1 and 1b (read the live block; it wins) + the Hard rules, and drop ~24 KB of auto-loaded context each. **The Hard rules stay embedded on purpose** — a leaked secret or an overwritten neighbour is irreversible, so those must hold even when the clone is unreachable. The old byte-diff guard was replaced rather than dropped (pointer conformance + an anti-regrowth byte ceiling, both canary-verified), and a real defect in it was fixed: it read a local bulletin checkout without ever checking that the checkout was current, and that clone was **215 commits behind**, so it compared against stale canon while reporting a shrinking gap.
- **v0.14.0** makes card-path bot protection a scaffold standard, and fixes a defect that made **every** Next.js scaffold's server modules impossible to unit-test. A new `acceptsCards` input inserts a standard pointing at [[stripe-elements-confirm-path]] §8 for every project type that has a runtime request surface, and — for Next.js — emits the protected endpoint shape rather than a bare one: a Turnstile verifier whose verdict is five-valued and **fails open**, an abuse policy sized for card entry (30 per 10 minutes per source, no global cap) rather than copied from order submission, the browser widget with `appearance: "interaction-only"` and every failure callback wired, and an emitted test that greps the consuming project's own `app/` and fails the next card path added bare. The enumeration grep ships *with* the standard, because a rule that says "protect the card path" without saying how to enumerate card paths passes review and misses a route. Separately: `import "server-only"` is a build-time poison pill that made every server module in every scaffold fail to LOAD under vitest — while `next build`, `tsc --noEmit` and the emitted suite all passed — so scaffolds now emit a `server-only` test stub plus the `@/*` alias that mirrors `tsconfig.json`.
- **v0.9.5** gives the Project Registry a **write path for the events it describes**. `z2w-agent-command-center` reported two stale `production_status` rows and asked for a writer; investigating found two different causes wearing one face. `site-control`'s row would have been fixed by the reconciler that already exists — nothing ever ran it. `email-engine`'s row **could not** be fixed, because `scripts/rollup-integrations.mjs` allowed a backtick before the status value but not an asterisk, so every project that **bolded** its Live status parsed as null and vanished from the report — indistinguishable from a row already in sync. That was 3 of the 15 sections declaring a status (20%), two of which said LIVE. The parser now reads the forms agents actually write, and reports declared-but-unreadable separately from never-declared, at zero as well as non-zero. Six rows reconciled. Also: the `command_center` role (scoped to the two approval columns), a CHECK that makes `summary_state = 'approved'` unreachable without them, and `status_qualifier` for the projects that kept writing a sixth value into a five-value enum.
- **v0.9.4** corrects a **privacy understatement in the emitted Sentry standard**. It had told every scaffolded web app to set `sendDefaultPii: true` and described the consequence as *"user IP + request headers"*. Verified against `@sentry/core` 10.69.0's own `defaultPiiToCollectionOptions()`, that flag resolves **ten** axes on — including `httpBodies` in all four directions, `databaseQueryData`, `stackFrameVariables` (local variable values), and `genAI: { inputs: true, outputs: true }`, which is the user's prompt and the model's completion. The standard now prescribes one complete `dataCollection` block, carries both counter-intuitive traps (a partial block flips the baseline to all-on; `stackFrameVariables` is on in *both* branches so `sendDefaultPii: false` cannot reach it), and makes the **resolver** the binding check rather than any list in a doc. Also retires the registry's `project_type` column in favour of `stack` + `audience`.
- **v0.9.3** fixes a scaffold-corrupting splice bug and the "GitHub org" wording. The canonical Agent Coordination block contains the anchor `` (`^## Current focus$`) ``, whose `$` and closing backtick form `` $` `` — a `String.replace()` special pattern meaning *"everything before the match"*. Because the block was passed as a replacement **string**, every scaffold emitted since 2026-07-30 pasted its own entire document head into the middle of that sentence. All four canonical-body splices now use a replacer **function**, whose return value is never pattern-interpreted. Separately, the brand block now reads `- **GitHub owner:**` and states the account type: `zero2webmaster` is a GitHub **User**, not an Organization, so `/organizations/...` URLs 404 — wording that had sent two agents to those 404s.
- **v0.9.2** re-syncs the emitted Capture Learnings block to canonical **v1.1.0** (the `AGENTS.md`/`GEMINI.md` mirror is now conditional). **v0.9.1** republishes v0.9.0 with the corrected LICENSE + README. The 0.9.0 tarball shipped the old text by accident — see CHANGELOG. **v0.9.0** gives every Next.js scaffold accessible form primitives — `components/FormBits.tsx` (`<Field>` / `<Required>` / `<InfoHint>`), the `app/globals.css` they need, a vitest runner, and an *emitted* `test/form-standards.test.ts` that fails on the next hand-rolled form which labels a field "(optional)", hides help text in a placeholder, or marks required with a bare `*`.
- **v0.8.0 (published)** emits the canonical Capture Learnings block into every scaffolded `CLAUDE.md`, so the session-end "should this become a skill?" step actually reaches the agent working in the new project.
- **v0.7.0** added `lib/seo/page-metadata.ts` — a `pageMetadata()` helper that returns a complete `openGraph` + `twitter` object every time. Next.js merges metadata *shallowly per top-level key*, so a page that hand-writes a partial `openGraph` silently drops `og:type`, `og:site_name` and `og:image` and downgrades its Twitter card.
- **v0.6.0** shipped the magic-link `CANONICAL_HOSTS` seam, a shared `lib/seo/canonical-host.mjs` so `robots.ts` and the Vercel alias redirect cannot disagree, and a `404`-on-unmapped-Host guard for Host-routed multi-tenant apps.
- Automatic **GitHub repo creation is a free default**: after a scaffold the CLI runs `git init` + first commit + `gh repo create --private` + push, with `--no-repo` to opt out. It is skipped for `--dry-run` and for the non-interactive `--input` CI path, and it warns rather than failing if `gh` is unavailable.
- **The monetization question is settled: the IP stays in-house (Kerry, 2026-07-30).** Zero2Webmaster does not sell licences to this tool or to the framework it encodes; what gets monetized is the work built with it. The paid-subscription language that earlier releases carried in `LICENSE` and in this README has been withdrawn. The `license` subcommands and the `--with-pro-seo` gate are now **dormant machinery pending a removal decision** — they are built and tested but have nothing to validate against.

> **Note on the hosted Templates mirror:** earlier releases (v0.2.0 / v0.2.1) shipped a hosted mirror at `templates.z2w.us` that let the CLI run without a local Templates folder. **That mirror was retired on 2026-06-23** and the CLI now reads canonical content from the local Templates folder only — see *Templates resolution* above.

## License

**Proprietary. All rights reserved. No license is granted, and this tool is not for sale.**

© 2026 Dr. Kerry Kriger, Zero2Webmaster.

This package is published to npm for the operational convenience of Zero2Webmaster's own machines. Publication to a public registry is not a license, an offer, or a grant of any kind — see [LICENSE](LICENSE) for the full terms.

**This is an internal build tool, not a product.** Earlier releases of this package invited the public to evaluate it and buy a subscription; that invitation was withdrawn on 2026-07-30 to protect the framework encoded in this tool and in the templates it emits.

**This says nothing about Zero2Webmaster's products.** Zero2Webmaster licenses its software to customers and clients under separate terms — this notice concerns this internal tool alone.

Inquiries: kerry@zero2webmaster.com.

---

*Scaffolded 2026-05-28 via the [[instantiate-z2w-project]] Claude Code skill v1; reconciled against the consolidated skill v1.1.0 (brief-first + Agent Coordination block) on 2026-05-29.*
*Version: 0.32.1 | Last Updated: 2026-09-19*
