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에서 보기