andesine 0.0.1-alpha.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,18 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Add `types generate`, read-only `--check`, foreground `--watch`, atomic output
6
+ replacement, and optional first generation through `init --generate`.
7
+ - Add the pure content type generator with workspace maps, collection/revision
8
+ types, property and fragment constraints, and optional entry/tree declarations.
9
+ - Expose current and published type metadata through generated API commands.
10
+ - Add generated public API commands with SDK transport, JSON/file input, binary
11
+ output, snapshot-aware pagination, SSE output, and shared CLI progress visuals.
12
+ - Add guided and non-interactive project initialization with public API validation,
13
+ configuration previews, and safe updates to existing files.
14
+ - Add the CLI foundation, project configuration, and package build.
15
+ - Add device login, online authentication status, logout, OS credential storage,
16
+ explicit private-file storage, and coordinated OAuth refresh.
17
+ - Add formatted authentication panels and progress spinners, with plain output
18
+ for non-interactive use and unchanged JSON results on stdout.
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Vrite, Inc.
3
+ Copyright (c) 2026 Vrite, Inc.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -1,163 +1,375 @@
1
- # Vrite JavaScript SDK
1
+ # Andesine CLI
2
2
 
3
- [Vrite JavaScript SDK](https://github.com/vriteio/vrite/tree/main/packages/sdk) is a one-stop-shop when building any kind of Vrite-powered website or web app.
3
+ MIT-licensed command-line tools for Andesine. Requires Node.js 22.12 or later.
4
4
 
5
- Currently it includes the following parts:
5
+ ```sh
6
+ npm install --save-dev andesine @andesine/sdk
7
+ npx andesine init
8
+ ```
6
9
 
7
- - **API client** — for easy access to Vrite API;
8
- - **Content transformers** — functions used to transform Vrite JSON content into different formats;
9
- - **Astro integration** — dedicated integration for [Astro](<%5Bhttps://astro.build/%5D(https://astro.build/)>);
10
- - **Extensions SDK** - for building custom extensions for Vrite;
10
+ Use `npm install --global andesine` for a system-wide executable. The CLI depends
11
+ on an exact SDK release. Keep `@andesine/sdk` as a direct project dependency when
12
+ application code imports the generated types or creates a client. Optional native
13
+ credential bindings are only loaded for OS-store authentication; API keys and
14
+ explicit file storage also work when those bindings are omitted.
11
15
 
12
- ## Installation
16
+ This package is under development. Help, configuration, OAuth authentication,
17
+ project initialization, public API commands, and content type generation are implemented.
13
18
 
14
- ```shell
15
- npm i @vrite/sdk
19
+ ```sh
20
+ andesine --help
21
+ andesine --version
16
22
  ```
17
23
 
18
- ## Usage
19
-
20
- The Vrite SDK is modular, so you can import only the parts of it you need. On top of that, it's **isomorphic**, with both the API client and content transformers library being able to run both in the browser and Node.js.
24
+ ## Project configuration
21
25
 
22
- ### API client
26
+ Run `andesine init` for guided setup. It uses Clack prompts and progress indicators
27
+ to select a workspace, current or published content, collections, and a generated
28
+ types path. If credentials are missing, it offers browser login. API keys use their
29
+ own workspace; OAuth users select from their accessible workspaces.
23
30
 
24
- Start by initializing the API client, by importing the library, and providing your API access token:
25
-
26
- ```javascript
27
- import { createClient } from "@vrite/sdk/api";
28
- const vrite = createClient({
29
- token: "[YOUR_API_TOKEN]"
30
- });
31
+ ```sh
32
+ andesine init
33
+ andesine init --config ./config/andesine.json
34
+ andesine init --no-interactive --workspace ws_example \
35
+ --source published --channel published \
36
+ --collection /Docs coll_example/Guides --output src/content.generated.ts
37
+ andesine init --no-interactive --source current --all-collections --yes
31
38
  ```
32
39
 
33
- With the client configured, you can access [all the endpoints of the Vrite API](https://generator.swagger.io/?url=https://api.vrite.io/swagger.json) via convenient JavaScript methods. Here are a few examples:
34
-
35
- ```javascript
36
- // Get all content groups
37
- const contentGroups = await vrite.contentGroups.list();
38
- // Get all content pieces from a content group
39
- const contentPieces = await vrite.contentPieces.list({
40
- contentGroupId: "[CONTENT_GROUP_ID]"
41
- });
42
- // Get the entire conten piece with JSON content
43
- const contentPiece = await vrite.contentPieces.get({
44
- id: "[CONTENT_PIECE_ID]",
45
- content: true
46
- });
47
- ```
40
+ Non-interactive setup requires a saved login or `ANDESINE_API_KEY`. Supply
41
+ `--workspace` when the OAuth account has multiple workspaces and no configured
42
+ selection. Defaults are the `published` channel, all accessible collections, and
43
+ `src/andesine.generated.ts`; existing configuration values are retained unless
44
+ changed by flags. `--snapshot snp_…` selects a fixed snapshot instead of a channel.
45
+ Use `--include-entry-ids`, `--include-entry-paths`, and `--include-tree` to opt into
46
+ additional types, or their `--no-…` forms to remove existing selections.
47
+
48
+ The collection picker offers IDs for rename stability or paths for readability.
49
+ `--collection` accepts one or more IDs or decoded paths, including collection-ID
50
+ anchors. The API validates them against the selected source. `--all-collections`
51
+ clears an existing selection. Empty collection selections mean all accessible
52
+ collections, including collections added later.
53
+
54
+ Setup shows changes before saving. An existing configuration requires confirmation
55
+ or `--yes`; non-interactive updates require `--yes`. Unchanged configurations are
56
+ not rewritten. A file changed during setup is rejected. Only the selected JSON
57
+ configuration is written; tokens stay in user credential storage. After saving,
58
+ interactive setup offers first generation. Use `--generate` to request it directly
59
+ or `--no-generate` to skip it. Non-interactive setup generates only with `--generate`;
60
+ `--yes` alone does not enable generation. Initialization does not install packages.
61
+ If generation fails, the saved configuration and last successful types file remain.
62
+
63
+ Commands use the nearest `andesine.json`, searching from the working directory up
64
+ to the filesystem root. `--config <file>` or `ANDESINE_CONFIG` selects an explicit
65
+ file instead. Explicit files must exist except when `init` creates them. Help and version do not load any config.
66
+ Configuration is JSON only; the CLI does not load `.env` files or execute config.
48
67
 
49
- To explore available endpoints, check out [Vrite API docs](https://generator.swagger.io/?url=https://api.vrite.io/swagger.json). On top of that, the API client is **fully typed** (written in TypeScript) so you can easily find your way using your modern code editor's autocomplete functionality.
50
-
51
- ### Content transformers
52
-
53
- **Content transformers** are functions dedicated to processing Vrite JSON output and adapting it to required string-based formats.
54
-
55
- The JSON output, based on the [ProseMirror](https://prosemirror.net/) library powering the Vrite Editor, is very versatile and easy to process, making it the ideal choice, no matter how your content has to be delivered. Content transformers help with this process by further simplifying the processing of these recursive JSON trees to output strings.
56
-
57
- ```javascript
58
- import { createContentTransformer, gfmTransformer, htmlTransformer } from "@vrite/sdk/transformers";
59
-
60
- const sampleVriteJSON = {
61
- // ...
62
- };
63
- // Transforms JSON to GitHub Flavored Markdown format
64
- const gfmOutput = gfmTransformer(sampleVriteJSON);
65
- // Transforms JSON to HTML format
66
- const htmlOutput = htmlTransformer(sampleVriteJSON);
67
- // Create your own content transformers
68
- const customContentTransformer = createContentTransformer({
69
- applyInlineFormatting(type, attrs, content) {
70
- switch (type) {
71
- case "link":
72
- return `<a href="${attrs.href}">${content}</a>`;
73
- case "bold":
74
- return `<strong>${content}</strong>`;
75
- case "code":
76
- return `<code>${content}</code>`;
77
- case "italic":
78
- return `<em>${content}</em>`;
79
- default:
80
- return content;
81
- }
82
- },
83
- transformNode(type, attrs, content) {
84
- switch (type) {
85
- case "paragraph":
86
- return `<p>${content}</p>`;
87
- case "heading":
88
- return `<h${attrs?.level || 1}>${content}</h${attrs?.level || 1}>`;
89
- case "blockquote":
90
- return `<blockquote>${content}</blockquote>`;
91
- case "bulletList":
92
- return `<ul>${content}</ul>`;
93
- case "orderedList":
94
- return `<ol>${content}</ol>`;
95
- case "listItem":
96
- return `<li>${content}</li>`;
97
- case "horizontalRule":
98
- return `<hr/>`;
99
- default:
100
- return content;
101
- }
68
+ ```json
69
+ {
70
+ "$schema": "./node_modules/andesine/dist/config.schema.json",
71
+ "version": 1,
72
+ "baseURL": "https://api.andesine.app",
73
+ "workspaceID": "ws_example",
74
+ "types": {
75
+ "source": { "kind": "published", "channel": "published" },
76
+ "collections": ["/Tutorials"],
77
+ "output": "src/andesine.generated.ts",
78
+ "includeEntryIDs": false,
79
+ "includeEntryPaths": false,
80
+ "includeTree": false
102
81
  }
103
- });
82
+ }
104
83
  ```
105
84
 
106
- The SDK provides built-in content transformers for both HTML and GFM formats.
85
+ The JSON Schema ships at `dist/config.schema.json` and is also exported as
86
+ `andesine/config.schema.json`. Unknown configuration fields are rejected.
87
+ Keep credentials out of this file.
107
88
 
108
- ### Astro integration
89
+ Values use this precedence: flags, environment, project configuration, selected
90
+ user profile, then defaults. Supported global overrides:
109
91
 
110
- Vrite Astro integration makes integrating Vrite with your Astro-powered website really easy!
92
+ | Flag | Environment variable | Project field |
93
+ | ------------- | ----------------------- | ------------- |
94
+ | `--config` | `ANDESINE_CONFIG` | — |
95
+ | `--base-url` | `ANDESINE_BASE_URL` | `baseURL` |
96
+ | `--workspace` | `ANDESINE_WORKSPACE_ID` | `workspaceID` |
97
+ | `--profile` | `ANDESINE_PROFILE` | `profile` |
111
98
 
112
- Start by adding the Vrite plugin into the Astro config:
99
+ The default API root is `https://api.andesine.app`. Self-hosted roots can include
100
+ a deployment prefix. Credentials, query strings, and fragments in the URL are
101
+ rejected. Trailing slashes are removed.
113
102
 
114
- ```javascript
115
- import { defineConfig } from "astro/config";
116
- import { vritePlugin } from "@vrite/sdk/astro";
117
- export default defineConfig({
118
- integrations: [
119
- // ...
120
- vritePlugin({
121
- accessToken: "[YOUR_ACCESS_TOKEN]",
122
- contentGroupId: "[CONTENT_GROUP_ID]"
123
- })
124
- // ...
125
- ]
126
- });
127
- ```
103
+ The default type source is the `published` channel. Use
104
+ `{ "kind": "published", "snapshotID": "snp_example" }` for a fixed snapshot, or
105
+ `{ "kind": "current" }` for current schemas. Channel and snapshot are exclusive.
106
+ Collection selectors are IDs or paths, resolved by the API in the generation
107
+ step. An omitted or empty collection list selects all accessible collections.
108
+ Entry IDs, entry paths, and tree types are off by default.
128
109
 
129
- In the configuration provide both your **API access token**, as well as the **ID of the content group** containing the posts for publishing on your Astro-powered website.
110
+ Output defaults to `src/andesine.generated.ts`. Relative output paths resolve
111
+ against the configuration directory, or the working directory if no config
112
+ exists. No files are generated during package installation or import.
130
113
 
131
- If you're using **TypeScript**, add the following to your _tsconfig.json_, under `compilerOptions.types`:
114
+ ## User profiles
115
+
116
+ User metadata lives in `andesine/profiles.json` under `XDG_CONFIG_HOME`, or under
117
+ the platform default: `~/Library/Application Support` on macOS, `%APPDATA%` on
118
+ Windows, and `~/.config` on Linux. Override directories must be absolute paths.
132
119
 
133
120
  ```json
134
- { "compilerOptions": { "types": ["@vrite/sdk/types"] } }
121
+ {
122
+ "version": 1,
123
+ "defaultProfile": "local",
124
+ "profiles": {
125
+ "local": {
126
+ "baseURL": "http://localhost:3000",
127
+ "workspaceID": "ws_example"
128
+ }
129
+ }
130
+ }
135
131
  ```
136
132
 
137
- Now you can import from a `virtual:vrite` module that provides configured API client, along with a few utils for integrating Vrite with Astro:
138
-
139
- ```astro
140
- ---
141
- import { Content, client, getContentPieces, getStaticPaths } from "virtual:vrite";
133
+ Profiles can also contain `accountID` and an opaque `credentialRef`. Tokens live
134
+ in the selected credential store, never in this metadata file. A profile's workspace and credential reference
135
+ are used only when its normalized API root matches the resolved instance,
136
+ including its deployment prefix. Missing selected profiles are errors except during `auth login`, which can create
137
+ the named profile. The first login uses `default` if no profile is selected and
138
+ sets the first saved profile as the default. A profile cannot be replaced by a
139
+ login to a different instance; supply a new `--profile` name instead.
140
+
141
+ ## Authentication
142
+
143
+ ```sh
144
+ andesine auth login
145
+ andesine --profile work auth login
146
+ andesine --base-url https://api.example.com auth login --no-browser
147
+ andesine auth login --credential-store file --no-browser --no-interactive
148
+ andesine auth status
149
+ andesine auth logout
150
+ ```
142
151
 
143
- // Pre-configured Vrite API client
144
- const settings = await client.userSettings.get();
145
- // Utility to retrieve content pieces from the configured content group
146
- const contentPieces = await getContentPieces();
147
- // Shortcut for retriving all content pieces for use with Astro SSG
148
- export getStaticPaths;
149
- ---
152
+ Login displays the instance, browser URL, and user code on stderr. The browser
153
+ shows the account and asks you to approve or deny access. `--no-browser` keeps the same
154
+ flow but leaves opening the URL to you. The CLI polls until approval, denial,
155
+ expiry, or cancellation. Ctrl+C stops polling. No callback server is started.
156
+
157
+ In a terminal, authentication commands use formatted panels, status icons, and
158
+ progress spinners. Login shows the browser URL and code while it waits for approval.
159
+ Progress stops on success, failure, or cancellation. CI, redirected stderr,
160
+ `TERM=dumb`, and `--no-interactive` use plain text without animation. Colors respect
161
+ `NO_COLOR`. JSON results on stdout remain unchanged.
162
+
163
+ The default store is the OS credential store: macOS Keychain, Windows Credential
164
+ Manager, or Linux Secret Service. Linux requires a running Secret Service; the CLI
165
+ does not silently use the nonpersistent kernel keyring. Native bindings are optional
166
+ and loaded only when needed. No install script downloads credentials or generates
167
+ project files.
168
+
169
+ If the OS store is unavailable, interactive login offers **unencrypted file
170
+ storage** with an explicit confirmation. `--credential-store file` selects that
171
+ option directly, including in non-interactive sessions. Credentials are stored in
172
+ `andesine/credentials` beside `profiles.json`. POSIX directories use `0700` and
173
+ files use `0600`. Windows directories use a protected ACL for the current user,
174
+ with inheritance for new files; PowerShell is required to establish that ACL.
175
+ File writes use a temporary file and atomic replacement. Do not put the user
176
+ configuration directory inside a shared or committed project.
177
+
178
+ `auth status` verifies the credential online and emits JSON with its source,
179
+ instance, account (or API-key ID), selected profile, and workspace ID. A missing,
180
+ expired, or rejected credential fails with a nonzero exit code. OAuth refresh
181
+ occurs when the access token has less than one minute left. Concurrent processes
182
+ serialize refreshes. An interrupted or uncertain exchange requires a new login;
183
+ the CLI never retries a refresh token that might already have been rotated. API
184
+ requests are not replayed to recover authentication.
185
+
186
+ `ANDESINE_API_KEY` takes precedence over saved OAuth credentials for API requests
187
+ and `auth status`. An empty or rejected key does not fall back to OAuth. Login and
188
+ logout still manage the selected saved profile and do not change the environment.
189
+ No credential is used for a different API root, including a different deployment
190
+ prefix. HTTP redirects are not followed for authenticated requests.
191
+
192
+ Login and status emit metadata only on stdout. Tokens are never printed. API
193
+ commands use the same SDK authentication helper.
194
+
195
+ Logout revokes the latest known refresh token and removes local credentials.
196
+ Its JSON result reports `localRemoved` and `revocation` separately. If remote
197
+ revocation cannot be confirmed, local removal still occurs and the command exits
198
+ with an error. Browser sessions are not signed out. Older access tokens may remain
199
+ valid for up to 15 minutes. Provider refresh-token reuse can invalidate other CLI
200
+ logins for the same user/client; complete per-device revocation is deferred.
201
+
202
+ ## Public API commands
203
+
204
+ `andesine api <resource> <operation>` exposes the public API through the SDK.
205
+ Names use kebab-case: `content.getTree` becomes `api content get-tree`.
206
+ Each operation has `--help` with its fields, description, authentication, and
207
+ example input. `--schema` prints its bundled input JSON Schema. Both work offline
208
+ without loading project configuration or credentials.
209
+
210
+ After OAuth login, run `andesine init` to save a workspace for this project, or
211
+ select one for a command with `--workspace <id>`. Login alone does not select a
212
+ workspace. Use `andesine api workspaces list` to find the available IDs; that
213
+ command does not require a workspace. Workspace operations report a configuration
214
+ error before sending the API request if no workspace is selected.
215
+
216
+ ```sh
217
+ andesine api --help
218
+ andesine api content get-tree --help
219
+ andesine api entries create --schema
220
+ andesine api content get-tree --channel published
221
+ andesine api content get --path /Docs/Introduction --channel published --full
222
+ andesine api content list-entries --channel published --paginate
223
+ andesine api search published --input @search.json --semantic false
224
+ andesine api search ask-published-stream --input @question.json --format text
225
+ andesine api assets upload --asset-id ast_example --file ./image.png
226
+ andesine api type-metadata get-published --collections '["/Tutorials"]'
227
+ ```
150
228
 
151
- <!-- Renders the Vrite content piece, specified by ID or slug -->
152
- <content contentPieceId="..." slug="..." />
229
+ Pass simple values as flags, or supply an API input object through `--input @file`
230
+ or `--input -` for stdin. JSON uses API field names, such as `collectionID`; flags
231
+ use `--collection-id`. Flags replace matching JSON fields. Boolean flags require
232
+ `true` or `false`; arrays and objects require JSON. Nullable fields accept `null`.
233
+ Use `--help` for required inputs. File fields accept a path or `-` for stdin;
234
+ stdin can supply either the JSON object or one file per command.
235
+
236
+ Normal responses are one JSON value per line. `--full` includes HTTP status,
237
+ headers, and data, for example to retrieve an ETag. Endpoints with conditional
238
+ reads accept `--if-none-match`; unchanged content emits a `notModified` result.
239
+ Empty responses emit no stdout unless `--full` is selected. Binary responses
240
+ require `--output <new-file>` or `--output -` for raw bytes; existing files are
241
+ never replaced.
242
+
243
+ `--paginate` emits one complete page per line, including pagination and snapshot
244
+ metadata. Published content retains the first page's snapshot. To resume published
245
+ pagination, provide both `--cursor` and `--snapshot-id` from the same result.
246
+ `--full` and conditional reads cannot be combined with `--paginate`.
247
+
248
+ AI streams emit NDJSON events by default. `--format text` emits answer text as it
249
+ arrives. A failed or incomplete stream keeps the output already emitted, reports
250
+ the error on stderr, and exits with a failure code.
251
+
252
+ Requests use a 30-second timeout; `--timeout <ms>` changes it and `--timeout 0`
253
+ disables it. Ctrl+C cancels the request. Retries are disabled, including for writes
254
+ and AI calls. Writes execute directly. Use the operation's documented expected
255
+ revision or snapshot fields when it supports change checks.
256
+
257
+ Commands use the configured API root and credentials. OAuth workspace selection
258
+ uses `--workspace`; API keys remain bound to their workspace. OAuth-only operations
259
+ require a saved login with `ANDESINE_API_KEY` unset. Anonymous asset reads do not
260
+ send credentials. Public API errors retain their code, details, hints, and request
261
+ ID on stderr when available.
262
+
263
+ In a terminal, commands use the shared Clack headings, request spinners, and status
264
+ messages on stderr. Redirected output and non-interactive use keep plain diagnostics
265
+ and machine-readable stdout. Command definitions are bundled with the package;
266
+ the CLI does not download executable definitions from the server.
267
+
268
+ `api type-metadata get-current` and `get-published` return collection/schema
269
+ metadata for type generation. Use `--include-entries true` or `--include-tree true`
270
+ for optional entry and ordered tree data. Published metadata retains its snapshot
271
+ and recorded schema revisions; collections with no recorded schema use a general
272
+ type without warnings. These API commands return metadata only; use
273
+ `andesine types generate` to write TypeScript files.
274
+
275
+ ## Content type generator
276
+
277
+ ```sh
278
+ andesine types generate
279
+ andesine types generate --check
280
+ andesine types generate --watch
281
+ andesine types generate --source current --collection /Tutorials
282
+ andesine types generate --snapshot snp_example --include-entry-paths
283
+ andesine init --generate
153
284
  ```
154
285
 
155
- - `Content` — renders Vrite content piece specified either by ID (`contentPieceId` prop) or a slug (`slug` prop); Useful when in SSR mode;
156
- - `client` — pre-configured Vrite API client for easy access;
157
- - `getContentPieces` — retrieves content pieces from the content group specified in the config file. Accepts an object with the following properties:
158
- - `limit?: number | "all" = 50` — how many content pieces to retrieve; `"all"` retrieves all of them, in batches of 50;
159
- - `startPage?: number = 1` — if `limit` is a number and pagination is used, what page to start retrieving content pieces from?
160
- - `tagId?: string` — Tag ID, if you want to filter results by specific tag.
161
- - `getStaticPaths` — shortcut function for easy re-export when using Vrite SSG and inside _[slug].astro_ file. It returns `params.slug` as the content piece's slug and all basic content piece properties as `props`.
286
+ Generation reads public metadata for the configured source and writes the configured
287
+ TypeScript file. Flags override saved settings without changing `andesine.json`.
288
+ Use the same source, collection, output, and optional-type flags as `init`.
289
+ OAuth requires a selected workspace; an API key uses its workspace binding.
290
+ Output paths are relative to the configuration directory, or the working directory
291
+ without configuration. Supported extensions are `.ts`, `.mts`, and `.cts`, including
292
+ declaration files such as `.d.ts`.
293
+
294
+ Writes are atomic. Identical output is not rewritten. An unrelated file requires
295
+ explicit `--force`; links and non-regular files are rejected. A file changed during
296
+ generation is preserved. Metadata, access, and generation failures leave the last
297
+ successful output intact.
298
+
299
+ Commit the generated file and run `andesine types generate --check` in CI, with
300
+ `ANDESINE_API_KEY` available as a secret. Check mode makes no types-file writes and
301
+ returns exit code `3` for missing or stale output. Request and generation failures
302
+ return `1`; invalid options/configuration return `2`. A channel or current source is
303
+ checked against live metadata. Pin `--snapshot` for a fixed source while that
304
+ snapshot is retained. Authentication failures are never reported as stale output.
305
+
306
+ Watch runs in the foreground, polling every 10 seconds. `--interval <seconds>`
307
+ accepts 1–300 seconds. It holds the resolved configuration fixed; restart after
308
+ config changes. Polls do not overlap. Metadata fingerprints avoid repeated type
309
+ generation, and identical files stay untouched. Local deletion or changes to a
310
+ marked generated file are corrected on the next successful poll. `--force` only
311
+ authorizes unrelated-file replacement until the first successful generation.
312
+
313
+ Transient metadata read failures, rate limits, and schema migrations retry with
314
+ backoff, respecting `Retry-After`. Other failures stop the command. Watch refreshes
315
+ OAuth credentials between polls and preserves the last output during failures.
316
+ Press Ctrl+C to stop (exit `130`). There is no daemon or install-time generation.
317
+ `--check` cannot be combined with `--watch` or `--force`.
318
+
319
+ Commands print JSON results on stdout and use Clack progress/status on stderr.
320
+ Watch prints its initial result, changed output, and recovery; unchanged polls stay
321
+ quiet. The pure generator remains separate in `src/types/generate.ts`, with no
322
+ network, credential, or filesystem access.
323
+
324
+ Generated files export `Workspace` for `createClient<Workspace>()`, plus types
325
+ such as `TutorialsCollection`, `TutorialsCollectionContent`, and
326
+ `TutorialsCollectionStructuredContent`. Each recorded revision has a named schema
327
+ and content type. Names use stable suffixes when collection names conflict after
328
+ conversion to TypeScript identifiers. Field keys keep the API's derived spelling.
329
+
330
+ All declared schema fields are required. Numbers use `number | null`, selects
331
+ include `""`, and multi-selects use arrays of allowed values. Fragment types narrow
332
+ allowed block kinds. Custom element names and properties remain general because
333
+ schemas do not define them. Defaults are not treated as value constraints.
334
+ Empty and schema-free collections use general content types without warnings.
335
+
336
+ Entry ID bindings, entry path bindings, and ordered tree types are separate
337
+ opt-ins. Tree exports include their entry identities. Published trees narrow
338
+ `content.getTree()`; current trees describe the metadata's child-ID lists only.
339
+ The SDK generic changes types only: callers must configure the matching workspace
340
+ and source. Channel generation omits changing snapshot IDs and expiry; explicit
341
+ snapshot generation records the configured snapshot. Output contains no executable
342
+ values, content bodies, or timestamps.
343
+
344
+ ## Command conventions
345
+
346
+ - Command data uses stdout. JSON responses use one JSON value per line; streamed
347
+ events use NDJSON. Text output preserves the supplied text.
348
+ - Diagnostics and Clack prompts use stderr. Prompts require a terminal for stdin
349
+ and stderr, are disabled in CI, and can be disabled with `--no-interactive`.
350
+ - Exit codes: `0` success, `1` runtime failure, `2` usage/configuration error,
351
+ `3` stale generated types, `130` cancellation.
352
+ - API retries are disabled.
353
+
354
+ ## Development
355
+
356
+ ```sh
357
+ pnpm --filter ./packages/public/cli typecheck
358
+ pnpm --filter ./packages/public/cli generate:api
359
+ pnpm --filter ./packages/public/cli build
360
+ node packages/public/cli/dist/cli.js --help
361
+ ```
162
362
 
163
- For a more detailed guide on using the Vrite Astro integration, [check out this blog post](https://vrite.io/blog/start-programming-blog-in-minutes-with-astro-and-vrite/).
363
+ `src/program.ts` owns command registration. Actions create a shared context
364
+ through `src/context.ts`; help does not create that context. Custom commands own
365
+ their logic. The `api` group uses metadata generated at build time from the
366
+ SDK OpenAPI artifact. It executes through the SDK, with no backend modules
367
+ or remotely downloaded executable command definitions at runtime.
368
+
369
+ The Rolldown build emits the executable and creates the JSON Schema from the same
370
+ Zod definition used to validate configuration. It also regenerates API metadata
371
+ and typed SDK bindings. Commit those generated sources with spec changes.
372
+ `prepack` builds the package, checks types, and runs Publint. The public-package
373
+ workflows check and publish the CLI archive after its exact SDK release is available.
374
+ See the [release guide](https://github.com/vriteio/andesine/blob/andesine/packages/public/README.md) and [release review](https://github.com/vriteio/andesine/blob/andesine/packages/public/CLI-RELEASE-REVIEW.md)
375
+ for rollout order and checks that still need browser or platform access.