diff --git a/.agents/skills/add-block/SKILL.md b/.agents/skills/add-block/SKILL.md index cf4f36e6b7f..9563e2785fb 100644 --- a/.agents/skills/add-block/SKILL.md +++ b/.agents/skills/add-block/SKILL.md @@ -627,19 +627,96 @@ export const ServiceV2Block: BlockConfig = { } ``` +## Block Metadata (BlockMeta) + +Every integration block **must** export a `{Service}BlockMeta` object at the bottom of the block file. This metadata drives the integration catalog, tag filters, and workflow template suggestions shown to users. + +### Structure + +```typescript +import type { BlockConfig, BlockMeta } from '@/blocks/types' + +// ... block definition above ... + +export const {Service}BlockMeta = { + tags: ['messaging', 'automation'], // Same tags as the block's tags field + templates: [ // Optional but strongly encouraged + { + icon: {Service}Icon, + title: '{Service} use-case title', + prompt: 'Build a workflow that ...', + modules: ['agent', 'workflows'], // Modules the template uses + category: 'productivity', // Template category + tags: ['automation'], // Template-level tags + alsoIntegrations: ['slack'], // Other blocks referenced in the prompt (optional) + }, + ], + skills: [ // Optional but strongly encouraged + { + name: 'summarize-thread', // kebab-case, becomes the created skill's name + description: 'One line: what it does and when to use it.', + content: + '# Summarize Thread\n\n...\n\n## Steps\n1. ...\n\n## Output\n...', // markdown + }, + ], +} as const satisfies BlockMeta +``` + +### Rules + +- **Import `BlockMeta`** from `@/blocks/types` alongside `BlockConfig` +- **`tags`** must match the `tags` array on the block config exactly +- **Templates are optional** but should be added for any integration that has a recognizable use case — aim for 2–4 templates per block +- **Template `prompt`** should start with "Build a workflow that..." or "Create a workflow that..." and be concrete enough to generate a real workflow in Mothership +- **Template `modules`** lists the Sim modules the template relies on: `'knowledge-base' | 'tables' | 'files' | 'workflows' | 'scheduled' | 'agent'` +- **Template `category`** is one of: `'popular' | 'sales' | 'support' | 'engineering' | 'marketing' | 'productivity' | 'operations'` +- **`alsoIntegrations`** names other block types (e.g. `'slack'`, `'linear'`) referenced in the template prompt — helps the catalog surface this template when those blocks are selected +- Place the export **after** the main `{Service}Block` export, at the very bottom of the file + +#### `skills` — curated, ready-to-add agent skills + +`skills` is an optional array of `SuggestedSkill` (`{ name, description, content }`) shown on the integration's detail page; users click **Add** to create the skill in their workspace. Aim for 3–5 skills for mainstream services, 2–3 for niche/low-level ones. + +- **`name`** — kebab-case, lowercase letters/numbers/hyphens, ≤ 64 chars, unique within the integration, verb-led (e.g. `summarize-thread`). +- **`description`** — one line, ≤ 1024 chars: what it does and when to use it. +- **`content`** — markdown instructions for the agent (literal `\n` for newlines): a `# Title`, then `## Steps` and an output/guidance section. Keep ~600–2000 chars. +- **Ground every skill in operations the block actually exposes.** Cross-check each skill's steps against the block's `tools.access` list — never describe an action the integration cannot perform (e.g. "receive messages" when the block only sends). +- **Skills MUST be derived from real, popular use cases found online — never invented.** Before adding a skill, web-search the service's documented use cases (vendor use-case/solutions pages, official docs describing the workflow, reputable "top automations for X" articles). If you cannot source a use case as something people genuinely do with the service, do not add it. Do not hallucinate skills. + +### Register in the blocksMeta object + +After adding `{Service}BlockMeta` to the block file, register it in `apps/sim/blocks/registry.ts`: + +```typescript +// Add import (alongside the block import, alphabetically) +import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/service' + +// Add to blocksMeta object (alphabetically) +export const blocksMeta = { + // ... existing entries ... + service: ServiceBlockMeta, +} +``` + ## Registering Blocks After creating the block, remind the user to: -1. Import in `apps/sim/blocks/registry.ts` +1. Import `{Service}Block` and `{Service}BlockMeta` in `apps/sim/blocks/registry.ts` 2. Add to the `registry` object (alphabetically): +3. Add to the `blocksMeta` object (alphabetically): ```typescript -import { ServiceBlock } from '@/blocks/blocks/service' +import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/service' export const registry: Record = { // ... existing blocks ... service: ServiceBlock, } + +export const blocksMeta = { + // ... existing entries ... + service: ServiceBlockMeta, +} ``` ## Complete Example @@ -827,7 +904,11 @@ All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MU - [ ] Tools.access lists all tool IDs (snake_case) - [ ] Tools.config.tool returns correct tool ID (snake_case) - [ ] Outputs match tool outputs -- [ ] Block registered in registry.ts +- [ ] Block registered in `registry.ts` blocks object (alphabetically) +- [ ] `{Service}BlockMeta` exported at bottom of block file with `tags` and `templates` +- [ ] `skills` added to `{Service}BlockMeta`, each grounded in the block's `tools.access` and derived from a real online-sourced use case (not invented) +- [ ] `BlockMeta` imported from `@/blocks/types` alongside `BlockConfig` +- [ ] Block meta registered in `registry.ts` blocksMeta object (alphabetically) - [ ] If icon missing: asked user to provide SVG - [ ] If triggers exist: `triggers` config set, trigger subBlocks spread - [ ] Optional/rarely-used fields set to `mode: 'advanced'` diff --git a/.agents/skills/add-integration/SKILL.md b/.agents/skills/add-integration/SKILL.md index 25983f357fd..6c9b3bed627 100644 --- a/.agents/skills/add-integration/SKILL.md +++ b/.agents/skills/add-integration/SKILL.md @@ -128,7 +128,7 @@ export const {service}{Action}Tool: ToolConfig = { ### Block Structure ```typescript import { {Service}Icon } from '@/components/icons' -import type { BlockConfig } from '@/blocks/types' +import type { BlockConfig, BlockMeta } from '@/blocks/types' import { AuthMode } from '@/blocks/types' import { getScopesForService } from '@/lib/oauth/utils' @@ -177,8 +177,32 @@ export const {Service}Block: BlockConfig = { outputs: { /* ... */ }, } + +export const {Service}BlockMeta = { + tags: ['tag1', 'tag2'], // IntegrationTag values matching the service's capabilities + templates: [ + { + icon: {Service}Icon, + title: '{Service} use-case title', + prompt: 'Build a workflow that ...', + modules: ['agent', 'workflows'], + category: 'productivity', + tags: ['automation'], + alsoIntegrations: ['slack'], // Optional: other blocks referenced in the prompt + }, + ], +} as const satisfies BlockMeta ``` +### BlockMeta rules + +- **Tags**: Use `IntegrationTag` values from `@/blocks/types`. Do NOT add a `tags` field to the `BlockConfig` object — tags belong only in `BlockMeta`. +- **`integrationType`**: Must be a valid `IntegrationType` enum value (AI, Analytics, Commerce, Communication, Databases, DevOps, Documents, Email, HR, Marketing, Observability, Productivity, Sales, Search, Security, Support). Never invent a value that doesn't exist in the enum. +- **Templates**: Aim for 2–4 templates per integration. Prompts should be concrete enough to generate a real workflow in Mothership. Start with "Build a workflow that..." or "Create a workflow that...". +- **`alsoIntegrations`**: List other block type IDs referenced in the template prompt (e.g. `'slack'`, `'linear'`). +- Place `{Service}BlockMeta` at the very bottom of the file, after the main block export. +- Register it in both the import and the `blocksMeta` object in `registry.ts`. + ### Key SubBlock Patterns **Condition-based visibility:** @@ -359,14 +383,20 @@ export const tools: Record = { ### Block Registry (`apps/sim/blocks/registry.ts`) ```typescript -// Add import (alphabetically) -import { {Service}Block } from '@/blocks/blocks/{service}' +// Add import (alphabetically) — include BlockMeta +import { {Service}Block, {Service}BlockMeta } from '@/blocks/blocks/{service}' -// Add to registry (alphabetically) +// Add to blocks registry (alphabetically) export const registry: Record = { // ... existing blocks ... {service}: {Service}Block, } + +// Add to blocksMeta registry (alphabetically) +export const blocksMeta = { + // ... existing entries ... + {service}: {Service}BlockMeta, +} ``` ### Trigger Registry (`apps/sim/triggers/registry.ts`) - If triggers exist @@ -433,7 +463,12 @@ If creating V2 versions (API-aligned outputs): - [ ] Configured tools.access with all tool IDs - [ ] Configured tools.config.tool selector - [ ] Defined outputs matching tool outputs -- [ ] Registered block in `blocks/registry.ts` +- [ ] `integrationType` uses a valid `IntegrationType` enum value (no invented values) +- [ ] No `tags` field on the `BlockConfig` object (tags live only in `BlockMeta`) +- [ ] `{Service}BlockMeta` exported at bottom of file with `tags` and `templates` +- [ ] `BlockMeta` type imported from `@/blocks/types` alongside `BlockConfig` +- [ ] Block registered in `blocks/registry.ts` blocks object (alphabetically) +- [ ] Block meta registered in `blocks/registry.ts` blocksMeta object (alphabetically) - [ ] If triggers: set `triggers.enabled` and `triggers.available` - [ ] If triggers: spread trigger subBlocks with `getTrigger()` diff --git a/.agents/skills/emcn-design-review/SKILL.md b/.agents/skills/emcn-design-review/SKILL.md index 04e39da1f8e..b53d3e19582 100644 --- a/.agents/skills/emcn-design-review/SKILL.md +++ b/.agents/skills/emcn-design-review/SKILL.md @@ -13,12 +13,12 @@ User arguments: $ARGUMENTS ## Context -This codebase uses **emcn**, a custom component library built on Radix UI primitives with CVA (class-variance-authority) variants and CSS variable design tokens. All UI must use emcn components and tokens — never raw HTML elements or hardcoded colors. +This codebase uses **emcn**, a custom component library built on Radix UI primitives with CVA variants and CSS variable design tokens. All UI must use emcn components and tokens. ## Steps -1. Read the emcn barrel export at `apps/sim/components/emcn/components/index.ts` to know what's available -2. Read `apps/sim/app/_styles/globals.css` for the full set of CSS variable tokens +1. Read the emcn public barrel at `apps/sim/components/emcn/index.ts` (re-exports components, Calendar, Table*, and icons) to know what's available; for the full icon set read `apps/sim/components/emcn/icons/index.ts` +2. Read `apps/sim/app/_styles/globals.css` for CSS variable tokens 3. Analyze the specified scope against every rule below 4. If fix=true, apply the fixes. If fix=false, propose the fixes without applying. @@ -26,313 +26,59 @@ This codebase uses **emcn**, a custom component library built on Radix UI primit ## Imports -- Import components from `@/components/emcn`, never from subpaths -- Import icons from `@/components/emcn/icons` or `lucide-react` -- Import `cn` from `@/lib/core/utils/cn` for conditional class merging -- Import app-specific wrappers (Select, VerifiedBadge) from `@/components/ui` +- Import from `@/components/emcn` barrel, never subpaths +- Icons from `@/components/emcn/icons` or `lucide-react` +- Use `cn` from `@/lib/core/utils/cn` for conditional classes -```tsx -// Good -import { Button, Modal, Badge } from '@/components/emcn' -// Bad -import { Button } from '@/components/emcn/components/button/button' -``` +## Design Tokens ---- - -## Design Tokens (CSS Variables) - -Never use raw color values. Always use CSS variable tokens via Tailwind arbitrary values: `text-[var(--text-primary)]`, not `text-gray-500` or `#333`. The CSS variable pattern is canonical (1,700+ uses) — do not use Tailwind semantic classes like `text-muted-foreground`. - -### Text hierarchy -| Token | Use | -|-------|-----| -| `text-[var(--text-primary)]` | Main content text | -| `text-[var(--text-secondary)]` | Secondary/supporting text | -| `text-[var(--text-tertiary)]` | Tertiary text | -| `text-[var(--text-muted)]` | Disabled, placeholder text | -| `text-[var(--text-icon)]` | Icon tinting | -| `text-[var(--text-inverse)]` | Text on dark backgrounds | -| `text-[var(--text-error)]` | Error/warning messages | - -### Surfaces (elevation) -| Token | Use | -|-------|-----| -| `bg-[var(--bg)]` | Page background | -| `bg-[var(--surface-2)]` through `bg-[var(--surface-7)]` | Increasing elevation | -| `bg-[var(--surface-hover)]` | Hover state backgrounds | -| `bg-[var(--surface-active)]` | Active/selected backgrounds | - -### Borders -| Token | Use | -|-------|-----| -| `border-[var(--border)]` | Default borders | -| `border-[var(--border-1)]` | Stronger borders (inputs, cards) | -| `border-[var(--border-muted)]` | Subtle dividers | - -### Status -| Token | Use | -|-------|-----| -| `--success` | Success states | -| `--error` | Error states | -| `--caution` | Warning states | - -### Brand -| Token | Use | -|-------|-----| -| `--brand-secondary` | Brand color | -| `--brand-accent` | Accent/CTA color | - -### Shadows -Use shadow tokens, never raw box-shadow values: -- `shadow-subtle`, `shadow-medium`, `shadow-overlay` -- `shadow-kbd`, `shadow-card` - -### Z-Index -Use z-index tokens for layering: -- `z-[var(--z-dropdown)]` (100), `z-[var(--z-modal)]` (200), `z-[var(--z-popover)]` (300), `z-[var(--z-tooltip)]` (400), `z-[var(--z-toast)]` (500) - ---- - -## Component Usage Rules - -### Buttons -Available variants: `default`, `primary`, `destructive`, `ghost`, `outline`, `active`, `secondary`, `tertiary`, `subtle`, `ghost-secondary`, `3d` - -| Action type | Variant | Frequency | -|-------------|---------|-----------| -| Toolbar, icon-only, utility actions | `ghost` | Most common (28%) | -| Primary action (create, save, submit) | `primary` | Very common (24%) | -| Cancel, close, secondary action | `default` | Common | -| Delete, remove, destructive action | `destructive` | Targeted use only | -| Active/selected state | `active` | Targeted use only | -| Toggle, mode switch | `outline` | Moderate | - -Sizes: `sm` (compact, 32% of buttons) or `md` (default, used when no size specified). Never create custom button styles — use an existing variant. - -Buttons without an explicit variant prop get `default` styling. This is acceptable for cancel/secondary actions. - -### Modals (Dialogs) -Use `Modal` + subcomponents. Never build custom dialog overlays. - -```tsx - - - Title - Content - - - - - - -``` - -Modal sizes by frequency: `sm` (440px, most common — confirmations and simple dialogs), `md` (500px, forms), `lg` (600px, content-heavy), `xl` (800px, rare), `full` (1200px, rare). - -Footer buttons: Cancel on left (`variant="default"`), primary action on right. This pattern is followed 100% across the codebase. - -### Delete/Remove Confirmations -Always use Modal with `size="sm"`. The established pattern: +Use CSS variable pattern (`text-[var(--text-primary)]`), never Tailwind semantics (`text-muted-foreground`) or hardcoded colors (`text-gray-500`, `#333`). -```tsx - - - Delete {itemType} - -

