@getvitops/cli 2.0.0 → 3.0.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/CHANGELOG.md +206 -0
- package/README.md +47 -0
- package/dist/cli.mjs +374 -15
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
- package/skill/SKILL.md +17 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,211 @@
|
|
|
1
1
|
# @getvitops/cli
|
|
2
2
|
|
|
3
|
+
## 3.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- 6e68ace: **Breaking:** the site config is now a three-section `Config` — `designSystem`,
|
|
8
|
+
`organization`, `site`.
|
|
9
|
+
|
|
10
|
+
The flat `SiteConfig` held the company (`organization`, `contact`, `locations`) and the
|
|
11
|
+
deployment (`analytics`, `environments`, `seo`, `legal`) as peers, so no single noun
|
|
12
|
+
described it, and a second site sharing the same company had no way to say so. The three
|
|
13
|
+
sections split those apart: several sites can now carry one `organization` and differ only
|
|
14
|
+
in `site`.
|
|
15
|
+
|
|
16
|
+
**Migrating.** `designSystem` stays at the root, and the fields already under
|
|
17
|
+
`organization` stay where they are. Everything else moves into one of two sections:
|
|
18
|
+
- → `site`: `defaultLocale`, `locales`, `domains`, `dns`, `cloudflare`, `environments`,
|
|
19
|
+
`abTesting`, `fonts`, `tags`, `postTypes`, `galleries`, `testimonials`, `templates`,
|
|
20
|
+
`navigation`, `seo`, `analytics`, `notifications`, `tracking`, `security`, `legal`,
|
|
21
|
+
`icons`, `favicon`, `deployment`
|
|
22
|
+
- → `organization`: `contact`, `primaryLocation`, `locations`, `services`, `links`
|
|
23
|
+
|
|
24
|
+
You do not have to work this out from the list: `vitops validate` detects a pre-3.0 flat
|
|
25
|
+
config and prints every move by name, rather than reporting a dozen unknown keys.
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{
|
|
29
|
+
"designSystem": {
|
|
30
|
+
"themes": {
|
|
31
|
+
"default": {
|
|
32
|
+
/* … */
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
"organization": {
|
|
37
|
+
"name": "Acme",
|
|
38
|
+
"contact": "hq",
|
|
39
|
+
"locations": {
|
|
40
|
+
"hq": {
|
|
41
|
+
/* … */
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
"site": {
|
|
46
|
+
"defaultLocale": "en",
|
|
47
|
+
"domains": {
|
|
48
|
+
/* … */
|
|
49
|
+
},
|
|
50
|
+
"analytics": {
|
|
51
|
+
/* … */
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
**Renamed exports** (`@getvitops/generator`): `SiteConfigSchema` → `ConfigSchema`,
|
|
58
|
+
`SiteConfig` → `Config`, `validateSite` → `validateConfig`, `resolveSiteConfig` →
|
|
59
|
+
`resolveConfig`, `isSiteConfig` → `isConfig`, `siteJsonSchema` → `configJsonSchema`,
|
|
60
|
+
`SITE_SCHEMA_URL` → `CONFIG_SCHEMA_URL`, `SiteValidationResult` →
|
|
61
|
+
`ConfigValidationResult`. `ResolvedInput.site` is now `ResolvedInput.config`. New:
|
|
62
|
+
`OrganizationConfig` and `SiteSection` for the two sections.
|
|
63
|
+
|
|
64
|
+
**Renamed schema file:** `@getvitops/generator/site.schema.json` →
|
|
65
|
+
`@getvitops/generator/config.schema.json`. Update the `$schema` key in your config.
|
|
66
|
+
|
|
67
|
+
Option names are unchanged — `vitops({ site: { input } })`, the Vite plugin's `site`,
|
|
68
|
+
and `generate({ site })` still point at the config file.
|
|
69
|
+
|
|
70
|
+
**The Astro integration is now `vitops()`, not `getvitops()`.** It is a default export,
|
|
71
|
+
so the name is yours and nothing breaks on upgrade, but every example now reads
|
|
72
|
+
`import vitops from '@getvitops/astro'` — matching the Vite plugin, which has always been
|
|
73
|
+
`vitops`. The `@getvitops/*` package scope and the internal `virtual:getvitops/*` module
|
|
74
|
+
ids are unchanged; neither is an import name.
|
|
75
|
+
|
|
76
|
+
**Added:** a generated config authoring reference — `vitops docs config`, `docs/config.md`
|
|
77
|
+
in the OKF bundle, and _Config reference_ on the docs site. It is walked from the published
|
|
78
|
+
JSON Schema by the same helper that renders the `design-system.json` reference, so it
|
|
79
|
+
cannot describe a field validation does not accept.
|
|
80
|
+
|
|
81
|
+
### Patch Changes
|
|
82
|
+
|
|
83
|
+
- Updated dependencies [6e68ace]
|
|
84
|
+
- Updated dependencies [b115993]
|
|
85
|
+
- @getvitops/generator@3.0.0
|
|
86
|
+
- @getvitops/utils@3.0.0
|
|
87
|
+
|
|
88
|
+
## 2.1.0
|
|
89
|
+
|
|
90
|
+
### Minor Changes
|
|
91
|
+
|
|
92
|
+
- 20e518e: Add `vitops indexing` — tell search engines about a deploy, instead of opening Search Console by hand.
|
|
93
|
+
|
|
94
|
+
Configure it in your site config under `seo.indexing`, then run it after deploying:
|
|
95
|
+
|
|
96
|
+
```jsonc
|
|
97
|
+
"seo": {
|
|
98
|
+
"indexing": {
|
|
99
|
+
"indexNow": { "key": "…" },
|
|
100
|
+
"searchConsole": { "siteUrl": "sc-domain:acme.ca" },
|
|
101
|
+
"priorityUrls": ["https://acme.ca/", "https://acme.ca/services"]
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
vitops indexing --dry # print the plan, make no requests
|
|
108
|
+
vitops indexing # submit
|
|
109
|
+
vitops indexing --check # a day or two later: did Google actually index them?
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
It reads your sitemap, diffs it against the previous run, and submits only what changed —
|
|
113
|
+
pinging **IndexNow** and re-submitting your sitemap through the **Search Console API**.
|
|
114
|
+
`--check` inspects `priorityUrls` and exits non-zero on a page Google hasn't indexed, so a
|
|
115
|
+
scheduled CI job can catch a page that quietly fell out of the index.
|
|
116
|
+
|
|
117
|
+
**Be clear-eyed about what this can do.** Google exposes no API that requests indexing — the
|
|
118
|
+
button in Search Console isn't available anywhere, URL Inspection is read-only, and the sitemap
|
|
119
|
+
ping endpoint was removed in 2023. IndexNow reaches Bing, Yandex, Naver, Seznam and Yep; Google
|
|
120
|
+
doesn't participate. So this automates every sanctioned step and then verifies the outcome; it
|
|
121
|
+
does not make Google re-index on demand, and nothing can. Google's Indexing API is deliberately
|
|
122
|
+
not wired: it's scoped to job postings and livestreams, and using it for ordinary pages violates
|
|
123
|
+
its terms.
|
|
124
|
+
|
|
125
|
+
Also new, and worth wiring at the same time — **`gitLastmod()` for real sitemap dates**:
|
|
126
|
+
|
|
127
|
+
```js
|
|
128
|
+
import vitops, { gitLastmod } from '@getvitops/astro';
|
|
129
|
+
vitops({ sitemap: { serialize: await gitLastmod() } });
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`@astrojs/sitemap` emits no `<lastmod>` by default, which means a crawler is told your pages
|
|
133
|
+
exist but never that one changed — and it's what lets `vitops indexing` submit a handful of URLs instead
|
|
134
|
+
of all of them. `gitLastmod()` derives each date from the source file's last commit, and leaves a
|
|
135
|
+
page alone rather than guessing (dynamic routes, ambiguous slugs, shallow clones): an inaccurate
|
|
136
|
+
`lastmod` is worse than none, because Google stops trusting the field site-wide.
|
|
137
|
+
|
|
138
|
+
Requires `fetch-depth: 0` in CI — the default shallow clone has no history to read, and it warns
|
|
139
|
+
when that's the case.
|
|
140
|
+
|
|
141
|
+
**Credentials.** IndexNow's key is public by design (it's served at `/<key>.txt` as the ownership
|
|
142
|
+
proof), so it lives in your config, and the Astro integration writes the file into `public/` for
|
|
143
|
+
you — `vitops indexing --new-key` generates one, `--write-key <dir>` writes it for non-Astro stacks.
|
|
144
|
+
Search Console needs a service account in `VITOPS_GSC_SERVICE_ACCOUNT` or
|
|
145
|
+
`GOOGLE_APPLICATION_CREDENTIALS`, added as an owner of the property; it's never read from the
|
|
146
|
+
config file.
|
|
147
|
+
|
|
148
|
+
Persist `.vitops/` between runs (a CI cache) — that's the changed-URL state, and without it every
|
|
149
|
+
run submits everything.
|
|
150
|
+
|
|
151
|
+
An environment whose `robots` policy says `noindex` is refused outright, so pointing this at
|
|
152
|
+
staging can't publish it to a search engine.
|
|
153
|
+
|
|
154
|
+
- 9bf975a: Add `vitops media` — encode raw video into web-ready outputs, instead of hand-rolling an ffmpeg
|
|
155
|
+
script per project.
|
|
156
|
+
|
|
157
|
+
Keep unprocessed video in a `raw/` directory and run:
|
|
158
|
+
|
|
159
|
+
```
|
|
160
|
+
vitops media --raw raw --out src/assets/processed
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Each source becomes three files: **VP9/WebM**, an **H.264/MP4** fallback, and a **JPG poster**.
|
|
164
|
+
Import them like any other asset, so your bundler content-hashes them:
|
|
165
|
+
|
|
166
|
+
```astro
|
|
167
|
+
---
|
|
168
|
+
import hero from '../assets/processed/hero.webm';
|
|
169
|
+
import poster from '../assets/processed/hero.jpg';
|
|
170
|
+
---
|
|
171
|
+
<video poster={poster.src} autoplay muted loop playsinline>
|
|
172
|
+
<source src={hero} type="video/webm" />
|
|
173
|
+
</video>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
In Astro it's an integration option — `vitops({ css, media: { raw: 'raw', out: 'src/assets/processed' } })`
|
|
177
|
+
— and it runs in the same pass that generates your CSS. `@getvitops/vite` gains a matching `media`
|
|
178
|
+
option, and `@getvitops/utils/media` exports `processMedia()` for anything else.
|
|
179
|
+
|
|
180
|
+
Defaults: capped at 1920px wide, CRF 32, audio dropped (the common case is a muted autoplay loop),
|
|
181
|
+
poster from frame 0. All of them are flags. `--dry` prints exactly what a run would do.
|
|
182
|
+
|
|
183
|
+
**Runs are cached**, on source content plus encode settings, in `.vitops/media-manifest.json` — a
|
|
184
|
+
24 MB clip that took 88 seconds the first time takes 0.14 seconds the second. A missing output
|
|
185
|
+
re-encodes; a corrupt manifest re-encodes everything. Neither ever reads as "already done", because
|
|
186
|
+
that failure is silent and no rebuild would fix it.
|
|
187
|
+
|
|
188
|
+
**Commit the outputs and the manifest.** A fresh CI clone has neither and would re-encode from
|
|
189
|
+
scratch; committing both means CI never needs ffmpeg. It also keeps your history clean — ffmpeg
|
|
190
|
+
output isn't byte-reproducible across versions, so a CI re-encode would rewrite every video on any
|
|
191
|
+
toolchain bump. Use `--force` when you mean to re-encode.
|
|
192
|
+
|
|
193
|
+
**`ffmpeg` is an external tool, not an npm dependency** — install it yourself (`brew install
|
|
194
|
+
ffmpeg`, `apt install ffmpeg`, `winget install Gyan.FFmpeg`). The command fails without it rather
|
|
195
|
+
than skipping: a page referencing a video that was never encoded is broken, not degraded.
|
|
196
|
+
|
|
197
|
+
Two defaults worth knowing about. The MP4 exists because older iOS and the in-app webviews inside
|
|
198
|
+
social apps still don't decode VP9, and it's written with `+faststart` — without which a browser
|
|
199
|
+
downloads the whole file before showing frame one. And a poster taken from frame 0 is often black
|
|
200
|
+
on a clip that fades in; that's `--poster-time`, not a bug.
|
|
201
|
+
|
|
202
|
+
### Patch Changes
|
|
203
|
+
|
|
204
|
+
- Updated dependencies [20e518e]
|
|
205
|
+
- Updated dependencies [9bf975a]
|
|
206
|
+
- @getvitops/generator@2.1.0
|
|
207
|
+
- @getvitops/utils@2.1.0
|
|
208
|
+
|
|
3
209
|
## 2.0.0
|
|
4
210
|
|
|
5
211
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ npx vitops generate --format bricks,css --out dist
|
|
|
25
25
|
| `vitops favicon -i <svg\|png> -o <dir>` | generate a favicon set from a source image |
|
|
26
26
|
| `vitops agents [-o AGENTS.md] [--docs-dir <dir>]` | link the agent skill + AGENTS.md pointer |
|
|
27
27
|
| `vitops docs [topic] [--all]` | print live reference docs to stdout |
|
|
28
|
+
| `vitops indexing [--dry] [--check]` | tell search engines about a deploy |
|
|
29
|
+
| `vitops media [--raw <dir>] [-o <dir>]` | encode raw video to WebM + MP4 + poster |
|
|
28
30
|
|
|
29
31
|
Every `--input` takes a `design-system.json` **or** the larger site config that embeds one
|
|
30
32
|
(`company.json` / `site.json`) — told apart by shape, so name the file whatever you like. Add
|
|
@@ -98,6 +100,51 @@ vitops generate --input design-system.json --format design --out .
|
|
|
98
100
|
whenever your stylesheet goes somewhere other than the repo root. Regenerate it with your CSS; it's
|
|
99
101
|
derived from the same config, so it can't drift from what the browser gets.
|
|
100
102
|
|
|
103
|
+
## Video (`vitops media`)
|
|
104
|
+
|
|
105
|
+
Keep unprocessed video in a `raw/` directory and encode it into web-ready outputs:
|
|
106
|
+
|
|
107
|
+
```sh
|
|
108
|
+
vitops media --raw raw --out src/assets/processed
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Each source becomes three files — VP9/WebM, an H.264/MP4 fallback, and a JPG poster frame:
|
|
112
|
+
|
|
113
|
+
```astro
|
|
114
|
+
---
|
|
115
|
+
import hero from '../assets/processed/hero.webm';
|
|
116
|
+
import poster from '../assets/processed/hero.jpg';
|
|
117
|
+
---
|
|
118
|
+
<video poster={poster.src} autoplay muted loop playsinline>
|
|
119
|
+
<source src={hero} type="video/webm" />
|
|
120
|
+
</video>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Outputs land under `src/` so your bundler content-hashes them and they can be served immutable,
|
|
124
|
+
while the raw sources stay outside anything it scans.
|
|
125
|
+
|
|
126
|
+
Defaults: capped at 1920px wide, CRF 32, audio dropped (the common case is a muted autoplay loop),
|
|
127
|
+
poster taken from frame 0. Every one is a flag — `--max-width`, `--crf`, `--audio`,
|
|
128
|
+
`--poster-time`, `--max-bitrate`, `--outputs`. `vitops media --dry` prints exactly what a run
|
|
129
|
+
would do without encoding anything.
|
|
130
|
+
|
|
131
|
+
**Needs `ffmpeg` on `PATH`.** It's an external tool, not an npm dependency — install it yourself
|
|
132
|
+
(`brew install ffmpeg`, `apt install ffmpeg`, `winget install Gyan.FFmpeg`). The command fails
|
|
133
|
+
rather than skipping: a page referencing a video that was never encoded is broken, not degraded.
|
|
134
|
+
|
|
135
|
+
**Commit the outputs and `.vitops/media-manifest.json`.** Runs are cached on source content plus
|
|
136
|
+
encode settings, so a second run costs a hash per file instead of minutes — but a fresh CI clone
|
|
137
|
+
has neither, and would re-encode everything. Committing both means CI never needs ffmpeg at all.
|
|
138
|
+
It also keeps your history clean: ffmpeg output isn't reproducible across versions, so a CI
|
|
139
|
+
re-encode would rewrite every video on any toolchain bump. Use `--force` when you mean to
|
|
140
|
+
re-encode, and commit the result.
|
|
141
|
+
|
|
142
|
+
Two things worth knowing before you conclude something is broken:
|
|
143
|
+
|
|
144
|
+
- **Frame 0 is often black** on a clip that fades in. That's `--poster-time`, not a bug.
|
|
145
|
+
- **A missing output re-encodes, a corrupt manifest re-encodes everything.** Neither ever reads as
|
|
146
|
+
"already done" — the failure that would cause is silent, and a rebuild would never fix it.
|
|
147
|
+
|
|
101
148
|
## Teaching AI agents (`vitops agents` + `vitops docs`)
|
|
102
149
|
|
|
103
150
|
This package **ships an agent skill** (`skill/SKILL.md`, the [Agent Skills](https://code.claude.com/docs/en/skills)
|