agentsclimarketplace

Mistral local dev loop

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/mistral-pack/skills/mistral-local-dev-loop

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill mistral-local-dev-loop

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Configure Mistral AI local development with hot reload, testing, and mocking.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

7.4 KB, as published. Nobody here has run it

Mistral AI Local Dev Loop

Overview

Set up a fast, reproducible local development workflow for Mistral AI integrations: project scaffold, environment config, hot reload with tsx, unit tests with Vitest mocking, and integration tests against the live API.

Prerequisites

  • Completed mistral-install-auth setup
  • Node.js 18+ with npm/pnpm
  • MISTRAL_API_KEY set in environment

Instructions

Step 1: Project Structure

my-mistral-project/
├── src/
│   ├── mistral/
│   │   ├── client.ts       # Singleton client
│   │   ├── config.ts       # Config with Zod validation
│   │   └── types.ts        # TypeScript types
│   └── index.ts
├── tests/
│   ├── unit/
│   │   └── mistral.test.ts
│   └── integration/
│       └── mistral.integration.test.ts
├── .env.local              # Local secrets (git-ignored)
├── .env.example            # Template for team
├── tsconfig.json
├── vitest.config.ts
└── package.json

Step 2: Package Configuration

package.json

{
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "test": "vitest run",
    "test:watch": "vitest",
    "test:integration": "vitest run tests/integration/",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@mistralai/mistralai": "^1.0.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "dotenv": "^16.0.0",
    "tsx": "^4.0.0",
    "typescript": "^5.0.0",
    "vitest": "^1.0.0"
  }
}

tsconfig.json

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}

Step 3: Environment Setup

# Create environment template
cat > .env.example << 'EOF'
MISTRAL_API_KEY=your-api-key-here
MISTRAL_MODEL=mistral-small-latest
LOG_LEVEL=debug
EOF

cp .env.example .env.local
echo '.env.local' >> .gitignore
echo '.env' >> .gitignore

Step 4: Client Module

// src/mistral/client.ts
import { Mistral } from '@mistralai/mistralai';
import 'dotenv/config';

let instance: Mistral | null = null;

export function getMistralClient(): Mistral {
  if (!instance) {
    const apiKey = process.env.MISTRAL_API_KEY;
    if (!apiKey) throw new Error('MISTRAL_API_KEY not set');
    instance = new Mistral({ apiKey, timeoutMs: 30_000 });
  }
  return instance;
}

export function resetClient(): void {
  instance = null;
}

Step 5: Unit Tests with Mocking

vitest.config.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true,
    environment: 'node',
    include: ['tests/**/*.test.ts'],
    coverage: { provider: 'v8', reporter: ['text', 'json'] },
  },
});

tests/unit/mistral.test.ts

import { describe, it, expect, vi, beforeEach } from 'vitest';

// Mock the entire SDK
vi.mock('@mistralai/mistralai', () => ({
  Mistral: vi.fn().mockImplementation(() => ({
    chat: {
      complete: vi.fn().mockResolvedValue({
        id: 'test-id',
        model: 'mistral-small-latest',
        choices: [{
          index: 0,
          message: { role: 'assistant', content: 'Mocked response' },
          finishReason: 'stop',
        }],
        usage: { promptTokens: 10, completionTokens: 5, totalTokens: 15 },
      }),
      stream: vi.fn().mockImplementation(async function* () {
        yield { data: { choices: [{ delta: { content: 'Streamed ' } }] } };
        yield { data: { choices: [{ delta: { content: 'response' } }] } };
      }),
    },
    embeddings: {
      create: vi.fn().mockResolvedValue({
        data: [{ embedding: new Array(1024).fill(0.1) }],
        usage: { totalTokens: 5 },
      }),
    },
    models: {
      list: vi.fn().mockResolvedValue({ data: [{ id: 'mistral-small-latest' }] }),
    },
  })),
}));

describe('Mistral Client', () => {
  beforeEach(() => { vi.clearAllMocks(); });

  it('should complete chat', async () => {
    const { Mistral } = await import('@mistralai/mistralai');
    const client = new Mistral({ apiKey: 'test' });

    const response = await client.chat.complete({
      model: 'mistral-small-latest',
      messages: [{ role: 'user', content: 'Test' }],
    });

    expect(response.choices?.[0]?.message?.content).toBe('Mocked response');
    expect(response.usage?.totalTokens).toBe(15);
  });

  it('should generate embeddings', async () => {
    const { Mistral } = await import('@mistralai/mistralai');
    const client = new Mistral({ apiKey: 'test' });

    const response = await client.embeddings.create({
      model: 'mistral-embed',
      inputs: ['test text'],
    });

    expect(response.data[0].embedding).toHaveLength(1024);
  });
});

Step 6: Integration Test (Live API)

// tests/integration/mistral.integration.test.ts
import { describe, it, expect } from 'vitest';
import { Mistral } from '@mistralai/mistralai';

const apiKey = process.env.MISTRAL_API_KEY;

describe.skipIf(!apiKey)('Mistral Integration', () => {
  const client = new Mistral({ apiKey: apiKey! });

  it('should list models', async () => {
    const models = await client.models.list();
    expect(models.data?.length).toBeGreaterThan(0);
  }, 10_000);

  it('should complete chat', async () => {
    const response = await client.chat.complete({
      model: 'mistral-small-latest',
      messages: [{ role: 'user', content: 'Reply with "ok"' }],
      maxTokens: 10,
      temperature: 0,
    });
    expect(response.choices?.[0]?.message?.content).toBeTruthy();
  }, 15_000);

  it('should generate embeddings', async () => {
    const response = await client.embeddings.create({
      model: 'mistral-embed',
      inputs: ['test'],
    });
    expect(response.data[0].embedding).toHaveLength(1024);
  }, 10_000);
});

Output

  • Working dev environment with hot reload (tsx watch)
  • Unit tests with full SDK mocking
  • Integration tests against live API (skip when no key)
  • Environment variable management with .env.local

Error Handling

ErrorCauseSolution
Module not foundMissing dependencyRun npm install
Env not loadedMissing .env.localCopy from .env.example
Integration timeoutSlow API responseIncrease test timeout
Mock type errorsSDK interface changedUpdate mock to match current SDK

Resources

Next Steps

See mistral-sdk-patterns for production-ready code patterns.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.