@getvitops/create 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,65 @@
1
+ # @getvitops/create
2
+
3
+ Project templates for the Vitops design system, consumed by
4
+ [Vite+](https://viteplus.dev)'s `vp create` via the `createConfig.templates`
5
+ manifest in this package's `package.json`.
6
+
7
+ ## Usage
8
+
9
+ ```bash
10
+ # Interactive picker over this package's templates
11
+ vp create @getvitops
12
+
13
+ # Scaffold a specific template directly
14
+ vp create @getvitops:emdash
15
+ ```
16
+
17
+ `vp create` copies the template, initializes git, installs dependencies, and
18
+ (optionally) writes agent/editor config — see `vp create --help`.
19
+
20
+ ## Templates
21
+
22
+ ### `emdash`
23
+
24
+ An [EmDash CMS](https://docs.emdashcms.com) website on Cloudflare Workers,
25
+ with every relevant `@getvitops/*` package pre-wired:
26
+
27
+ - **`@getvitops/astro`** — the `getvitops()` integration: design-system CSS
28
+ generated from `design-system.json` (Tailwind v4 format, layout-scoped via
29
+ `css.inject: false`), favicons/PWA from `src/assets/logo.svg`, and the
30
+ web-component runtime copied into `public/vitops/`.
31
+ - **`@getvitops/emdash`** — the EmDash native plugin: Vitops Portable Text
32
+ blocks (image compare, copy snippet, banner, disclosure, carousel) editors
33
+ insert from the admin slash menu, rendered with the design-system web
34
+ components (accessible no-JS fallbacks included).
35
+ - **`@getvitops/cli`** — `vitops` on the scaffolded project's PATH for
36
+ `vitops validate`, `vitops agents`, and manual `vitops generate` runs.
37
+ - **EmDash on Cloudflare** — D1 database + R2 media via
38
+ `@emdash-cms/cloudflare`, a Worker entry with the cron trigger for
39
+ scheduled publishing, and a seed (`seed/seed.json`): the `pages`
40
+ collection, `primary`/`footer` menus, and settings apply on first boot; the
41
+ starter pages (home, about, terms, privacy — plus a 404 route) publish when
42
+ the setup wizard's "include sample content" option is chosen.
43
+ - **dev → main promotion flow** — GitHub Actions deploying `dev` to an
44
+ isolated `-dev` worker (`dev.<domain>` with a custom domain; own D1/R2 via
45
+ `scripts/build-dev-wrangler.mjs`, which hard-fails without `DEV_D1_ID`) and
46
+ `main` to prod, plus a manual `promote.yml` workflow and a matching
47
+ `promote` agent skill (`.agents/skills/promote/`) that opens the
48
+ `dev → main` PR with auto-merge.
49
+ - **AI tooling baked in** — `.mcp.json` with the Astro + EmDash docs MCP
50
+ servers, and a Claude Code web SessionStart hook that installs Node 24
51
+ (matching `devEngines`) and the pinned pnpm in the remote container.
52
+
53
+ The scaffolded README walks through local dev (`pnpm dev` — no Cloudflare
54
+ account needed) and deploy (`wrangler login && pnpm deploy` — wrangler
55
+ provisions the D1/R2 resources on first deploy).
56
+
57
+ ## Adding a template
58
+
59
+ 1. Add a directory under `templates/<name>/` (copied as-is; ship `_gitignore`
60
+ instead of `.gitignore` — `vp create` renames it).
61
+ 2. Add a `{ name, description, template: './templates/<name>' }` entry to
62
+ `createConfig.templates` in `package.json`.
63
+
64
+ Template dependencies must use concrete npm ranges (no `workspace:*` /
65
+ `catalog:` — the scaffolded project lives outside this monorepo).
package/package.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "@getvitops/create",
3
+ "version": "0.1.0",
4
+ "description": "Project templates for the Vitops design system — scaffold wired-up sites with `vp create @getvitops`.",
5
+ "license": "MIT",
6
+ "files": [
7
+ "templates"
8
+ ],
9
+ "type": "module",
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "createConfig": {
14
+ "templates": [
15
+ {
16
+ "name": "emdash",
17
+ "description": "EmDash CMS website on Cloudflare Workers (D1 + R2) with the Vitops design system pre-wired",
18
+ "template": "./templates/emdash"
19
+ }
20
+ ]
21
+ }
22
+ }
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: promote
3
+ description: Promote dev → main to deploy the site to production. Use ONLY when the user explicitly asks to promote, ship, or release dev to prod (e.g. "/promote", "promote to prod", "ship dev to production"). This is a deliberate production deploy — never trigger it automatically from generic "deploy" talk or as a side effect of other work.
4
+ ---
5
+
6
+ # Promote (dev → main → production)
7
+
8
+ Promotion is the **manual** step that ships reviewed `dev` code to production.
9
+ It dispatches the **"Promote dev → main"** GitHub Action
10
+ (`.github/workflows/promote.yml`), which opens a PR from `dev` into the
11
+ protected `main` branch and enables auto-merge. Merging `main` triggers
12
+ **Deploy prod** (`deploy-prod.yml`), which ships the production worker.
13
+
14
+ Deploys that are **already automatic** (do not use this skill for them):
15
+
16
+ - **dev** auto-deploys on every push/merge to `dev` (`deploy-dev.yml` → the
17
+ `-dev` worker / `dev.<domain>`).
18
+ - **prod** auto-deploys on every push to `main` (`deploy-prod.yml`).
19
+
20
+ So this skill's only job is to kick off the `dev → main` promotion. It does
21
+ **not** promote schema/content — see below.
22
+
23
+ ## Steps
24
+
25
+ 1. **Confirm intent.** This ships to production. Briefly state what will happen
26
+ ("This opens a PR from `dev` into `main`; merging deploys production.") and
27
+ proceed — the user invoking this skill is the confirmation. Only pause to
28
+ ask if they haven't clearly asked to go to prod.
29
+
30
+ 2. **Dispatch the workflow** with the `gh` CLI (works from any checkout of this
31
+ repo; `--ref main` picks which copy of the workflow file runs — the workflow
32
+ itself operates on `dev` and `main` regardless):
33
+
34
+ ```bash
35
+ gh workflow run promote.yml --ref main
36
+ ```
37
+
38
+ 3. **Find the run and report back.** Dispatch prints no run ID, so list the
39
+ runs to get the URL:
40
+
41
+ ```bash
42
+ gh run list --workflow=promote.yml --limit 1 --json databaseId,url,status,conclusion
43
+ ```
44
+
45
+ Give the user the run's `url`. If they want, watch it:
46
+ `gh run watch <databaseId>`, then report the conclusion and the promotion
47
+ PR: `gh pr view dev --base main --json url,state`.
48
+
49
+ ## What to tell the user after dispatch
50
+
51
+ - The workflow opens (or reuses) a PR **dev → main** and enables auto-merge.
52
+ - **`main` should be branch-protected.** Whether the merge completes hands-off
53
+ depends on the repo setup:
54
+ - With **required reviews** (default), the PR waits for a human to approve —
55
+ the default `GITHUB_TOKEN` can't approve its own PR. Point the user to the
56
+ PR to approve it; only then does prod deploy.
57
+ - If a `PROMOTE_TOKEN` secret (a PAT / GitHub App token allowed to bypass
58
+ protection) is set, auto-merge completes on its own and prod deploys with
59
+ no further action.
60
+ - Once `main` advances, **Deploy prod** runs automatically.
61
+
62
+ ## Schema / content changes
63
+
64
+ If this release changes the content model, code promotion is **not** enough —
65
+ the schema lives in the EmDash database, not git. Apply the same schema change
66
+ to the prod site separately (via its admin at `/_emdash/admin` or its MCP
67
+ server at `/_emdash/api/mcp`). Ordering: additive changes (new collection /
68
+ optional field) **before** the code deploy that uses them; removals **after**
69
+ the code that stops using them. Mention this to the user only if the release
70
+ plausibly touched the schema.
@@ -0,0 +1,40 @@
1
+ #!/bin/bash
2
+ #
3
+ # SessionStart hook for Claude Code on the web.
4
+ #
5
+ # The web container ships Node 20/21/22, but this project pins Node 24 via
6
+ # `devEngines` in package.json. This hook installs Node 24 (via the image's
7
+ # nvm), makes it win over the baked-in Node on PATH for the whole session,
8
+ # activates the pinned pnpm, and installs dependencies so `astro check`,
9
+ # builds, and the EmDash CLI all run against the version the project expects.
10
+ set -euo pipefail
11
+
12
+ # Only relevant in the remote web container; local sessions manage their own Node.
13
+ if [ "${CLAUDE_CODE_REMOTE:-}" != "true" ]; then
14
+ exit 0
15
+ fi
16
+
17
+ # --- Node 24 (matches package.json > devEngines.runtime) ---------------------
18
+ export NVM_DIR="${NVM_DIR:-/opt/nvm}"
19
+ # shellcheck disable=SC1091
20
+ [ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
21
+
22
+ nvm install 24
23
+ nvm alias default 24
24
+
25
+ # The base image prepends its own node bin dir to PATH, ahead of nvm's shims,
26
+ # so a plain `node` resolves to the baked-in version. Put Node 24's bin dir
27
+ # first for this session and persist it to every subsequent tool call via
28
+ # $CLAUDE_ENV_FILE.
29
+ NODE24_BIN="$(dirname "$(nvm which 24)")"
30
+ export PATH="$NODE24_BIN:$PATH"
31
+ echo "export PATH=\"$NODE24_BIN:\$PATH\"" >> "$CLAUDE_ENV_FILE"
32
+
33
+ # --- Dependencies (pnpm, pinned via package.json > packageManager) -----------
34
+ cd "$CLAUDE_PROJECT_DIR"
35
+ export COREPACK_ENABLE_DOWNLOAD_PROMPT=0
36
+ corepack enable
37
+ # `install` (not `--frozen-lockfile`) so the warmed container cache is reusable.
38
+ pnpm install
39
+
40
+ echo "Ready: Node $(node --version), pnpm $(pnpm --version)"
@@ -0,0 +1,14 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/session-start.sh"
9
+ }
10
+ ]
11
+ }
12
+ ]
13
+ }
14
+ }
@@ -0,0 +1,41 @@
1
+ # Deploys the `dev` branch to the isolated dev worker (<name>-dev, and
2
+ # dev.<domain> if a custom domain is configured in wrangler.jsonc).
3
+ # `dev` is the integration branch — every push here refreshes the review site.
4
+ # Promote to production with the "Promote dev → main" workflow (promote.yml).
5
+ #
6
+ # Required repo secrets:
7
+ # CLOUDFLARE_API_TOKEN — token with Workers + D1 + R2 edit on this account
8
+ # CLOUDFLARE_ACCOUNT_ID — the Cloudflare account id
9
+ # DEV_D1_ID — id from `wrangler d1 create <name>-dev`
10
+ # (without it the build script hard-fails, on purpose)
11
+ name: Deploy dev
12
+
13
+ on:
14
+ push:
15
+ branches: [dev]
16
+
17
+ permissions:
18
+ contents: read
19
+
20
+ concurrency:
21
+ group: deploy-dev
22
+ cancel-in-progress: true
23
+
24
+ jobs:
25
+ deploy:
26
+ runs-on: ubuntu-latest
27
+ environment: dev
28
+ steps:
29
+ - uses: actions/checkout@v4
30
+ - uses: pnpm/action-setup@v4
31
+ - uses: actions/setup-node@v4
32
+ with:
33
+ node-version: '24'
34
+ cache: pnpm
35
+ - run: pnpm install --frozen-lockfile
36
+ - name: Build + deploy dev worker
37
+ run: pnpm run deploy:dev
38
+ env:
39
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
40
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
41
+ DEV_D1_ID: ${{ secrets.DEV_D1_ID }}
@@ -0,0 +1,36 @@
1
+ # Deploys `main` to production.
2
+ # `main` should be branch-protected — it then only advances via the Promote
3
+ # workflow (promote.yml), so a push here always represents promoted code.
4
+ #
5
+ # Required repo secrets:
6
+ # CLOUDFLARE_API_TOKEN, CLOUDFLARE_ACCOUNT_ID
7
+ name: Deploy prod
8
+
9
+ on:
10
+ push:
11
+ branches: [main]
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ concurrency:
17
+ group: deploy-prod
18
+ cancel-in-progress: false
19
+
20
+ jobs:
21
+ deploy:
22
+ runs-on: ubuntu-latest
23
+ environment: production
24
+ steps:
25
+ - uses: actions/checkout@v4
26
+ - uses: pnpm/action-setup@v4
27
+ - uses: actions/setup-node@v4
28
+ with:
29
+ node-version: '24'
30
+ cache: pnpm
31
+ - run: pnpm install --frozen-lockfile
32
+ - name: Build + deploy prod worker
33
+ run: pnpm run deploy
34
+ env:
35
+ CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
36
+ CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
@@ -0,0 +1,49 @@
1
+ # Promote dev → main (code). Run manually after reviewing the dev site.
2
+ # Opens a PR from dev into the protected `main` branch and enables auto-merge;
3
+ # the merge triggers Deploy prod.
4
+ #
5
+ # Branch protection on `main` means the merge only completes when its rules are
6
+ # satisfied. Choose ONE:
7
+ # (a) let a human approve the PR (required reviews) — safest, full audit trail;
8
+ # (b) set repo secret PROMOTE_TOKEN to a PAT / GitHub App token allowed to
9
+ # bypass protection, for hands-off promotion.
10
+ # The default GITHUB_TOKEN cannot approve its own PR, so with required reviews
11
+ # the PR waits for a human — which is usually what you want for a prod promotion.
12
+ #
13
+ # Schema/content promotion is deliberately NOT here — the content model lives in
14
+ # the EmDash database, not git. Apply schema changes to prod via the admin or
15
+ # the site's MCP server, additive changes before the code deploy that uses
16
+ # them, removals after the code that stops using them.
17
+ name: Promote dev → main
18
+
19
+ on:
20
+ workflow_dispatch:
21
+
22
+ permissions:
23
+ contents: write
24
+ pull-requests: write
25
+
26
+ jobs:
27
+ promote:
28
+ runs-on: ubuntu-latest
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ with:
32
+ fetch-depth: 0
33
+ - name: Open promotion PR and enable auto-merge
34
+ env:
35
+ GH_TOKEN: ${{ secrets.PROMOTE_TOKEN || github.token }}
36
+ run: |
37
+ set -euo pipefail
38
+ if gh pr view dev --base main --json number >/dev/null 2>&1; then
39
+ echo "Promotion PR already open; ensuring auto-merge is enabled."
40
+ else
41
+ # printf, not a multi-line string: an unindented continuation line
42
+ # would end this YAML block and break the whole workflow file.
43
+ gh pr create --base main --head dev \
44
+ --title "Promote dev → main" \
45
+ --body "$(printf '%s\n\n%s' \
46
+ "Automated code promotion from \`dev\`. Merging deploys to production." \
47
+ "Remember: schema/content changes live in the EmDash database, not git — promote them separately if this release changes the content model.")"
48
+ fi
49
+ gh pr merge dev --auto --merge
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "astro-docs": {
4
+ "type": "http",
5
+ "url": "https://mcp.docs.astro.build/mcp"
6
+ },
7
+ "emdash-docs": {
8
+ "type": "http",
9
+ "url": "https://docs.emdashcms.com/mcp"
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,50 @@
1
+ This is an EmDash CMS website (Astro SSR on Cloudflare Workers, D1 + R2)
2
+ styled by the Vitops design system.
3
+
4
+ ## Ground rules
5
+
6
+ - `design-system.json` is the single source of truth for the design system
7
+ (colors, type, spacing, patterns, animations). Never edit
8
+ `src/styles/tailwind.css` — it is generated from the JSON by the
9
+ `getvitops()` integration on every dev/build and is gitignored.
10
+ - Content lives in the EmDash database, not in the repo. Query it with
11
+ `getEmDashCollection` / `getEmDashEntry` from `emdash`; render Portable Text
12
+ with `<PortableText />` from `emdash/ui` (the Vitops blocks from
13
+ `@getvitops/emdash` are merged in automatically).
14
+ - `seed/seed.json` only applies against an empty database: structure
15
+ (collections, menus, settings) on first boot; the `content` entries only
16
+ when the setup wizard's "include sample content" option is chosen. Changing
17
+ the seed does not migrate an existing site — use the admin
18
+ (`/_emdash/admin`) or the site's MCP server (`/_emdash/api/mcp`) to change
19
+ the content model of a running site.
20
+ - Prefer framework CSS classes (utilities, patterns, type roles like
21
+ `font-heading`) over bespoke CSS. Run `npx vitops agents` to append the
22
+ full generated class-vocabulary reference to this file.
23
+ - The Worker cron trigger in `wrangler.jsonc` drives scheduled publishing —
24
+ don't remove it.
25
+
26
+ ## Branch model
27
+
28
+ - Work on **`dev`** (the integration branch). Every push auto-deploys the
29
+ isolated `-dev` worker (own D1/R2; `dev.<domain>` with a custom domain).
30
+ - **`main`** is production and branch-protected — never push or merge to it
31
+ directly. Production ships only via the manual **promote** flow: the
32
+ `promote` skill (`.agents/skills/promote/SKILL.md`) dispatches
33
+ `.github/workflows/promote.yml`, which opens a `dev → main` PR with
34
+ auto-merge; merging deploys prod. Only run it when the user explicitly asks
35
+ to promote/ship to production.
36
+ - Sample legal pages (`/terms`, `/privacy`) ship as seeded placeholder content
37
+ — remind the user to replace them before launch.
38
+
39
+ ## Docs MCP servers
40
+
41
+ `.mcp.json` preconfigures the Astro docs and EmDash docs MCP servers — prefer
42
+ searching them over pre-trained knowledge for Astro/EmDash questions.
43
+
44
+ ## Commands
45
+
46
+ - `pnpm dev` — local dev with local D1/R2 emulation (no Cloudflare account)
47
+ - `pnpm typecheck` — `astro check`
48
+ - `pnpm deploy` — build + `wrangler deploy` prod (provisions D1/R2 on first run)
49
+ - `pnpm deploy:dev` — build + deploy the isolated dev worker (needs `DEV_D1_ID`)
50
+ - `npx vitops validate design-system.json` — schema-check the design config
@@ -0,0 +1,136 @@
1
+ # My EmDash Site
2
+
3
+ An [EmDash CMS](https://docs.emdashcms.com) website on Cloudflare Workers,
4
+ styled by the [Vitops design system](https://www.npmjs.com/org/getvitops).
5
+
6
+ Scaffolded from `vp create @getvitops:emdash`.
7
+
8
+ ## Stack
9
+
10
+ - **Astro** (SSR) on **Cloudflare Workers** — D1 database, R2 media storage
11
+ - **EmDash** — the admin lives at `/_emdash/admin`; content is queried with
12
+ `getEmDashCollection` / `getEmDashEntry` (Astro live content collections)
13
+ - **`@getvitops/astro`** — generates the design-system CSS from
14
+ `design-system.json` (Tailwind v4 format), generates favicons/PWA assets
15
+ from `src/assets/logo.svg`, and serves the web-component runtime
16
+ - **`@getvitops/emdash`** — design-system Portable Text blocks in the editor
17
+ slash menu: image compare, copy snippet, banner, disclosure, carousel
18
+
19
+ ## Local development
20
+
21
+ ```bash
22
+ pnpm install
23
+ pnpm dev
24
+ ```
25
+
26
+ No Cloudflare account needed: `astro dev` runs the Worker locally with local
27
+ D1/R2 emulation (state in `.wrangler/`). On first boot EmDash runs its
28
+ migrations and applies the structure from `seed/seed.json` — a `pages`
29
+ collection, a `primary` menu, and the site settings.
30
+
31
+ - Site: <http://localhost:4321>
32
+ - Admin: <http://localhost:4321/_emdash/admin>
33
+
34
+ Open the admin and complete the setup wizard — it creates your first account,
35
+ and choosing to **include sample content** publishes the starter pages from
36
+ the seed: home, about, terms (`/terms`), and privacy policy (`/privacy`).
37
+ Until then the home route shows a friendly empty state. Unknown paths render
38
+ `src/pages/404.astro`.
39
+
40
+ ## Deploy
41
+
42
+ ```bash
43
+ npx wrangler login
44
+ pnpm deploy
45
+ ```
46
+
47
+ Wrangler provisions the D1 database and R2 bucket named in `wrangler.jsonc`
48
+ on the first deploy; EmDash migrates + applies the seed structure on first
49
+ boot (sample content again comes from the setup wizard). Later deploys leave
50
+ existing content alone. The cron trigger in `wrangler.jsonc` drives
51
+ scheduled publishing — don't remove it. For a custom domain, uncomment the
52
+ `routes` block in `wrangler.jsonc` and set your apex.
53
+
54
+ ## Environments: dev → main promotion
55
+
56
+ The repo ships a two-environment flow modeled on branch = environment:
57
+
58
+ - **`dev`** is the integration branch. Every push auto-deploys the isolated
59
+ `-dev` worker (`.github/workflows/deploy-dev.yml`) — its own D1 database and
60
+ R2 bucket, served at `dev.<domain>` when a custom domain is configured (or
61
+ the worker's `workers.dev` URL otherwise).
62
+ - **`main`** is production. Every push auto-deploys the prod worker
63
+ (`.github/workflows/deploy-prod.yml`).
64
+ - **Promotion is manual**: run the **"Promote dev → main"** workflow
65
+ (`.github/workflows/promote.yml`) — or ask your AI agent to run the
66
+ `promote` skill (`.agents/skills/promote/`) — and it opens a PR from `dev`
67
+ into `main` with auto-merge enabled. With required reviews on `main`, a
68
+ human approves the PR; merging deploys prod.
69
+
70
+ One-time setup:
71
+
72
+ 1. Create the branches: `git branch dev && git push -u origin main dev`, and
73
+ make `dev` the default working branch. Branch-protect `main` (require the
74
+ promotion PR + review).
75
+ 2. Create the isolated dev resources:
76
+ `wrangler d1 create my-emdash-site-dev` and
77
+ `wrangler r2 bucket create my-emdash-site-media-dev`.
78
+ 3. Add repo secrets: `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_ACCOUNT_ID`, and
79
+ `DEV_D1_ID` (the id printed by `d1 create`). Optionally `PROMOTE_TOKEN`
80
+ (a PAT allowed to bypass protection) for hands-off promotion.
81
+
82
+ `pnpm deploy:dev` reproduces the dev deploy locally: it rewrites the
83
+ adapter-generated wrangler config (`scripts/build-dev-wrangler.mjs`) so the
84
+ dev worker never touches prod data — it hard-fails without `DEV_D1_ID`,
85
+ on purpose. Schema/content changes live in the EmDash database, not git, so
86
+ promote them separately (via the prod admin or its MCP server).
87
+
88
+ ## AI tooling
89
+
90
+ - **MCP servers** (`.mcp.json`): the Astro docs (`mcp.docs.astro.build`) and
91
+ EmDash docs (`docs.emdashcms.com/mcp`) servers are preconfigured, so coding
92
+ agents answer from current documentation. Your running site also exposes its
93
+ own content MCP server at `/_emdash/api/mcp` (authenticated).
94
+ - **Skills** (`.agents/skills/`): `promote` dispatches the dev → main
95
+ promotion workflow. Run `npx vitops agents` to also write the design-system
96
+ class reference into `AGENTS.md`.
97
+ - **Claude Code on the web**: the SessionStart hook
98
+ (`.claude/hooks/session-start.sh`) installs Node 24 (matching
99
+ `devEngines`), activates the pinned pnpm, and installs dependencies in the
100
+ remote container automatically. Point the remote environment at the `dev`
101
+ branch so pushes land on the auto-deployed review site.
102
+
103
+ ## The design system
104
+
105
+ `design-system.json` is the single source of truth for colors, type, spacing,
106
+ shadows, patterns, and animations. The `getvitops()` integration regenerates
107
+ `src/styles/tailwind.css` from it on dev/build (the file is gitignored — edit
108
+ the JSON, not the CSS). `Layout.astro` imports the stylesheet so the admin
109
+ routes stay unstyled by it.
110
+
111
+ Useful commands (via `@getvitops/cli`):
112
+
113
+ ```bash
114
+ npx vitops validate design-system.json # check the config against the schema
115
+ npx vitops agents # write the design-system reference into AGENTS.md
116
+ pnpm build:css # regenerate the CSS manually
117
+ ```
118
+
119
+ ## Project map
120
+
121
+ | Path | Purpose |
122
+ | -------------------------------- | ----------------------------------------------------------------- |
123
+ | `design-system.json` | Design tokens + patterns → generated CSS |
124
+ | `seed/seed.json` | First-boot content model + starter content |
125
+ | `src/worker.ts` | Worker entry (fetch + EmDash scheduled handler) |
126
+ | `src/live.config.ts` | Astro live content collections wiring for EmDash |
127
+ | `src/layouts/Layout.astro` | Site chrome: EmDash + Vitops head tags, nav from the CMS menu |
128
+ | `src/pages/index.astro` | Home — renders the `home` page entry |
129
+ | `src/pages/[...slug].astro` | Catch-all — renders any `pages` entry at its slug |
130
+ | `src/pages/404.astro` | Not-found page (the catch-all rewrites here) |
131
+ | `wrangler.jsonc` | Cloudflare bindings (D1/R2) + cron trigger |
132
+ | `scripts/build-dev-wrangler.mjs` | Rewrites the built wrangler config for the isolated dev worker |
133
+ | `.github/workflows/` | deploy-dev (push to `dev`), deploy-prod (push to `main`), promote |
134
+ | `.agents/skills/promote/` | Agent skill: dispatch the dev → main promotion |
135
+ | `.mcp.json` | Astro + EmDash docs MCP servers for coding agents |
136
+ | `.claude/hooks/` | Claude Code web: Node 24 + pnpm setup on session start |
@@ -0,0 +1,25 @@
1
+ # build output
2
+ dist/
3
+
4
+ # dependencies
5
+ node_modules/
6
+
7
+ # generated types
8
+ .astro/
9
+ emdash-env.d.ts
10
+ worker-configuration.d.ts
11
+
12
+ # wrangler local state (local D1/R2 data lives here during `astro dev`)
13
+ .wrangler/
14
+
15
+ # generated by the getvitops() integration on dev/build
16
+ src/styles/
17
+ public/vitops/
18
+
19
+ # environment
20
+ .env
21
+ .env.*
22
+ !.env.example
23
+
24
+ # os / editor
25
+ .DS_Store
@@ -0,0 +1,41 @@
1
+ import cloudflare from '@astrojs/cloudflare';
2
+ import react from '@astrojs/react';
3
+ import { d1, r2 } from '@emdash-cms/cloudflare';
4
+ import getvitops from '@getvitops/astro';
5
+ import { vitopsEmdash } from '@getvitops/emdash';
6
+ import { defineConfig } from 'astro/config';
7
+ import emdash from 'emdash/astro';
8
+
9
+ export default defineConfig({
10
+ output: 'server',
11
+ adapter: cloudflare(),
12
+ image: {
13
+ layout: 'constrained',
14
+ responsiveStyles: true,
15
+ },
16
+ integrations: [
17
+ // The EmDash admin UI (/_emdash/admin) is a React app.
18
+ react(),
19
+ emdash({
20
+ database: d1({ binding: 'DB', session: 'auto' }),
21
+ storage: r2({ binding: 'MEDIA' }),
22
+ // Vitops design-system blocks (image compare, copy snippet, banner,
23
+ // disclosure, carousel) in the editor slash menu. Default 'integration'
24
+ // script delivery: Layout.astro renders <Head /> from @getvitops/astro,
25
+ // which emits the web-component runtime tags.
26
+ plugins: [vitopsEmdash()],
27
+ }),
28
+ getvitops({
29
+ favicon: {
30
+ source: 'src/assets/logo.svg',
31
+ name: 'My EmDash Site',
32
+ themeColor: '#2e9b73',
33
+ backgroundColor: '#ffffff',
34
+ },
35
+ // inject: false — Layout.astro imports the generated stylesheet, so
36
+ // EmDash's /_emdash/admin routes don't inherit the design system while
37
+ // your pages (and EmDash previews rendered through the layout) do.
38
+ css: { format: 'tailwind', inject: false },
39
+ }),
40
+ ],
41
+ });