Skip to content

Commit e257d06

Browse files
authored
feat(integrations): suggest curated skills per integration with one-click add (#4912)
* feat(integrations): suggest curated skills per integration with one-click add Curate research-backed, capability-grounded skills for every catalog integration and surface them on the integration detail page. Each skill maps to operations the block actually supports and can be added to the workspace in one click; track adds in PostHog. - Add SuggestedSkill type + skills field on BlockMeta; populate skills for all 193 catalog integrations (3 audit passes for grounding/sourcing) - getSuggestedSkillsForBlock() with versioned-type (e.g. notion_v2) base fallback - Skills section on the integration detail page with add/added states - integration_skill_added PostHog event with workspace/integration metadata * fix(integrations): flip suggested-skill row to Added immediately after add The row derived Added state solely from the useSkills cache, so between a successful create and the list refetch the row still showed Add and could be clicked again, hitting the server duplicate-name check. Track added names in local state so the row reflects the add immediately. * fix(integrations): harden suggested-skill add flow; document skill authoring Address PR review feedback on the suggested-skills section: - Make useSkills the single source of truth for Added state by writing the created skill into the React Query cache onSuccess (fixes stale Added that survived a delete, and the lag that allowed a duplicate click) - Track in-flight adds in a Set so concurrent adds keep independent pending state and cannot be double-submitted - Surface failures with toast.error instead of swallowing the rejection - Extract the duplicated SkillTile into a shared workspace component Also document the new BlockMeta.skills field in the add-block and validate-integration skills (+ blocks AGENTS.md): skills must be grounded in the block's tools.access and sourced from real online use cases, never invented. * fix(integrations): synchronous in-flight guard for skill add; align cursor docs - Guard handleAdd with a ref so two rapid clicks cannot both fire a create before the disabled state re-renders (pendingNames is async) - Fold the create-cache-merge rationale into the hook's TSDoc and drop non-TSDoc inline comments to match the repo convention - Align .cursor add-block/validate-integration command docs with the newer .claude/.agents versions: BlockMeta section + skills authoring/validation guidance (grounded in tools.access, sourced from real online use cases) * fix(integrations): gate skill Add/Added on authoritative workspace skills useSkills uses keepPreviousData, so during initial load or a workspace switch the list could be empty or a prior workspace's placeholder — making rows show a misleading Add (duplicate-submittable) or a false Added. Derive skillsReady from !isPending && !isPlaceholderData, only mark Added when ready, and disable Add until the current workspace's list has loaded. * chore(integrations): drop local-variable comments in skills section Keep to the repo's TSDoc-on-declarations convention — the in-flight guard and skillsReady derivation are self-evident from naming.
1 parent 27fc6dd commit e257d06

210 files changed

Lines changed: 5232 additions & 15 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/add-block/SKILL.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -651,6 +651,14 @@ export const {Service}BlockMeta = {
651651
alsoIntegrations: ['slack'], // Other blocks referenced in the prompt (optional)
652652
},
653653
],
654+
skills: [ // Optional but strongly encouraged
655+
{
656+
name: 'summarize-thread', // kebab-case, becomes the created skill's name
657+
description: 'One line: what it does and when to use it.',
658+
content:
659+
'# Summarize Thread\n\n...\n\n## Steps\n1. ...\n\n## Output\n...', // markdown
660+
},
661+
],
654662
} as const satisfies BlockMeta
655663
```
656664

@@ -665,6 +673,16 @@ export const {Service}BlockMeta = {
665673
- **`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
666674
- Place the export **after** the main `{Service}Block` export, at the very bottom of the file
667675

676+
#### `skills` — curated, ready-to-add agent skills
677+
678+
`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.
679+
680+
- **`name`** — kebab-case, lowercase letters/numbers/hyphens, ≤ 64 chars, unique within the integration, verb-led (e.g. `summarize-thread`).
681+
- **`description`** — one line, ≤ 1024 chars: what it does and when to use it.
682+
- **`content`** — markdown instructions for the agent (literal `\n` for newlines): a `# Title`, then `## Steps` and an output/guidance section. Keep ~600–2000 chars.
683+
- **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).
684+
- **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.
685+
668686
### Register in the blocksMeta object
669687