Description of consequences

-

Warning about irreversibility

-
- - - - -
-
-``` +**Text**: `--text-primary`, `--text-secondary`, `--text-tertiary`, `--text-muted`, `--text-body` (canonical value text), `--text-icon`, `--text-placeholder`, `--text-subtle`, `--text-inverse`, `--text-error` +**Surfaces**: `--bg`, `--surface-1` through `--surface-7`, `--surface-hover`, `--surface-active` +**Borders**: `--border`, `--border-1`, `--border-muted` +**Brand/accent**: `--brand-secondary`, `--brand-accent` +**Z-Index**: `--z-dropdown` (100), `--z-modal` (200), `--z-popover` (300), `--z-tooltip` (400), `--z-toast` (500) +**Shadows**: `shadow-subtle`, `shadow-medium`, `shadow-overlay`, `shadow-card` +**Badges**: `--badge-*` semantic families (success/error/gray/blue/purple/orange/amber/teal/cyan/pink, each with `-bg`/`-text`) -Rules: -- Title: "Delete {ItemType}" or "Remove {ItemType}" (use "Remove" for membership/association changes) -- Include consequence description -- Use `text-[var(--text-error)]` for warning text when the action is irreversible -- `variant="destructive"` for the action button (100% compliance) -- `variant="default"` for cancel (100% compliance) -- Cancel left, destructive right (100% compliance) -- For high-risk deletes (workspaces), require typing the name to confirm -- Include recovery info if soft-delete: "You can restore it from Recently Deleted in Settings" +## Buttons -### Toast Notifications -Use the imperative `toast` API from `@/components/emcn`. Never build custom notification UI. +Intent-to-variant mapping (read the actual `buttonVariants` in `apps/sim/components/emcn/components/button/button.tsx` for the full variant set — it exposes more than listed here): -```tsx -import { toast } from '@/components/emcn' +| Action | Variant | +|--------|---------| +| Toolbar, icon-only | `ghost` | +| Create, save, submit | `primary` | +| Cancel, close | `default` | +| Delete, remove | `destructive` | +| Selected state | `active` | +| Toggle | `outline` | -toast.success('Item saved') -toast.error('Something went wrong') -toast.success('Deleted', { action: { label: 'Undo', onClick: handleUndo } }) -``` +## Delete/Remove Confirmations -Variants: `default`, `success`, `error`. Auto-dismiss after 5s. Supports optional action buttons with callbacks. +`ChipModal` `size='sm'`, title "Delete/Remove {ItemType}", destructive confirm button, plain Cancel (follow the chip footer layout in `.claude/rules/emcn-components.md`). Use `text-[var(--text-error)]` for irreversible warnings. -### Badges -Use semantic color variants for status: +## Toast -| Status | Variant | Usage | -|--------|---------|-------| -| Error, failed, disconnected | `red` | Most common (15 uses) | -| Metadata, roles, auth types, scopes | `gray-secondary` | Very common (12 uses) | -| Type annotations (TS types, field types) | `type` | Very common (12 uses) | -| Success, active, enabled, running | `green` | Common (7 uses) | -| Neutral, default, unknown | `gray` | Common (6 uses) | -| Outline, parameters, public | `outline` | Moderate (6 uses) | -| Warning, processing | `amber` | Moderate (5 uses) | -| Paused, warning | `orange` | Occasional | -| Info, queued | `blue` | Occasional | -| Data types (arrays) | `purple` | Occasional | -| Generic with border | `default` | Occasional | +`toast.success()`, `toast.error()`, `toast()` from `@/components/emcn`. Never custom notification UI. -Use `dot` prop for status indicators (19 instances in codebase). `icon` prop is available but rarely used. +## Badges -### Tooltips -Use `Tooltip` from emcn with namespace pattern: +`red`=error/failed, `gray-secondary`=metadata/roles, `type`=type annotations, `green`=success/active, `gray`=neutral, `amber`=processing, `orange`=paused, `blue`=info. Use `dot` prop for status indicators. -```tsx - - - - - Helpful text - -``` +## Icons -Use tooltips for icon-only buttons and truncated text. Don't tooltip self-explanatory elements. - -### Popovers -Use for filters, option menus, and nested navigation: - -```tsx - - - - - - Section Title - - Item Label - - - - -``` - -### Dropdown Menus -Use for context menus and action menus: - -```tsx - - - - - - Edit - - - Delete - - - -``` - -Destructive items go last, after a separator, in error color. - -### Forms -Use `FormField` wrapper for labeled inputs: - -```tsx - - setName(e.target.value)} /> - -``` - -Rules: -- Use `Input` from emcn, never raw `` (exception: hidden file inputs) -- Use `Textarea` from emcn, never raw `