@getvitops/create 0.9.0-alpha.3 → 0.9.0-alpha.5

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.
@@ -33,10 +33,17 @@
33
33
  },
34
34
  "ui-primary": "brand",
35
35
  "brand-primary": "brand",
36
- "info": "sky",
36
+ "info": {
37
+ "hue": "sky",
38
+ "solid": 700
39
+ },
37
40
  "success": "brand",
38
41
  "warning": "sun",
39
- "danger": "rose"
42
+ "danger": {
43
+ "hue": "rose",
44
+ "solid": 700
45
+ },
46
+ "ui-accent": "sun"
40
47
  },
41
48
  "utilities": ["bg", "text", "icon", "border"]
42
49
  },
@@ -106,6 +113,13 @@
106
113
  "br": "0.25rem",
107
114
  "p": "0.25em 0.5em",
108
115
  "fs": "0.75rem"
116
+ },
117
+ "content": {
118
+ "ds": "var(--ds-default)",
119
+ "b": "var(--b-default)",
120
+ "br": "0.5rem",
121
+ "p": "1rem",
122
+ "fs": "var(--fs-default)"
109
123
  }
110
124
  },
111
125
  "z": {
@@ -115,22 +129,77 @@
115
129
  "top": 1000
116
130
  },
117
131
  "items": {
118
- "button": {
132
+ "btn": {
119
133
  "group": "control",
120
134
  "element": "button",
121
- "default_role": "brand-primary",
135
+ "class": "btn",
136
+ "fill": false,
137
+ "overrides": {
138
+ "b": "1px solid transparent",
139
+ "fs": "inherit"
140
+ },
141
+ "base": {
142
+ "display": "inline-flex",
143
+ "align-items": "center",
144
+ "justify-content": "center",
145
+ "gap": "0.4em",
146
+ "padding": "var(--p-btn-group)",
147
+ "border-radius": "var(--br-btn-group)",
148
+ "border": "var(--b-btn-group)",
149
+ "box-shadow": "var(--ds-btn-group)",
150
+ "background": "transparent",
151
+ "color": "inherit",
152
+ "font-family": "inherit",
153
+ "font-size": "var(--fs-btn-group)",
154
+ "font-weight": "inherit",
155
+ "text-decoration": "none",
156
+ "cursor": "pointer"
157
+ },
158
+ "states": {
159
+ "hover": {
160
+ "step": 1,
161
+ "css": {
162
+ "background-color": "var(--color-bg-surface-muted)"
163
+ }
164
+ },
165
+ "focus-visible": {
166
+ "ring": true
167
+ }
168
+ },
169
+ "roles": []
170
+ },
171
+ "cta": {
172
+ "group": "control",
173
+ "class": "cta",
174
+ "fill": true,
175
+ "default_role": "ui-primary",
176
+ "overrides": {
177
+ "p": "0.75em 1.5em",
178
+ "b": "none",
179
+ "ds": "var(--shadow-sm)",
180
+ "fs": "inherit"
181
+ },
122
182
  "base": {
123
- "padding": "0.6em 1.2em",
124
- "border-radius": "var(--br-control, 0.25rem)",
125
- "border": "none",
183
+ "display": "inline-flex",
184
+ "align-items": "center",
185
+ "justify-content": "center",
186
+ "gap": "0.5em",
187
+ "padding": "var(--p-cta-group)",
188
+ "border-radius": "var(--br-cta-group)",
189
+ "border": "var(--b-cta-group)",
190
+ "font-family": "inherit",
191
+ "font-size": "var(--fs-cta-group)",
126
192
  "font-weight": "600",
127
- "color": "#fff",
193
+ "line-height": "inherit",
194
+ "text-decoration": "none",
128
195
  "cursor": "pointer",
129
- "box-shadow": "var(--shadow-sm)"
196
+ "color": "var(--color-text-on-ui-primary)",
197
+ "box-shadow": "var(--ds-cta-group)"
130
198
  },
131
199
  "states": {
132
200
  "hover": {
133
- "step": 1
201
+ "step": 1,
202
+ "lift": "1px"
134
203
  },
135
204
  "active": {
136
205
  "scale": 0.97
@@ -139,8 +208,27 @@
139
208
  "ring": true
140
209
  }
141
210
  },
142
- "roles": ["success", "danger", "warning", "info"],
143
- "class": "btn"
211
+ "roles": ["success", "danger", "warning", "info"]
212
+ },
213
+ "link": {
214
+ "group": "control",
215
+ "element": "a",
216
+ "class": "link",
217
+ "default_role": "ui-primary",
218
+ "base": {
219
+ "color": "var(--color-text-ui-primary)",
220
+ "text-decoration": "underline",
221
+ "text-underline-offset": "0.15em"
222
+ },
223
+ "states": {
224
+ "hover": {
225
+ "step": 1
226
+ },
227
+ "focus-visible": {
228
+ "ring": true
229
+ }
230
+ },
231
+ "roles": []
144
232
  },
145
233
  "card": {
146
234
  "group": "panel",
@@ -199,6 +287,23 @@
199
287
  },
200
288
  "states": {},
201
289
  "roles": ["success", "danger", "warning", "info"]
290
+ },
291
+ "details": {
292
+ "group": "content"
293
+ },
294
+ "steps": {
295
+ "group": "content",
296
+ "class": "steps",
297
+ "overrides": {
298
+ "b": "1px solid var(--color-border-surface-muted)",
299
+ "br": "0.75rem",
300
+ "p": "1.5rem",
301
+ "ds": "none"
302
+ },
303
+ "base": {
304
+ "--steps-marker-size": "2.5rem",
305
+ "--steps-line-width": "2px"
306
+ }
202
307
  }
203
308
  }
