@getvitops/create 0.5.0 → 0.7.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 (56) hide show
  1. package/CHANGELOG.md +44 -5
  2. package/README.md +7 -0
  3. package/package.json +18 -2
  4. package/templates/emdash/.agents/skills/setup/SKILL.md +61 -0
  5. package/templates/emdash/.env +1 -0
  6. package/templates/emdash/.github/workflows/ci.yml +12 -0
  7. package/templates/emdash/.github/workflows/deploy-dev.yml +17 -19
  8. package/templates/emdash/.github/workflows/deploy-prod.yml +17 -21
  9. package/templates/emdash/.github/workflows/promote.yml +8 -36
  10. package/templates/emdash/.mcp.json +4 -0
  11. package/templates/emdash/.nvmrc +1 -0
  12. package/templates/emdash/AGENTS.md +23 -11
  13. package/templates/emdash/README.md +44 -42
  14. package/templates/emdash/SETUP.html +526 -0
  15. package/templates/emdash/_gitignore +3 -0
  16. package/templates/emdash/astro.config.mjs +7 -1
  17. package/templates/emdash/package.json +6 -4
  18. package/templates/emdash/seed/seed.json +1 -1
  19. package/templates/emdash/site.json +228 -0
  20. package/templates/emdash/src/assets/placeholder/wireframe-400x400.png +0 -0
  21. package/templates/emdash/src/assets/placeholder/wireframe-400x400.svg +1 -0
  22. package/templates/emdash/src/assets/placeholder/wireframe-400x600.png +0 -0
  23. package/templates/emdash/src/assets/placeholder/wireframe-400x600.svg +1 -0
  24. package/templates/emdash/src/assets/placeholder/wireframe-600x400.png +0 -0
  25. package/templates/emdash/src/assets/placeholder/wireframe-600x400.svg +1 -0
  26. package/templates/emdash/vite.config.ts +1 -1
  27. package/templates/emdash/wrangler.jsonc +8 -3
  28. package/templates/static/.agents/skills/setup/SKILL.md +61 -0
  29. package/templates/static/.env +1 -0
  30. package/templates/static/.github/workflows/ci.yml +12 -0
  31. package/templates/static/.github/workflows/deploy-dev.yml +33 -0
  32. package/templates/static/.github/workflows/deploy-prod.yml +29 -0
  33. package/templates/static/.github/workflows/promote.yml +16 -0
  34. package/templates/static/.nvmrc +1 -0
  35. package/templates/static/AGENTS.md +64 -0
  36. package/templates/static/README.md +80 -0
  37. package/templates/static/SETUP.html +526 -0
  38. package/templates/static/_gitignore +27 -0
  39. package/templates/static/astro.config.mjs +23 -0
  40. package/templates/{emdash → static}/design-system.json +1 -1
  41. package/templates/static/package.json +41 -0
  42. package/templates/static/scripts/dev-wrangler-config.mjs +28 -0
  43. package/templates/static/site.json +228 -0
  44. package/templates/static/src/assets/placeholder/wireframe-400x400.png +0 -0
  45. package/templates/static/src/assets/placeholder/wireframe-400x400.svg +1 -0
  46. package/templates/static/src/assets/placeholder/wireframe-400x600.png +0 -0
  47. package/templates/static/src/assets/placeholder/wireframe-400x600.svg +1 -0
  48. package/templates/static/src/assets/placeholder/wireframe-600x400.png +0 -0
  49. package/templates/static/src/assets/placeholder/wireframe-600x400.svg +1 -0
  50. package/templates/static/src/layouts/Layout.astro +26 -0
  51. package/templates/static/src/pages/404.astro +11 -0
  52. package/templates/static/src/pages/index.astro +13 -0
  53. package/templates/static/tsconfig.json +5 -0
  54. package/templates/static/wrangler.jsonc +22 -0
  55. package/templates/emdash/scripts/init-github.mjs +0 -102
  56. package/templates/emdash/scripts/init-hosting.mjs +0 -150
