@getvitops/create 0.6.0 → 0.8.0-alpha.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +178 -5
  2. package/package.json +2 -2
  3. package/templates/emdash/.agents/skills/setup/SKILL.md +61 -0
  4. package/templates/emdash/.astro/types.d.ts +3 -0
  5. package/templates/emdash/.github/workflows/deploy-prod.yml +1 -1
  6. package/templates/emdash/AGENTS.md +19 -11
  7. package/templates/emdash/README.md +45 -42
  8. package/templates/emdash/SETUP.html +576 -0
  9. package/templates/emdash/_gitignore +3 -0
  10. package/templates/emdash/astro.config.mjs +33 -3
  11. package/templates/emdash/package.json +14 -9
  12. package/templates/emdash/seed/seed.json +1 -1
  13. package/templates/emdash/site.json +303 -0
  14. package/templates/emdash/src/layouts/Layout.astro +7 -1
  15. package/templates/emdash/src/pages/404.astro +5 -0
  16. package/templates/emdash/src/pages/[...slug].astro +7 -0
  17. package/templates/emdash/src/pages/index.astro +7 -0
  18. package/templates/emdash/vite.config.ts +19 -5
  19. package/templates/emdash/wrangler.jsonc +8 -3
  20. package/templates/static/.agents/skills/setup/SKILL.md +61 -0
  21. package/templates/static/.github/workflows/deploy-prod.yml +1 -1
  22. package/templates/static/AGENTS.md +14 -7
  23. package/templates/static/README.md +15 -20
  24. package/templates/static/SETUP.html +576 -0
  25. package/templates/static/_gitignore +3 -0
  26. package/templates/static/astro.config.mjs +5 -1
  27. package/templates/static/package.json +8 -6
  28. package/templates/static/site.json +228 -0
  29. package/templates/static/src/pages/index.astro +2 -2
  30. package/templates/static/wrangler.jsonc +10 -3
  31. package/templates/emdash/design-system.json +0 -148
  32. package/templates/emdash/scripts/init-github.mjs +0 -186
  33. package/templates/emdash/scripts/init-hosting.mjs +0 -150
  34. package/templates/static/scripts/init-github.mjs +0 -186
  35. package/templates/static/scripts/init-hosting.mjs +0 -62