204
309
  },
@@ -309,14 +414,40 @@
309
414
  },
310
415
  "site": {
311
416
  "defaultLocale": "en",
417
+ "chrome": {
418
+ "masthead": {
419
+ "position": "sticky",
420
+ "behavior": "always",
421
+ "surface": "glass",
422
+ "layout": "full",
423
+ "elevation": { "shadow": "sm", "onScroll": true },
424
+ "height": "4rem"
425
+ },
426
+ "nav": { "compact": "drawer", "drawerSide": "end" }
427
+ },
312
428
  "templates": {
313
429
  "contact": {
314
430
  "type": "form",
315
431
  "fields": [
316
- { "name": "name", "type": "text", "required": true },
317
- { "name": "email", "type": "email", "required": true },
318
- { "name": "tel", "type": "tel" },
319
- { "name": "message", "type": "textarea", "required": true }
432
+ {
433
+ "name": "name",
434
+ "type": "text",
435
+ "required": true
436
+ },
437
+ {
438
+ "name": "email",
439
+ "type": "email",
440
+ "required": true
441
+ },
442
+ {
443
+ "name": "tel",
444
+ "type": "tel"
445
+ },
446
+ {
447
+ "name": "message",
448
+ "type": "textarea",
449
+ "required": true
450
+ }
320
451
  ]
321
452
  }
322
453
  },
@@ -5,9 +5,11 @@ import { getMenu, getSiteSettings } from 'emdash';
5
5
  import { createPublicPageContext } from 'emdash/page';
6
6
  import SiteFooter from '@getvitops/astro/components/SiteFooter.astro';
7
7
  import SiteNav from '@getvitops/astro/components/SiteNav.astro';
8
+ import Masthead from '@getvitops/astro/components/Masthead.astro';
9
+ import { resolveNavOptions } from '@getvitops/astro/components/masthead.ts';
8
10
  import { loadNavMenus } from '@getvitops/astro/components/sitenav.ts';
9
11
  import { EmDashHead } from 'emdash/ui';
10
- import { VitopsWidgetArea } from '@getvitops/emdash/astro';
12
+ import { VitopsMegaPanel, VitopsWidgetArea } from '@getvitops/emdash/astro';
11
13
  import config from '../../site.json' with { type: 'json' };
12
14
  // Design-system CSS, layout-scoped (vitops css.inject: false): only pages
13
15
  // rendered through this layout get it — keeps it out of /_emdash/admin, while
@@ -75,18 +77,19 @@ const orgName = config.organization?.name || siteTitle;
75
77
  <body>
76
78
  <!-- Widget areas (Widgets in the admin): the same content wherever they appear. -->
77
79
  <VitopsWidgetArea name="announcement" class="centered" />
78
- <header class="navbar-sticky centered">
79
- <div class="navbar">
80
- <a class="brand flex items-center" href="/" aria-label={`${siteTitle} home`}>
81
- <span class="brand-mark" set:html={logo} aria-hidden="true" />
82
- <span class="font-heading">{siteTitle}</span>
83
- </a>
84
- <div class="header-end flex items-center">
85
- <SiteNav {...nav} />
86
- <wc-color-scheme-toggle></wc-color-scheme-toggle>
87
- </div>
80
+ <!-- The masthead's look and the nav's behaviour come from `site.chrome` in site.json
81
+ (position, hide-on-scroll, glass, inset, shadow, drawer vs dropdown). A prop here
82
+ overrides one field for this layout. -->
83
+ <Masthead config={config}>
84
+ <a slot="brand" class="brand flex items-center" href="/" aria-label={`${siteTitle} home`}>
85
+ <span class="brand-mark" set:html={logo} aria-hidden="true" />
86
+ <span class="font-heading">{siteTitle}</span>
87
+ </a>
88
+ <div class="header-end flex items-center">
89
+ <SiteNav {...nav} {...resolveNavOptions(config)} megaPanel={VitopsMegaPanel} />
90
+ <wc-color-scheme-toggle></wc-color-scheme-toggle>
88
91
  </div>
89
- </header>
92
+ </Masthead>
90
93
 
91
94
  <main class="centered rhythm">
92
95
  <slot />
@@ -109,10 +112,6 @@ const orgName = config.organization?.name || siteTitle;
109
112
  block-size: 1.75rem;
110
113
  }
