Skip to main content
ClaudeWave
Skill1.1k estrellas del repoactualizado 24d ago

llm-provider

The llm-provider Claude Code skill scaffolds new LLM provider implementations for the caliber-ai-org/ai-setup project. Use this when adding a new model backend or third-party LLM API integration that requires implementing the LLMProvider interface with call() and stream() methods, integrating with the provider factory and config detection system, and enabling usage tracking for telemetry.

Instalar en Claude Code
Copiar
git clone --depth 1 https://github.com/caliber-ai-org/ai-setup /tmp/llm-provider && cp -r /tmp/llm-provider/skills/llm-provider ~/.claude/skills/llm-provider
Después abre una sesión nueva de Claude Code; el skill carga automáticamente.

SKILL.md

# LLM Provider

## Critical

1. **All providers MUST implement the LLMProvider interface** from src/llm/types.ts with three methods:
   - call(options: LLMCallOptions): Promise<string> — single non-streaming call returning text
   - stream(options: LLMStreamOptions, callbacks: LLMStreamCallbacks): Promise<void> — streaming call invoking callbacks
   - listModels?(): Promise<string[]> — optional; list available models from the API

2. **Initialize client in constructor and store defaultModel from config**. Example: `this.client = new YourSDK({ apiKey: config.apiKey })`. Never lazy-initialize on first call — providers are instantiated once and cached in src/llm/index.ts.

3. **For EVERY response in call() and stream(), invoke trackUsage(model, usage)** from src/llm/usage.js before returning/ending. This is mandatory — it captures token metrics for CLI telemetry and cost analysis. If the API doesn't return usage data, estimate via estimateTokens(text), which assumes ~4 chars per token.

4. **Both call() and stream() must respect the model parameter** using pattern: `options.model || this.defaultModel`. Never hardcode model names. Callers supply model overrides via LLMCallOptions.model.

5. **Error handling: catch all errors, preserve error messages unchanged**. The retry logic in src/llm/index.ts handles transient errors (ECONNRESET, socket hang up, 529 overload). For seat-based providers (Cursor, Claude CLI), wrap stderr via parseSeatBasedError() for user-friendly messages.

6. **Always update ProviderType union** (Step 2), DEFAULT_MODELS (Step 4), and createProvider() switch case (Step 5) in lock-step. Missing any one breaks the build or causes runtime Unknown provider error.

## Instructions

### Step 1: Create provider class file
Verify directory exists: `ls -la src/llm/`. Create `src/llm/your-provider.ts`. Match existing provider patterns (src/llm/anthropic.ts, src/llm/openai-compat.ts).

Minimal structure:
```typescript
import type { LLMProvider, LLMCallOptions, LLMStreamOptions, LLMStreamCallbacks, LLMConfig, TokenUsage } from './types.js';
import { trackUsage } from './usage.js';
import { estimateTokens } from './utils.js';

export class YourProviderProvider implements LLMProvider {
  private client: YourSDKType;
  private defaultModel: string;

  constructor(config: LLMConfig) {
    if (!config.apiKey) throw new Error('API key required');
    this.client = new YourSDK({ apiKey: config.apiKey, ...(config.baseUrl && { baseURL: config.baseUrl }) });
    this.defaultModel = config.model;
  }

  async call(options: LLMCallOptions): Promise<string> {
    const model = options.model || this.defaultModel;
    const response = await this.client.messages.create({ model, max_tokens: options.maxTokens || 4096, system: options.system, messages: [{ role: 'user', content: options.prompt }] });
    trackUsage(model, { inputTokens: response.usage?.input_tokens || 0, outputTokens: response.usage?.output_tokens || 0 });
    return response.content?.[0]?.text || '';
  }

  async stream(options: LLMStreamOptions, callbacks: LLMStreamCallbacks): Promise<void> {
    const model = options.model || this.defaultModel;
    const messages = [...(options.messages || []), { role: 'user' as const, content: options.prompt }];
    try {
      const stream = await this.client.stream({ model, max_tokens: options.maxTokens || 10240, system: options.system, messages });
      let stopReason: string | undefined, usage: TokenUsage | undefined;
      for await (const chunk of stream) {
        if (chunk.delta?.text) callbacks.onText(chunk.delta.text);
        if (chunk.delta?.stop_reason) stopReason = chunk.delta.stop_reason;
        if (chunk.usage) usage = { inputTokens: chunk.usage.input_tokens, outputTokens: chunk.usage.output_tokens };
      }
      if (usage) trackUsage(model, usage);
      callbacks.onEnd({ stopReason, usage });
    } catch (error) { callbacks.onError(error instanceof Error ? error : new Error(String(error))); }
  }
}
```

Verify: File exports the class; imports match existing providers.

