agentleFS
ankit4479ankit4479/outbound-builder-plugin/skills/scaffold-client/SKILL.md

scaffold-client

Skill0 starsChanged 5 months agoMITView on GitHub

Add a new API client with built-in rate limiting, retry, and timeout. Generates from the base client template and adds config entries. Use when integrating a new external API.

  • Reads credentials

The file

We split this skill into the rules it gives an agent

5 rules. About 0 are common, 1 is distinctive, and 4 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 4

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    If $ARGUMENTS provides an API name, use it as a starting point.

    line 16
  2. Very specific:

    Read ${CLAUDE_SKILL_DIR}/../../imp_doc/api-clients/patterns.md for client architecture patterns.

    line 29
  3. Very specific:

    Read .outbound-builder-plugin-memory.json (create with {"version":1,"entries":[],"proven_patterns":[]} if missing)

    line 164
  4. Distinctive:

    Always include BOTH token bucket (per-second smoothing) AND sliding window (per-minute ceiling)

    line 175
  5. Very specific:

    Every HTTP call must be preceded by rateLimiter.acquire("<api-name>")

    line 178

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Scaffold a New API Client

You create a production-ready API client with rate limiting, retry logic, and timeout handling, plus register it in pipeline config.

Process

Step 1: Gather API Details

If $ARGUMENTS provides an API name, use it as a starting point. Ask the user:

  1. API name (e.g., "twitter", "hubspot", "stripe") — used for file name and rate limiter key
  2. Base URL (e.g., https://api.twitter.com/2)
  3. Auth type:
    • API key in header (e.g., X-Api-Key: <key>)
    • Bearer token (e.g., Authorization: Bearer <token>)
    • Query parameter (e.g., ?api_key=<key>)
  4. Rate limits from the provider's docs:
    • Requests per second (or per 10 seconds)
    • Requests per minute (or per hour — convert to per minute)
  5. Typical response time (for timeout configuration)

Read ${CLAUDE_SKILL_DIR}/../../imp_doc/api-clients/patterns.md for client architecture patterns.

Step 2: Generate Client File

Copy from ${CLAUDE_SKILL_DIR}/../../templates/base-api-client.ts and customize.

Create src/clients/<api-name>.ts with:

  • Class name: <ApiName>Client (PascalCase)
  • Base URL from Step 1
  • Auth header/param configured from environment variable
  • rateLimiter.acquire("<api-name>") before every HTTP call
  • Retry logic: 3 attempts, linear backoff (1s, 2s, 3s) for 429/5xx
  • Non-transient errors (400, 401, 403, 404) fail immediately
  • Timeout via AbortSignal.timeout()
  • 2-3 example methods matching common API patterns:
export class <ApiName>Client {
  private baseUrl: string;
  private apiKey: string;

  constructor() {
    this.baseUrl = "<base-url>";
    this.apiKey = process.env.<API_NAME>_API_KEY!;
  }

  async getResource(id: string): Promise<ResourceResponse> {
    await rateLimiter.acquire("<api-name>");
    // fetch with retry, timeout, auth
  }

  async listResources(params: ListParams): Promise<ListResponse> {
    await rateLimiter.acquire("<api-name>");
    // fetch with retry, timeout, auth
  }
}

Step 3: Add Rate Limit Config

Edit src/lib/pipeline-config.ts, add to RATE_LIMITS:

<apiName>: {
  tokensPerSecond: <N>,        // Provider limit: <actual>/s — using 85% safety margin
  maxBurst: <burst>,           // Allow short bursts above sustained rate
  windowMaxRequests: <N>,      // Provider limit: <actual>/min — using 85%
  windowSeconds: 60,
},

Safety margin rule: Set token bucket and window limits to ~85% of the provider's documented limits. This accounts for clock drift, concurrent consumers, and measurement differences.

Step 4: Add Timeout Config

Edit src/lib/pipeline-config.ts, add to FETCH_TIMEOUTS:

<apiName>: <milliseconds>,  // Typical response: <N>ms, timeout at 3x

Set timeout to ~3x the typical response time. Minimum 5000ms, maximum 30000ms.

Step 5: Add Environment Variable

Append to .env.example:

# <API Display Name>
<API_NAME>_API_KEY=<YOUR_<API_NAME>_API_KEY>

Step 6: Generate Test File

Create tests/unit/<api-name>-client.test.ts:

import { describe, it, expect, vi } from "vitest";

describe("<ApiName>Client", () => {
  it("should include auth header in requests", async () => {
    // Verify auth is attached to every request
  });

  it("should retry on 429 and 5xx errors", async () => {
    // Mock fetch to return 429, then 200 — verify retry
  });

  it("should not retry on 4xx client errors", async () => {
    // Mock fetch to return 400 — verify immediate failure
  });

  it("should respect timeout", async () => {
    // Mock fetch to hang — verify abort after timeout
  });
});

Step 7: Report

API client "<api-name>" scaffolded:

  src/clients/<api-name>.ts                — Client class with rate limiting + retry
  tests/unit/<api-name>-client.test.ts     — Unit tests

Config updates:
  src/lib/pipeline-config.ts               — Added RATE_LIMITS.<apiName>
  src/lib/pipeline-config.ts               — Added FETCH_TIMEOUTS.<apiName>
  .env.example                             — Added <API_NAME>_API_KEY

Rate limits configured:
  Token bucket: <N>/s (burst <M>) — 85% of provider's <actual>/s
  Window: <N>/min — 85% of provider's <actual>/min
  Timeout: <T>ms

Next steps:
  1. Add your API key to .env
  2. Implement the specific methods you need in the client
  3. Import the client in your task file: import { <ApiName>Client } from "../src/clients/<api-name>"
  4. Run tests: npm test -- tests/unit/<api-name>-client.test.ts

Step 8: Record Learnings

Reflect on the scaffolding process. If you encountered any of the following, record them to .outbound-builder-plugin-memory.json:

  • Errors or workarounds (e.g., dependency conflicts, config issues)
  • Non-obvious behaviors (e.g., a template needed unexpected modifications)
  • API quirks or platform-specific gotchas
  • Schema decisions that differed from the template defaults

For each learning:

  1. Determine the category: error-fix, api-quirk, schema-pattern, config-gotcha, build-pattern, or domain-insight
  2. Generate a deterministic ID: mem- + first 8 chars of MD5 hash of category:title (use python3)
  3. Read .outbound-builder-plugin-memory.json (create with {"version":1,"entries":[],"proven_patterns":[]} if missing)
  4. Dedup by ID -- if the same ID exists, increment success_count and update date
  5. Otherwise append a new entry with success_count: 1, source: "scaffold", and context.stage: "scaffold-client"
  6. Rebuild proven_patterns from entries with success_count >= 2
  7. Write the file

Skip this step if nothing noteworthy happened during scaffolding.

Rules

  • Rate limits must use 85% of provider limits (safety margin for clock drift and concurrent consumers)
  • Always include BOTH token bucket (per-second smoothing) AND sliding window (per-minute ceiling)
  • Auth credentials must come from environment variables — never hardcoded
  • Retry only on transient errors (429, 5xx) — fail immediately on 4xx client errors
  • Every HTTP call must be preceded by rateLimiter.acquire("<api-name>")
  • Rate limiting is centralized in src/lib/pipeline-config.ts — never configure limits in client files
  • Reference ${CLAUDE_SKILL_DIR}/../../imp_doc/api-clients/patterns.md for client patterns

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.