Ensure accessibility in UI components including semantic HTML, ARIA attributes, keyboard navigation, and WCAG 2.2 AA compliance.
日本語の概要は準備中です。原文の説明を表示しています。
Expert-level Slack platform development using Bolt SDK — build Slack apps with slash commands, interactive components (buttons, modals, shortcuts), home tabs, event subscriptions, workflows, Socket Mode, and Block Kit. Use for building Slack bots, workflow automation, and rich interactive Slack experiences.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
Full-platform Slack development using the Bolt SDK. Covers interactive app development beyond basic messaging — slash commands, modals, shortcuts, home tabs, event subscriptions, workflow steps, and multi-workspace OAuth.
Related skill: slack-notifications — use that skill for simple one-way messaging/alerts. Use slack-expert when building interactive Slack apps or bots.
| Variable | Purpose | Required |
|---|---|---|
SLACK_BOT_TOKEN | Bot OAuth token (xoxb-...) | Yes (single workspace) |
SLACK_SIGNING_SECRET | Request signature verification | Yes |
SLACK_APP_TOKEN | Socket Mode app-level token (xapp-...) | Socket Mode only |
SLACK_CLIENT_ID | OAuth client ID | Multi-workspace only |
SLACK_CLIENT_SECRET | OAuth client secret | Multi-workspace only |
SLACK_STATE_SECRET | OAuth state token | Multi-workspace only |
const { App } = require('@slack/bolt');
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
// For Socket Mode, add:
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN,
});
// Slash command
app.command('/hello', async ({ command, ack, respond }) => {
await ack();
await respond(`Hello <@${command.user_id}>!`);
});
// Button click
app.action('approve_button', async ({ body, ack, client }) => {
await ack();
await client.views.open({
trigger_id: body.trigger_id,
view: confirmationModal,
});
});
// Event subscription
app.event('app_mention', async ({ event, say }) => {
await say({ text: `Hello <@${event.user}>!`, thread_ts: event.ts });
});
(async () => {
await app.start(process.env.PORT || 3000);
console.log('Bolt app running');
})();
// Message listener with regex
app.message(/deploy/, async ({ message, say }) => {
await say('Triggering deployment...');
});
// Slash command — MUST ack() within 3 seconds
app.command('/status', async ({ ack, respond, command }) => {
await ack();
// Long-running: defer actual work
respond({ text: 'Checking status...', response_type: 'ephemeral' });
const status = await fetchStatus(); // async work after ack
await respond({ text: `Status: ${status}` });
});
// Shortcut (global or message)
app.shortcut('create_ticket', async ({ shortcut, ack, client }) => {
await ack();
await client.views.open({
trigger_id: shortcut.trigger_id,
view: ticketModal,
});
});
const modal = {
type: 'modal',
callback_id: 'ticket_submission',
title: { type: 'plain_text', text: 'Create Ticket' },
submit: { type: 'plain_text', text: 'Submit' },
close: { type: 'plain_text', text: 'Cancel' },
blocks: [
{
type: 'input',
block_id: 'title_block',
element: {
type: 'plain_text_input',
action_id: 'title_input',
placeholder: { type: 'plain_text', text: 'Ticket title' },
},
label: { type: 'plain_text', text: 'Title' },
},
{
type: 'input',
block_id: 'priority_block',
element: {
type: 'static_select',
action_id: 'priority_select',
options: [
{ text: { type: 'plain_text', text: 'High' }, value: 'high' },
{ text: { type: 'plain_text', text: 'Medium' }, value: 'medium' },
{ text: { type: 'plain_text', text: 'Low' }, value: 'low' },
],
},
label: { type: 'plain_text', text: 'Priority' },
},
],
};
// Handle modal submission
app.view('ticket_submission', async ({ ack, view, client, body }) => {
await ack();
const title = view.state.values.title_block.title_input.value;
const priority = view.state.values.priority_block.priority_select.selected_option.value;
await createTicket({ title, priority, userId: body.user.id });
});
const richMessage = {
blocks: [
{
type: 'header',
text: { type: 'plain_text', text: 'Deployment Ready' },
},
{
type: 'section',
fields: [
{ type: 'mrkdwn', text: '*Service:*\napi-gateway' },
{ type: 'mrkdwn', text: '*Version:*\nv1.2.3' },
{ type: 'mrkdwn', text: '*Environment:*\nProduction' },
{ type: 'mrkdwn', text: '*Requested by:*\n<@U1234567>' },
],
},
{ type: 'divider' },
{
type: 'actions',
block_id: 'deploy_actions',
elements: [
{
type: 'button',
text: { type: 'plain_text', text: 'Approve' },
style: 'primary',
action_id: 'approve_deploy',
value: 'v1.2.3',
confirm: {
title: { type: 'plain_text', text: 'Confirm Deploy' },
text: { type: 'mrkdwn', text: 'Deploy *v1.2.3* to production?' },
confirm: { type: 'plain_text', text: 'Deploy' },
deny: { type: 'plain_text', text: 'Cancel' },
},
},
{
type: 'button',
text: { type: 'plain_text', text: 'Reject' },
style: 'danger',
action_id: 'reject_deploy',
value: 'v1.2.3',
},
],
},
],
};
// Publish home tab when user opens it
app.event('app_home_opened', async ({ event, client }) => {
if (event.tab !== 'home') return;
await client.views.publish({
user_id: event.user,
view: {
type: 'home',
blocks: [
{
type: 'header',
text: { type: 'plain_text', text: 'My App Dashboard' },
},
{
type: 'section',
text: { type: 'mrkdwn', text: 'Welcome to your app home!' },
accessory: {
type: 'button',
text: { type: 'plain_text', text: 'Refresh' },
action_id: 'refresh_home',
},
},
],
},
});
});
// Use lazy: for operations that take >3s
app.command(
'/report',
async ({ command, ack, respond, say }) => {
await ack(); // Must ack in <3s
},
async ({ command, respond }) => {
// lazy: runs asynchronously, can take minutes
const report = await generateReport(command.text);
await respond({ text: report, response_type: 'in_channel' });
}
);
// Socket Mode — no public URL required, ideal for internal tools
const app = new App({
token: process.env.SLACK_BOT_TOKEN,
signingSecret: process.env.SLACK_SIGNING_SECRET,
socketMode: true,
appToken: process.env.SLACK_APP_TOKEN, // xapp- token
});
const { App, ExpressReceiver } = require('@slack/bolt');
const { FileInstallationStore } = require('@slack/oauth');
const receiver = new ExpressReceiver({
signingSecret: process.env.SLACK_SIGNING_SECRET,
clientId: process.env.SLACK_CLIENT_ID,
clientSecret: process.env.SLACK_CLIENT_SECRET,
stateSecret: process.env.SLACK_STATE_SECRET,
scopes: ['chat:write', 'channels:read', 'commands'],
installationStore: new FileInstallationStore(),
});
const app = new App({ receiver });
// Legacy workflow steps (still used in some workspaces)
const ws = new WorkflowStep('copy_review', {
edit: async ({ ack, step, configure }) => {
await ack();
await configure({ blocks: editBlocks });
},
save: async ({ ack, step, view, update }) => {
await ack();
const { values } = view.state;
await update({ inputs: {}, outputs: [] });
},
execute: async ({ step, complete, fail }) => {
try {
// do work
await complete({ outputs: {} });
} catch (err) {
await fail({ error: { message: err.message } });
}
},
});
app.step(ws);
| Capability | Required Scopes |
|---|---|
| Post messages | chat:write |
| Read channels | channels:read, groups:read |
| Read messages | channels:history, groups:history |
| Slash commands | commands |
| React to messages | reactions:write |
| Upload files | files:write |
| Read user profiles | users:read, users:read.email |
| Home tab | im:history |
| Bot mentions | app_mentions:read |
| DMs | im:write, mpim:write |
app.command('/approve-request', async ({ command, ack, client }) => {
await ack();
// Post approval request to manager channel
await client.chat.postMessage({
channel: '#approvals',
text: `<@${command.user_id}> requests approval`,
blocks: buildApprovalBlocks(command),
});
});
app.action('approve_action', async ({ action, body, ack, client }) => {
await ack();
const requestId = action.value;
await markApproved(requestId, body.user.id);
// Update original message
await client.chat.update({
channel: body.channel.id,
ts: body.message.ts,
text: `Approved by <@${body.user.id}>`,
blocks: buildApprovedBlocks(requestId),
});
});
async function lookupUser(client, email) {
const result = await client.users.lookupByEmail({ email });
return result.user.id;
}
app.error(async error => {
console.error('Global error handler', error);
// Notify admin channel
await app.client.chat.postMessage({
channel: process.env.ADMIN_CHANNEL,
text: `App error: ${error.message}`,
});
});
SLACK_SIGNING_SECRET to authenticate requests from Slackresponse_type: 'ephemeral' for sensitive command responses.env in production# Enable Bolt debug logging
DEBUG=bolt:* node app.js
# Test with Slack CLI (requires Slack CLI installed)
slack run
slack triggers create --trigger-def "triggers/trigger.json"
# Replay events locally
slack events replay
slack-notifications skill — Simple one-way messaging and alertsまだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Ensure accessibility in UI components including semantic HTML, ARIA attributes, keyboard navigation, and WCAG 2.2 AA compliance.
日本語の概要は準備中です。原文の説明を表示しています。
Use when you want to improve response quality through meta-cognitive reasoning. Applies 15+ reasoning methods to reconsider and refine initial outputs.
日本語の概要は準備中です。原文の説明を表示しています。
N-round opposing-stance debates for trade-off analysis. Assigns pro/con roles to agents, runs structured debate rounds with quality scoring, and produces a moderator synthesis with confidence-rated recommendation. Generalizable to architecture, technology, security, and design decisions.
日本語の概要は準備中です。原文の説明を表示しています。
Force adversarial code review stance that eliminates confirmation bias — reviewer must find issues or re-analyze
日本語の概要は準備中です。原文の説明を表示しています。
Creates specialized AI agents on-demand when no existing agent matches a request. Use when the Router cannot find a suitable agent for a task. Enables self-evolution by generating persistent agents.
日本語の概要は準備中です。原文の説明を表示しています。
LLM-as-judge evaluation framework with 5-dimension rubric (accuracy, groundedness, coherence, completeness, helpfulness) for scoring AI-generated content quality with weighted composite scores and evidence citations
日本語の概要は準備中です。原文の説明を表示しています。