### Step 2: Add to ProviderType union
Edit `src/llm/types.ts` line 1. Add your provider in kebab-case:
```typescript
export type ProviderType = 'anthropic' | 'vertex' | 'openai' | 'cursor' | 'claude-cli' | 'your-provider';
```
Verify: `npx tsc --noEmit` shows no ProviderType errors.

### Step 3: Add config fields
If your provider needs fields beyond apiKey, model, baseUrl, extend LLMConfig in src/llm/types.ts:
```typescript
export interface LLMConfig {
  provider: ProviderType;
  model: string;
  fastModel?: string;
  apiKey?: string;
  baseUrl?: string;
  yourProviderSecret?: string;
}
```

### Step 4: Update config.ts
Edit `src/llm/config.ts`:

**Line 9:** Add to DEFAULT_MODELS:
```typescript
export const DEFAULT_MODELS: Record<ProviderType, string> = {
  anthropic: 'claude-sonnet-4-6',
  vertex: 'claude-sonnet-4-6',
  openai: 'gpt-5.4-mini',
  cursor: 'sonnet-4.6',
  'claude-cli': 'default',
  'your-provider': 'your-provider/default-model',
};
```

**Line 17:** Add to MODEL_CONTEXT_WINDOWS if known:
```typescript
export const MODEL_CONTEXT_WINDOWS: Record<string, number> = {
  'your-provider/model-name': 128_000,
};
```

**Line 59:** In resolveFromEnv(), add env detection before final return null:
```typescript
if (process.env.YOUR_PROVIDER_API_KEY) {
  return {
    provider: 'your-provider',
    apiKey: process.env.YOUR_PROVIDER_API_KEY,
    model: process.env.CALIBER_MODEL || DEFAULT_MODELS['your-provider'],
    baseUrl: process.env.YOUR_PROVIDER_BASE_URL,
  };
}
```

**Line 115:** In readConfigFile() validation, add 'your-provider' to includes list.

Verify: `npm run test -- src/llm/__tests__/ -t config` confirms env var detection works.

### Step 5: Register in factory
Edit `src/llm/index.ts`. Add import (line ~4):
```typescript
import { YourProviderProvider } from './your-provider.js';
```

In createProvider() switch (line ~24), add before default case:
```typescript
case 'your-provider':
  return new You
adding-a-commandSkill

Creates a new CLI command following the Commander.js pattern in src/commands/. Handles command registration in src/cli.ts, telemetry tracking via tracked() wrapper, and option parsing. Use when user says add command, new CLI command, create subcommand, or adds files to src/commands/. Do NOT use for modifying existing commands or fixing bugs in existing commands.

caliber-testingSkill

Writes Vitest tests following project patterns: __tests__/ directories, vi.mock() for module mocking with vi.hoisted() for test-time factories, global LLM mock from src/test/setup.ts, environment variable save/restore in beforeEach/afterEach, vi.clearAllMocks() lifecycle, and test file organization. Use when user says 'write tests', 'add test coverage', 'test this', creates *.test.ts files, or when test failures appear in CI. Do NOT use for non-test code or for debugging without writing tests.

find-skillsSkill

Discovers and installs community skills from the public registry. Use when the user mentions a technology, framework, or task that could benefit from specialized skills not yet installed, asks 'how do I do X', 'find a skill for X', or starts work in a new technology area. Proactively suggest when the user's task involves tools or frameworks without existing skills.

save-learningSkill

Saves user instructions as persistent learnings for future sessions. Use when the user says 'remember this', 'always do X', 'from now on', 'never do Y', or gives any instruction they want persisted across sessions. Proactively suggest when the user states a preference, convention, or rule they clearly want followed in the future.

scoring-checksSkill

Add a new deterministic scoring check in src/scoring/checks/ that evaluates config quality. Follows the Check[] return pattern, uses point constants from src/scoring/constants.ts, and integrates via filterChecksForTarget() in src/scoring/index.ts. Use when user says 'add scoring check', 'new check', 'modify scoring criteria', or works in src/scoring/checks/. Do NOT use for display changes or refactoring scoring logic.

setup-caliberSkill

Sets up Caliber for automatic AI agent context sync. Installs pre-commit hooks so CLAUDE.md, Cursor rules, and Copilot instructions update automatically on every commit. Use when Caliber hooks are not yet installed or when the user asks about keeping agent configs in sync.

writers-patternSkill

Add a new platform writer module in src/writers/ that generates and writes agent config files for a supported platform. Each writer exports a function that accepts a config interface, creates directories (rules/, skills/, mcp configs), writes files with proper formatting and frontmatter, and returns string[] of written file paths. Use when adding platform support for a new agent, integrating a new code AI tool, or extending caliber to support new targets. Do NOT use for modifying existing writers, refactoring scoring logic, or changing how writers are invoked.