Skip to main content
ClaudeWave
Skill29.5k repo starsupdated 2d ago

add-column-type

Add a new table column type to Sim — registry entry, icon, storage shape, coercion, and the behavioral hooks the grid and API read. Use when adding a value kind under `apps/sim/lib/table/column-types/`.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/simstudioai/sim /tmp/add-column-type && cp -r /tmp/add-column-type/.agents/skills/add-column-type ~/.claude/skills/add-column-type
Then start a new Claude Code session; the skill loads automatically.

SKILL.md

# Adding a Table Column Type

A column type is **one file** in `apps/sim/lib/table/column-types/` plus a registry entry. Everything that varies per type — label, icon, storage cast, coercion, validation, conversion compatibility, formatting, editor, filter operators — lives on that one object, so no consumer needs editing.

This was not always true: adding `currency` originally took ~40 edits across 32 `switch` arms and 26 UI branches, each of which failed **silently** when missed. The registry exists to make that impossible, so the rule is absolute: **if you find yourself adding a `case 'yourtype':` anywhere outside `column-types/`, the registry is missing a field. Add the field instead.**

## Hard Rule: the compiler tells you what to do

Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list:

```bash
cd apps/sim && bun run type-check
```

You will get two errors, naming `column-types/registry.ts` and `column-types/registry.server.ts`. Register in both.

If your type owns metadata, adding its key to `TYPE_SPECIFIC_COLUMN_KEYS` produces two more legitimate errors — `FOREIGN_METADATA_VERB` in `validation.ts` (a `Record` over those keys) and the key's absence from `ColumnDefinition`. Those are the gate working, not sites to "fix".

Any error beyond those four is a site reading a hardcoded type list that should read the registry — fix that site, don't work around it.

## Directory Structure

```
apps/sim/lib/table/column-types/
├── types.ts             # ColumnTypeDefinition — the contract you implement
├── types.server.ts      # ColumnTypeServerDefinition — cell migrations only
├── registry.ts          # Record<ColumnType, …>  ← client-safe, the gate
├── registry.server.ts   # Record<ColumnType, …>  ← adds migrations (drizzle)
├── index.ts             # barrel + accessors (columnTypeOf, columnTypeById, …)
└── {type}.ts            # one file per type — what you write
```

## Step 1: Pick the storage shape

Decide what a cell literally holds in `user_table_rows.data` (JSONB). This drives almost everything else:

| Storage | `jsonbCast` | Notes |
|---------|-------------|-------|
| number  | `'numeric'` | Filters/sorts compare numerically. `currency` does this. |
| ISO string | `'timestamptz'` | `date` does this. |
| string / bool / object | `null` | Text comparison is correct. |

**Prefer an existing primitive over a new shape.** `currency` stores a plain number and keeps its ISO code as *display metadata* — which is why filtering, sorting, uniqueness, and CSV export all reuse the numeric paths untouched, and why re-denominating a column rewrites zero rows.

## Step 2: Add the icon

Create `packages/emcn/src/icons/type-{name}.tsx`, copying the geometry conventions of its siblings exactly:

```tsx
import type { SVGProps } from 'react'

/**
 * Type {name} icon component - {what the glyph is} for {name} columns
 * @param props - SVG properties including className, fill, etc.
 */
export function Type{Pascal}(props: SVGProps<SVGSVGElement>) {
  return (
    <svg
      width='24'
      height='24'
      viewBox='-1.75 -1.5 24 24'
      fill='none'
      stroke='currentColor'
      strokeWidth='1.55'
      strokeLinecap='round'
      strokeLinejoin='round'
      xmlns='http://www.w3.org/2000/svg'
      aria-hidden='true'
      {...props}
    >
      <path d='…' />
    </svg>
  )
}
```