670688
After adding `{Service}BlockMeta` to the block file, register it in `apps/sim/blocks/registry.ts`:
@@ -888,6 +906,7 @@ All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MU
888906
- [ ] Outputs match tool outputs
889907
- [ ] Block registered in `registry.ts` blocks object (alphabetically)
890908
- [ ] `{Service}BlockMeta` exported at bottom of block file with `tags` and `templates`
909+
- [ ] `skills` added to `{Service}BlockMeta`, each grounded in the block's `tools.access` and derived from a real online-sourced use case (not invented)
891910
- [ ] `BlockMeta` imported from `@/blocks/types` alongside `BlockConfig`
892911
- [ ] Block meta registered in `registry.ts` blocksMeta object (alphabetically)
893912
- [ ] If icon missing: asked user to provide SVG

.agents/skills/validate-integration/SKILL.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,12 @@ For **each tool** in `tools.access`:
207207
- [ ] `authMode` is set correctly (`AuthMode.OAuth` or `AuthMode.ApiKey`)
208208
- [ ] Block is registered in `blocks/registry.ts` alphabetically
209209

210+
### BlockMeta Skills (catalog)
211+
- [ ] `{Service}BlockMeta.skills` is present (3–5 for mainstream services, 2–3 for niche/low-level)
212+
- [ ] **Every skill is grounded** — its steps only use operations the block exposes in `tools.access`; flag any skill that implies an unsupported action (e.g. "receive messages" when the block only sends)
213+
- [ ] **Every skill is real, not hallucinated** — web-search the service and confirm each skill maps to a popular use case attested online (vendor use-case/solutions pages, official docs describing the workflow, reputable "top automations for X" articles). Rewrite or remove any skill you cannot source as something people genuinely do with the service.
214+
- [ ] Each skill has a kebab-case `name` (≤64 chars, unique), a one-line `description`, and markdown `content` with `# Title` + `## Steps` + an output/guidance section
215+
210216
### Block Inputs
211217
- [ ] `inputs` section lists all subBlock params that the block accepts
212218
- [ ] Input types match the subBlock types

