Skip to content

Commit f3ba0e7

Browse files
avianionclaude
andcommitted
chore: merge upstream main to bring branch up to date
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2 parents f77f80c + 8c0a2e0 commit f3ba0e7

File tree

654 files changed

+70597
-12567
lines changed

Some content is hidden

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

654 files changed

+70597
-12567
lines changed

.claude/commands/add-block.md

Lines changed: 119 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ When the user asks you to create a block:
2020
import { {ServiceName}Icon } from '@/components/icons'
2121
import type { BlockConfig } from '@/blocks/types'
2222
import { AuthMode } from '@/blocks/types'
23+
import { getScopesForService } from '@/lib/oauth/utils'
2324

2425
export const {ServiceName}Block: BlockConfig = {
2526
type: '{service}', // snake_case identifier
@@ -115,12 +116,17 @@ export const {ServiceName}Block: BlockConfig = {
115116
id: 'credential',
116117
title: 'Account',
117118
type: 'oauth-input',
118-
serviceId: '{service}', // Must match OAuth provider
119+
serviceId: '{service}', // Must match OAuth provider service key
120+
requiredScopes: getScopesForService('{service}'), // Import from @/lib/oauth/utils
119121
placeholder: 'Select account',
120122
required: true,
121123
}
122124
```
123125

126+
**Scopes:** Always use `getScopesForService(serviceId)` from `@/lib/oauth/utils` for `requiredScopes`. Never hardcode scope arrays — the single source of truth is `OAUTH_PROVIDERS` in `lib/oauth/oauth.ts`.
127+
128+
**Scope descriptions:** When adding a new OAuth provider, also add human-readable descriptions for all scopes in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`.
129+
124130
### Selectors (with dynamic options)
125131
```typescript
126132
// Channel selector (Slack, Discord, etc.)
@@ -532,6 +538,41 @@ outputs: {
532538
}
533539
```
534540

541+
### Typed JSON Outputs
542+
543+
When using `type: 'json'` and you know the object shape in advance, **describe the inner fields in the description** so downstream blocks know what properties are available. For well-known, stable objects, use nested output definitions instead:
544+
545+
```typescript
546+
outputs: {
547+
// BAD: Opaque json with no info about what's inside
548+
plan: { type: 'json', description: 'Zone plan information' },
549+
550+
// GOOD: Describe the known fields in the description
551+
plan: {
552+
type: 'json',
553+
description: 'Zone plan information (id, name, price, currency, frequency, is_subscribed)',
554+
},
555+
556+
// BEST: Use nested output definition when the shape is stable and well-known
557+
plan: {
558+
id: { type: 'string', description: 'Plan identifier' },
559+
name: { type: 'string', description: 'Plan name' },
560+
price: { type: 'number', description: 'Plan price' },
561+
currency: { type: 'string', description: 'Price currency' },
562+
},
563+
}
564+
```
565+
566+
Use the nested pattern when:
567+
- The object has a small, stable set of fields (< 10)
568+
- Downstream blocks will commonly access specific properties
569+
- The API response shape is well-documented and unlikely to change
570+
571+
Use `type: 'json'` with a descriptive string when:
572+
- The object has many fields or a dynamic shape
573+
- It represents a list/array of items
574+
- The shape varies by operation
575+
535576
## V2 Block Pattern
536577

537578
When creating V2 blocks (alongside legacy V1):
@@ -589,6 +630,7 @@ export const registry: Record<string, BlockConfig> = {
589630
import { ServiceIcon } from '@/components/icons'
590631
import type { BlockConfig } from '@/blocks/types'
591632
import { AuthMode } from '@/blocks/types'
633+
import { getScopesForService } from '@/lib/oauth/utils'
592634

593635
export const ServiceBlock: BlockConfig = {
594636
type: 'service',
@@ -619,6 +661,7 @@ export const ServiceBlock: BlockConfig = {
619661
title: 'Service Account',
620662
type: 'oauth-input',
621663
serviceId: 'service',
664+
requiredScopes: getScopesForService('service'),
622665
placeholder: 'Select account',
623666
required: true,
624667
},
@@ -695,16 +738,88 @@ Please provide the SVG and I'll convert it to a React component.
695738
You can usually find this in the service's brand/press kit page, or copy it from their website.
696739
```
697740

741+
## Advanced Mode for Optional Fields
742+
743+
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. This includes:
744+
- Pagination tokens
745+
- Time range filters (start/end time)
746+
- Sort order options
747+
- Reply settings
748+
- Rarely used IDs (e.g., reply-to tweet ID, quote tweet ID)
749+
- Max results / limits
750+
751+
```typescript
752+
{
753+
id: 'startTime',
754+
title: 'Start Time',
755+
type: 'short-input',
756+
placeholder: 'ISO 8601 timestamp',
757+
condition: { field: 'operation', value: ['search', 'list'] },
758+
mode: 'advanced', // Rarely used, hide from basic view
759+
}
760+
```
761+
762+
## WandConfig for Complex Inputs
763+
764+
Use `wandConfig` for fields that are hard to fill out manually, such as timestamps, comma-separated lists, and complex query strings. This gives users an AI-assisted input experience.
765+
766+
```typescript
767+
// Timestamps - use generationType: 'timestamp' to inject current date context
768+
{
769+
id: 'startTime',
770+
title: 'Start Time',
771+
type: 'short-input',
772+
mode: 'advanced',
773+
wandConfig: {
774+
enabled: true,
775+
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
776+
generationType: 'timestamp',
777+
},
778+
}
779+
780+
// Comma-separated lists - simple prompt without generationType
781+
{
782+
id: 'mediaIds',
783+
title: 'Media IDs',
784+
type: 'short-input',
785+
mode: 'advanced',
786+
wandConfig: {
787+
enabled: true,
788+
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
789+
},
790+
}
791+
```
792+
793+
## Naming Convention
794+
795+
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.
796+
698797
## Checklist Before Finishing
699798

700799
- [ ] All subBlocks have `id`, `title` (except switch), and `type`
701800
- [ ] Conditions use correct syntax (field, value, not, and)
702801
- [ ] DependsOn set for fields that need other values
703802
- [ ] Required fields marked correctly (boolean or condition)
704-
- [ ] OAuth inputs have correct `serviceId`
705-
- [ ] Tools.access lists all tool IDs
706-
- [ ] Tools.config.tool returns correct tool ID
803+
- [ ] OAuth inputs have correct `serviceId` and `requiredScopes: getScopesForService(serviceId)`
804+
- [ ] Scope descriptions added to `SCOPE_DESCRIPTIONS` in `lib/oauth/utils.ts` for any new scopes
805+
- [ ] Tools.access lists all tool IDs (snake_case)
806+
- [ ] Tools.config.tool returns correct tool ID (snake_case)
707807
- [ ] Outputs match tool outputs
708808
- [ ] Block registered in registry.ts
709809
- [ ] If icon missing: asked user to provide SVG
710810
- [ ] If triggers exist: `triggers` config set, trigger subBlocks spread
811+
- [ ] Optional/rarely-used fields set to `mode: 'advanced'`
812+
- [ ] Timestamps and complex inputs have `wandConfig` enabled
813+
814+
## Final Validation (Required)
815+
816+
After creating the block, you MUST validate it against every tool it references:
817+
818+
1. **Read every tool definition** that appears in `tools.access` — do not skip any
819+
2. **For each tool, verify the block has correct:**
820+
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
821+
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
822+
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
823+
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
824+
3. **Verify block outputs** cover the key fields returned by all tools
825+
4. **Verify conditions** — each subBlock should only show for the operations that actually use it

.claude/commands/add-integration.md

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ export const {service}{Action}Tool: ToolConfig<Params, Response> = {
102102
- Always use `?? []` for optional array fields
103103
- Set `optional: true` for outputs that may not exist
104104
- Never output raw JSON dumps - extract meaningful fields
105+
- When using `type: 'json'` and you know the object shape, define `properties` with the inner fields so downstream consumers know the structure. Only use bare `type: 'json'` when the shape is truly dynamic
105106

106107
## Step 3: Create Block
107108

@@ -113,6 +114,7 @@ export const {service}{Action}Tool: ToolConfig<Params, Response> = {
113114
import { {Service}Icon } from '@/components/icons'
114115
import type { BlockConfig } from '@/blocks/types'
115116
import { AuthMode } from '@/blocks/types'
117+
import { getScopesForService } from '@/lib/oauth/utils'
116118

117119
export const {Service}Block: BlockConfig = {
118120
type: '{service}',
@@ -143,6 +145,7 @@ export const {Service}Block: BlockConfig = {
143145
title: '{Service} Account',
144146
type: 'oauth-input',
145147
serviceId: '{service}',
148+
requiredScopes: getScopesForService('{service}'),
146149
required: true,
147150
},
148151
// Conditional fields per operation
@@ -408,7 +411,7 @@ If creating V2 versions (API-aligned outputs):
408411
### Block
409412
- [ ] Created `blocks/blocks/{service}.ts`
410413
- [ ] Defined operation dropdown with all operations
411-
- [ ] Added credential field (oauth-input or short-input)
414+
- [ ] Added credential field with `requiredScopes: getScopesForService('{service}')`
412415
- [ ] Added conditional fields per operation
413416
- [ ] Set up dependsOn for cascading selectors
414417
- [ ] Configured tools.access with all tool IDs
@@ -418,6 +421,12 @@ If creating V2 versions (API-aligned outputs):
418421
- [ ] If triggers: set `triggers.enabled` and `triggers.available`
419422
- [ ] If triggers: spread trigger subBlocks with `getTrigger()`
420423

424+
### OAuth Scopes (if OAuth service)
425+
- [ ] Defined scopes in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS`
426+
- [ ] Added scope descriptions in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`
427+
- [ ] Used `getCanonicalScopesForProvider()` in `auth.ts` (never hardcode)
428+
- [ ] Used `getScopesForService()` in block `requiredScopes` (never hardcode)
429+
421430
### Icon
422431
- [ ] Asked user to provide SVG
423432
- [ ] Added icon to `components/icons.tsx`
@@ -436,6 +445,12 @@ If creating V2 versions (API-aligned outputs):
436445
- [ ] Ran `bun run scripts/generate-docs.ts`
437446
- [ ] Verified docs file created
438447

448+
### Final Validation (Required)
449+
- [ ] Read every tool file and cross-referenced inputs/outputs against the API docs
450+
- [ ] Verified block subBlocks cover all required tool params with correct conditions
451+
- [ ] Verified block outputs match what the tools actually return
452+
- [ ] Verified `tools.config.params` correctly maps and coerces all param types
453+
439454
## Example Command
440455

441456
When the user asks to add an integration:
@@ -685,13 +700,61 @@ return NextResponse.json({
685700
| `isUserFile` | `@/lib/core/utils/user-file` | Type guard for UserFile objects |
686701
| `FileInputSchema` | `@/lib/uploads/utils/file-schemas` | Zod schema for file validation |
687702

703+
### Advanced Mode for Optional Fields
704+
705+
Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. Examples: pagination tokens, time range filters, sort order, max results, reply settings.
706+
707+
### WandConfig for Complex Inputs
708+
709+
Use `wandConfig` for fields that are hard to fill out manually:
710+
- **Timestamps**: Use `generationType: 'timestamp'` to inject current date context into the AI prompt
711+
- **JSON arrays**: Use `generationType: 'json-object'` for structured data
712+
- **Complex queries**: Use a descriptive prompt explaining the expected format
713+
714+
```typescript
715+
{
716+
id: 'startTime',
717+
title: 'Start Time',
718+
type: 'short-input',
719+
mode: 'advanced',
720+
wandConfig: {
721+
enabled: true,
722+
prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
723+
generationType: 'timestamp',
724+
},
725+
}
726+
```
727+
728+
### OAuth Scopes (Centralized System)
729+
730+
Scopes are maintained in a single source of truth and reused everywhere:
731+
732+
1. **Define scopes** in `lib/oauth/oauth.ts` under `OAUTH_PROVIDERS[provider].services[service].scopes`
733+
2. **Add descriptions** in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts` for the OAuth modal UI
734+
3. **Reference in auth.ts** using `getCanonicalScopesForProvider(providerId)` from `@/lib/oauth/utils`
735+
4. **Reference in blocks** using `getScopesForService(serviceId)` from `@/lib/oauth/utils`
736+
737+
**Never hardcode scope arrays** in `auth.ts` or block `requiredScopes`. Always import from the centralized source.
738+
739+
```typescript
740+
// In auth.ts (Better Auth config)
741+
scopes: getCanonicalScopesForProvider('{service}'),
742+
743+
// In block credential sub-block
744+
requiredScopes: getScopesForService('{service}'),
745+
```
746+
688747
### Common Gotchas
689748

690749
1. **OAuth serviceId must match** - The `serviceId` in oauth-input must match the OAuth provider configuration
691-
2. **Tool IDs are snake_case** - `stripe_create_payment`, not `stripeCreatePayment`
750+
2. **All tool IDs MUST be snake_case** - `stripe_create_payment`, not `stripeCreatePayment`. This applies to tool `id` fields, registry keys, `tools.access` arrays, and `tools.config.tool` return values
692751
3. **Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
693752
4. **Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
694753
5. **Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
695754
6. **DependsOn clears options** - When a dependency changes, selector options are refetched
696755
7. **Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
697756
8. **Always handle legacy file params** - Keep hidden `fileContent` params for backwards compatibility
757+
9. **Optional fields use advanced mode** - Set `mode: 'advanced'` on rarely-used optional fields
758+
10. **Complex inputs need wandConfig** - Timestamps, JSON arrays, and other hard-to-type values should have `wandConfig` enabled
759+
11. **Never hardcode scopes** - Use `getScopesForService()` in blocks and `getCanonicalScopesForProvider()` in auth.ts
760+
12. **Always add scope descriptions** - New scopes must have entries in `SCOPE_DESCRIPTIONS` within `lib/oauth/utils.ts`

.claude/commands/add-tools.md

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,18 @@ closedAt: {
147147
},
148148
```
149149

150-
### Nested Properties
151-
For complex outputs, define nested structure:
150+
### Typed JSON Outputs
151+
152+
When using `type: 'json'` and you know the object shape in advance, **always define the inner structure** using `properties` so downstream consumers know what fields are available:
153+
152154
```typescript
155+
// BAD: Opaque json with no info about what's inside
156+
metadata: {
157+
type: 'json',
158+
description: 'Response metadata',
159+
},
160+
161+
// GOOD: Define the known properties
153162
metadata: {
154163
type: 'json',
155164
description: 'Response metadata',
@@ -159,7 +168,10 @@ metadata: {
159168
count: { type: 'number', description: 'Total count' },
160169
},
161170
},
171+
```
162172

173+
For arrays of objects, define the item structure:
174+
```typescript
163175
items: {
164176
type: 'array',
165177
description: 'List of items',
@@ -173,6 +185,8 @@ items: {
173185
},
174186
```
175187

188+
Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.
189+
176190
## Critical Rules for transformResponse
177191

178192
### Handle Nullable Fields
@@ -272,13 +286,36 @@ If creating V2 tools (API-aligned outputs), use `_v2` suffix:
272286
- Version: `'2.0.0'`
273287
- Outputs: Flat, API-aligned (no content/metadata wrapper)
274288

289+
## Naming Convention
290+
291+
All tool IDs MUST use `snake_case`: `{service}_{action}` (e.g., `x_create_tweet`, `slack_send_message`). Never use camelCase or PascalCase for tool IDs.
292+
275293
## Checklist Before Finishing
276294

295+
- [ ] All tool IDs use snake_case
277296
- [ ] All params have explicit `required: true` or `required: false`
278297
- [ ] All params have appropriate `visibility`
279298
- [ ] All nullable response fields use `?? null`
280299
- [ ] All optional outputs have `optional: true`
281300
- [ ] No raw JSON dumps in outputs
282301
- [ ] Types file has all interfaces
283302
- [ ] Index.ts exports all tools
284-
- [ ] Tool IDs use snake_case
303+
304+
## Final Validation (Required)
305+
306+
After creating all tools, you MUST validate every tool before finishing:
307+
308+
1. **Read every tool file** you created — do not skip any
309+
2. **Cross-reference with the API docs** to verify:
310+
- All required params are marked `required: true`
311+
- All optional params are marked `required: false`
312+
- Param types match the API (string, number, boolean, json)
313+
- Request URL, method, headers, and body match the API spec
314+
- `transformResponse` extracts the correct fields from the API response
315+
- All output fields match what the API actually returns
316+
- No fields are missing from outputs that the API provides
317+
- No extra fields are defined in outputs that the API doesn't return
318+
3. **Verify consistency** across tools:
319+
- Shared types in `types.ts` match all tools that use them
320+
- Tool IDs in the barrel export match the tool file definitions
321+
- Error handling is consistent (error checks, meaningful messages)

0 commit comments

Comments
 (0)