@@ -0,0 +1,576 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>Setup</title>
7
+ <!--
8
+ Opened straight from disk (file://) before anything is deployed, which is
9
+ when most of these steps happen — so it links a relative, pre-generated
10
+ stylesheet rather than the site's own build output. Run `pnpm run sop`
11
+ first (it's a one-line `vitops generate -f css` into .vitops/sop/); the
12
+ banner below explains this if the stylesheet 404s, and the markup stays
13
+ legible either way — every item here is a real checkbox in a real
14
+ <details>, readable and tickable with JS off.
15
+
16
+ This file is template DATA copied verbatim by `vp create`, not framework
17
+ code — see AGENTS.md's "Client onboarding" section for why it isn't a
18
+ <wc-*> element. Only class names from @getvitops/core's own vocabulary
19
+ are used here; if you need something this page can't express, that's a
20
+ missing framework pattern, not a reason to hand-write CSS.
21
+ -->
22
+ <link id="sop-stylesheet" rel="stylesheet" href=".vitops/sop/styles.css" />
23
+ <style>
24
+ /* Minimal fallback so the page is still legible before `pnpm run sop`
25
+ has run once — intentionally not framework CSS, since it only ever
26
+ applies in the narrow window before the real stylesheet exists. */
27
+ body {
28
+ font-family:
29
+ system-ui,
30
+ -apple-system,
31
+ 'Segoe UI',
32
+ sans-serif;
33
+ max-width: 64rem;
34
+ margin-inline: auto;
35
+ padding: 2rem 1.5rem 6rem;
36
+ line-height: 1.5;
37
+ }
38
+ </style>
39
+ </head>
40
+ <body class="centered">
41
+ <div class="rhythm">
42
+ <header class="region rhythm">
43
+ <h1>Client onboarding checklist</h1>
44
+ <p id="no-stylesheet-notice" class="banner" hidden>
45
+ This page isn't styled yet — run <code>pnpm run sop</code> once (it generates
46
+ <code>.vitops/sop/styles.css</code>), then reload.
47
+ </p>
48
+ <p>
49
+ A local working document — it is never deployed, never committed to a build, and nothing
50
+ here is read by the site itself. Work top to bottom; each stage gates the next. Every
51
+ command is copy-pasteable as written. Progress is saved in this browser only
52
+ (<code>localStorage</code>), so it won't follow you to another machine.
53
+ </p>
54
+ <p>
55
+ <span class="badge" id="progress-badge">0 / 0 done</span>
56
+ </p>
57
+ </header>
58
+
59
+ <main class="rhythm">
60
+ <details class="details" open data-sop-stage="host">
61
+ <summary>1. Choose the host</summary>
62
+ <div class="rhythm">
63
+ <label class="custom-check">
64
+ <input type="checkbox" data-sop="host.decide" />
65
+ <span class="custom-check__box"
66
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
67
+ ></span>
68
+ <span>
69
+ Decide the hosting target — <strong>Cloudflare Workers</strong> is the default and
70
+ what the shipped deploy workflows target. A <code>node</code> target exists for the
71
+ EmDash template (VPS / docker-compose / k8s) but needs manual setup — see that
72
+ template's README "Hosting" section — and the deploy workflows here are
73
+ Cloudflare-specific either way.
74
+ </span>
75
+ </label>
76
+ </div>
77
+ </details>
78
+
79
+ <details class="details" data-sop-stage="repo">
80
+ <summary>2. GitHub repo + branch policy</summary>
81
+ <div class="rhythm">
82
+ <p>
83
+ <code>dev</code> is the unprotected integration branch — push and merge to it
84
+ directly. <code>main</code> is production: protected, gated by a
85
+ <code>production</code> environment that pauses for an admin's approval, and reachable
86
+ only through the <code>promote</code> workflow (never a direct push).
87
+ </p>
88
+ <label class="custom-check">
89
+ <input type="checkbox" data-sop="repo.plan" />
90
+ <span class="custom-check__box"
91
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
92
+ ></span>
93
+ <span> Preview it: <code>npx vitops setup repo --dry</code> </span>
94
+ </label>
95
+ <label class="custom-check">
96
+ <input type="checkbox" data-sop="repo.run" />
97
+ <span class="custom-check__box"
98
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
99
+ ></span>
100
+ <span>
101
+ Run it: <code>npx vitops setup repo</code> — creates the GitHub repo, pushes
102
+ <code>main</code> + <code>dev</code>, makes <code>dev</code> default, and requests
103
+ branch protection + the <code>production</code>/<code>dev</code>
104
+ environments.
105
+ </span>
106
+ </label>
107
+ <label class="custom-check">
108
+ <input type="checkbox" data-sop="repo.verify" />
109
+ <span class="custom-check__box"
110
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
111
+ ></span>
112
+ <span>
113
+ Verify by hand in GitHub → Settings → Branches / Environments. Branch protection and
114
+ environment reviewers need GitHub Pro/Team on a private repo — if the command
115
+ reported that as blocked, this is where you set it up manually.
116
+ </span>
117
+ </label>
118
+ </div>
119
+ </details>
120
+
121
+ <details class="details" data-sop-stage="hosting">
122
+ <summary>3. Hosting resources</summary>
123
+ <div class="rhythm">
124
+ <label class="custom-check">
125
+ <input type="checkbox" data-sop="hosting.plan" />
126
+ <span class="custom-check__box"
127
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
128
+ ></span>
129
+ <span> Preview it: <code>npx vitops setup hosting --dry</code> </span>
130
+ </label>
131
+ <label class="custom-check">
132
+ <input type="checkbox" data-sop="hosting.run" />
133
+ <span class="custom-check__box"
134
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
135
+ ></span>
136
+ <span>
137
+ Run it: <code>npx vitops setup hosting</code> — provisions the isolated dev D1
138
+ database + R2 bucket (EmDash template) and sets the <code>DEV_D1_ID</code> /
139
+ <code>CLOUDFLARE_ACCOUNT_ID</code> repo secrets. Requires
140
+ <code>npx wrangler login</code> first.
141
+ </span>
142
+ </label>
143
+ <label class="custom-check">
144
+ <input type="checkbox" data-sop="hosting.token" />
145
+ <span class="custom-check__box"
146
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
147
+ ></span>
148
+ <span>
149
+ <strong>Create a Cloudflare API token by hand</strong> — no API can mint one:
150
+ <a
151
+ class="link"
152
+ href="https://dash.cloudflare.com/profile/api-tokens"
153
+ target="_blank"
154
+ rel="noopener"
155
+ >dash.cloudflare.com/profile/api-tokens</a
156
+ >
157
+ (Workers + D1 + R2 edit), then
158
+ <code>gh secret set CLOUDFLARE_API_TOKEN</code>. The first workflow runs fail until
159
+ this exists.
160
+ </span>
161
+ </label>
162
+ <label class="custom-check">
163
+ <input type="checkbox" data-sop="hosting.emdash-fingerprint" />
164
+ <span class="custom-check__box"
165
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
166
+ ></span>
167
+ <span>
168
+ <strong>(EmDash template)</strong> After the first deploy to each environment, run
169
+ <code>npx emdash migrate --status</code> locally against that environment's real
170
+ database, review the printed account/database identity, and set the reviewed
171
+ fingerprint:
172
+ <code
173
+ >gh variable set EMDASH_TARGET_FINGERPRINT --env dev --body &lt;value&gt;</code
174
+ >
175
+ and again with <code>--env production</code>. No command can do this for you — it's
176
+ the human review the fingerprint guard exists for. Deploys fail clearly on the
177
+ migration-apply step until it's set for that environment.
178
+ </span>
179
+ </label>
180
+ <label class="custom-check">
181
+ <input type="checkbox" data-sop="hosting.design-system-editor-token" />
182
+ <span class="custom-check__box"
183
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
184
+ ></span>
185
+ <span>
186
+ <strong>(EmDash template, only if <code>designSystemEditor</code> is on)</strong>
187
+ Create a fine-grained GitHub PAT scoped to this one repository,
188
+ <code>Contents: Read and write</code> only — no <code>admin</code> scope, no access
189
+ to any other repo — then <code>gh secret set VITOPS_GITHUB_TOKEN</code>. Also set
190
+ <code>site.deployment.repo</code> (<code>"owner/name"</code>) and
191
+ <code>site.deployment.branch</code> (default <code>"dev"</code>) in
192
+ <code>site.json</code>. Without these the live design-system editor still works —
193
+ only the auto-commit-to-source half is skipped, and the admin page's response says
194
+ so.
195
+ </span>
196
+ </label>
197
+ </div>
198
+ </details>
199
+
200
+ <details class="details" data-sop-stage="domain">
201
+ <summary>4. Domain strategy</summary>
202
+ <div class="rhythm">
203
+ <p>Two mutually exclusive tracks. Pick one — do not do both in the same deploy.</p>
204
+
205
+ <h3>4a. Go live on <code>*.workers.dev</code> (default, safe)</h3>
206
+ <p>
207
+ Leaves any existing live site untouched at its current domain. Do this first, always —
208
+ cutover is a separate, later step.
209
+ </p>
210
+ <label class="custom-check">
211
+ <input type="checkbox" data-sop="domain.workersdev.leave" />
212
+ <span class="custom-check__box"
213
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
214
+ ></span>
215
+ <span>
216
+ Leave <code>workers_dev: true</code> and the <code>routes</code> block commented out
217
+ in <code>wrangler.jsonc</code> — this is the shipped default.
218
+ </span>
219
+ </label>
220
+ <label class="custom-check">
221
+ <input type="checkbox" data-sop="domain.workersdev.url" />
222
+ <span class="custom-check__box"
223
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
224
+ ></span>
225
+ <span>
226
+ Deploy once (<code>pnpm deploy</code>), note the printed
227
+ <code>*.workers.dev</code> URL, and set
228
+ <code>site.environments.production.url</code> in <code>site.json</code> to it.
229
+ </span>
230
+ </label>
231
+ <label class="custom-check">
232
+ <input type="checkbox" data-sop="domain.workersdev.noindex" />
233
+ <span class="custom-check__box"
234
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
235
+ ></span>
236
+ <span>
237
+ Set <code>site.environments.production.robots</code> to
238
+ <code>"noindex,nofollow"</code> — a workers.dev URL is indexable, and this is the
239
+ launch gate that stops search engines picking it up as a duplicate of the real site.
240
+ </span>
241
+ </label>
242
+
243
+ <h3>4b. Cut over to the custom domain</h3>
244
+ <p>
245
+ <strong>This step overwrites the zone's DNS record the moment you deploy</strong>
246
+ — it is the one irreversible-feeling moment in this whole checklist, and it takes an
247
+ existing live site down at that instant. Do it deliberately, not as part of a bigger
248
+ batch of changes. It is safe to complete stage 6 (Search Console / GCP / ads) before
249
+ this — <code>vitops search setup</code> only ever <em>creates</em> a verification TXT
250
+ record, it never touches the apex.
251
+ </p>
252
+ <label class="custom-check">
253
+ <input type="checkbox" data-sop="domain.cutover.ttl" />
254
+ <span class="custom-check__box"
255
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
256
+ ></span>
257
+ <span> If replacing a live site, lower that site's DNS TTL well ahead of time. </span>
258
+ </label>
259
+ <label class="custom-check">
260
+ <input type="checkbox" data-sop="domain.cutover.plan" />
261
+ <span class="custom-check__box"
262
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
263
+ ></span>
264
+ <span>
265
+ Preview it: <code>npx vitops setup domain --domain &lt;apex&gt; --dry</code> — this
266
+ only plans and checks; it never applies the cutover itself.
267
+ </span>
268
+ </label>
269
+ <label class="custom-check">
270
+ <input type="checkbox" data-sop="domain.cutover.uncomment" />
271
+ <span class="custom-check__box"
272
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
273
+ ></span>
274
+ <span>
275
+ Uncomment the <code>routes</code> block in <code>wrangler.jsonc</code>, set your
276
+ apex, and deploy: <code>pnpm deploy</code>.
277
+ </span>
278
+ </label>
279
+ <label class="custom-check">
280
+ <input type="checkbox" data-sop="domain.cutover.config" />
281
+ <span class="custom-check__box"
282
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
283
+ ></span>
284
+ <span>
285
+ Set <code>site.domains.canonical</code> and
286
+ <code>site.environments.production.url</code> to the custom domain, and flip
287
+ <code>environments.production.robots</code> back to <code>"index,follow"</code> (or
288
+ remove it).
289
+ </span>
290
+ </label>
291
+ <label class="custom-check">
292
+ <input type="checkbox" data-sop="domain.cutover.notify" />
293
+ <span class="custom-check__box"
294
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
295
+ ></span>
296
+ <span>
297
+ Re-run <code>npx vitops robots</code> and <code>npx vitops search notify</code>.
298
+ </span>
299
+ </label>
300
+ </div>
301
+ </details>
302
+
303
+ <details class="details" data-sop-stage="config">
304
+ <summary>5. Configuration</summary>
305
+ <div class="rhythm">
306
+ <label class="custom-check">
307
+ <input type="checkbox" data-sop="config.fill" />
308
+ <span class="custom-check__box"
309
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
310
+ ></span>
311
+ <span>
312
+ Fill in <code>site.json</code>: <code>organization</code> (name, address, contact),
313
+ <code>site.locales</code>, <code>site.environments</code>, <code>site.seo</code>,
314
+ <code>site.analytics</code>, <code>site.legal.*.enabled</code>,
315
+ <code>site.favicon</code>, <code>site.icons</code>.
316
+ </span>
317
+ </label>
318
+ <label class="custom-check">
319
+ <input type="checkbox" data-sop="config.validate" />
320
+ <span class="custom-check__box"
321
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
322
+ ></span>
323
+ <span> Check it: <code>npx vitops validate site.json</code> </span>
324
+ </label>
325
+ </div>
326
+ </details>
327
+
328
+ <details class="details" data-sop-stage="connections">
329
+ <summary>6. Search Console / GCP / ads</summary>
330
+ <div class="rhythm">
331
+ <label class="custom-check">
332
+ <input type="checkbox" data-sop="connections.gcp" />
333
+ <span class="custom-check__box"
334
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
335
+ ></span>
336
+ <span>
337
+ Set <code>site.google.project</code>, and the environment variables:
338
+ <code>VITOPS_GOOGLE_CLIENT_ID</code>, <code>VITOPS_GOOGLE_CLIENT_SECRET</code>,
339
+ <code>VITOPS_GOOGLE_REFRESH_TOKEN</code>, <code>CLOUDFLARE_API_TOKEN</code>.
340
+ </span>
341
+ </label>
342
+ <label class="custom-check">
343
+ <input type="checkbox" data-sop="connections.search" />
344
+ <span class="custom-check__box"
345
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
346
+ ></span>
347
+ <span>
348
+ Preview then run: <code>npx vitops setup search --dry</code>, then
349
+ <code>npx vitops setup search</code>. This is a <strong>user OAuth</strong>
350
+ action, deliberately — verifying a site makes the caller an owner, and that should
351
+ be a person, not a deploy robot.
352
+ </span>
353
+ </label>
354
+ <label class="custom-check">
355
+ <input type="checkbox" data-sop="connections.ads" />
356
+ <span class="custom-check__box"
357
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
358
+ ></span>
359
+ <span>
360
+ Preview then run: <code>npx vitops setup ads --dry</code>, then
361
+ <code>npx vitops setup ads</code>.
362
+ </span>
363
+ </label>
364
+ <label class="custom-check">
365
+ <input type="checkbox" data-sop="connections.email" />
366
+ <span class="custom-check__box"
367
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
368
+ ></span>
369
+ <span>
370
+ If <code>site.notifications.email</code> sends mail, onboard its sender domain:
371
+ <code>npx vitops email setup --dry</code>, then <code>npx vitops email setup</code>.
372
+ Cloudflare onboards an exact name — usually a subdomain like
373
+ <code>send.&lt;domain&gt;</code>, not the apex — so a mismatch here is silent at
374
+ build time and only fails when a form is actually submitted in production. Both
375
+ templates' <code>deploy</code> script already runs
376
+ <code>vitops email check</code> first as a non-blocking warning.
377
+ </span>
378
+ </label>
379
+ <label class="custom-check">
380
+ <input type="checkbox" data-sop="connections.manual" />
381
+ <span class="custom-check__box"
382
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
383
+ ></span>
384
+ <span>
385
+ Two things are never automated, by design: adding a Search Console
386
+ <strong>Full User</strong> (via a Google Group), and clicking each platform's own
387
+ "Verify" button. Both are named in the command output when they apply.
388
+ </span>
389
+ </label>
390
+ </div>
391
+ </details>
392
+
393
+ <details class="details" data-sop-stage="legal">
394
+ <summary>7. Legal</summary>
395
+ <div class="rhythm">
396
+ <label class="custom-check">
397
+ <input type="checkbox" data-sop="legal.generate" />
398
+ <span class="custom-check__box"
399
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
400
+ ></span>
401
+ <span>
402
+ Generate real documents from the filled-in config —
403
+ <code>npx vitops legal --format portable-text</code> (EmDash; paste into the admin)
404
+ or <code>npx vitops legal --format md</code> (static; commit the output). The seeded
405
+ <code>/terms</code> and <code>/privacy</code> pages are deliberately short,
406
+ obviously-unfinished placeholders.
407
+ </span>
408
+ </label>
409
+ <label class="custom-check">
410
+ <input type="checkbox" data-sop="legal.review" />
411
+ <span class="custom-check__box"
412
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
413
+ ></span>
414
+ <span>
415
+ <strong>Have it reviewed before publishing.</strong> Every generated document opens
416
+ with a non-optional review banner — it is a starting point from your config, not
417
+ legal advice, and a plausible-looking policy shipped unread is the one failure mode
418
+ here with real consequences.
419
+ </span>
420
+ </label>
421
+ </div>
422
+ </details>
423
+
424
+ <details class="details" data-sop-stage="launch">
425
+ <summary>8. Launch gates</summary>
426
+ <div class="rhythm">
427
+ <label class="custom-check">
428
+ <input type="checkbox" data-sop="launch.robots" />
429
+ <span class="custom-check__box"
430
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
431
+ ></span>
432
+ <span>
433
+ Robots directive matches the live origin's intent (blocked while on workers.dev,
434
+ open after cutover) — <code>npx vitops robots</code>.
435
+ </span>
436
+ </label>
437
+ <label class="custom-check">
438
+ <input type="checkbox" data-sop="launch.canonical" />
439
+ <span class="custom-check__box"
440
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
441
+ ></span>
442
+ <span>
443
+ Canonical domain in <code>site.json</code> matches the actual live origin.
444
+ </span>
445
+ </label>
446
+ <label class="custom-check">
447
+ <input type="checkbox" data-sop="launch.favicon" />
448
+ <span class="custom-check__box"
449
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
450
+ ></span>
451
+ <span> Favicon / PWA assets present and correct. </span>
452
+ </label>
453
+ <label class="custom-check">
454
+ <input type="checkbox" data-sop="launch.lint" />
455
+ <span class="custom-check__box"
456
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
457
+ ></span>
458
+ <span>
459
+ <code>npx vitops lint --src</code> and <code>pnpm typecheck</code> both clean.
460
+ </span>
461
+ </label>
462
+ <label class="custom-check">
463
+ <input type="checkbox" data-sop="launch.approve" />
464
+ <span class="custom-check__box"
465
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
466
+ ></span>
467
+ <span>
468
+ Prod deploy approved by an admin in the <code>production</code> GitHub Environment
469
+ gate.
470
+ </span>
471
+ </label>
472
+ <label class="custom-check">
473
+ <input type="checkbox" data-sop="launch.notify" />
474
+ <span class="custom-check__box"
475
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
476
+ ></span>
477
+ <span> <code>npx vitops search notify</code> run once live. </span>
478
+ </label>
479
+ <label class="custom-check">
480
+ <input type="checkbox" data-sop="launch.check" />
481
+ <span class="custom-check__box"
482
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
483
+ ></span>
484
+ <span>
485
+ A day or two later: <code>npx vitops search notify --check</code> confirms Google
486
+ actually indexed it.
487
+ </span>
488
+ </label>
489
+ </div>
490
+ </details>
491
+ </main>
492
+
493
+ <footer class="region rhythm">
494
+ <div class="card">
495
+ <p><strong>Working alongside an AI agent?</strong></p>
496
+ <p>
497
+ Point it at <code>.agents/skills/setup/</code> — it works this same list autonomously
498
+ and only stops at the steps marked above as requiring a person (the Cloudflare API
499
+ token, the domain cutover, and publishing any generated legal document).
500
+ </p>
501
+ </div>
502
+ </footer>
503
+ </div>
504
+
505
+ <script type="module">
506
+ // The whole persistence contract. An admin portal that wants this same
507
+ // checklist backed by shared/server storage defines
508
+ // `window.__vitopsSopStore` before this script runs; everything below is
509
+ // DOM wiring against whichever store is present. Do not add a second
510
+ // storage backend inline here — swap the store, not this file.
511
+ //
512
+ // { get(id): boolean, set(id, v): void|Promise<void>, all(): Record<string, boolean> }
513
+ function localStore(key) {
514
+ const read = () => {
515
+ try {
516
+ return JSON.parse(localStorage.getItem(key) ?? '{}');
517
+ } catch {
518
+ return {};
519
+ }
520
+ };
521
+ return {
522
+ get: (id) => Boolean(read()[id]),
523
+ set: (id, value) => {
524
+ const state = read();
525
+ state[id] = value;
526
+ localStorage.setItem(key, JSON.stringify(state));
527
+ },
528
+ all: () => read(),
529
+ };
530
+ }
531
+
532
+ const store = window.__vitopsSopStore ?? localStore('vitops.sop.v1');
533
+ const checkboxes = Array.from(document.querySelectorAll('input[data-sop]'));
534
+ const badge = document.getElementById('progress-badge');
535
+
536
+ // A <link> still loading (network/disk latency) reports zero cssRules just
537
+ // like a 404'd one — the check has to wait for the sheet to settle rather
538
+ // than sampling it once at parse time, or a real stylesheet that simply
539
+ // hasn't finished loading yet reads as "not styled".
540
+ function checkStylesheet() {
541
+ const link = document.getElementById('sop-stylesheet');
542
+ try {
543
+ if (link.sheet && link.sheet.cssRules.length > 0) return;
544
+ } catch {
545
+ // A cross-origin or 404'd sheet throws on `.cssRules` under `file://` —
546
+ // that failure IS the signal the stylesheet isn't there yet.
547
+ }
548
+ document.getElementById('no-stylesheet-notice').hidden = false;
549
+ }
550
+ const sopLink = document.getElementById('sop-stylesheet');
551
+ if (sopLink.sheet) checkStylesheet();
552
+ else {
553
+ sopLink.addEventListener('load', checkStylesheet);
554
+ sopLink.addEventListener('error', checkStylesheet);
555
+ }
556
+
557
+ function updateBadge() {
558
+ const done = checkboxes.filter((c) => c.checked).length;
559
+ badge.textContent = `${done} / ${checkboxes.length} done`;
560
+ }
561
+
562
+ const saved = store.all();
563
+ for (const box of checkboxes) {
564
+ box.checked = Boolean(saved[box.dataset.sop]);
565
+ }
566
+ updateBadge();
567
+
568
+ document.addEventListener('change', (event) => {
569
+ const box = event.target;
570
+ if (!(box instanceof HTMLInputElement) || !box.dataset.sop) return;
571
+ store.set(box.dataset.sop, box.checked);
572
+ updateBadge();
573
+ });
574
+ </script>
575
+ </body>
576
+ </html>
@@ -19,6 +19,9 @@ data/
19
19
  src/styles/
