This is a VS Code extension that provides OpenCode Zen models via the Language Model Chat Provider API. The codebase is TypeScript with strict mode, using the ai-sdk for API communication.
# Compile TypeScript (outputs to ./out)
npm run compile
# Watch mode for development
npm run watch
# Run ESLint on all TypeScript files
npm run lint
- in the .context Folder you can find the opencode Project. Use it when ever reference is needed.
- Use namespace import for VS Code API:
import * as vscode from 'vscode' - Use named imports for local modules:
import { functionName } from './module' - Use named imports for packages:
import { createOpenAICompatible } from '@ai-sdk/openai-compatible'
- Strict mode enabled - no implicit
any, strict null checks, strict function types - Allowed
any: The eslint rule@typescript-eslint/no-explicit-anyis disabled. Useanysparingly for:- Avoiding hard-coupling to evolving SDK types (e.g., ai-sdk CoreMessage shape)
- AI SDK runtime validation where exact types aren't needed
- Use
ReadonlyMap,readonlyparameters, and proper type annotations - Use interface for object types, type alias for primitives/unions
- Classes: PascalCase (
OpenCodeZenChatProvider,ModelRegistry) - Functions/variables: camelCase (
streamZen,getApiKey,apiKey) - Constants: SCREAMING_SASE for config values (
ZEN_BASE_URL,MODELS_DEV_URL) - Exported constants: camelCase for runtime values (
VENDOR_ID = 'opencode') - Types: PascalCase (e.g.,
ModelsDevModel,StreamCallbacks) - Tool names: lowercase with dots (
opencodeZen.selfTest.getTime)
- Wrap API errors with contextual messages (see
wrapApiErrorinzenClient.ts:13) - Use
try/catchblocks withfinallyfor cleanup (e.g., cancellation tokens) - Surface user-friendly messages: "Run 'OpenCode Zen: Set API Key'" for missing credentials
- Log errors to console or VS Code output channel, never throw during activation unless critical
- Check for
instanceof Errorbefore accessingerr.message
- Use
vscode.ExtensionContext.subscriptionsfor disposables - Create output channels with
vscode.window.createOutputChannel(..., { log: true }) - Use
vscode.EventEmitterfor change notifications - Store secrets in
vscode.SecretStorage, never in settings - Store cached data in
vscode.WorkspaceStatewith TTL - Register commands with
vscode.commands.registerCommand - Cancellation: use
vscode.CancellationTokenandAbortController
- 4-space indentation (TypeScript default)
- Opening brace on same line for functions/control flow
- No comments unless explaining non-obvious behavior
- Blank line between function definitions
- Sort imports: npm packages, VS Code, local modules
- Always handle promise rejections with try/catch
- Use
asyncfor functions returning promises - Prefer
awaitover.then()chains for readability - Clean up resources in
finallyblocks or withusingpattern where applicable
- VS Code handles tool execution; provider only emits
LanguageModelToolCallPart - Tools defined with JSON Schema, wrapped with
jsonSchema()for AI SDK - Provider emits tool calls via callbacks, not streaming deltas
- Use
toolCallStreaming: falseto get complete tool calls
- Use vercel
ai-sdkfor API Requests to the models. (which sdk should be used is defined per model by models.dev) - Base URL:
https://opencode.ai/zen/v1 - Fetch model list from
https://models.dev/api.json - Cache model metadata with configurable TTL (default 15 minutes)
src/extension.ts- Activation/deactivation, command registrationsrc/provider.ts-OpenCodeZenChatProviderimplementationsrc/zenClient.ts- API client and streaming logicsrc/modelRegistry.ts- Model fetching and cachingsrc/secrets.ts- API key storage via SecretStoragesrc/types.ts- Shared type definitions
- Self-test command (
opencodeZen.selfTest) validates streaming + tool calling - Test output written to 'OpenCode Zen' output channel
- Tests run against live models with configured API key
- Best-effort refresh: Don't crash activation on model fetch failure
- Graceful degradation: Return empty model list on error rather than throwing
- Tool call loop: 5 iteration limit with explicit error if exceeded
- Output channel logging: Use
output.info(),output.error(),output.append()