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
- آخر نشاط في المصدر
- ٤ مارس ٢٠٢٦ في ٠٥:٣٤
- لغة SKILL.md المكتشفة
- الإنجليزية
- النجوم
- ٣
- التفرعات
- ٠
خيارات التثبيت
يُحدَّد 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