111
114
 
112
- .navbar {
113
- block-size: 64px;
114
- }
115
-
116
115
  .header-end {
117
116
  gap: var(--space-s, 0.75rem);
118
117
  }
@@ -8,7 +8,7 @@ import Layout from '../layouts/Layout.astro';
8
8
  // the content database — `scripts/pull-content.mjs` loads production's into the
9
9
  // local build database first (it is part of `deploy`/`deploy:dev`) — so an edit
10
10
  // in the admin goes live on the next rebuild, which a publish webhook triggers
11
- // (see .agents/skills/content-and-deploys).
11
+ // (see .agents/skills/emdash-content-and-deploys).
12
12
  //
13
13
  // There is no `Astro.rewrite('/404')` here and no on-demand fallback: a URL that
14
14
  // isn't in the list below simply has no route, so Astro serves the prerendered
@@ -2,7 +2,7 @@
2
2
  * The site's one on-demand endpoint: contact-form posts (the `vitops_form` block) and the
3
3
  * conversion beacons (`<Tracking />`, `tel:`/`mailto:` clicks) all arrive here.
4
4
  *
5
- * Every public page is prerendered (see .agents/skills/content-and-deploys); this route sets
5
+ * Every public page is prerendered (see .agents/skills/emdash-content-and-deploys); this route sets
6
6
  * `prerender = false` because it has to run on each request. It is the one exception, and the
7
7
  * Cloudflare adapter handles it in the Worker beside the EmDash admin.
8
8
  *
@@ -5,7 +5,7 @@ import Layout from '../layouts/Layout.astro';
5
5
 
6
6
  // Prerendered, like every page in this template: written to static HTML at build
7
7
  // time from the `home` entry (seed/seed.json → pages/home). Edit it in the admin;
8
- // a publish triggers a rebuild that bakes the change in (.agents/skills/content-and-deploys,
8
+ // a publish triggers a rebuild that bakes the change in (.agents/skills/emdash-content-and-deploys,
9
9
  // "Rendering and freshness"). The Vitops blocks from @getvitops/emdash render automatically —
10
10
  // EmDash merges the plugin's block components into <PortableText>.
11
11
  //
@@ -59,3 +59,10 @@ report that verbatim rather than guessing at a fix. A step reported as
59
59
  "blocked" is not a failure to retry; it is waiting on one of the hard stops
60
60
  above, or on a `site.json` field the user still needs to fill in (stage 5,
61
61
  Configuration).