@@ -0,0 +1,526 @@
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
+ </div>
163
+ </details>
164
+
165
+ <details class="details" data-sop-stage="domain">
166
+ <summary>4. Domain strategy</summary>
167
+ <div class="rhythm">
168
+ <p>Two mutually exclusive tracks. Pick one — do not do both in the same deploy.</p>
169
+
170
+ <h3>4a. Go live on <code>*.workers.dev</code> (default, safe)</h3>
171
+ <p>
172
+ Leaves any existing live site untouched at its current domain. Do this first, always —
173
+ cutover is a separate, later step.
174
+ </p>
175
+ <label class="custom-check">
176
+ <input type="checkbox" data-sop="domain.workersdev.leave" />
177
+ <span class="custom-check__box"
178
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
179
+ ></span>
180
+ <span>
181
+ Leave <code>workers_dev: true</code> and the <code>routes</code> block commented out
182
+ in <code>wrangler.jsonc</code> — this is the shipped default.
183
+ </span>
184
+ </label>
185
+ <label class="custom-check">
186
+ <input type="checkbox" data-sop="domain.workersdev.url" />
187
+ <span class="custom-check__box"
188
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
189
+ ></span>
190
+ <span>
191
+ Deploy once (<code>pnpm deploy</code>), note the printed
192
+ <code>*.workers.dev</code> URL, and set
193
+ <code>site.environments.production.url</code> in <code>site.json</code> to it.
194
+ </span>
195
+ </label>
196
+ <label class="custom-check">
197
+ <input type="checkbox" data-sop="domain.workersdev.noindex" />
198
+ <span class="custom-check__box"
199
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
200
+ ></span>
201
+ <span>
202
+ Set <code>site.environments.production.robots</code> to
203
+ <code>"noindex,nofollow"</code> — a workers.dev URL is indexable, and this is the
204
+ launch gate that stops search engines picking it up as a duplicate of the real site.
205
+ </span>
206
+ </label>
207
+
208
+ <h3>4b. Cut over to the custom domain</h3>
209
+ <p>
210
+ <strong>This step overwrites the zone's DNS record the moment you deploy</strong>
211
+ — it is the one irreversible-feeling moment in this whole checklist, and it takes an
212
+ existing live site down at that instant. Do it deliberately, not as part of a bigger
213
+ batch of changes. It is safe to complete stage 6 (Search Console / GCP / ads) before
214
+ this — <code>vitops search setup</code> only ever <em>creates</em> a verification TXT
215
+ record, it never touches the apex.
216
+ </p>
217
+ <label class="custom-check">
218
+ <input type="checkbox" data-sop="domain.cutover.ttl" />
219
+ <span class="custom-check__box"
220
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
221
+ ></span>
222
+ <span> If replacing a live site, lower that site's DNS TTL well ahead of time. </span>
223
+ </label>
224
+ <label class="custom-check">
225
+ <input type="checkbox" data-sop="domain.cutover.plan" />
226
+ <span class="custom-check__box"
227
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
228
+ ></span>
229
+ <span>
230
+ Preview it: <code>npx vitops setup domain --domain &lt;apex&gt; --dry</code> — this
231
+ only plans and checks; it never applies the cutover itself.
232
+ </span>
233
+ </label>
234
+ <label class="custom-check">
235
+ <input type="checkbox" data-sop="domain.cutover.uncomment" />
236
+ <span class="custom-check__box"
237
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
238
+ ></span>
239
+ <span>
240
+ Uncomment the <code>routes</code> block in <code>wrangler.jsonc</code>, set your
241
+ apex, and deploy: <code>pnpm deploy</code>.
242
+ </span>
243
+ </label>
244
+ <label class="custom-check">
245
+ <input type="checkbox" data-sop="domain.cutover.config" />
246
+ <span class="custom-check__box"
247
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
248
+ ></span>
249
+ <span>
250
+ Set <code>site.domains.canonical</code> and
251
+ <code>site.environments.production.url</code> to the custom domain, and flip
252
+ <code>environments.production.robots</code> back to <code>"index,follow"</code> (or
253
+ remove it).
254
+ </span>
255
+ </label>
256
+ <label class="custom-check">
257
+ <input type="checkbox" data-sop="domain.cutover.notify" />
258
+ <span class="custom-check__box"
259
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
260
+ ></span>
261
+ <span>
262
+ Re-run <code>npx vitops robots</code> and <code>npx vitops search notify</code>.
263
+ </span>
264
+ </label>
265
+ </div>
266
+ </details>
267
+
268
+ <details class="details" data-sop-stage="config">
269
+ <summary>5. Configuration</summary>
270
+ <div class="rhythm">
271
+ <label class="custom-check">
272
+ <input type="checkbox" data-sop="config.fill" />
273
+ <span class="custom-check__box"
274
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
275
+ ></span>
276
+ <span>
277
+ Fill in <code>site.json</code>: <code>organization</code> (name, address, contact),
278
+ <code>site.locales</code>, <code>site.environments</code>, <code>site.seo</code>,
279
+ <code>site.analytics</code>, <code>site.legal.*.enabled</code>,
280
+ <code>site.favicon</code>, <code>site.icons</code>.
281
+ </span>
282
+ </label>
283
+ <label class="custom-check">
284
+ <input type="checkbox" data-sop="config.validate" />
285
+ <span class="custom-check__box"
286
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
287
+ ></span>
288
+ <span> Check it: <code>npx vitops validate site.json</code> </span>
289
+ </label>
290
+ </div>
291
+ </details>
292
+
293
+ <details class="details" data-sop-stage="connections">
294
+ <summary>6. Search Console / GCP / ads</summary>
295
+ <div class="rhythm">
296
+ <label class="custom-check">
297
+ <input type="checkbox" data-sop="connections.gcp" />
298
+ <span class="custom-check__box"
299
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
300
+ ></span>
301
+ <span>
302
+ Set <code>site.google.project</code>, and the environment variables:
303
+ <code>VITOPS_GOOGLE_CLIENT_ID</code>, <code>VITOPS_GOOGLE_CLIENT_SECRET</code>,
304
+ <code>VITOPS_GOOGLE_REFRESH_TOKEN</code>, <code>CLOUDFLARE_API_TOKEN</code>.
305
+ </span>
306
+ </label>
307
+ <label class="custom-check">
308
+ <input type="checkbox" data-sop="connections.search" />
309
+ <span class="custom-check__box"
310
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
311
+ ></span>
312
+ <span>
313
+ Preview then run: <code>npx vitops setup search --dry</code>, then
314
+ <code>npx vitops setup search</code>. This is a <strong>user OAuth</strong>
315
+ action, deliberately — verifying a site makes the caller an owner, and that should
316
+ be a person, not a deploy robot.
317
+ </span>
318
+ </label>
319
+ <label class="custom-check">
320
+ <input type="checkbox" data-sop="connections.ads" />
321
+ <span class="custom-check__box"
322
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
323
+ ></span>
324
+ <span>
325
+ Preview then run: <code>npx vitops setup ads --dry</code>, then
326
+ <code>npx vitops setup ads</code>.
327
+ </span>
328
+ </label>
329
+ <label class="custom-check">
330
+ <input type="checkbox" data-sop="connections.manual" />
331
+ <span class="custom-check__box"
332
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
333
+ ></span>
334
+ <span>
335
+ Two things are never automated, by design: adding a Search Console
336
+ <strong>Full User</strong> (via a Google Group), and clicking each platform's own
337
+ "Verify" button. Both are named in the command output when they apply.
338
+ </span>
339
+ </label>
340
+ </div>
341
+ </details>
342
+
343
+ <details class="details" data-sop-stage="legal">
344
+ <summary>7. Legal</summary>
345
+ <div class="rhythm">
346
+ <label class="custom-check">
347
+ <input type="checkbox" data-sop="legal.generate" />
348
+ <span class="custom-check__box"
349
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
350
+ ></span>
351
+ <span>
352
+ Generate real documents from the filled-in config —
353
+ <code>npx vitops legal --format portable-text</code> (EmDash; paste into the admin)
354
+ or <code>npx vitops legal --format md</code> (static; commit the output). The seeded
355
+ <code>/terms</code> and <code>/privacy</code> pages are deliberately short,
356
+ obviously-unfinished placeholders.
357
+ </span>
358
+ </label>
359
+ <label class="custom-check">
360
+ <input type="checkbox" data-sop="legal.review" />
361
+ <span class="custom-check__box"
362
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
363
+ ></span>
364
+ <span>
365
+ <strong>Have it reviewed before publishing.</strong> Every generated document opens
366
+ with a non-optional review banner — it is a starting point from your config, not
367
+ legal advice, and a plausible-looking policy shipped unread is the one failure mode
368
+ here with real consequences.
369
+ </span>
370
+ </label>
371
+ </div>
372
+ </details>
373
+
374
+ <details class="details" data-sop-stage="launch">
375
+ <summary>8. Launch gates</summary>
376
+ <div class="rhythm">
377
+ <label class="custom-check">
378
+ <input type="checkbox" data-sop="launch.robots" />
379
+ <span class="custom-check__box"
380
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
381
+ ></span>
382
+ <span>
383
+ Robots directive matches the live origin's intent (blocked while on workers.dev,
384
+ open after cutover) — <code>npx vitops robots</code>.
385
+ </span>
386
+ </label>
387
+ <label class="custom-check">
388
+ <input type="checkbox" data-sop="launch.canonical" />
389
+ <span class="custom-check__box"
390
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
391
+ ></span>
392
+ <span>
393
+ Canonical domain in <code>site.json</code> matches the actual live origin.
394
+ </span>
395
+ </label>
396
+ <label class="custom-check">
397
+ <input type="checkbox" data-sop="launch.favicon" />
398
+ <span class="custom-check__box"
399
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
400
+ ></span>
401
+ <span> Favicon / PWA assets present and correct. </span>
402
+ </label>
403
+ <label class="custom-check">
404
+ <input type="checkbox" data-sop="launch.lint" />
405
+ <span class="custom-check__box"
406
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
407
+ ></span>
408
+ <span>
409
+ <code>npx vitops lint --src</code> and <code>pnpm typecheck</code> both clean.
410
+ </span>
411
+ </label>
412
+ <label class="custom-check">
413
+ <input type="checkbox" data-sop="launch.approve" />
414
+ <span class="custom-check__box"
415
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
416
+ ></span>
417
+ <span>
418
+ Prod deploy approved by an admin in the <code>production</code> GitHub Environment
419
+ gate.
420
+ </span>
421
+ </label>
422
+ <label class="custom-check">
423
+ <input type="checkbox" data-sop="launch.notify" />
424
+ <span class="custom-check__box"
425
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
426
+ ></span>
427
+ <span> <code>npx vitops search notify</code> run once live. </span>
428
+ </label>
429
+ <label class="custom-check">
430
+ <input type="checkbox" data-sop="launch.check" />
431
+ <span class="custom-check__box"
432
+ ><span class="custom-check__indicator custom-check__indicator--check"></span
433
+ ></span>
434
+ <span>
435
+ A day or two later: <code>npx vitops search notify --check</code> confirms Google
436
+ actually indexed it.
437
+ </span>
438
+ </label>
439
+ </div>
440
+ </details>
441
+ </main>
442
+
443
+ <footer class="region rhythm">
444
+ <div class="card">
445
+ <p><strong>Working alongside an AI agent?</strong></p>
446
+ <p>
447
+ Point it at <code>.agents/skills/setup/</code> — it works this same list autonomously
448
+ and only stops at the steps marked above as requiring a person (the Cloudflare API
449
+ token, the domain cutover, and publishing any generated legal document).
450
+ </p>
451
+ </div>
452
+ </footer>
453
+ </div>
454
+
455
+ <script type="module">
456
+ // The whole persistence contract. An admin portal that wants this same
457
+ // checklist backed by shared/server storage defines
458
+ // `window.__vitopsSopStore` before this script runs; everything below is
459
+ // DOM wiring against whichever store is present. Do not add a second
460
+ // storage backend inline here — swap the store, not this file.
461
+ //
462
+ // { get(id): boolean, set(id, v): void|Promise<void>, all(): Record<string, boolean> }
463
+ function localStore(key) {
464
+ const read = () => {
465
+ try {
466
+ return JSON.parse(localStorage.getItem(key) ?? '{}');
467
+ } catch {
468
+ return {};
469
+ }
470
+ };
471
+ return {
472
+ get: (id) => Boolean(read()[id]),
473
+ set: (id, value) => {
474
+ const state = read();
475
+ state[id] = value;
476
+ localStorage.setItem(key, JSON.stringify(state));
477
+ },
478
+ all: () => read(),
479
+ };
480
+ }
481
+
482
+ const store = window.__vitopsSopStore ?? localStore('vitops.sop.v1');
483
+ const checkboxes = Array.from(document.querySelectorAll('input[data-sop]'));
484
+ const badge = document.getElementById('progress-badge');
485
+
486
+ // A <link> still loading (network/disk latency) reports zero cssRules just
487
+ // like a 404'd one — the check has to wait for the sheet to settle rather
488
+ // than sampling it once at parse time, or a real stylesheet that simply
489
+ // hasn't finished loading yet reads as "not styled".
490
+ function checkStylesheet() {
491
+ const link = document.getElementById('sop-stylesheet');
492
+ try {
493
+ if (link.sheet && link.sheet.cssRules.length > 0) return;
494
+ } catch {
495
+ // A cross-origin or 404'd sheet throws on `.cssRules` under `file://` —
496
+ // that failure IS the signal the stylesheet isn't there yet.
497
+ }
498
+ document.getElementById('no-stylesheet-notice').hidden = false;
499
+ }
500
+ const sopLink = document.getElementById('sop-stylesheet');
501
+ if (sopLink.sheet) checkStylesheet();
502
+ else {
503
+ sopLink.addEventListener('load', checkStylesheet);
504
+ sopLink.addEventListener('error', checkStylesheet);
505
+ }
506
+
507
+ function updateBadge() {
508
+ const done = checkboxes.filter((c) => c.checked).length;
509
+ badge.textContent = `${done} / ${checkboxes.length} done`;
510
+ }
511
+
512
+ const saved = store.all();
513
+ for (const box of checkboxes) {
514
+ box.checked = Boolean(saved[box.dataset.sop]);
515
+ }
516
+ updateBadge();
517
+
518
+ document.addEventListener('change', (event) => {
519
+ const box = event.target;
520
+ if (!(box instanceof HTMLInputElement) || !box.dataset.sop) return;
521
+ store.set(box.dataset.sop, box.checked);
522
+ updateBadge();
523
+ });
524
+ </script>
525
+ </body>
526
+ </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.*
@@ -42,7 +42,13 @@ export default defineConfig({
42
42
  // inject: false — Layout.astro imports the generated stylesheet, so
43
43
  // EmDash's /_emdash/admin routes don't inherit the design system while
44
44
  // your pages (and EmDash previews rendered through the layout) do.
45
- css: { format: 'tailwind', inject: false },
45
+ //
46
+ // site.json is the umbrella Config — organization + site facts plus the
47
+ // design system at designSystem.themes.default — so css.input points
48
+ // there instead of a bare design-system.json. legal/fonts/search/ads/
49
+ // reviews all read the same file (see AGENTS.md's "Client onboarding").
50
+ css: { format: 'tailwind', inject: false, input: 'site.json' },
51
+ site: { input: 'site.json' },
46
52
  }),