20
20
  public/vitops/
21
21
 
22
+ # generated by `pnpm run sop` — styles SETUP.html for a file:// open
23
+ .vitops/
24
+
22
25
  # environment
23
26
  .env
24
27
  .env.*
@@ -1,8 +1,17 @@
1
1
  import react from '@astrojs/react';
2
2
  import vitops from '@getvitops/astro';
3
+ import { resolveOrganizationName } from '@getvitops/astro/routes';
3
4
  import { vitopsEmdash, vitopsHosting } from '@getvitops/emdash';
4
5
  import { defineConfig } from 'astro/config';
5
6
  import emdash from 'emdash/astro';
7
+ import config from './site.json' with { type: 'json' };
8
+
9
+ // site.json's organization.name is the one build-time-readable source for a
10
+ // display name — EmDash's own getSiteSettings() only resolves inside a live
11
+ // Worker request (AsyncLocalStorage + a DB binding), so it can't be read here
12
+ // even though it's what Layout.astro shows on rendered pages. See AGENTS.md's
13
+ // "EmDash plugin as the design-system editing surface".
14
+ const organizationName = resolveOrganizationName(config) ?? 'My EmDash Site';
6
15
 
7
16
  // Hosting seam: adapter + EmDash database/storage for the configured target.
8
17
  // Default 'cloudflare' (Workers + D1 + R2 — see wrangler.jsonc / src/worker.ts).