62
+
63
+ ## Naming the project
64
+
65
+ `vp create` takes the package name from the target directory and invents a random slug when none
66
+ is given, so pass one: `vp create @getvitops:<template> --directory <client-slug>`. `wrangler.jsonc`'s
67
+ `name` (and on EmDash the D1 `database_name` and R2 `bucket_name`) are separate and still say what
68
+ the template shipped; rename them to match before the first deploy.
@@ -0,0 +1,6 @@
1
+ {
2
+ "permissions": {
3
+ "allow": ["Bash(pnpm exec playwright cli:*)", "Bash(npx --no-install playwright cli:*)"],
4
+ "deny": ["mcp__claude-in-chrome__*", "mcp__playwright__*"]
5
+ }
6
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "browser": {
3
+ "browserName": "chromium",
4
+ "launchOptions": { "headless": true }
5
+ },
6
+ "outputDir": ".playwright-cli"
7
+ }
@@ -3,138 +3,50 @@
3
3
  Built with the Vitops design system (`@getvitops/*`); tokens and site facts live in `site.json`.
4
4
  Prefer its classes and patterns over hand-written CSS. Load the skill that matches the task:
5
5
 
6
- - [contact-form](.agents/skills/contact-form/SKILL.md) — Use when adding or changing a form, fixing submissions that don't arrive, setting up noti…
7
- - [content-and-deploys](.agents/skills/content-and-deploys/SKILL.md) — Use for any deploy or build problem, stale or missing content, "my publish isn't live", s…
8
- - [csp](.agents/skills/csp/SKILL.md) — Use when changing `public/_headers`, adding an embed, tracking snippet or third-party scr…
9
- - [custom-blocks](.agents/skills/custom-blocks/SKILL.md) — Use before creating, renaming or deleting any block type, any `src/components/blocks/*` r…
10
- - [vitops-design-system](.agents/skills/vitops-design-system/SKILL.md) — Use when styling or authoring pages or components (choosing utility/pattern classes, colo…
6
+ - [vitops-ads](.agents/skills/vitops-ads/SKILL.md) — Use when adding or changing a Google/Meta/LinkedIn/TikTok/Microsoft/Reddit/Pinterest/Snapchat pixel
7
+ - [vitops-consent](.agents/skills/vitops-consent/SKILL.md) — Use before gating, adding or changing any non-essential third-party tag, cookie, embed
8
+ - [vitops-csp](.agents/skills/vitops-csp/SKILL.md) — Use when changing `public/_headers`, adding an embed, tracking snippet or third-party script, turning the CSP on
9
+ - [vitops-design-system](.agents/skills/vitops-design-system/SKILL.md) — Use when styling or authoring pages or components (choosing utility/pattern classes, colours, spacing, typography)
10
+ - [vitops-see-the-page](.agents/skills/vitops-see-the-page/SKILL.md) — How to look at a rendered page of this site
11
11
 
12
- Reference, rendered from this project: `vitops docs <topic>` (no topic lists them).
13
- <!-- managed by `vitops agents sync` · @getvitops/cli@8.3.0-alpha.2 — edit outside this block -->
12
+ Reference, rendered from this project: `vp exec vitops docs [topic]` (no topic lists them).
14
13
  <!-- vitops:end -->
15
14
 
16
- This is a static Astro website styled by the Vitops design system, deployed
17
- as Cloudflare Workers static assets (no server runtime, no database).
15
+ This is a static Astro website styled by the Vitops design system, deployed as Cloudflare Workers
16
+ static assets (no server runtime, no database).
18
17
 
19
18
  ## Ground rules
20
19
 
21
- - `site.json` is the single source of truth — organization + site facts, and
22
- the design system (colors, type, spacing, patterns, animations) under
23
- `designSystem.themes.default`. Never edit `src/styles/` by hand — it is
24
- generated from `site.json` by the `vitops()` integration on every dev/build
25
- and is gitignored. (There is no separate `design-system.json`: a scaffolded
26
- config can't merely reference a sibling file, so the token set lives inside
27
- `site.json`.)
28
- - `src/assets/placeholder/` holds neutral wireframe stand-ins (square,
29
- portrait, landscape; SVG + PNG) for layout work before real imagery
30
- exists. Delete the directory once real assets land.
31
- - Prefer framework CSS classes (utilities, patterns, type roles like
32
- `font-heading`) over bespoke CSS. `vp exec vitops docs classes` prints the
33
- full class vocabulary.
34
- - **Layout foundations — these are the answer, not a starting point.** Six
35
- substitutions carry the structure of nearly every page:
36
- | About to write… | Write instead |
37
- | ------------------------------------------------------- | --------------- |
38
- | a `wrap`/`wrapper`/`container`/`inner` class | **`centered`** |
39
- | `max-width` + `margin-inline: auto` | **`centered`** |
40
- | `padding-block` on a section | **`region`** |
41
- | margins between headings/paragraphs/lists | **`rhythm`** |
42
- | `display: grid` + `grid-template-columns: repeat(n, …)` | **`subgrid`** |
43
- | `repeat(auto-fit, minmax(…))` | **`grid-auto`** |
44
- | `display: flex` + `gap` for a row of controls | **`cluster`** |
45
- - Every page is prerendered by default (`output: 'static'`) — there is no
46
- server to fall back to, so nothing here can read request-time data
47
- (cookies, headers, query params) at render time. If a page genuinely needs
48
- that, it needs an adapter and a different template — this one stays pure
49
- static on purpose.
50
- - **`SETUP.html` is the client-onboarding checklist.** When working it via the
51
- `.agents/skills/setup/` skill, tick a box only for a step you actually
52
- completed — never on the operator's behalf for a step marked human-only
53
- (the Cloudflare API token, the domain cutover, publishing legal documents).
54
-
55
- ## Branch model
56
-
57
- - Work on **`dev`** (the integration branch). Every push auto-deploys the
58
- isolated `-dev` worker (`dev.<domain>` with a custom domain).
59
- - **`main`** is production and branch-protected — never push or merge to it
60
- directly. Production ships only via the manual **promote** flow:
61
- `.github/workflows/promote.yml`, dispatched manually, opens a `dev → main`
62
- PR with auto-merge; merging deploys prod. Only run it when the user
63
- explicitly asks to promote/ship to production.
64
-
65
- ## Labels: badge and tag
66
-
67
- Two label patterns, both enabled in `site.json`'s `patterns.items`: **`badge`** is a
68
- static label (a status, count or category) and **`tag`** is a label that may be
69
- static _or_ dynamic (editable, dismissable, in a `tag-list`). Don't invent a third
70
- name — `chip` and `pill` are not part of this vocabulary. The button pattern's class
71
- is `btn` (`<a class="btn">` is styled, not just a bare `<button>`).
72
-
73
- ## Content Security Policy
74
-
75
- `public/_headers` ships a **`Content-Security-Policy-Report-Only`** header for every
76
- page (they are all static assets): violations are logged to the browser console and
77
- nothing is blocked, so it can't break a page. Its `script-src` is `'self'` plus the
78
- SHA-256 of each inline script `<Head />` emits (the colour-scheme bootstrap and the
79
- consent-gate stub), with no `'unsafe-inline'`. `contentSecurityPolicy()` and
80
- `inlineScriptHashes()` from `@getvitops/astro` produce the same strings.
81
-
82
- Before enforcing it (rename the header to `Content-Security-Policy`), browse the pages
83
- with the console open: add the origins of any third-party tag the consent gate
84
- activates to `script-src` / `connect-src`, any embed to `frame-src`, and hash any
85
- inline script you add yourself. Hashes rather than nonces because a static page has no
86
- per-request response to carry one.
87
-
88
- ## Skills
89
-
90
- `@getvitops/skills` ships agent skills that update with the package. `skills experimental_sync` (run
91
- by `prepare`, so by `vp install`) links them into `.agents/skills/` and `.claude/skills/`; they are
92
- gitignored. The set is written for EmDash sites, so `csp` is the one that applies here. A skill you
93
- write yourself with the same name is never replaced. `setup` is this site's own.
20
+ - `site.json` is the single source of truth: organization + site facts, and the design system under
21
+ `designSystem.themes.default`. Never edit `src/styles/`: it is generated from `site.json` on every
22
+ dev/build and gitignored.
23
+ - Every page is prerendered (`output: 'static'`): nothing can read request-time data (cookies,
24
+ headers, query params). A page that needs that needs an adapter and a different template.
25
+ - Prefer framework classes (utilities, patterns, type roles like `font-heading`) over bespoke CSS.
26
+ `vpr lint:design` fails the pre-commit hook on unresolvable classes and layout primitives you
27
+ hand-rolled (`centered`, `region`, `rhythm`, `subgrid`, `grid-auto`, `cluster` exist for those).
28
+ - `src/assets/placeholder/` is wireframe stand-in imagery; delete it once real assets land.
29
+ - `SETUP.html` is the onboarding checklist (the `setup` skill). Tick a box only for a step you
30
+ completed, never for a human-only one (the Cloudflare API token, domain cutover, legal documents).
31
+
32
+ ## Branches
33
+
34
+ Work on **`dev`**; every push auto-deploys the isolated `-dev` worker. **`main`** is production and
35
+ protected: never push or merge to it directly; production ships only through the manual `promote`
36
+ workflow (`.github/workflows/promote.yml`), only when the user explicitly asks. Shipping is not a
37
+ package release: no changeset or version bump.
94
38
 
95
39
  ## Commands
96
40
 
97
- This is a **Vite+** project: use `vp` (and its `vpr` shorthand), not `pnpm`/`npm`/`npx`.
98
- The package manager underneath is pnpm, but `vp` picks it up for you.
99
-
100
- | To… | Run |
101
- | ---------------------------------------- | ---------------------------------------------------- |
102
- | install dependencies | `vp install` |
103
- | add / remove / update a package | `vp add <pkg>` · `vp remove <pkg>` · `vp update` |
104
- | run a `package.json` script | `vpr <script>` (alias for `vp run <script>`) |
105
- | run a locally installed binary | `vp exec <bin>` (e.g. `vp exec vitops docs classes`) |
106
- | run a one-off package without installing | `vp dlx <pkg>` |
107
- | format + lint + typecheck (files) | `vp check` |
108
- | typecheck Astro (`.astro` files) | `vpr typecheck` |
109
-
110
- **Scripts that wrap Astro must go through `vpr`.** `vp dev` / `vp build` run Vite
111
- directly and skip Astro, so use `vpr dev`, `vpr build`, `vpr preview`.
112
-
113
- **`vpr dev` returns immediately.** `astro dev` now starts the server in the
114
- background. Check it with `vp exec astro dev status`, read its output with
115
- `vp exec astro dev logs --follow`, and stop it with `vp exec astro dev stop`; the
116
- site is at <http://localhost:4321>. Don't wait on it as if it were a foreground
117
- process.
118
-
119
- | Script | What it does |
120
- | ----------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
121
- | `vpr dev` | local dev |
122
- | `vpr build:css` | regenerate the framework CSS from `site.json` |
123
- | `vpr deploy` | build + `wrangler deploy` to prod |
124
- | `vpr deploy:dev` | build + deploy the isolated dev worker |
125
- | `vpr init:github` | one-time: create + push the GitHub repo (main + dev, dev default, branch protection + environment policy). Only when the user asks. |
126
- | `vpr init:hosting` | one-time: read the Cloudflare account id and set it as a repo secret. Only when the user asks. |
127
- | `vp exec vitops validate site.json` | schema-check the site config |
128
- | `vp exec vitops lint -i site.json -f css` | find framework classes in source that resolve to nothing under this format |
41
+ This is a **Vite+** project: use `vp` (and `vpr` for scripts), not `pnpm`/`npm`/`npx`. Scripts that
42
+ wrap Astro go through `vpr` (`vpr dev`, `vpr build`), because `vp dev`/`vp build` skip Astro.
43
+ `vpr dev` returns immediately (the server runs in the background): check it with
44
+ `vp exec astro dev status`, read `vp exec astro dev logs --follow`, stop with
45
+ `vp exec astro dev stop`; the site is at <http://localhost:4321>. `package.json` lists the scripts;
46
+ `vp check` and `vpr typecheck` verify a change. `vpr init:github` and `vpr init:hosting` are one-time
47
+ setup, only when the user asks.
129
48
 
130
- **Release flow.** A site is not a published package, so there is no changeset or
131
- version bump: ship by committing to `dev` (auto-deploys the dev worker) and then
132
- running the **promote** workflow. `vp` has no `vp change` / `vp pm change` command
133
- (checked against vp 1.5.0); for a _package_ it does have `vp pm version`,
134
- `vp pm publish` and `vp pm stage`.
49
+ ## Where to look
135
50
 
136
- **Naming the project.** `vp create` takes the package name from the target
137
- directory and invents a random slug when none is given, so pass one:
138
- `vp create @getvitops:static --directory <client-slug>`. `wrangler.jsonc`'s `name`
139
- is separate and still says what the template shipped — rename it to match before
140
- the first deploy.
51
+ - **Skills** are listed in the block at the top; load the one matching the task before starting.
52
+ - **Reference** for this project's own tokens, classes and config: `vp exec vitops docs`.
@@ -27,11 +27,11 @@ public/vitops/
27
27
  .DS_Store
28
28
 
29
29
  # agent skills linked from @getvitops/skills by `skills experimental_sync` (see skills-lock.json)
30
- .agents/skills/custom-blocks
31
- .agents/skills/content-and-deploys
32
- .agents/skills/contact-form
33
- .agents/skills/csp
34
- .claude/skills/custom-blocks
35
- .claude/skills/content-and-deploys
36
- .claude/skills/contact-form
37
- .claude/skills/csp
30
+ # (the `vitops-` and `emdash-` prefixes are reserved for toolkit skills)
31
+ .agents/skills/vitops-*
32
+ .agents/skills/emdash-*
33
+ .claude/skills/vitops-*
34
+ .claude/skills/emdash-*
35
+ # playwright-cli: skill regenerated by `prepare`; output is per-session scratch
36
+ .claude/skills/playwright-cli
37
+ .playwright-cli/
@@ -16,7 +16,7 @@
16
16
  "build:css": "vitops generate -i site.json -f css -o src/styles",
17
17
  "lint:design": "vitops lint -i site.json -f css --strict",
18
18
  "sop": "vitops generate -i site.json -f css -o .vitops/sop",
19
- "prepare": "vp config && skills experimental_sync -y -a claude-code --no-frozen-lockfile && vitops agents sync -i site.json"
19
+ "prepare": "vp config && skills experimental_sync -y -a claude-code --no-frozen-lockfile && playwright cli install --skills && vitops agents sync -i site.json"
20
20
  },
