An 'Adding a provider' checklist

provider.ts sells adding a provider as "a variant + an adapter", but TS exhaustiveness actually forces about six touchpoints — and that's exactly where a contributor gets stuck. The new ARCHITECTURE.md section enumerates them with file refs.

The checklist; steps 1 and 6 don't type-check until the rest are done.

docs/ARCHITECTURE.md · 134 lines
docs/ARCHITECTURE.md134 lines · Markdown
⋯ 100 lines hidden (lines 1–100)
1# Vibe Themer Architecture (v2)
2 
3Vibe Themer turns a natural-language "vibe" into a VS Code color theme, streaming
4each setting from the configured model provider (OpenAI or Anthropic) and applying
5it live. v2 is a ground-up rewrite
6around a **functional core, imperative shell** (hexagonal / ports-and-adapters)
7design with strict, domain-driven typing.
8 
9## The guiding idea
10 
11Make illegal states unrepresentable, then push every side effect to the edge.
12The center of the program is pure and total; the messy parts (VS Code, the network,
13the clock) live behind interfaces and are swapped for fakes in tests.
14 
15```
16 ┌──────────────────────────────────────────────┐
17 │ extension.ts (composition root) │
18 │ builds adapters → Capabilities → commands │
19 └───────────────┬───────────────┬──────────────┘
20 │ │
21 adapters/ application/ ← orchestration (impure-ish)
22 (vscode, providers) use cases returns Result, no throws
23 │ │
24 └──────┬────────┘
25 │ depends only on
26 ports/ (interfaces) ← the seam
27
28 domain/ + protocol/ + fp/ ← pure, total, no I/O
29```
30 
31Dependencies point inward. `domain`, `protocol`, and `fp` know nothing about VS
32Code or the model SDKs. `application` depends on `ports` (interfaces), never on
33adapters. `adapters` and `extension.ts` are the only files that import `vscode`,
34`openai`, or `@anthropic-ai/sdk`.
35 
36## Layers
37 
38### `fp/` — the functional prelude
39 
40A small, dependency-free kernel: `Result`/`Option`/`AsyncResult` (errors and
41absence as values, never thrown), `pipe`, `matchTag` (exhaustive tagged-union
42matching), `Brand` (nominal types), `Redacted` (secrets that can't be printed),
43and a tiny parser-combinator set. One import surface: `import { ... } from './fp'`.
44 
45### `domain/` — value objects and rules
46 
47Every domain value has a **smart constructor** and no other way in, so possession
48of the value is proof it's valid: `Vibe`, `ColorValue` (Hex | Named | Remove),
49`WorkbenchSelector`, `TokenScope`, `FontStyle`, `ApiKey` (wrapped in `Redacted`),
50`Provider`, `Model` (`{provider, id}`), `StreamingDirective`, `ThemeSetting`,
51`ConfigurationScope`,
52`CurrentTheme`, `Coverage`. Errors are tagged unions with centralized rendering.
53 
54### `protocol/` — the streaming grammar
55 
56`streamingParser.ts` decodes one wire line (`COUNT:` / `SELECTOR:` / `TOKEN:` /
57`MESSAGE:`) using the combinators for structure and the domain constructors for
58field validation. A parsed directive is fully well-typed.
59 
60### `ports/` — the seam
61 
62Interfaces for every effect: `ModelGateway` (the provider-blind façade),
63`ConfigStore`, `SecretStore`, `Preferences`, `PromptLibrary`, `Ui`, `Clock`,
64`Logger`, bundled into one `Capabilities` record. Plus the port-error unions and
65their `UserMessage` rendering.
66 
67### `application/` — use cases
68 
69Pure-ish orchestration returning `Result`: `generateTheme` (the streaming loop),
70`provisionApiKey` (the key state machine), and the maintenance commands
71(`resetTheme`, `clearApiKey`, `selectModel`, `resetModel`). Decisions are
72delegated to pure functions (`parseLine`, `progressPercent`, `coverage`); effects
73go through ports.
74 
75### `adapters/` — the shell
76 
77`adapters/vscode/*` implement the ports against the VS Code API.
78`adapters/openai/` and `adapters/anthropic/` each implement a `ProviderAdapter`
79against their SDK, classifying errors by HTTP status; `adapters/gateway.ts` is the
80provider-blind `ModelGateway` that dispatches each call to the right one. Thin and
81obviously correct.
82 
83### `commands.ts` + `extension.ts`
84 
85`commands.ts` is the single source of truth for command IDs and their handlers
86(a test asserts these match `package.json`). `extension.ts` is the composition
87root: build adapters, assemble `Capabilities`, register commands — fully
88synchronous activation.
89 
90## Why this shape
91 
92- **Testability.** The entire core runs against in-memory fakes (`test/support/
93 harness.ts`), so the full Change Theme flow — streaming, live application,
94 cancellation, error tolerance — is exercised with no VS Code and no network.
95- **Safety by type.** Branded values, `Redacted` secrets, and exhaustive matches
96 turn whole classes of v1 bugs (a leaked key, an invalid color, an unhandled
97 variant) into compile errors.
98- **One direction of dependency.** Swapping an adapter (or a model provider) never
99 touches the core.
100 
101## Adding a provider
102 
103`Provider` is a closed union, so the compiler walks you through every site. The
104full checklist (none of it in the pure use cases):
105 
1061. `src/domain/provider.ts` — add the variant to `Provider` and `allProviders`,
107 and a `providerInfo` case (display name + key prefix + hint).
1082. `src/domain/apiKey.ts` — extend `hasValidPrefix` if the new prefix overlaps an
109 existing one (the way `sk-` is a prefix of `sk-ant-`).
1103. `src/domain/model.ts` — add a `CATALOG` entry so it's offered in the picker.
1114. `src/adapters/<provider>/gateway.ts` — implement `ProviderAdapter`
112 (`verify` + `streamTheme`); reuse `adapters/classify.ts` for status mapping.
1135. `src/adapters/vscode/secrets.ts` — add the storage key in `STORAGE_KEY`.
1146. `src/extension.ts` — construct the adapter and pass it to `createModelGateway`.
115 
116Steps 1 and 6 won't type-check until the others are done, which is the point: the
117union and the `Record<Provider, ProviderAdapter>` make a half-wired provider a
118compile error.
119 
⋯ 15 lines hidden (lines 120–134)
120## Configuration & secrets
121 
122- API keys: one per provider in VS Code `SecretStorage`, wrapped in `Redacted`
123 from validation onward.
124- Selected model: a `Model` (`{provider, id}`) in `globalState` via the
125 `Preferences` port (default `gpt-5.5` on OpenAI), chosen from a curated catalog.
126- Theme state: read per-scope via `config.inspect()` so global vs workspace are
127 actually distinguished.
128 
129## Testing & build
130 
131- `npm run check-types``tsc --noEmit` at maximum strictness over `src` + `test`.
132- `npm test` — esbuild bundles `test/run.ts` and runs it on Node's built-in
133 `node:test` (no test-runner dependency, single process).
134- `npm run compile` — check-types + ESLint + the esbuild extension bundle.

Privacy precision

The README said only color values are sent on iteration, but context.ts also serializes token-rule overrides from editor.tokenColorCustomizations (both scopes). The sentence now names the two settings keys and what they include, so the disclosure matches the code.

Names workbench.colorCustomizations and editor.tokenColorCustomizations.

README.md · 89 lines
README.md89 lines · Markdown
⋯ 64 lines hidden (lines 1–64)
1<div align="center">
2 <img src="logo-with-text.png" alt="Vibe Themer Logo" width="500" style="margin-top: 15px; margin-bottom: 15px;" />
3</div>
4 
5<div align="center">
6 
7[![GitHub Sponsors](https://img.shields.io/github/sponsors/mseeks?style=for-the-badge&logo=github&logoColor=white&label=Sponsor&labelColor=ea4aaa&color=ff69b4)](https://github.com/sponsors/mseeks)
8 
9</div>
10 
11---
12 
13AI-generated VS Code color themes. Describe a vibe in plain language and Vibe Themer
14streams a complete theme — UI and syntax — applying each color live as the model
15generates it.
16 
17> **Reset, don't switch.** Generated themes are written as VS Code color overrides,
18> so they persist until you remove them. Run **"Vibe Themer: Reset Theme
19> Customizations"** to restore your original theme. Changing the theme in Settings
20> will not clear them.
21 
22## Features
23 
24- **Describe a vibe** — "cozy autumn evening", "cyberpunk neon", "calm ocean
25 depths" — and get a full, cohesive palette.
26- **Iterate in place.** "Make it warmer", "darker background", "remove the purple
27 accents" — refine instead of starting over.
28- **Live streaming.** Watch the theme apply as each setting arrives.
29- **Comprehensive.** Editor, activity bar, sidebar, status bar, tabs, panels,
30 terminal, and syntax tokens.
31- **OpenAI or Anthropic.** Pick from a short curated list — GPT-5.5 (default),
32 GPT-5.4 mini, or Claude Sonnet 4.6 — or enter a custom model id.
33- **Your keys, your storage.** Each provider's key lives in VS Code's encrypted
34 secret storage and is never logged.
35 
36## Requirements
37 
38- VS Code 1.74.0 or higher
39- An API key for your chosen provider — OpenAI ([get one](https://platform.openai.com/))
40 or Anthropic ([get one](https://console.anthropic.com/))
41- Internet access
42 
43## Quick start
44 
451. Install Vibe Themer from the Marketplace.
462. Open the Command Palette (`Cmd/Ctrl+Shift+P`) and run **Vibe Themer: Change
47 Theme**.
483. Enter your provider API key when prompted (stored securely, only once).
494. Describe a vibe, e.g. `warm sunset over mountains`.
505. To change back, run **Vibe Themer: Reset Theme Customizations**.
51 
52Already have a theme applied? Run Change Theme again and describe an adjustment.
53Vibe Themer sends your current colors as context and streams only what changes.
54 
55## Commands
56 
57| Command | Description |
58| --- | --- |
59| **Vibe Themer: Change Theme** | Generate or iterate a theme from a description |
60| **Vibe Themer: Reset Theme Customizations** | Remove all overrides, restore your original theme |
61| **Vibe Themer: Select Model** | Pick a model from the curated list or enter a custom id |
62| **Vibe Themer: Reset Model Selection** | Revert to the default model (GPT-5.5) |
63| **Vibe Themer: Clear API Keys** | Remove the stored keys for all providers |
64 
65## Privacy
66 
67Only your description — and, when iterating, your current Vibe Themer overrides
68under `workbench.colorCustomizations` and `editor.tokenColorCustomizations` (color
69values and token-rule styling) — is sent to your chosen provider (OpenAI or
70Anthropic). Nothing else from your settings, and no code, files, or analytics, is
71collected. Your keys are stored in VS Code's encrypted secret storage. AI-generated
72text is not filtered, so you are responsible for the prompts you write.
⋯ 17 lines hidden (lines 73–89)
73 
74## Background
75 
76Vibe Themer started as an experiment in how far agent-driven development could go;
77it was written entirely by an AI agent. That's ordinary now, so this README is
78about the extension itself, not how it was built. For the design — a strictly-typed
79functional core behind a thin VS Code shell — see
80[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md).
81 
82## Development
83 
84- `npm run compile` — type-check, lint, and bundle
85- `npm test` — unit and integration suite (Node's built-in test runner)
86 
87---
88 
89Licensed under the terms in [LICENSE.txt](LICENSE.txt).