.claude/commands/add-block.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,11 +807,25 @@ export const {Service}BlockMeta = {
807807
},
808808
// ... at least 6 more
809809
],
810+
skills: [ // SuggestedSkill[] — 3–5 mainstream, 2–3 niche
811+
{
812+
name: 'summarize-thread', // kebab-case, ≤64 chars, unique, verb-led
813+
description: 'One line: what it does and when to use it.', // ≤1024 chars
814+
content:
815+
'# Summarize Thread\n\n...\n\n## Steps\n1. ...\n\n## Output\n...', // markdown
816+
},
817+
// ... more
818+
],
810819
} as const satisfies BlockMeta
811820
```
812821

813822
Derive templates from the service's real use cases. Each prompt should name a concrete trigger, transformation, and output — not a generic description of what the service does.
814823

824+
`skills` are curated, ready-to-add agent skills shown on the integration's detail page (users click **Add** to create them in their workspace). Two hard rules:
825+
826+
- **Ground every skill in operations the block actually exposes** — cross-check each skill's steps against `tools.access`. Never describe an action the integration cannot perform.
827+
- **Derive skills from real, popular use cases found online — never invent them.** Web-search the service's documented use cases (vendor use-case/solutions pages, official docs describing the workflow, reputable "top automations for X" articles) and only add a skill you can source as something people genuinely do with the service. Do not hallucinate skills.
828+
815829
## Checklist Before Finishing
816830

817831
- [ ] `integrationType` is set to the correct `IntegrationType` enum value
@@ -831,6 +845,7 @@ Derive templates from the service's real use cases. Each prompt should name a co
831845
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
832846
- [ ] Timestamps and complex inputs have `wandConfig` enabled
833847
- [ ] Exported `{Service}BlockMeta` with at least 7 templates
848+
- [ ] `skills` added to `{Service}BlockMeta`, each grounded in `tools.access` and sourced from a real online use case (not invented)
834849

835850
## Final Validation (Required)
836851

.claude/commands/validate-integration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,8 @@ For **each tool** in `tools.access`:
197197
- [ ] Has at least 7 templates, each with `icon`, `title`, `prompt`, `modules`, `category`, and `tags`
198198
- [ ] Prompts describe concrete use cases, not generic descriptions of what the service does
199199
- [ ] `alsoIntegrations` is set on any template whose prompt references another service
200+
- [ ] `skills` present (3–5 mainstream, 2–3 niche), each grounded in `tools.access` — flag any skill implying an unsupported action
201+
- [ ] **Each skill is real, not hallucinated** — web-search and confirm it maps to a popular use case attested online (vendor use-case pages, official docs describing the workflow, reputable "top automations" articles); rewrite/remove any you cannot source
200202

201203
### Block Inputs
202204
- [ ] `inputs` section lists all subBlock params that the block accepts

.cursor/commands/add-block.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,47 @@ Use `wandConfig` for fields that are hard to fill out manually, such as timestam
793793

794794
All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MUST use `snake_case` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase.
795795

796+
## BlockMeta (Required)
797+
798+
Every block file must export a `{Service}BlockMeta` alongside the block — **minimum 7 templates**. Look at existing examples in `apps/sim/blocks/blocks/` (e.g. `browser_use.ts`, `google_sheets.ts`) for the pattern.
799+
800+
```typescript
801+
import type { BlockMeta } from '@/blocks/types'
802+
803+
export const {Service}BlockMeta = {
804+
tags: ['tag1', 'tag2'], // IntegrationTag[]
805+
templates: [
806+
{
807+
icon: {Service}Icon,
808+
title: '{Service} <use-case>', // 2–5 words
809+
prompt: 'Build a workflow that...', // specific use case, 1–3 sentences
810+
modules: ['agent', 'workflows'], // 'agent' | 'workflows' | 'tables' | 'files' | 'scheduled' | 'knowledge-base'
811+
category: 'operations', // 'operations' | 'marketing' | 'sales' | 'engineering' | 'productivity' | 'support' | 'popular'
812+
tags: ['automation'],
813+
alsoIntegrations: ['slack'], // optional — other block IDs referenced in the prompt
814+
featured: true, // optional
815+
},
816+
// ... at least 6 more
817+
],
818+
skills: [ // SuggestedSkill[] — 3–5 mainstream, 2–3 niche
819+
{
820+
name: 'summarize-thread', // kebab-case, ≤64 chars, unique, verb-led
821+
description: 'One line: what it does and when to use it.', // ≤1024 chars
822+
content:
823+
'# Summarize Thread\n\n...\n\n## Steps\n1. ...\n\n## Output\n...', // markdown
824+
},
825+
// ... more
826+
],
827+
} as const satisfies BlockMeta
828+
```
829+
830+
Derive templates from the service's real use cases. Each prompt should name a concrete trigger, transformation, and output — not a generic description of what the service does.
831+
832+
`skills` are curated, ready-to-add agent skills shown on the integration's detail page (users click **Add** to create them in their workspace). Two hard rules:
833+
834+
- **Ground every skill in operations the block actually exposes** — cross-check each skill's steps against `tools.access`. Never describe an action the integration cannot perform.
835+
- **Derive skills from real, popular use cases found online — never invent them.** Web-search the service's documented use cases (vendor use-case/solutions pages, official docs describing the workflow, reputable "top automations for X" articles) and only add a skill you can source as something people genuinely do with the service. Do not hallucinate skills.
836+
796837
## Checklist Before Finishing
797838

798839
- [ ] `integrationType` is set to the correct `IntegrationType` enum value
@@ -811,6 +852,8 @@ All tool IDs referenced in `tools.access` and returned by `tools.config.tool` MU
811852
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
812853
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
813854
- [ ] Timestamps and complex inputs have `wandConfig` enabled
855+
- [ ] Exported `{Service}BlockMeta` with at least 7 templates
856+
- [ ] `skills` added to `{Service}BlockMeta`, each grounded in `tools.access` and sourced from a real online use case (not invented)
814857

815858
## Final Validation (Required)
816859

.cursor/commands/validate-integration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,14 @@ For **each tool** in `tools.access`:
187187
- [ ] `authMode` is set correctly (`AuthMode.OAuth` or `AuthMode.ApiKey`)
188188
- [ ] Block is registered in `blocks/registry.ts` alphabetically
189189

190+
### BlockMeta
191+
- [ ] `{Service}BlockMeta` is exported in the same file as the block
192+
- [ ] Has at least 7 templates, each with `icon`, `title`, `prompt`, `modules`, `category`, and `tags`
193+
- [ ] Prompts describe concrete use cases, not generic descriptions of what the service does
194+
- [ ] `alsoIntegrations` is set on any template whose prompt references another service
195+
- [ ] `skills` present (3–5 mainstream, 2–3 niche), each grounded in `tools.access` — flag any skill implying an unsupported action
196+
- [ ] **Each skill is real, not hallucinated** — web-search and confirm it maps to a popular use case attested online (vendor use-case pages, official docs describing the workflow, reputable "top automations" articles); rewrite/remove any you cannot source
197+
190198
### Block Inputs
191199
- [ ] `inputs` section lists all subBlock params that the block accepts
192200
- [ ] Input types match the subBlock types

apps/sim/app/workspace/[workspaceId]/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export type {
2727
SelectableConfig,
2828
} from './resource/resource'
2929
export { EMPTY_CELL_PLACEHOLDER, Resource, ResourceTable } from './resource/resource'
30+
export { SkillTile } from './skill-tile'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { SkillTile } from './skill-tile'
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { AgentSkillsIcon } from '@/components/icons'
2+
3+
/**
4+
* Square tile bearing the agent-skills glyph. Shared chrome for any surface
5+
* that lists a skill (the Skills page and integration detail pages) so the two
6+
* do not drift.
7+
*/
8+
export function SkillTile() {
9+
return (
10+
<div className='size-9 flex-shrink-0'>
11+
<div className='flex size-full items-center justify-center rounded-xl border border-[var(--border-1)] bg-[var(--surface-4)] dark:bg-[var(--surface-5)]'>
12+
<AgentSkillsIcon className='size-5 text-[var(--text-icon)]' />
13+
</div>
14+
</div>
15+
)
16+
}

apps/sim/app/workspace/[workspaceId]/integrations/[block]/integration-block-detail.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,19 @@ import {
1414
} from '@/lib/integrations'
1515
import { getServiceConfigByProviderId } from '@/lib/oauth'
1616
import { ConnectOAuthModal } from '@/app/workspace/[workspaceId]/components/connect-oauth-modal'
17+
import { IntegrationSkillsSection } from '@/app/workspace/[workspaceId]/integrations/[block]/integration-skills-section'
1718
import { ConnectServiceAccountModal } from '@/app/workspace/[workspaceId]/integrations/components/connect-service-account-modal'
1819
import { IntegrationSection } from '@/app/workspace/[workspaceId]/integrations/components/integration-section'
1920
import { IntegrationTile } from '@/app/workspace/[workspaceId]/integrations/components/integrations-showcase'
2021
import {
2122
CONNECT_MODE,
2223
CONNECT_QUERY_PARAM,
2324
} from '@/app/workspace/[workspaceId]/integrations/connect-route'
24-
import { getTemplatesForBlock, type ScopedBlockTemplate } from '@/blocks/registry'
25+
import {
26+
getSuggestedSkillsForBlock,
27+
getTemplatesForBlock,
28+
type ScopedBlockTemplate,
29+
} from '@/blocks/registry'
2530
import { useWorkspaceCredentials } from '@/hooks/queries/credentials'
2631
import { useOAuthReturnRouter } from '@/hooks/use-oauth-return'
2732

@@ -46,6 +51,7 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
4651
const searchParams = useSearchParams()
4752
const Icon = blockTypeToIconMap[integration.type]
4853
const matchingTemplates = getTemplatesForBlock(integration.type)
54+
const suggestedSkills = getSuggestedSkillsForBlock(integration.type)
4955
const oauthService = resolveOAuthServiceForIntegration(integration)
5056
const [oauthOpen, setOAuthOpen] = useState(false)
5157

@@ -210,6 +216,14 @@ export function IntegrationBlockDetail({ integration, workspaceId }: Integration
210216
</IntegrationSection>
211217
)}
212218

219+
{suggestedSkills.length > 0 && (
220+
<IntegrationSkillsSection
221+
skills={suggestedSkills}
222+
workspaceId={workspaceId}
223+
integrationType={integration.type}
224+
/>
225+
)}
226+
213227
{matchingTemplates.length > 0 && (
214228
<TemplatesSection
215229
integration={integration}

0 commit comments

Comments
 (0)