agentsclimarketplace

Anima sdk patterns

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/anima-pack/skills/anima-sdk-patterns

'Apply production-ready patterns for the Anima SDK design-to-code pipeline.From its SKILL.md

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill anima-sdk-patterns

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

What its file declares

Copied from the file, not written here

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

5.0 KB, ~1.1k tokens by cl100k_base, as published. Nobody here has run it

Anima SDK Patterns

Overview

Production patterns for @animaapp/anima-sdk: singleton client, generation caching, output normalization, and configurable settings presets.

Instructions

Step 1: Singleton Client with Configuration

// src/anima/client.ts
import { Anima } from '@animaapp/anima-sdk';

let instance: Anima | null = null;

export function getAnimaClient(): Anima {
  if (!instance) {
    if (!process.env.ANIMA_TOKEN) throw new Error('ANIMA_TOKEN not set');
    instance = new Anima({ auth: { token: process.env.ANIMA_TOKEN } });
  }
  return instance;
}

// Preset configurations for different project needs
export const PRESETS = {
  nextjs: { language: 'typescript' as const, framework: 'react' as const, styling: 'tailwind' as const, uiLibrary: 'shadcn' as const },
  vite: { language: 'typescript' as const, framework: 'react' as const, styling: 'tailwind' as const },
  vue: { language: 'typescript' as const, framework: 'vue' as const, styling: 'tailwind' as const },
  static: { language: 'javascript' as const, framework: 'html' as const, styling: 'css' as const },
} as const;

Step 2: Generation Cache

// src/anima/cache.ts
import crypto from 'crypto';
import fs from 'fs';

interface CacheEntry {
  files: Array<{ fileName: string; content: string }>;
  generatedAt: string;
  settingsHash: string;
}

class AnimaCache {
  private cacheDir: string;

  constructor(cacheDir: string = '.anima-cache') {
    this.cacheDir = cacheDir;
    fs.mkdirSync(cacheDir, { recursive: true });
  }

  private getKey(fileKey: string, nodeId: string, settings: object): string {
    const hash = crypto.createHash('md5')
      .update(`${fileKey}:${nodeId}:${JSON.stringify(settings)}`)
      .digest('hex');
    return hash;
  }

  get(fileKey: string, nodeId: string, settings: object): CacheEntry | null {
    const key = this.getKey(fileKey, nodeId, settings);
    const path = `${this.cacheDir}/${key}.json`;
    if (!fs.existsSync(path)) return null;
    return JSON.parse(fs.readFileSync(path, 'utf8'));
  }

  set(fileKey: string, nodeId: string, settings: object, files: any[]): void {
    const key = this.getKey(fileKey, nodeId, settings);
    const entry: CacheEntry = {
      files,
      generatedAt: new Date().toISOString(),
      settingsHash: key,
    };
    fs.writeFileSync(`${this.cacheDir}/${key}.json`, JSON.stringify(entry));
  }
}

export { AnimaCache };

Step 3: Output Normalizer

// src/anima/normalizer.ts
// Normalize Anima output to match project conventions

interface NormalizationConfig {
  componentNameCase: 'PascalCase' | 'kebab-case';
  addBarrelExport: boolean;
  wrapWithCn: boolean;
  addTypeAnnotations: boolean;
}

function normalizeOutput(
  files: Array<{ fileName: string; content: string }>,
  config: NormalizationConfig,
): Array<{ fileName: string; content: string }> {
  return files.map(file => {
    let content = file.content;

    if (config.wrapWithCn && file.fileName.endsWith('.tsx')) {
      // Add cn() import and wrap className strings
      if (!content.includes("import { cn }")) {
        content = content.replace(
          /^(import .+\n)/m,
          "$1import { cn } from '@/lib/utils';\n"
        );
      }
    }

    if (config.addTypeAnnotations && file.fileName.endsWith('.tsx')) {
      content = content.replace(
        /export default function (\w+)\(\)/g,
        'export default function $1(): React.ReactElement'
      );
    }

    return { fileName: file.fileName, content };
  });
}

export { normalizeOutput, NormalizationConfig };

Step 4: Error Recovery Pattern

// src/anima/retry.ts
async function generateWithRetry(
  anima: Anima,
  params: any,
  maxRetries: number = 3,
): Promise<any> {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await anima.generateCode(params);
    } catch (err: any) {
      if (attempt === maxRetries) throw err;
      const delay = 2000 * Math.pow(2, attempt - 1);
      console.log(`Generation failed, retry ${attempt}/${maxRetries} in ${delay}ms`);
      await new Promise(r => setTimeout(r, delay));
    }
  }
}

Output

  • Singleton client with preset configurations
  • File-based generation cache (avoid redundant API calls)
  • Output normalizer for project convention matching
  • Retry pattern for API resilience

Resources

Next Steps

Apply patterns in anima-core-workflow-a for automated design pipelines.

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Keep looking

Skills are one crate of 326,144. 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.