21
21
  "dependencies": {
22
22
  "@getvitops/astro": "latest",
@@ -26,10 +26,11 @@
26
26
  "devDependencies": {
27
27
  "@astrojs/check": "^0.9.12",
28
28
  "@getvitops/cli": "latest",
29
+ "playwright": "^1.62.1",
29
30
  "skills": "^1.7.6",
30
31
  "typescript": "^6.0.3",
31
- "vite": "latest",
32
- "vite-plus": "latest",
32
+ "vite": "npm:@voidzero-dev/vite-plus-core@^1.5.1",
33
+ "vite-plus": "^1.5.1",
33
34
  "wrangler": "^4.111.0"
34
35
  },
35
36
  "devEngines": {
@@ -5,17 +5,12 @@ allowBuilds:
5
5
  workerd: true
6
6
  sharp: false
7
7
 
8
- # Supply-chain hardening: nothing installs within 3 days of its publish
9
- # (toolchain exempt), and no non-registry transitive sources.
8
+ # Supply-chain hardening: nothing installs within 3 days of its publish, and no non-registry
9
+ # transitive sources. Only our own lockstep `@getvitops/*` is exempt. Do NOT exempt a third-party
10
+ # toolchain: an exempt package's exact pins are not exempt (vite-plus pins `@oxc-project/*`
11
+ # exactly), so the install fails whenever one is fresher than the window. A security patch may be
12
+ # admitted with an exact `name@x.y.z` line and a dated reason; remove it once it matures.
10
13
  minimumReleaseAge: 4320
11
14
  minimumReleaseAgeExclude:
12
15
  - '@getvitops/*'
13
- - vite-plus
14
- - '@voidzero-dev/*'
15
- - oxlint
16
- - '@oxlint/*'
17
- - oxlint-tsgolint
18
- - '@oxlint-tsgolint/*'
19
- - oxfmt
20
- - '@oxfmt/*'
21
16
  blockExoticSubdeps: true