Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 108 additions & 2 deletions .claude/commands/add-block.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,41 @@ outputs: {
}
```

### Typed JSON Outputs

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:

```typescript
outputs: {
// BAD: Opaque json with no info about what's inside
plan: { type: 'json', description: 'Zone plan information' },

// GOOD: Describe the known fields in the description
plan: {
type: 'json',
description: 'Zone plan information (id, name, price, currency, frequency, is_subscribed)',
},

// BEST: Use nested output definition when the shape is stable and well-known
plan: {
id: { type: 'string', description: 'Plan identifier' },
name: { type: 'string', description: 'Plan name' },
price: { type: 'number', description: 'Plan price' },
currency: { type: 'string', description: 'Price currency' },
},
}
```

Use the nested pattern when:
- The object has a small, stable set of fields (< 10)
- Downstream blocks will commonly access specific properties
- The API response shape is well-documented and unlikely to change

Use `type: 'json'` with a descriptive string when:
- The object has many fields or a dynamic shape
- It represents a list/array of items
- The shape varies by operation

## V2 Block Pattern

When creating V2 blocks (alongside legacy V1):
Expand Down Expand Up @@ -695,16 +730,87 @@ Please provide the SVG and I'll convert it to a React component.
You can usually find this in the service's brand/press kit page, or copy it from their website.
```

## Advanced Mode for Optional Fields

Optional fields that are rarely used should be set to `mode: 'advanced'` so they don't clutter the basic UI. This includes:
- Pagination tokens
- Time range filters (start/end time)
- Sort order options
- Reply settings
- Rarely used IDs (e.g., reply-to tweet ID, quote tweet ID)
- Max results / limits

```typescript
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
placeholder: 'ISO 8601 timestamp',
condition: { field: 'operation', value: ['search', 'list'] },
mode: 'advanced', // Rarely used, hide from basic view
}
```

## WandConfig for Complex Inputs

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.

```typescript
// Timestamps - use generationType: 'timestamp' to inject current date context
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an ISO 8601 timestamp based on the user description. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
}

// Comma-separated lists - simple prompt without generationType
{
id: 'mediaIds',
title: 'Media IDs',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate a comma-separated list of media IDs. Return ONLY the comma-separated values.',
},
}
```

## Naming Convention

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.

## Checklist Before Finishing

- [ ] All subBlocks have `id`, `title` (except switch), and `type`
- [ ] Conditions use correct syntax (field, value, not, and)
- [ ] DependsOn set for fields that need other values
- [ ] Required fields marked correctly (boolean or condition)
- [ ] OAuth inputs have correct `serviceId`
- [ ] Tools.access lists all tool IDs
- [ ] Tools.config.tool returns correct tool ID
- [ ] 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
- [ ] 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'`
- [ ] Timestamps and complex inputs have `wandConfig` enabled

## Final Validation (Required)

After creating the block, you MUST validate it against every tool it references:

1. **Read every tool definition** that appears in `tools.access` — do not skip any
2. **For each tool, verify the block has correct:**
- SubBlock inputs that cover all required tool params (with correct `condition` to show for that operation)
- SubBlock input types that match the tool param types (e.g., dropdown for enums, short-input for strings)
- `tools.config.params` correctly maps subBlock IDs to tool param names (if they differ)
- Type coercions in `tools.config.params` for any params that need conversion (Number(), Boolean(), JSON.parse())
3. **Verify block outputs** cover the key fields returned by all tools
4. **Verify conditions** — each subBlock should only show for the operations that actually use it
36 changes: 35 additions & 1 deletion .claude/commands/add-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ export const {service}{Action}Tool: ToolConfig<Params, Response> = {
- Always use `?? []` for optional array fields
- Set `optional: true` for outputs that may not exist
- Never output raw JSON dumps - extract meaningful fields
- 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

## Step 3: Create Block

Expand Down Expand Up @@ -436,6 +437,12 @@ If creating V2 versions (API-aligned outputs):
- [ ] Ran `bun run scripts/generate-docs.ts`
- [ ] Verified docs file created

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

## Example Command

When the user asks to add an integration:
Expand Down Expand Up @@ -685,13 +692,40 @@ return NextResponse.json({
| `isUserFile` | `@/lib/core/utils/user-file` | Type guard for UserFile objects |
| `FileInputSchema` | `@/lib/uploads/utils/file-schemas` | Zod schema for file validation |

### Advanced Mode for Optional Fields

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.

### WandConfig for Complex Inputs

Use `wandConfig` for fields that are hard to fill out manually:
- **Timestamps**: Use `generationType: 'timestamp'` to inject current date context into the AI prompt
- **JSON arrays**: Use `generationType: 'json-object'` for structured data
- **Complex queries**: Use a descriptive prompt explaining the expected format

```typescript
{
id: 'startTime',
title: 'Start Time',
type: 'short-input',
mode: 'advanced',
wandConfig: {
enabled: true,
prompt: 'Generate an ISO 8601 timestamp. Return ONLY the timestamp string.',
generationType: 'timestamp',
},
}
```

### Common Gotchas

1. **OAuth serviceId must match** - The `serviceId` in oauth-input must match the OAuth provider configuration
2. **Tool IDs are snake_case** - `stripe_create_payment`, not `stripeCreatePayment`
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
3. **Block type is snake_case** - `type: 'stripe'`, not `type: 'Stripe'`
4. **Alphabetical ordering** - Keep imports and registry entries alphabetically sorted
5. **Required can be conditional** - Use `required: { field: 'op', value: 'create' }` instead of always true
6. **DependsOn clears options** - When a dependency changes, selector options are refetched
7. **Never pass Buffer directly to fetch** - Convert to `new Uint8Array(buffer)` for TypeScript compatibility
8. **Always handle legacy file params** - Keep hidden `fileContent` params for backwards compatibility
9. **Optional fields use advanced mode** - Set `mode: 'advanced'` on rarely-used optional fields
10. **Complex inputs need wandConfig** - Timestamps, JSON arrays, and other hard-to-type values should have `wandConfig` enabled
43 changes: 40 additions & 3 deletions .claude/commands/add-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,18 @@ closedAt: {
},
```

### Nested Properties
For complex outputs, define nested structure:
### Typed JSON Outputs

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:

```typescript
// BAD: Opaque json with no info about what's inside
metadata: {
type: 'json',
description: 'Response metadata',
},

// GOOD: Define the known properties
metadata: {
type: 'json',
description: 'Response metadata',
Expand All @@ -159,7 +168,10 @@ metadata: {
count: { type: 'number', description: 'Total count' },
},
},
```

For arrays of objects, define the item structure:
```typescript
items: {
type: 'array',
description: 'List of items',
Expand All @@ -173,6 +185,8 @@ items: {
},
```

Only use bare `type: 'json'` without `properties` when the shape is truly dynamic or unknown.

## Critical Rules for transformResponse

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

## Naming Convention

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.

## Checklist Before Finishing

- [ ] All tool IDs use snake_case
- [ ] All params have explicit `required: true` or `required: false`
- [ ] All params have appropriate `visibility`
- [ ] All nullable response fields use `?? null`
- [ ] All optional outputs have `optional: true`
- [ ] No raw JSON dumps in outputs
- [ ] Types file has all interfaces
- [ ] Index.ts exports all tools
- [ ] Tool IDs use snake_case

## Final Validation (Required)

After creating all tools, you MUST validate every tool before finishing:

1. **Read every tool file** you created — do not skip any
2. **Cross-reference with the API docs** to verify:
- All required params are marked `required: true`
- All optional params are marked `required: false`
- Param types match the API (string, number, boolean, json)
- Request URL, method, headers, and body match the API spec
- `transformResponse` extracts the correct fields from the API response
- All output fields match what the API actually returns
- No fields are missing from outputs that the API provides
- No extra fields are defined in outputs that the API doesn't return
3. **Verify consistency** across tools:
- Shared types in `types.ts` match all tools that use them
- Tool IDs in the barrel export match the tool file definitions
- Error handling is consistent (logger imports, error checks)
Loading