slack-agent
Use when working on Slack agent/bot code, Slack Bolt applications, or projects using slack-agent. Provides development patterns, testing requirements, and quality standards.
ソース情報
- リポジトリ
- arvindrk/slack-agent
- ソースの最終更新活動
- 2026年3月4日 05:34
- 検出された SKILL.md の言語
- 英語
- スター
- 3
- フォーク
- 0
インストール方法
デフォルトでは、最初にソースを確認する Prompt が選択されています。直接コマンドに切り替えるか、ローカルコピーをダウンロードすることもできます。
ソースファイルを確認
インストールを決める前に、SKILL.md と SkillsMP に表示されている付属ファイルをお読みください。
ファイルエクスプローラー
23 ファイルSKILL.md を表示中
SKILL.md
ソースの指示 · 読み取り専用プレビュー- name
- slack-agent
- description
- Use when working on Slack agent/bot code, Slack Bolt applications, or projects using slack-agent. Provides development patterns, testing requirements, and quality standards.
- version
- 2.0.0
- user-invocable
- true
# Vercel Slack Agent Development Skill
## Skill Invocation Handling
When this skill is invoked via `/slack-agent`, check for arguments and route accordingly:
### Command Arguments
| Argument | Action |
|----------|--------|
| `new` | **Run the setup wizard from Phase 1.** Read `./wizard/1-project-setup.md` and guide the user through creating a new Slack agent. |
| `configure` | Start wizard at Phase 2 or 3 for existing projects |
| `deploy` | Start wizard at Phase 5 for production deployment |
| `test` | Start wizard at Phase 6 to set up testing |
| (no argument) | Auto-detect based on project state (see below) |
### Auto-Detection (No Argument)
If invoked without arguments, detect the project state and route appropriately:
1. **No `package.json` with `@slack/bolt`** → Treat as `new`, start Phase 1
2. **Has project but no customized `manifest.json`** → Start Phase 2
3. **Has project but no `.env` file** → Start Phase 3
4. **Has `.env` but not tested** → Start Phase 4
5. **Tested but not deployed** → Start Phase 5
6. **Otherwise** → Provide general assistance using this skill's patterns
### Wizard Phases
The wizard is located in `./wizard/` with these phases:
- `1-project-setup.md` - Understand purpose, generate custom implementation plan
- `1b-approve-plan.md` - Present plan for user approval before cloning
- `2-create-slack-app.md` - Customize manifest, create app in Slack
- `3-configure-environment.md` - Set up .env with credentials
- `4-test-locally.md` - Dev server + ngrok tunnel
- `5-deploy-production.md` - Vercel deployment
- `6-setup-testing.md` - Vitest configuration
**IMPORTANT:** For `new` projects, you MUST:
1. Read `./wizard/1-project-setup.md` first
2. Ask the user what kind of agent they want to build
3. Generate a custom implementation plan using `./reference/agent-archetypes.md`
4. Present the plan for approval (Phase 1b) BEFORE cloning the repository
5. Only proceed to clone after the plan is approved
---
## General Development Guidance
You are working on a Slack agent project built with Slack Agent. Follow these mandatory practices for all code changes.
## Project Stack
This project uses:
- **Server**: Nitro (H3-based) with file-based routing
- **Slack SDK**: `@vercel/slack-bolt` for serverless Slack apps (wraps Bolt for JavaScript)
- **AI**: AI SDK v6 with @ai-sdk/gateway
- **Workflows**: Workflow DevKit for durable execution
- **Linting**: Biome
- **Package Manager**: pnpm
### Dependencies
```json
{
"dependencies": {
"ai": "^6.0.0",
"@ai-sdk/gateway": "latest",
"@slack/bolt": "^4.x",
"@vercel/slack-bolt": "^1.0.2",
"zod": "^3.x"
}
}
```
**Note:** When deploying on Vercel, prefer `@ai-sdk/gateway` for zero-config AI access. Use direct provider SDKs (`@ai-sdk/openai`, `@ai-sdk/anthropic`, etc.) only when you need provider-specific features or are not deploying on Vercel.
---
## Quality Standards (MANDATORY)
These quality requirements MUST be followed for every code change. There are no exceptions.
### After EVERY File Modification
1. **Run linting immediately:**
```bash
pnpm lint
```
- If errors exist, run `pnpm lint --write` for auto-fixes
- Manually fix remaining issues
- Re-run `pnpm lint` to verify
2. **Check for corresponding test file:**
- If you modified `foo.ts`, check if `foo.test.ts` exists
- If no test file exists and the file exports functions, create one
### Before Completing ANY Task
You MUST run all quality checks and fix any issues before marking a task complete:
```bash
# 1. TypeScript compilation - must pass
pnpm typecheck
# 2. Linting - must pass with no errors
pnpm lint
# 3. Tests - all tests must pass
pnpm test
```
**Do NOT complete a task if any of these fail.** Fix the issues first.
### Unit Tests Required
**For ANY code change, you MUST write or update unit tests.**
- **Location**: Co-located `*.test.ts` files or `server/__tests__/`
- **Framework**: Vitest
- **Coverage**: All exported functions must have tests
Example test structure:
```typescript
import { describe, it, expect, vi } from 'vitest';
import { myFunction } from './my-module';
describe('myFunction', () => {
it('should handle normal input', () => {
expect(myFunction('input')).toBe('expected');
});
it('should handle edge cases', () => {
expect(myFunction('')).toBe('default');
});
});
```
### E2E Tests for User-Facing Changes
If you modify:
- Slack message handlers
- Slash commands
- Interactive components (buttons, modals)
- Bot responses
You MUST add or update E2E tests that verify the full flow.
---
## Events Endpoint Pattern (CRITICAL)
Use `@vercel/slack-bolt` to handle all Slack events. This package automatically handles:
- Content-type detection (JSON vs form-urlencoded)
- URL verification challenges
- 3-second ack timeout (built-in `ackTimeoutMs: 3001`)
- Background processing via Vercel Fluid Compute's `waitUntil`
### Bolt App Setup
```typescript
// server/bolt/app.ts
import { App } from "@slack/bolt";
import { VercelReceiver } from "@vercel/slack-bolt";
const receiver = new VercelReceiver();
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
receiver,
deferInitialization: true,
});
export { app, receiver };
```
### Events Handler
```typescript
// server/api/slack/events.post.ts
import { createHandler } from "@vercel/slack-bolt";
import { defineEventHandler, getRequestURL, readRawBody } from "h3";
import { app, receiver } from "../../bolt/app";
const handler = createHandler(app, receiver);
export default defineEventHandler(async (event) => {
// Read and cache the raw body first to avoid stream consumption issues
// with toWebRequest on serverless platforms (h3 issues #570, #578, #615)
const rawBody = await readRawBody(event, "utf8");
// Create a new Request with the buffered body
const request = new Request(getRequestURL(event), {
method: event.method,
headers: event.headers,
body: rawBody,
});
return await handler(request);
});
```
**Why this pattern?** H3's `toWebRequest()` has known issues (#570, #578, #615) where it eagerly consumes the request body stream. When `@vercel/slack-bolt` later calls `req.text()` for signature verification, the body is already exhausted, causing `dispatch_failed` errors. Buffering the body manually avoids this issue.
### VercelReceiver Options Reference
| Parameter | Default | Description |
|-----------|---------|-------------|
| `signingSecret` | `SLACK_SIGNING_SECRET` env var | Request verification secret |
| `signatureVerification` | `true` | Enable/disable signature verification |
| `ackTimeoutMs` | `3001` | Ack timeout in milliseconds |
| `logLevel` | `INFO` | Logging level |
### Key Benefits
1. **60+ lines of boilerplate eliminated** - No manual content-type detection, URL verification, or form parsing
2. **Automatic timeout handling** - Built-in 3-second ack with `ackTimeoutMs: 3001`
3. **Background processing** - Uses Vercel Fluid Compute's `waitUntil` automatically
4. **Framework support** - Works with Next.js, Hono, and Nitro (H3)
---
## Implementation Gotchas
### 1. Private Channel Access
Slash commands work in private channels even if the bot isn't a member, but the bot **cannot read messages or post** to private channels it hasn't been invited to.
When creating features that will later post to a channel:
```typescript
// Validate channel access upfront
const channelInfo = await client.conversations.info({ channel: channelId });
if (channelInfo.channel?.is_private && !channelInfo.channel?.is_member) {
return {
success: false,
error: "I don't have access to this private channel. Please add me with `/invite @BotName` first.",
};
}
```
### 2. Graceful Degradation for Channel Context
When fetching channel context for AI features, wrap in try/catch and fall back gracefully:
```typescript
let channelContext = "";
try {
const history = await client.conversations.history({
channel: channelId,
limit: 10,
});
channelContext = history.messages?.map(m => m.text).join("\n") ?? "";
} catch (error) {
// Bot can't access channel - continue without context
console.log("Could not fetch channel context:", error);
}
```
### 3. Vercel Cron Endpoint Authentication
Protect cron endpoints with a `CRON_SECRET` environment variable:
```typescript
// server/api/cron/my-job.get.ts
export default defineEventHandler(async (event) => {
const authHeader = getHeader(event, "authorization");
if (authHeader !== `Bearer ${process.env.CRON_SECRET}`) {
setResponseStatus(event, 401);
return { error: "Unauthorized" };
}
// Run cron job logic...
return { success: true };
});
```
### 4. vercel.json Cron Configuration
Configure cron jobs in `vercel.json`:
```json
{
"crons": [
{
"path": "/api/cron/my-job",
"schedule": "0 * * * *"
}
]
}
```
Schedule format is standard cron syntax: `minute hour day month weekday`
Common schedules:
- `* * * * *` - Every minute
- `0 * * * *` - Every hour
- `0 0 * * *` - Daily at midnight
- `0 9 * * 1-5` - Weekdays at 9am
### 5. AWS Credentials on Vercel (Use OIDC)
When connecting to AWS services (Aurora, S3, etc.) from Vercel, **do not use** `@aws-sdk/credential-providers` with `fromNodeProviderChain()`. It won't work because Vercel uses its own OIDC token mechanism.
**Wrong approach:**
```typescript
import { fromNodeProviderChain } from "@aws-sdk/credential-providers";
const credentials = fromNodeProviderChain(); // Won't work on Vercel!
```
**Correct approach:**
```typescript
import { awsCredentialsProvider } from "@vercel/functions/oidc";
// For AWS RDS/Aurora with IAM auth
const signer = new Signer({
hostname: process.env.PGHOST,
port: Number(process.env.PGPORT),
username: process.env.PGUSER,
region: process.env.AWS_REGION,
credentials: awsCredentialsProvider({ roleArn: process.env.AWS_ROLE_ARN! }),
});
const token = await signer.getAuthToken();
// For other AWS services (S3, etc.)
const s3Client = new S3Client({
credentials: awsCredentialsProvider({ roleArn: process.env.AWS_ROLE_ARN! }),
});
```
**Required setup:**
1. Enable Vercel OIDC in Project Settings > Security
2. Configure AWS IAM trust relationship for your Vercel project
3. Set `AWS_ROLE_ARN` environment variable in Vercel
**Reference:** [Vercel OIDC for AWS](https://vercel.com/docs/security/oidc/aws)
### 6. dispatch_failed Error (500)
If slash commands fail with `dispatch_failed`, the issue is usually H3's `toWebRequest` consuming the body stream before signature verification.
**Fix:** Buffer the body manually before creating the Request:
```typescript
const rawBody = await readRawBody(event, "utf8");
const request = new Request(getRequestURL(event), {
method: event.method,
headers: event.headers,
body: rawBody,
});
return await handler(request);
```
See the Events Handler section above for the complete pattern.
### 7. operation_timeout Error
If slash commands with AI processing fail with `operation_timeout`, you're blocking the HTTP response too long. Slack requires a response within 3 seconds.
**Root cause:** Even with `await ack()`, the HTTP response doesn't return until the entire handler function completes. If you `await` AI generation after `ack()`, the HTTP response is blocked.
**Fix:** Use fire-and-forget pattern:
```typescript
app.command('/mycommand', async ({ ack, command, logger }) => {
// 1. Acknowledge immediately
await ack();
// 2. Fire-and-forget: DON'T await this promise
generateAndRespond(command.response_url, command.text, logger).catch((error) => {
logger.error("Background operation failed:", error);
});
// HTTP response returns immediately here
});
async function generateAndRespond(responseUrl: string, topic: string, logger: Logger) {
try {
const result = await generateWithAI(topic); // Takes >3 seconds
// Post result via response_url
await fetch(responseUrl, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
response_type: "in_channel",
text: result,
}),
GitHubで見るこの SKILL.md は非常に大きいため、SkillsMP では最初のセクションだけを表示しています。 GitHubで見る