@@ -30,19 +39,40 @@ export default defineConfig({
30
39
  // disclosure, carousel) in the editor slash menu. Default 'integration'
31
40
  // script delivery: Layout.astro renders <Head /> from @getvitops/astro,
32
41
  // which emits the web-component runtime tags.
33
- plugins: [vitopsEmdash()],
42
+ //
43
+ // designSystemEditor mounts a live <wc-theme-editor> admin page at
44
+ // /design-system — edits are visible site-wide immediately (no
45
+ // rebuild) and auto-sync back to source once site.deployment.repo and
46
+ // a VITOPS_GITHUB_TOKEN secret are set (see SETUP.html) — see
47
+ // AGENTS.md's "EmDash plugin as the design-system editing surface".
48
+ // Until then the live half still works; only the sync half is skipped.
49
+ plugins: [vitopsEmdash({ designSystemEditor: { config } })],
50
+ // 1.0's runtime migration mode defaults to 'auto' (apply pending core
51
+ // migrations on first request) — fine for local dev, but a deploy
52
+ // should have already migrated before traffic arrives. 'check' 503s a
53
+ // request instead of migrating behind it if the explicit `emdash
54
+ // migrate` step in site-deploy-emdash.yml (see AGENTS.md's CI/CD
55
+ // section) was skipped or failed. `dev: 'auto'` keeps the convenient
56
+ // first-run behaviour for `pnpm dev`.
57
+ migrations: { runtime: 'check', dev: 'auto' },
34
58
  }),
35
59
  vitops({
36
60
  favicon: {
37
61
  source: 'src/assets/logo.svg',
38
- name: 'My EmDash Site',
62
+ name: organizationName,
39
63
  themeColor: '#2e9b73',
40
64
  backgroundColor: '#ffffff',
41
65
  },
42
66
  // inject: false — Layout.astro imports the generated stylesheet, so
43
67
  // EmDash's /_emdash/admin routes don't inherit the design system while
44
68
  // your pages (and EmDash previews rendered through the layout) do.
45
- css: { format: 'tailwind', inject: false },
69
+ //
70
+ // site.json is the umbrella Config — organization + site facts plus the
71
+ // design system at designSystem.themes.default — so css.input points
72
+ // there instead of a bare design-system.json. legal/fonts/search/ads/
73
+ // reviews all read the same file (see AGENTS.md's "Client onboarding").
74
+ css: { format: 'tailwind', inject: false, input: 'site.json' },
75
+ site: { input: 'site.json' },
46
76
  }),
47
77
  ],
48
78
  });