- `viewBox='-1.75 -1.5 24 24'` is the **`type-*` family** value, not the set-wide default. Match the family.
- Center the glyph on the viewBox's optical center (**y = 10.5**, **x = 10.25**) — every sibling does, and a few tenths off is visible at `size-[14px]`.
- Export alphabetically **by component name** in `packages/emcn/src/icons/index.ts`.

## Step 3: Write the type file

`apps/sim/lib/table/column-types/{name}.ts`. Copy the closest existing type and change what differs. Every field is required by the interface, so the compiler enumerates them for you — read the TSDoc in `types.ts` rather than guessing.

The three that are easy to get wrong:

- **`coerce`** is the *single* write-path implementation. The server runs it before persisting **and** the grid runs it to fill the optimistic cache. Accept every shape the value legitimately arrives in (paste, CSV, tool write), because rejecting means the cell is nulled.
- **`isCompatibleWith`** gates type conversion and must read the value **exactly as `coerce` will**, or a conversion will pass its check and then null the cell.
- **`ownedMetadata`** lists the `ColumnDefinition` keys your type owns. Anything you add must also be added to `TYPE_SPECIFIC_COLUMN_KEYS` in `types.ts` and given a phrase in `FOREIGN_METADATA_VERB` in `validation.ts` — both are `Record`-typed, so the compiler will tell you.

## Step 4: Register

Add the entry to `COLUMN_TYPE_REGISTRY` in `registry.ts` **and** `COLUMN_TYPE_SERVER_REGISTRY` in `registry.server.ts`.

`COLUMN_TYPES` is declared in `types.ts` (not derived from the registry — the registry is annotated `Record<ColumnType, …>` against it, which is the gate). `constants.ts` re-exports it, so `columnTypeSchema = z.enum(COLUMN_TYPES)` picks your type up with no edit. **Type-specific metadata does not** — see the next step.

## Step 5: Migrations (only if the stored bytes change)

If converting an existing column **to** your type must rewrite cells, add `migrateCellsTo` in `registry.server.ts`; if converting **away** must rewrite them, add `migrateCellsFrom`.

This is load-bearing, not cosmetic: filters and sorts apply `jsonbCast` to whatever is stored, so leaving a non-castable string behind makes **every query on that column fail** — not merely render oddly.

Prefer set-based SQL. When the transform genuinely needs JS (`currency`'s separator disambiguation), compute the values during the compatibility scan and pass them through `resolved`, then apply them in one batched statement.

## Naming Convention

- Type id: lowercase, singular — `currency`, not `Currency` or `currencies`
- F
add-blockSkill

Create or update a Sim integration block with correct subBlocks, conditions, dependsOn, modes, canonicalParamId usage, outputs, and tool wiring. Use when working on `apps/sim/blocks/blocks/{service}.ts` or aligning a block with its tools.

add-connectorSkill

Add or update a Sim knowledge base connector for syncing documents from an external source, including auth mode, config fields, pagination, document mapping, tags, and registry wiring. Use when working in `apps/sim/connectors/{service}/` or adding a new external document source.

add-enrichmentSkill

Add a code-defined table enrichment (registry entry) under `apps/sim/enrichments/` backed by an ordered provider cascade, ensuring every provider tool it calls has hosted-key support. Use when adding a per-row table enrichment that fills cells via existing Sim tools.

add-hosted-keySkill

Add hosted API key support to a tool so Sim provides the key (metered and billed to the workspace) when a user has not brought their own. Use when adding a `hosting` config to a tool under `apps/sim/tools/{service}/`.

add-integrationSkill

Add a complete Sim integration from API docs, covering tools, block, icon, optional triggers, registrations, resolved-secret/model-input safety, and integration conventions. Use when introducing a new service under `apps/sim/tools`, `apps/sim/blocks`, and `apps/sim/triggers`.

add-modelSkill

Add a new LLM model to apps/sim/providers/models.ts with specs verified against the provider's live API docs (no hallucination)

add-toolsSkill

Create tool configurations for a Sim integration by reading API docs

add-triggerSkill

Create webhook or polling triggers for a Sim integration