47
53
  ],
48
54
  });
@@ -9,11 +9,13 @@
9
9
  "preview": "astro preview",
10
10
  "deploy": "astro build && wrangler deploy",
11
11
  "deploy:dev": "astro build && node scripts/build-dev-wrangler.mjs && wrangler deploy --config dist/server/wrangler.dev.json",
12
- "init:github": "node scripts/init-github.mjs",
13
- "init:hosting": "node scripts/init-hosting.mjs",
12
+ "init:github": "vitops setup repo",
13
+ "init:hosting": "vitops setup hosting",
14
+ "setup": "vitops setup all",
14
15
  "typecheck": "astro check",
15
- "build:css": "vitops generate -i design-system.json -f tailwind -o src/styles",
16
- "lint:design": "vitops lint -i design-system.json -f tailwind --strict",
16
+ "build:css": "vitops generate -i site.json -f tailwind -o src/styles",
17
+ "lint:design": "vitops lint -i site.json -f tailwind --strict",
18
+ "sop": "vitops generate -i site.json -f css -o .vitops/sop",
17
19
  "prepare": "vp config"
18
20
  },
19
21
  "dependencies": {
@@ -97,7 +97,7 @@
97
97
  "_type": "vitops.details",
98
98
  "_key": "next-steps",
99
99
  "summary": "Next steps",
100
- "body": "Adjust design-system.json to your brand (colors, type, patterns), replace src/assets/logo.svg, then deploy with `pnpm deploy`.",
100
+ "body": "Adjust site.json (under designSystem.themes.default) to your brand (colors, type, patterns), replace src/assets/logo.svg, then deploy with `pnpm deploy`.",
101
101
  "open": false
102
102
  }
103
103
  ]