Agent skill

SDK Design

by gridaco in gridaco/grida

Doctrine for designing and evolving any SDK Grida ships — TypeScript, Rust, or otherwise.

Apache-2.0Auto-check passed

Install SDK Design

skills CLI
$ npx skills add gridaco/grida --skill sdk-design -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install gridaco/grida sdk-design --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/gridaco/grida.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/sdk-design .claude/skills/sdk-design && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
sdk-design
GitHub stars
2.7k
Token cost
~3.9k tokens
SKILL.md length
2,146 words
Files
1
Skills in repo
29
Repo updated
First seen
Licence
Apache-2.0

At a glance

Doctrine for designing and evolving any SDK Grida ships — TypeScript, Rust, or otherwise.

  • Works in 3 steps: Named built-in. Things every consumer of… → Host-fed extras. Transient,… → Escape hatch. The host owns some…
  • Evolving any such surface — @grida/ published packages
  • SKILL.md covers The thesis, Scope: what counts as an SDK, The deciding table and Five disciplines, plus 6 more sections
  • Calls git

What it does

SDK Design is an agent skill from gridaco/grida. Doctrine for designing and evolving any SDK Grida ships — TypeScript, Rust, or otherwise. "SDK" here means a surface that crosses a foreign-or-foreign-treated boundary: published packages, separately-versioned consumers, FFI bindings, public-by-design modules. An SDK's job is to refuse; a strict, honest surface rejects the wrong contents and keeps the package testable in isolation. Default is "core, not customizable"; customization is the exception, defended by a deciding table. Use when authoring or evolving any…

Its SKILL.md is about 3.9k tokens, which your agent loads only when the skill is triggered. It is a single SKILL.md file with no bundled scripts.

It works with Rust and TypeScript. The licence is Apache-2.0.

When your agent uses it

  • Evolving any such surface — @grida/ published packages
  • Engine crates (gridaco/nothing crates/) published
  • Intent/message vocabularies
  • Any contract a second author will compile against

Example prompts

  • “core, not customizable”
  • “/sdk-design”

Requirements

  • Python 3

Workflow steps

