Skip to main content

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で見る