Skip to content

Commit 85d60ea

Browse files
merge origin/staging into feat/remove-fractional-index-ff
2 parents b56a673 + 4e6594d commit 85d60ea

4,626 files changed

Lines changed: 649966 additions & 66345 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/design-taste-frontend/SKILL.md

Lines changed: 1206 additions & 0 deletions
Large diffs are not rendered by default.

.agents/skills/emil-design-eng/SKILL.md

Lines changed: 679 additions & 0 deletions
Large diffs are not rendered by default.

.agents/skills/memory-load-check/SKILL.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,35 @@ Read these when doing a deeper pass:
4949
- cap downloads and parsed output separately
5050
- preserve partial results when a later item exceeds the cap
5151
- never read untrusted response bodies without a byte cap
52+
- KB connector file downloads in `apps/sim/connectors/utils.ts`
53+
- `CONNECTOR_MAX_FILE_BYTES`: shared per-file cap (aligned with the manual KB upload limit)
54+
- `readBodyWithLimit`: stream a download body to a Buffer with a hard byte cap (null on overflow)
55+
- `stubOrSkipBySize`: listing-time skip when the reported size exceeds the cap
56+
- `markSkipped` / `sizeLimitSkipReason`: surface oversized files as failed (skipped) KB rows
57+
- `ConnectorFileTooLargeError`: thrown mid-download when the listing under-reported size
5258
- Large workflow value payloads
5359
- prefer durable references/manifests over inlining large arrays or files
5460
- materialize refs only behind an explicit byte budget
5561

62+
## KB Connector File Size Handling
63+
64+
The connector size pattern in `apps/sim/connectors/utils.ts` (`CONNECTOR_MAX_FILE_BYTES` + `readBodyWithLimit` + `stubOrSkipBySize`/`markSkipped`) exists for one risk: a knowledge-base connector downloading **arbitrary, user-controlled file bytes** that the source does not hard-cap. Apply it by that risk, not by the connector's name.
65+
66+
Use the pattern when the connector downloads file content via a stream/`download_url` where the user controls the size:
67+
- file-storage connectors: Dropbox, OneDrive, SharePoint, Google Drive, S3, GitHub, GitLab, Azure DevOps
68+
- any connector that fetches a file via a download URL even if it is not a "storage" service (e.g. the Zoom transcript `.vtt`)
69+
70+
For those, require all three:
71+
- stream the body with `readBodyWithLimit(resp, CONNECTOR_MAX_FILE_BYTES)` — never raw `response.text()`/`response.arrayBuffer()`
72+
- skip oversize at listing (`stubOrSkipBySize` with the reported size) and again at fetch time (overflow -> `markSkipped`), since the listing size can be missing or under-reported
73+
- never drop/truncate silently — oversized files become content-less failed rows carrying `skippedReason`, so they stay visible in the KB UI instead of vanishing from the index
74+
75+
Skip the pattern when the source already bounds the payload:
76+
- pure API/structured-data connectors (Jira, Linear, Notion, Confluence, Sentry, Slack, Zendesk, Gmail, ...) — paginated JSON/text; apply normal pagination + concurrency bounds instead of a per-file byte cap
77+
- native-document connectors capped by the platform (Google Docs ~50 MB, Google Sheets via `MAX_ROWS`, Evernote ~25 MB/note) — a 100 MB cap can never fire, and wrapping a `response.json()`/Thrift parse in `readBodyWithLimit` is cargo-culting
78+
79+
Litmus test: "Can a user make this one fetch arbitrarily large, with nothing upstream stopping it?" Yes -> use the pattern. No (platform hard-cap, or already paginated) -> a per-file byte cap adds noise, not safety. Borderline: a user-configured/self-hosted endpoint with no platform cap (e.g. Obsidian) — bound it only if the content is genuinely unbounded.
80+
5681
## Review Workflow
5782

5883
1. Identify every changed data source:
@@ -96,6 +121,7 @@ Read these when doing a deeper pass:
96121
- fetches all pages from an external API before processing
97122
- reads an entire file, HTTP response, or stream without a max byte budget
98123
- checks size only after `Buffer.concat`, `arrayBuffer`, `text`, `JSON.parse`, or parse expansion
124+
- a KB connector silently drops or truncates an oversized file instead of recording it as a failed (skipped) row
99125
- chunks only after loading the complete dataset
100126
- paginates with unbounded/deep `OFFSET` on a mutable or large table
101127
- creates one queue job per row without batching or a queue-level concurrency key