3 steps, taken from the first numbered list in SKILL.md.

  1. Named built-in. Things every consumer of this SDK will want
  2. Host-fed extras. Transient, host-computed inputs/outputs passed
  3. Escape hatch. The host owns some boundary (container element,

What it can do on your machine

Read from SKILL.md and the folder at commit 165496f. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • git

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

SDK Design loads about 3.9k tokens when it runs. Until then it costs about 227 tokens; SKILL.md has 2,146 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~227
When it runs · the whole SKILL.md, loaded when a task matches
~3.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from gridaco/grida at commit 165496f, republished under its Apache-2.0 licence (© gridaco). 2,146 words, ~3,926 tokens.

Download SKILL.mdSave it as .claude/skills/sdk-design/SKILL.md (or your agent's skills folder).
name
sdk-design
description
Doctrine for designing and evolving any **SDK** Grida ships — TypeScript, Rust, or otherwise. "SDK" here means a surface that crosses a foreign-or-foreign-treated boundary: published packages, separately-versioned consumers, FFI bindings, public-by-design modules. An SDK's job is to refuse; a strict, honest surface rejects the wrong contents and keeps the package testable in isolation. Default is "core, not customizable"; customization is the exception, defended by a deciding table. Use when authoring or evolving any such surface — `@grida/*` published packages, engine crates (gridaco/nothing `crates/*`) published or FFI-exported, intent/message vocabularies, any contract a second author will compile against. Internal-only helper packages are welcome to follow, not forced. Companion skill for two-sided contract work: $sdk-seam. Critique partners: $pedantic, $etiology. Related: $naming.

sdk-design

This is not a style guide. Style and language-specific code shape are downstream (see $code-ts, $code-react for the TS sides). This is about what an SDK refuses to do — the discipline that keeps a package small, legible, and replaceable, regardless of language.

The thesis

An SDK lives or dies by what it refuses to expose. Default is core; customization is the exception. Every public knob is a contract you cannot retract without a semver break and a coordinated migration across every downstream call site.

A library with too few knobs is easy to grow. A library with too many is impossible to retire. The asymmetry is brutal — design from it.

This doctrine applies whether the package ships as an npm scope, a crate, a header-only library, a WASM module, a Python wheel, a hosted service with an SDK, or a pair of microservices defining a shared message vocabulary. The mechanics of "publish" differ; the discipline of "refuse the wrong contents" does not.

Scope: what counts as an SDK

This is the gate. The skill says "SDK," not "package," because the two are different. An SDK is:

  • A surface that crosses a foreign-or-foreign-treated boundary. Published to a registry (npm, crates.io, PyPI); linked by a separately-versioned consumer (a desktop binary against a crate, a generated WASM/FFI binding); or authored as if a foreign consumer existed even if one doesn't yet (any package whose README documents it as a public surface, anything tagged for publication, anything in a *-hosted suffix family).
  • Versioned independently of its callers, even if today every caller lives in the same monorepo and ships on the same commit. The intent to be replaceable is what counts.

What this excludes — where the doctrine is welcome but not load-bearing:

  • A package with exactly one internal caller, shipping on the same commit, where if the caller's needs changed the package would be rewritten freely. That's not an SDK; that's a refactored module that happens to live in packages/. Adopt the parts of this skill that pay; skip the rest without apology.
  • One-off helper crates pulled in by a single binary in the engine repo's crates/. Same logic.

Don't extend the doctrine to internal-only utilities just because the file structure looks like an SDK. The discipline costs something — designed views over raw streams, anti-goals as perimeters, promotion-on-dogfooding — and that cost is paid by the foreign-callers it protects. If there are no foreign callers (now or planned), the strictness doesn't pay back.

sdk-seam triggers on the same gate from the other angle: any boundary that meets the SDK bar above, where the same author writes both sides. Include FFI bindings to internal crates here — binding regeneration cost makes the boundary foreign-treated even when the crate is same-repo.

The deciding table

When a new decision lands — "should this be a provider hook? a built-in toggle? a sibling package? a public type or an internal seam?" — walk these in order. First match wins.

QuestionIf yes →Why
Would customization let a consumer break the invariant this package exists to protect?Core, non-customizableSovereignty
Is this genuinely a host-owned concern (I/O, locale, surface, credentials, clock)?Provider at constructionHost knows what you can't
Is this per-variant edit/parse/render semantics, complex but bounded by a spec or schema?Internal seam, no public APICode organization, not API
Does the candidate have ≥2 internal consumers AND can be tested without mounting the SDK?Separate layer (own module/package)Earned its separation
Have ≥2 internal consumers shaped the contract already?Eligible for publicPublic only after dogfooding
OtherwiseCore, internally modularDefault-in, not default-out

The third rung — "complex but internal" — is where most "extension-point" mistakes get caught. A real spec or schema (SVG element table, MIDI event types, OpenType tables, USB device classes) is the registry; the SDK implements against it. Don't re-invite the spec to be re-implemented at runtime by consumers.

Five disciplines

D1. Subscribe to outcomes, not events

The public observation surface is designed, not raw. It exposes purpose-built views — selection, mode, dirty/version, computed property — each handling multi-target, capability variance, and bookkeeping internally. Consumers never receive raw input events, reducer actions, or internal state frames.

If a needed view doesn't exist, that's an API gap to close, not an internals hatch to open. Exposing the internal stream because "the consumer can compose it themselves" is how you wake up six months later unable to refactor the core.

The same rule applies to the other direction: emit named outcomes (intents, commands, requests), not "the user moved their pointer." If your outputs carry phase markers (preview / commit, begin / progress / end), the consumer wraps history/transactions without guessing internal state.

D2. Pure-logic core, thin adapter shell

One-directional dependency, layered:

text
primitives / math  ←  logic core  ←  adapter shell  ←  host

The math/logic core has no I/O, no DOM, no canvas, no UI runtime, no global clock. Plain function over plain inputs returns plain output. Runnable under the language's basic test runner with zero mocks. A Rust crate's core compiles under no_std where feasible; a TS package's core has no window / document import; a Python package's core does not touch the filesystem.

The shell is a thin wire: lifecycle, draw loop, host wiring. Its own logic should be trivial enough to verify by inspection, because it's the part you can't test headlessly.

Why this matters: when a shell grows logic, that logic ships unguarded. Common failure: the shell holds a switch (render, dispatch, route) and a new core variant is added without updating the switch — the core's tests pass; the shell silently drops the variant; downstreams hit it in production. Push logic into the core. Tests follow.

D3. Outputs that satisfy different constraints stay separate

Don't conflate outputs that exist to satisfy different constraints. Every paired-but-asymmetric surface — render vs. hit-test, read vs. write, declared vs. computed, preview vs. commit, encode vs. decode — earns its asymmetry from a real disagreement in requirements. When you find yourself unifying them "for elegance," you're about to break one.

Concrete pattern: a UI surface that draws and hit-tests as two separate outputs. Drawing optimizes for legibility at any zoom; hit-testing optimizes for Fitts'-reach (fat targets, virtual regions that extend past the visible shape). Collapsing them — sizing the visual to match the hit AABB, or shrinking the hit region to match the visual — breaks one of the two; each side has to compromise to satisfy the other.

The generalization: tests assert each side separately, and — where they intentionally differ — assert the direction of difference (e.g., the hit region strictly contains the rendered bbox).

D4. Anti-goals as defensive perimeters

Every published SDK ships an explicit Anti-goals section in its README. It is not aspirational; it is the perimeter that lets the package stay small. Examples that have already prevented bloat across various Grida packages:

  • "Not a host of plugins." — kills every PR that wants to add a widget registry.
  • "Not undo-aware." — host owns history; SDK emits phase markers.
  • "Not a private IR." — file bytes are the source of truth; the parsed view is rebuilt on load.
  • "Not a renderer." — the surface backend is intentionally minimal.

When a feature request arrives, the first question is which anti-goal it would violate. If it violates one, the right answer is "this is the wrong tool." If it threatens one without crossing it, write the anti-goal sharper.

Adding an anti-goal is the cheapest design work an SDK author does.

D5. Names commit you

See $naming for the full treatment. The SDK-specific corollary:

  • Public identifier costs ≫ directory cost. Directory rename is git mv; published-name rename is a coordinated downstream migration. Invest heavily before a name escapes its file.
  • Terseness is a uniqueness claim. A bare Surface, Encoder, Intent, Paint in a package asserts "nothing else competes for this slot here." If a peer could be added later, qualify now.
  • Suffix siblings over nested folders. Keep the parent's scope tight; new subdirectories quietly widen it.
  • Avoid leaking consumer concerns into the producer's names. A type field documented as "used by <consumer> for <feature>" is leaking the consumer's problem into the contract. The field name should justify itself in producer-only terms.
Show full SKILL.md (814 more words)Show less

The promotion contract

Internal seams stay internal until ≥2 internal consumers have shaped the contract. This is not a bureaucratic gate — it's the only way to avoid public APIs designed against one use case.

Promoting too early produces:

  • The shape ossifies around the first caller's quirks.
  • The second caller can't use it and writes a parallel API.
  • Now you have two surfaces that drift, and you can't kill either.

Promoting too late costs little. Internal callers reach into internals; you tighten when the second consumer arrives. Default direction of pressure is inward, not outward.

When you do promote, the contract test is: "could a stranger build the next caller against this API alone, without reading the SDK's source?" If no, it's not promoted; it's exposed.

For very new packages without a second internal consumer yet, the honest move is to mark the surface as unstable in its README ("v0.x.y — no compatibility guarantees") and let the second consumer's needs shape the contract before locking it.

Three extension paths, in order of preference

For any extensibility request, walk this ladder. Reach down only when the rung above doesn't fit.

  1. Named built-in. Things every consumer of this SDK will want live inside the package as first-class features with their own toggles. New canonical needs land here; open a PR against the SDK.
  2. Host-fed extras. Transient, host-computed inputs/outputs passed through a designed slot (per-frame draw, per-event hook, per-message middleware). Best for things the host already computes and just wants threaded through.
  3. Escape hatch. The host owns some boundary (container element, raw socket, file descriptor) and can splice its own logic in around the SDK. Deliberate escape hatch — reach for it only when (1) and (2) don't fit, and prefer pushing canonical needs into (1) over keeping them at (3).

What's absent from this ladder is a generic plugin / widget / middleware registry. That's the point. A registry is the path that turns small packages into god-classes — the lesson is repeated across the industry (jQuery plugins, Babel plugins of the early era, Webpack loaders) and locally (the Grida main editor's 6,800-line god-class grew partly from this).

Tests are spec

For an SDK, tests carry double weight:

  • The SDK is configurable — hosts pass styles, providers, callbacks. Small refactors land all the time.
  • There's no visible behavior to inspect from outside the package; one regression can ship silently across every downstream.

Discipline: every default behavior is locked by a test whose description names the behavior in plain language. The test name is the spec. The body proves the code obeys it. A comment above explains why — the design intent that the code itself can't carry.

Where applicable, embed scenario names verbatim in test text so "did we drop a rule?" is grep-able across implementations and ports. This matters most for SDKs that ship parallel implementations (TS + Rust + WASM bindings) of the same contract.

A PR that touches a public behavior without touching the matching test is a smell; a PR that flips a test's assertion without changing the test name is a near-certain regression.

Critique partners

  • $pedantic — before drafting a public API, run the design through pedantic. The probes for unfalsifiability, vague quantifiers, and assumed-bedrock catch the "this feels finished but isn't grounded" failure mode that produces APIs you can't retract.
  • $etiology — before patching across an SDK boundary, walk the diagnostic ladder. Most "quick fixes" at a boundary are API-contract bugs (rung 3), not call-site bugs (rung 2). Treating one as the other is how contracts rot.

Cross-package work — the seam

Work that touches more than one SDK — your producer and its consumer, two sibling packages, a published surface and its tests, two crates on either side of an FFI boundary — has a specific failure mode of its own: when you control both sides, you shotgun changes across them in a single edit and the contract silently degrades. The joint between the two sides is a seam; keeping seams clean has its own discipline. See $sdk-seam.

The short version

  • Default is core, not customizable. Customization is the exception, defended by the deciding table.
  • Subscribe to outcomes, not events. Designed views, not raw streams. Missing view = API gap, not internals hatch.
  • Pure core, thin shell. The logic is testable headlessly; the shell is boring on purpose. Logic in the shell is logic you can't defend.
  • Asymmetric outputs stay separate. Render vs. hit, read vs. write, declared vs. computed — different constraints earn different surfaces.
  • Anti-goals are defensive perimeters. Every SDK ships them. Sharpen them before adding features.
  • Promote on dogfooding. ≥2 internal consumers shape the contract before it escapes the package.
  • Three extension paths, in order: built-in → host-fed extra → escape hatch. Generic plugin registries are the road to god-classes.
  • Tests are spec. Every default behavior pinned by a test whose name is the rule and whose comment is the why.
  • The seam between two SDKs has its own discipline — see $sdk-seam.

© gridaco, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

Just SKILL.md in .agents/skills/sdk-design of gridaco/grida.

Open the folder on GitHubat commit 165496f

Compare with similar skills

SDK Design next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

SDK Design compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
SDK Design this skillgridaco/grida2.7k—~3.9kAutomated safety check: PassApache-2.0
Firecrawl Page Scrape Integrationfirecrawl/firecrawl190k1 repos~944Automated safety check: PassISC
Pnpm Engineteambit/bit18k—~1.9kAutomated safety check: PassCustom licence
Build Teaql Appteaql/teaql-agent-kit2.8k—~4.6kAutomated safety check: PassMIT
Testing Changespnpm/pnpm37k—~1.1kAutomated safety check: PassMIT
jscpd Code Migration Trackerkucherenko/jscpd6.4k—~5kAutomated safety check: PassMIT

Similar skills

  • Adds Firecrawl's /scrape endpoint to application code to pull markdown, HTML, links, screenshots or structured data from a single known URL.

    190k GitHub starsUsed in 1 repo~944 tokens
    Data & AnalyticsAuto-check passed
  • Pnpm Engine

    teambit/bit

    Work on the pnpm Rust engine (@pnpm/napi, the pacquet crates) that bit install runs through.

    18k GitHub stars~1.9k tokensUpdated today
    DevelopmentAuto-check passed
  • Build Teaql App

    teaql/teaql-agent-kit

    Build or change a TeaQL application in Java, Rust, Go, Swift, Python, C/.NET, or TypeScript, including Kotlin/JVM applications that consume Java-generated libraries.

    2.8k GitHub stars~4.6k tokensUpdated 11 days ago
    MobileAuto-check passed
  • Run the tests that cover a change in the pnpm repository, in the Rust workspace (pnpm/, pnpr/) or the TypeScript CLI (pnpm11/), and recognize the cases where a scoped run passes without testing…

    37k GitHub stars~1.1k tokensUpdated today
    DevelopmentAuto-check passed
  • Measures a code port between languages or frameworks with jscpd's function-level comparison, porting tests before code and tracking what is left unmatched.

    6.4k GitHub stars~5k tokensUpdated today
    DevelopmentAuto-check passed
  • Rgsm Gui Acceptance

    mcthesw/game-save-manager

    Prepare change-specific RGSM GUI acceptance environments and short manual scenarios when the user wants to try a change locally.

    1.1k GitHub stars~1.1k tokensUpdated today
    Auto-check passed

More from gridaco/grida

All 29 skills in this repo
  • Desktop

    gridaco/grida

    Grida Desktop Electron shell and release-impact work: BrowserWindow, preload, window.grida, menus, protocol/deep links, file associations, Forge, path-scoped bridge security, Electron-only UI bugs…

    2.7k GitHub stars~3.2k tokensUpdated yesterday
    Auto-check: notes
  • Io Figma

    gridaco/grida

    Guides work on the Figma I/O package (@grida/io-figma, packages/grida-canvas-io-figma/).

    2.7k GitHub stars~2.2k tokensUpdated yesterday
    Auto-check: notes
  • Opt Library

    gridaco/grida

    Set up, download, verify, and seed the optional Grida Library developer corpus into local Supabase.

    2.7k GitHub stars~1.4k tokensUpdated yesterday
    Auto-check passed
  • Vision

    gridaco/grida

    Query images with a local Ollama vision model without loading the image into the main agent context.

    2.7k GitHub stars~1.5k tokensUpdated yesterday
    Auto-check passed
  • AI Models

    gridaco/grida

    Research, compare, and update shared AI model JSON for TypeScript, web, and Rust consumers.

    2.7k GitHub stars~5.7k tokensUpdated yesterday
    Auto-check passed
  • Agent System

    gridaco/grida

    Grida AI agent system work: @grida/daemon (DaemonServer, loopback HTTP perimeter, files/workspaces, secrets store, daemon discovery) and @grida/agent (the agent tenant: sessions, providers/BYOK…

    2.7k GitHub stars~3.4k tokensUpdated yesterday
    Auto-check passed

Works with

Questions about SDK Design

What does SDK Design do?

Doctrine for designing and evolving any SDK Grida ships — TypeScript, Rust, or otherwise. SDK Design is an agent skill from gridaco/grida. Doctrine for designing and evolving any SDK Grida ships — TypeScript, Rust, or otherwise.

When should I use SDK Design?

SDK Design fits situations like: evolving any such surface — @grida/ published packages; engine crates (gridaco/nothing crates/) published; intent/message vocabularies; any contract a second author will compile against.

How do I install SDK Design in Claude Code?

Run `npx skills add gridaco/grida --skill sdk-design -a claude-code`. Or copy the skill folder (.agents/skills/sdk-design in gridaco/grida) into .claude/skills/sdk-design in your project. Claude Code loads it when a task matches its description.

How do I install SDK Design in Codex?

Run `npx skills add gridaco/grida --skill sdk-design -a codex`. Or copy the skill folder (.agents/skills/sdk-design in gridaco/grida) into .agents/skills/sdk-design in your project. Codex loads it when a task matches its description.

Can I use SDK Design in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add gridaco/grida --skill sdk-design -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/sdk-design, .gemini/skills/sdk-design, .github/skills/sdk-design and .opencode/skills/sdk-design in your project.

What does SDK Design need to run?

Going by SKILL.md and its folder, SDK Design needs the command-line tools its instructions call (git). Our summary lists: Python 3.

Does SDK Design access the network?

SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is SDK Design safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does SDK Design use?

SDK Design is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does SDK Design use?

About 3.9k tokens (SKILL.md is roughly 16k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to SDK Design?

Skills that share tags, products or a category with SDK Design: Firecrawl Page Scrape Integration (firecrawl/firecrawl, 190k stars), Pnpm Engine (teambit/bit, 18k stars), Build Teaql App (teaql/teaql-agent-kit, 2.8k stars) and Testing Changes (pnpm/pnpm, 37k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains SDK Design?

gridaco (a GitHub organization) maintains it in gridaco/grida, which has 2,659 GitHub stars. The repository holds 29 skills in this directory. The repository was last updated on October 7, 2026.

Source: gridaco/grida on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.