.agents/skills/react-query-best-practices/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Read these before analyzing:
3131

3232
### Query hooks
3333
- Every `queryFn` must forward `signal` for request cancellation
34-
- Every query must have an explicit `staleTime` (default 0 is almost never correct)
34+
- Every query must have an explicit `staleTime` (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
3535
- `keepPreviousData` / `placeholderData` only on variable-key queries (where params change), never on static keys
3636
- Use `enabled` to prevent queries from running without required params
3737

.claude/commands/add-block.md

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ export const {ServiceName}Block: BlockConfig = {
2727
name: '{Service Name}', // Human readable
2828
description: 'Brief description', // One sentence
2929
longDescription: 'Detailed description for docs',
30-
docsLink: 'https://docs.sim.ai/tools/{service}',
30+
docsLink: 'https://docs.sim.ai/integrations/{service}',
3131
category: 'tools', // 'tools' | 'blocks' | 'triggers'
3232
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
3333
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
@@ -600,17 +600,20 @@ export const ServiceV2Block: BlockConfig = {
600600

601601
## Registering Blocks
602602

603-
After creating the block, remind the user to:
604-
1. Import in `apps/sim/blocks/registry.ts`
605-
2. Add to the `registry` object (alphabetically):
603+
After creating the block, remind the user to register it in `apps/sim/blocks/registry-maps.ts` (the data maps live here; `registry.ts` holds only the accessor functions). Add the import and an entry to each map alphabetically:
606604

607605
```typescript
608-
import { ServiceBlock } from '@/blocks/blocks/service'
606+
import { ServiceBlock, ServiceBlockMeta } from '@/blocks/blocks/service'
609607

610-
export const registry: Record<string, BlockConfig> = {
608+
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
611609
// ... existing blocks ...
612610
service: ServiceBlock,
613611
}
612+
613+
export const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
614+
// ... existing metas ...
615+
service: ServiceBlockMeta,
616+
}
614617
```
615618

616619
## Complete Example
@@ -626,7 +629,7 @@ export const ServiceBlock: BlockConfig = {
626629
name: 'Service',
627630
description: 'Integrate with Service API',
628631
longDescription: 'Full description for documentation...',
629-
docsLink: 'https://docs.sim.ai/tools/service',
632+
docsLink: 'https://docs.sim.ai/integrations/service',
630633
category: 'tools',
631634
integrationType: IntegrationType.DeveloperTools,
632635
tags: ['oauth', 'api'],
@@ -729,6 +732,13 @@ Please provide the SVG and I'll convert it to a React component.
729732
You can usually find this in the service's brand/press kit page, or copy it from their website.
730733
```
731734

735+
When converting the SVG: a **monochrome** logo (single white or black mark) must
736+
use `fill='currentColor'`, never a hardcoded `#fff`/`#000000`. Block icons render
737+
both inside their `bgColor` tile and "bare" on a neutral page (the home Suggested
738+
actions list) in light and dark mode; a hardcoded white/black mark goes invisible
739+
bare on the matching background. Multi-color brand logos keep their own fills.
740+
Verify with `bun run check:bare-icons`.
741+
732742
## Advanced Mode for Optional Fields
733743

734744
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. This includes:
@@ -840,7 +850,7 @@ Derive templates from the service's real use cases. Each prompt should name a co
840850
- [ ] Tools.access lists all tool IDs (snake_case)
841851
- [ ] Tools.config.tool returns correct tool ID (snake_case)
842852
- [ ] Outputs match tool outputs
843-
- [ ] Block registered in registry.ts
853+
- [ ] Block + meta registered in registry-maps.ts (`BLOCK_REGISTRY` / `BLOCK_META_REGISTRY`)
844854
- [ ] If icon missing: asked user to provide SVG
845855
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
846856
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`

.claude/commands/add-integration.md

Lines changed: 40 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export const {Service}Block: BlockConfig = {
121121
name: '{Service}',
122122
description: '...',
123123
longDescription: '...',
124-
docsLink: 'https://docs.sim.ai/tools/{service}',
124+
docsLink: 'https://docs.sim.ai/integrations/{service}',
125125
category: 'tools',
126126
integrationType: IntegrationType.X, // Primary category (see IntegrationType enum)
127127
tags: ['oauth', 'api'], // Cross-cutting tags (see IntegrationTag type)
@@ -279,6 +279,31 @@ Once the user provides the SVG:
279279
2. Create a React component that spreads props
280280
3. Ensure viewBox is preserved from the original SVG
281281

282+
### Theme-safety (bare rendering) — REQUIRED
283+
284+
The icon renders both inside its colored `bgColor` tile AND "bare" (no tile) on a
285+
neutral page — e.g. the home **Suggested actions** list — in both light and dark
286+
mode. A monochrome logo whose paths hardcode a single near-white or near-black
287+
fill is invisible bare on the matching background (white-on-white in light mode,
288+
black-on-black in dark mode).
289+
290+
Rules when adding the SVG:
291+
292+
- **Monochrome logos** (a single white or black mark): draw the shape with
293+
`fill='currentColor'`, not `fill='#fff'` / `fill='#000000'`. It then inherits
294+
white inside dark tiles, near-black inside light tiles (via
295+
`getTileIconColorClass`), and the theme-aware `var(--text-icon)` bare — legible
296+
everywhere. Do NOT set `iconColor` for these.
297+
- **Multi-color brand logos** (their own vivid fills): keep the hardcoded fills.
298+
They read on any background. Only set `iconColor` (a vivid brand hex, never a
299+
near-black/near-white tile color) if the bare icon should adopt a brand tint.
300+
- A large white shape with a tiny vivid accent (e.g. a logo where the body is the
301+
white negative space) still vanishes bare — convert the body to `currentColor`.
302+
303+
Verify with `bun run check:bare-icons` (also runs in CI). It flags purely
304+
monochrome hazards; for partial-accent logos, eyeball the suggested-actions list
305+
in both light and dark mode.
306+
282307
## Step 5: Create Triggers (Optional)
283308

284309
If the service supports webhooks, create triggers using the generic `buildTriggerSubBlocks` helper.
@@ -364,17 +389,25 @@ export const tools: Record<string, ToolConfig> = {
364389
}
365390
```
366391

367-
### Block Registry (`apps/sim/blocks/registry.ts`)
392+
### Block Registry (`apps/sim/blocks/registry-maps.ts`)
393+
394+
The data maps (`BLOCK_REGISTRY` + `BLOCK_META_REGISTRY`) live in `registry-maps.ts`; `registry.ts` holds only the accessor functions. Add the import and an entry to each map alphabetically:
368395

369396
```typescript
370397
// Add import (alphabetically)
371-
import { {Service}Block } from '@/blocks/blocks/{service}'
398+
import { {Service}Block, {Service}BlockMeta } from '@/blocks/blocks/{service}'
372399

373-
// Add to registry (alphabetically)
374-
export const registry: Record<string, BlockConfig> = {
400+
// Add to the config map (alphabetically)
401+
export const BLOCK_REGISTRY: Record<string, BlockConfig> = {
375402
// ... existing blocks ...
376403
{service}: {Service}Block,
377404
}
405+
406+
// Add to the catalog-meta map (alphabetically)
407+
export const BLOCK_META_REGISTRY: Record<string, BlockMeta> = {
408+
// ... existing metas ...
409+
{service}: {Service}BlockMeta,
410+
}
378411
```
379412

380413
### Trigger Registry (`apps/sim/triggers/registry.ts`) - If triggers exist
@@ -443,7 +476,7 @@ If creating V2 versions (API-aligned outputs):
443476
- [ ] Configured tools.access with all tool IDs
444477
- [ ] Configured tools.config.tool selector
445478
- [ ] Defined outputs matching tool outputs
446-
- [ ] Registered block in `blocks/registry.ts`
479+
- [ ] Registered block + meta in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` / `BLOCK_META_REGISTRY`)
447480
- [ ] If triggers: set `triggers.enabled` and `triggers.available`
448481
- [ ] If triggers: spread trigger subBlocks with `getTrigger()`
449482
- [ ] Exported `{Service}BlockMeta` with at least 7 templates
@@ -458,6 +491,7 @@ If creating V2 versions (API-aligned outputs):
458491
- [ ] Asked user to provide SVG
459492
- [ ] Added icon to `components/icons.tsx`
460493
- [ ] Icon spreads props correctly
494+
- [ ] Monochrome marks use `fill='currentColor'` (not hardcoded white/black) so the icon renders bare in light AND dark mode — verified with `bun run check:bare-icons`
461495

462496
### Triggers (if service supports webhooks)
463497
- [ ] Created `triggers/{service}/` directory

.claude/commands/add-trigger.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -374,7 +374,7 @@ export const {service}PollingHandler: PollingProviderHandler = {
374374

375375
try {
376376
// For OAuth services:
377-
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId, logger)
377+
const accessToken = await resolveOAuthCredential(webhookData, '{service}', requestId)
378378
const config = webhookData.providerConfig as unknown as {Service}WebhookConfig
379379

380380
// First poll: seed state, emit nothing
@@ -421,7 +421,7 @@ export const {service}PollingTrigger: TriggerConfig = {
421421
polling: true, // REQUIRED — routes to polling infrastructure
422422

423423
subBlocks: [
424-
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger', supportsCredentialSets: true },
424+
{ id: 'triggerCredentials', type: 'oauth-input', title: 'Credentials', serviceId: '{service}', requiredScopes: [], required: true, mode: 'trigger' },
425425
// ... service-specific config fields (dropdowns, inputs, switches) ...
426426
{ id: 'triggerInstructions', type: 'text', title: 'Setup Instructions', hideFromPreview: true, mode: 'trigger', defaultValue: '...' },
427427
],

.claude/commands/cleanup.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
description: Run all code quality skills in sequence — effects, memo, callbacks, state, React Query, and emcn design review
2+
description: Run all code quality skills in sequence — effects, memo, callbacks, state, React Query, emcn design review, and url-state
33
argument-hint: [scope] [fix=true|false]
44
---
55

@@ -21,5 +21,6 @@ Run each of these skills in order on the specified scope, passing through the sc
2121
4. `/you-might-not-need-state $ARGUMENTS`
2222
5. `/react-query-best-practices $ARGUMENTS`
2323
6. `/emcn-design-review $ARGUMENTS`
24+
7. `/you-might-not-need-url-state $ARGUMENTS`
2425

25-
After all skills have run, output a summary of what was found and fixed (or proposed) across all six passes.
26+
After all skills have run, output a summary of what was found and fixed (or proposed) across all seven passes.

.claude/commands/react-query-best-practices.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ Read these before analyzing:
3131

3232
### Query hooks
3333
- Every `queryFn` must forward `signal` for request cancellation
34-
- Every query must have an explicit `staleTime` (default 0 is almost never correct)
34+
- Every query must have an explicit `staleTime` (default 0 is almost never correct), assigned from a named exported constant — never an inline numeric literal. A server-side prefetch hydrating the same query key must import and reuse that constant instead of restating the number
3535
- `keepPreviousData` / `placeholderData` only on variable-key queries (where params change), never on static keys
3636
- Use `enabled` to prevent queries from running without required params
3737

.claude/commands/validate-integration.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ Read **every** file for the integration — do not skip any:
2424
apps/sim/tools/{service}/ # All tool files, types.ts, index.ts
2525
apps/sim/blocks/blocks/{service}.ts # Block definition
2626
apps/sim/tools/registry.ts # Tool registry entries for this service
27-
apps/sim/blocks/registry.ts # Block registry entry for this service
27+
apps/sim/blocks/registry-maps.ts # Block + meta registry entry (BLOCK_REGISTRY / BLOCK_META_REGISTRY)
2828
apps/sim/components/icons.tsx # Icon definition
2929
apps/sim/lib/auth/auth.ts # OAuth config — should use getCanonicalScopesForProvider()
3030
apps/sim/lib/oauth/oauth.ts # OAuth provider config — single source of truth for scopes
@@ -185,12 +185,12 @@ For **each tool** in `tools.access`:
185185
- [ ] `name` is human-readable (e.g., `'X'`, `'Cloudflare'`)
186186
- [ ] `description` is a concise one-liner
187187
- [ ] `longDescription` provides detail for docs
188-
- [ ] `docsLink` points to `'https://docs.sim.ai/tools/{service}'`
188+
- [ ] `docsLink` points to `'https://docs.sim.ai/integrations/{service}'`
189189
- [ ] `category` is `'tools'`
190190
- [ ] `bgColor` uses the service's brand color hex
191191
- [ ] `icon` references the correct icon component from `@/components/icons`
192192
- [ ] `authMode` is set correctly (`AuthMode.OAuth` or `AuthMode.ApiKey`)
193-
- [ ] Block is registered in `blocks/registry.ts` alphabetically
193+
- [ ] Block + meta are registered in `blocks/registry-maps.ts` (`BLOCK_REGISTRY` / `BLOCK_META_REGISTRY`) alphabetically
194194

195195
### BlockMeta
196196
- [ ] `{Service}BlockMeta` is exported in the same file as the block

0 commit comments

Comments
 (0)