diff --git a/.agents/skills/slack-connector-functions/SKILL.md b/.agents/skills/slack-connector-functions/SKILL.md new file mode 100644 index 0000000..bdfae10 --- /dev/null +++ b/.agents/skills/slack-connector-functions/SKILL.md @@ -0,0 +1,103 @@ +--- +name: "slack-connector-functions" +description: "Use when building or modifying Slack Deno SDK workflows in this repository and the user asks to integrate an external SaaS, create/update/read records in another tool, send external email/SMS, schedule meetings, manage issues/tasks/incidents, or avoid implementing OAuth/API authentication by using Slack Platform connector functions." +--- + +# Slack Connector Functions + +Guide Slack Deno SDK workflow work in this repository toward official Slack +Platform connector functions when they can replace custom authentication and +external API code. + +## When to Use + +Use this skill when the user request involves a Slack workflow that performs an +action in an external service, especially: + +- Creating, updating, selecting, copying, sending, or resolving items in + supported SaaS tools. +- Adding rows to spreadsheets, creating calendar events, sending email or SMS, + creating issues, opening support tickets, updating CRM records, or triggering + incidents. +- Asking to integrate with GitHub, Jira, Linear, Google Workspace, Microsoft + 365, Salesforce, Zendesk, PagerDuty, ServiceNow, Asana, Notion, or another + service listed in Slack's connector catalog. +- Asking to avoid building OAuth, token storage, refresh logic, or bespoke API + client code for an external service. + +## When Not to Use + +Do not propose a connector as the main approach when: + +- The requested service or operation is not in the current official connector + catalog. +- The workflow needs fine-grained API behavior, custom pagination, complex + search, transformation-heavy reads, or multi-call orchestration beyond a + connector's inputs and outputs. +- The app must own external OAuth tokens, call a private API, or use a + tenant-specific API surface that Slack connectors do not expose. +- The workflow is intended for Slack Connect external users; Slack documents + connector workflows as home-team-only in this context. +- The work is Slack-native only, such as posting a message, creating a channel, + opening a form, or updating a canvas. Prefer Slack functions for those. + +## Decision Flow + +1. Identify the external service, desired operation, expected inputs, expected + outputs, and whether the user is trying to avoid custom authentication. +2. Check the current official connector catalog before coding: + https://docs.slack.dev/tools/deno-slack-sdk/reference/connector-functions/ +3. If a connector matches the requested operation closely, propose it before + implementing a custom function that calls the external API directly. +4. Confirm operational constraints: connected account, admin approval, execution + identity, write scope, destructive side effects, audit needs, and failure + behavior. +5. Decide between: + - Connector function: supported service and operation, simple step-level + action, auth handled by Slack connector setup. + - Slack function: Slack-native action. + - Custom function: unsupported connector, custom business logic, complex API + behavior, or repo-owned auth is required. +6. When implementing, wire the connector as a workflow step with `addStep`, keep + local validation and i18n patterns from this repository, and run the repo's + Deno checks. + +## Implementation Guidance + +- Re-check official docs for the selected connector immediately before + implementation because connector availability and signatures can change. +- Inspect the connector page's Facts, Input parameters, Output parameters, + Authentication, and Usage info sections. +- Check `import_map.json`; this repository currently imports `deno-slack-sdk/` + and `deno-slack-api/`, but connector usage may require adding the current + Slack connector module import from the official docs. +- Prefer composing connector steps in `workflows/` over adding a custom external + API client in `functions/` when the connector fully covers the user need. +- Keep workflow inputs explicit and typed with `Schema` where possible. +- For write operations, add a confirmation or review step when the action + affects money, contracts, hiring, production operations, customer records, or + destructive changes. +- Do not store connector credentials in `.env`; connector account connection is + handled through Slack Platform connector setup, while repo `.env` should + remain for this app's own configuration. + +## References + +- `references/connector-catalog-summary.md` for supported service categories and + representative connector examples. +- `references/adoption-checklist.md` for the connector-vs-custom-function + decision checklist. +- `references/implementation-notes.md` for repo-specific implementation and + validation notes. + +## Output Expectations + +When using this skill, respond with: + +- Whether a Slack connector should be proposed and why. +- The likely service and connector function name when identifiable. +- Any constraints or risks the user must confirm before implementation. +- The implementation location in this repo, usually `workflows/` and only + sometimes `functions/`. +- The validation commands to run, usually `deno task fmt`, `deno task lint`, + `deno task check`, and `deno task test`. diff --git a/.agents/skills/slack-connector-functions/references/adoption-checklist.md b/.agents/skills/slack-connector-functions/references/adoption-checklist.md new file mode 100644 index 0000000..b68aba6 --- /dev/null +++ b/.agents/skills/slack-connector-functions/references/adoption-checklist.md @@ -0,0 +1,50 @@ +# Connector Adoption Checklist + +Use this checklist before choosing a Slack connector function over a custom +function with external API calls. + +## Fit + +- The user request is part of a Slack Deno SDK workflow. +- The target service appears in Slack's official connector catalog. +- The requested operation is a close match to a listed connector function. +- The connector's inputs can be supplied by workflow inputs or previous steps. +- The connector's outputs are sufficient for the next workflow step or final + response. + +## Authentication and Ownership + +- The user wants to avoid implementing OAuth, token refresh, secret storage, or + an external API client. +- The team accepts Slack connector account setup as the authentication path. +- The implementation does not require storing external service credentials in + `.env`. +- The owner of the connected account and its permissions are clear. + +## Safety + +- The action's side effects are understood. +- Destructive, financial, hiring, contractual, production, or customer-data + changes have a confirmation or approval step. +- Failure handling is defined, including what message or fallback path users + see. +- Audit expectations are clear enough for the workflow's risk level. + +## Constraints + +- The workflow is not intended for Slack Connect external users. +- Workspace admin approval requirements are understood. +- Rate limits, connector-specific restrictions, and required external-service + permissions have been checked in the official docs. +- The connector is available in the user's Slack plan and workspace context. + +## Decision + +Prefer a connector when the fit is high and authentication should be handled by +Slack Platform setup. + +Prefer a Slack function when the action is Slack-native. + +Prefer a custom function when the connector catalog does not cover the +operation, the flow needs complex API orchestration, or this app must own +external auth and API behavior. diff --git a/.agents/skills/slack-connector-functions/references/connector-catalog-summary.md b/.agents/skills/slack-connector-functions/references/connector-catalog-summary.md new file mode 100644 index 0000000..fbfddf9 --- /dev/null +++ b/.agents/skills/slack-connector-functions/references/connector-catalog-summary.md @@ -0,0 +1,70 @@ +# Slack Connector Catalog Summary + +Source: +https://docs.slack.dev/tools/deno-slack-sdk/reference/connector-functions/ + +Checked: 2026-04-24 + +Slack connector functions are workflow steps for external services. The catalog +is broad and changes over time, so treat this file as an orientation aid, not a +complete authority. Always verify the selected connector in the official docs +before implementation. + +## Common Categories + +- Task, project, and issue tracking: Asana, Jira Cloud, Linear, GitHub, GitHub + Enterprise Server, GitLab, ClickUp, Monday, Workast, Wrike. +- Calendar, meeting, email, and messaging: Google Calendar, Google Mail, Google + Meet, Microsoft Outlook Calendar, Microsoft Outlook Email, Microsoft Teams, + Zoom, Webex, Twilio, Dialpad, RingCentral. +- Files, documents, spreadsheets, and knowledge: Google Sheets, Microsoft Excel, + Microsoft OneDrive, Microsoft OneNote, Box, Dropbox, Notion, Guru, Lucid, + Miro, Smartsheet. +- CRM, support, and ITSM: Salesforce, Zendesk, Intercom, ServiceNow. +- Incidents and operations: PagerDuty, FireHydrant, Rootly, LaunchDarkly, Travis + CI, Snyk. +- HR, recruiting, onboarding, contracts, and signatures: Greenhouse, Lever, + SmartRecruiters, Deel, DocuSign, Adobe Sign, Dropbox Sign. +- Marketing, forms, surveys, and content: Mailchimp, SurveyMonkey, Typeform, + Loopio, Giphy. +- Finance and business operations: Ramp. + +## Representative Operations + +- Create: issues, tasks, projects, events, records, folders, tickets, incidents, + campaigns, pages, meetings. +- Update: issues, tasks, spreadsheet rows, worksheet rows, feature flags, + incidents, tickets, records. +- Select/read: spreadsheet rows, worksheet rows, records, incidents, spend + requests, campaign reports. +- Send: email, SMS, signature requests, status updates, campaigns. +- Copy/move/delete/archive: files, documents, folders, pages, spreadsheet rows, + boards. + +## Useful Examples + +- Google Sheets: `add_spreadsheet_row`, `select_spreadsheet_row`, + `update_spreadsheet_row`, `delete_spreadsheet_row`. +- Microsoft Excel: `add_worksheet_row`, `select_worksheet_row`, + `update_worksheet_row`, `delete_worksheet_row`. +- GitHub: `create_issue`, `update_issue`. +- Jira Cloud: `create_issue`, `edit_issue`. +- Linear: `create_issue`, `update_issue`, `add_comment`, `create_project`. +- Salesforce: `create_record`, `read_record`, `update_record`, `delete_record`, + `run_flow`. +- Zendesk: `create_ticket`, `update_ticket`, `add_tags`. +- PagerDuty: `create_incident`, `resolve_incident`, `escalate_incident`, + `send_status_update`, `update_incident`. +- Google Calendar: `create_event`, `update_event`, `add_to_event`. +- Outlook Calendar: `create_event`. +- Zoom: `create_meeting`. + +## Keep Current + +Before implementation, open the target connector page and confirm: + +- Service namespace and function name. +- Input parameter names, required fields, and accepted Slack data types. +- Output fields needed by later workflow steps. +- Authentication and account connection requirements. +- Usage notes, limitations, or workspace/admin approval requirements. diff --git a/.agents/skills/slack-connector-functions/references/implementation-notes.md b/.agents/skills/slack-connector-functions/references/implementation-notes.md new file mode 100644 index 0000000..1131543 --- /dev/null +++ b/.agents/skills/slack-connector-functions/references/implementation-notes.md @@ -0,0 +1,51 @@ +# Implementation Notes for This Repo + +This repository is a Slack Deno SDK template with workflows, functions, +triggers, i18n, and validation patterns. + +## Repo Context + +- Slack SDK imports are configured in `import_map.json`. +- Example workflow code lives in `workflows/example_workflow.ts`. +- Custom function code lives under `functions/`. +- App manifest configuration lives in `manifest.ts`. +- Localized messages live in `locales/en.json` and `locales/ja.json`. + +## Implementation Pattern + +1. Start in `workflows/` and try to model the external SaaS operation as a + connector `addStep`. +2. Add custom functions only for validation, Slack-specific preparation, + post-processing, or unsupported logic. +3. Keep user-visible errors and labels aligned with the repository's i18n + conventions. +4. Add or adjust workflow inputs rather than hard-coding service identifiers + when users need reuse. +5. Update `manifest.ts` only when the workflow/function set or Slack scopes + actually change. +6. Do not add outgoing domains for a connector unless custom API calls are also + introduced. + +## Docs to Recheck + +- Connector catalog: + https://docs.slack.dev/tools/deno-slack-sdk/reference/connector-functions/ +- Connector guide: + https://docs.slack.dev/tools/deno-slack-sdk/guides/creating-connector-functions/ +- Function types overview: + https://docs.slack.dev/tools/deno-slack-sdk/guides/creating-functions/ + +## Validation + +Run the relevant checks after changes: + +```bash +deno task fmt +deno task lint +deno task check +deno task test +``` + +If connector imports are added, run `deno task check` against the changed +workflow files and verify that `import_map.json` uses the current import path +from the official Slack docs. diff --git a/.serena/project.yml b/.serena/project.yml index c0db2b4..e67c2f3 100644 --- a/.serena/project.yml +++ b/.serena/project.yml @@ -1,17 +1,9 @@ -# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) -# * For C, use cpp -# * For JavaScript, use typescript -# Special requirements: -# * csharp: Requires the presence of a .sln file in the project folder. -language: typescript - -# whether to use the project's gitignore file to ignore files -# Added on 2025-04-07 +# whether to use project's .gitignore files to ignore files ignore_all_files_in_gitignore: true -# list of additional paths to ignore -# same syntax as gitignore, so you can use * and ** -# Was previously called `ignored_dirs`, please update your config if you are using that. -# Added (renamed) on 2025-04-07 + +# list of additional paths to ignore in this project. +# Same syntax as gitignore, so you can use * and **. +# Note: global ignored_paths from serena_config.yml are also applied additively. ignored_paths: [] # whether the project is in read-only mode @@ -19,49 +11,142 @@ ignored_paths: [] # Added on 2025-04-18 read_only: false -# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# list of tool names to exclude. +# This extends the existing exclusions (e.g. from the global configuration) +# # Below is the complete list of tools for convenience. # To make sure you have the latest list of tools, and to view their descriptions, # execute `uv run scripts/print_tool_overview.py`. # -# * `activate_project`: Activates a project by name. +# * `activate_project`: Activates a project based on the project name or path. # * `check_onboarding_performed`: Checks whether project onboarding was already performed. # * `create_text_file`: Creates/overwrites a file in the project directory. -# * `delete_lines`: Deletes a range of lines within a file. -# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `delete_memory`: Delete a memory file. Should only happen if a user asks for it explicitly, +# for example by saying that the information retrieved from a memory file is no longer correct +# or no longer relevant for the project. +# * `edit_memory`: Replaces content matching a regular expression in a memory. # * `execute_shell_command`: Executes a shell command. -# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. -# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). -# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `find_file`: Finds files in the given relative paths +# * `find_referencing_symbols`: Finds symbols that reference the given symbol using the language server backend +# * `find_symbol`: Performs a global (or local) search using the language server backend. # * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. # * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file. -# * `initial_instructions`: Gets the initial instructions for the current project. -# Should only be used in settings where the system prompt cannot be set, -# e.g. in clients you have no control over, like Claude Desktop. +# * `initial_instructions`: Provides instructions Serena usage (i.e. the 'Serena Instructions Manual') +# for clients that do not read the initial instructions when the MCP server is connected. # * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. -# * `insert_at_line`: Inserts content at a given line in a file. # * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. # * `list_dir`: Lists files and directories in the given directory (optionally with recursion). -# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `list_memories`: List available memories. Any memory can be read using the `read_memory` tool. # * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). -# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). # * `read_file`: Reads a file within the project directory. -# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. -# * `remove_project`: Removes a project from the Serena configuration. -# * `replace_lines`: Replaces a range of lines within a file with new content. -# * `replace_symbol_body`: Replaces the full definition of a symbol. -# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `read_memory`: Read the content of a memory file. This tool should only be used if the information +# is relevant to the current task. You can infer whether the information +# is relevant from the memory file name. +# You should not read the same memory file multiple times in the same conversation. +# * `rename_memory`: Renames or moves a memory. Moving between project and global scope is supported +# (e.g., renaming "global/foo" to "bar" moves it from global to project scope). +# * `rename_symbol`: Renames a symbol throughout the codebase using language server refactoring capabilities. +# For JB, we use a separate tool. +# * `replace_content`: Replaces content in a file (optionally using regular expressions). +# * `replace_symbol_body`: Replaces the full definition of a symbol using the language server backend. +# * `safe_delete_symbol`: # * `search_for_pattern`: Performs a search for a pattern in the project. -# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. -# * `switch_modes`: Activates modes by providing a list of their names -# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. -# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. -# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. -# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +# * `write_memory`: Write some information (utf-8-encoded) about this project that can be useful for future tasks to a memory in md format. +# The memory name should be meaningful. excluded_tools: [] # initial prompt for the project. It will always be given to the LLM upon activating the project # (contrary to the memories, which are loaded on demand). initial_prompt: "" - +# the name by which the project can be referenced within Serena project_name: "slack-utils" + +# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default). +# This extends the existing inclusions (e.g. from the global configuration). +included_optional_tools: [] + +# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools. +# This cannot be combined with non-empty excluded_tools or included_optional_tools. +fixed_tools: [] + +# list of mode names to that are always to be included in the set of active modes +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the base_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this setting overrides the global configuration. +# Set this to [] to disable base modes for this project. +# Set this to a list of mode names to always include the respective modes for this project. +base_modes: + +# list of mode names that are to be activated by default. +# The full set of modes to be activated is base_modes + default_modes. +# If the setting is undefined, the default_modes from the global configuration (serena_config.yml) apply. +# Otherwise, this overrides the setting from the global configuration (serena_config.yml). +# This setting can, in turn, be overridden by CLI parameters (--mode). +default_modes: + +# time budget (seconds) per tool call for the retrieval of additional symbol information +# such as docstrings or parameter information. +# This overrides the corresponding setting in the global configuration; see the documentation there. +# If null or missing, use the setting from the global configuration. +symbol_info_budget: + +# The language backend to use for this project. +# If not set, the global setting from serena_config.yml is used. +# Valid values: LSP, JetBrains +# Note: the backend is fixed at startup. If a project with a different backend +# is activated post-init, an error will be returned. +language_backend: + +# line ending convention to use when writing source files. +# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default) +# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings. +line_ending: + +# list of regex patterns which, when matched, mark a memory entry as read‑only. +# Extends the list from the global configuration, merging the two lists. +read_only_memory_patterns: [] + +# list of regex patterns for memories to completely ignore. +# Matching memories will not appear in list_memories or activate_project output +# and cannot be accessed via read_memory or write_memory. +# To access ignored memory files, use the read_file tool on the raw file path. +# Extends the list from the global configuration, merging the two lists. +# Example: ["_archive/.*", "_episodes/.*"] +ignored_memory_patterns: [] + +# advanced configuration option allowing to configure language server-specific options. +# Maps the language key to the options. +# Have a look at the docstring of the constructors of the LS implementations within solidlsp (e.g., for C# or PHP) to see which options are available. +# No documentation on options means no options are available. +ls_specific_settings: {} + +# the encoding used by text files in the project +# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings +encoding: utf-8 + +# list of languages for which language servers are started; choose from: +# al bash clojure cpp csharp +# csharp_omnisharp dart elixir elm erlang +# fortran fsharp go groovy haskell +# haxe java julia kotlin lua +# markdown +# matlab nix pascal perl php +# php_phpactor powershell python python_jedi r +# rego ruby ruby_solargraph rust scala +# swift terraform toml typescript typescript_vts +# vue yaml zig +# (This list may be outdated. For the current list, see values of Language enum here: +# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py +# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.) +# Note: +# - For C, use cpp +# - For JavaScript, use typescript +# - For Free Pascal/Lazarus, use pascal +# Special requirements: +# Some languages require additional setup/installations. +# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers +# When using multiple languages, the first language server that supports a given file will be used for that file. +# The first language is the default language and the respective language server will be used as a fallback. +# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored. +languages: + - typescript diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3091e6d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,850 @@ +# Agent Rules for slack-utils + +このドキュメントはCodex、Claude、CursorなどのAIエージェントがslack-utilsプロジェクトで作業する際のルールとガイドラインを定義します。 + +## 📋 プロジェクト概要 + +**slack-utils** は Slack Deno SDK v2.x +を使用したSlackアプリケーション開発テンプレートです。 + +- **言語**: TypeScript (Deno v2互換) +- **主な用途**: Slack Functions、Workflows、Triggersの管理 +- **デプロイ**: Slack CLI を使用 +- **ライセンス**: MIT + +### プロジェクト構成 + +``` +slack-utils/ +├── AGENTS.md # AIエージェント共通の作業ルール +├── CLAUDE.md # Claude Code向けのAGENTS.md import stub +├── functions/ # Slack Functions(各関数にtest.tsを配置) +├── workflows/ # Slack Workflows +├── triggers/ # Slack Triggers +├── lib/ # 共通ライブラリ(i18nなど) +├── locales/ # 多言語対応(en.json, ja.json) +├── scripts/ # ビルド・デプロイスクリプト +├── docs/ # ドキュメント +├── .agents/skills/ # Codex/AIエージェント用スキル +├── .github/ # CI/CD設定 +├── deno.jsonc # Deno設定 +├── import_map.json # インポートマップ +└── manifest.ts # Slackアプリマニフェスト +``` + +## 🔍 コードベース検索・調査のルール + +### 必須ツールの使用 + +このプロジェクトでは以下のツールを**必ず使用**してください: + +#### 1. **Serena** - リポジトリ内コード検索 + +プロジェクト内のコード検索には`serena`を使用: + +```typescript +// ✅ 正しい使用例 +mcp_serena_find_symbol({ + name_path: "functionName", + relative_path: "functions/", +}); + +mcp_serena_search_for_pattern({ + substring_pattern: "エラーパターン", + relative_path: "lib/", +}); +``` + +**使用するシーン:** + +- 関数やクラスの定義を探す +- 特定のパターンやキーワードを検索 +- プロジェクト構造の理解 +- シンボルの参照箇所を調べる + +#### 2. **Context7** - モジュール・ライブラリ調査 + +外部モジュールやライブラリの調査には`context7`を使用: + +```typescript +// ✅ 正しい使用例 +// まずライブラリIDを解決 +mcp_Context7_resolve - library - id({ + libraryName: "deno slack sdk", +}); + +// 次にドキュメントを取得 +mcp_Context7_get - library - docs({ + context7CompatibleLibraryID: "/slackapi/deno-slack-sdk", + topic: "functions", +}); +``` + +**主要ライブラリ:** + +- `/slackapi/deno-slack-sdk` - Slack Deno SDK +- `/denoland/deno` - Deno runtime +- `/websites/deno` - Deno公式ドキュメント + +## 💻 技術スタック + +### Deno (v2.x) + +- 公式ドキュメント: `/websites/deno` +- セキュアなランタイム +- TypeScript標準サポート +- 組み込みテストランナー + +### Slack Deno SDK (v2.15.1+) + +- ライブラリID: `/slackapi/deno-slack-sdk` +- Slack Functions、Workflows、Triggersをサポート +- 最新のSlack Platform featuresに対応 + +### Slack Connector Functions + +外部SaaS連携をSlack +Workflowに追加する場合は、カスタムOAuth実装や独自APIクライアントを作る前に、公式Slack +Platform connector functionの採用可否を確認してください。 + +**使用するスキル:** + +- `.agents/skills/slack-connector-functions/SKILL.md` + +**確認するシーン:** + +- Google Sheets、Microsoft + Excel、GitHub、Jira、Linear、Salesforce、Zendesk、PagerDuty、ServiceNow、Asana、Notionなど外部サービスをWorkflow + stepから操作したい +- レコード作成・更新・取得、メール/SMS送信、会議作成、issue/task/incident作成などを行いたい +- OAuth、token + storage、refresh処理、外部API認証をこのリポジトリ側で実装したくない + +**注意:** + +- connector catalogにない操作、複雑なAPI制御、独自認証が必要な場合はCustom + Functionを検討する +- Slack Connect外部ユーザー向けWorkflowではconnector functionの制約を確認する +- 破壊的操作、金銭、契約、採用、本番運用、顧客データに関わる操作は確認・承認ステップを設ける + +## 📝 コーディング規約 + +### TypeScript + +- **Strictモード必須**: `deno.jsonc`で`strict: true`を設定 +- **暗黙的なanyを禁止**: 全ての型を明示的に定義 +- **JSDocコメント必須**: 全ての公開関数に追加 + +```typescript +/** + * Slackチャンネルの情報を取得します + * + * @param client - Slack APIクライアント + * @param channelId - 取得対象のチャンネルID + * @returns チャンネルの概要情報 + * @throws {Error} チャンネル情報の取得に失敗した場合 + */ +export async function retrieveChannelSummary( + client: SlackAPIClient, + channelId: string, +): Promise { + // 実装 +} +``` + +### インポート + +- **`import_map.json`を使用**: 相対パスを避ける +- **標準ライブラリ**: `std/` プレフィックスを使用 + +```typescript +// ✅ 良い例 +import { assertEquals } from "std/testing/asserts.ts"; +import { DefineFunction } from "deno-slack-sdk/mod.ts"; + +// ❌ 悪い例 +import { assertEquals } from "https://deno.land/std@0.200.0/testing/asserts.ts"; +``` + +### ファイル構造 + +``` +functions/example_function/ +├── mod.ts # 関数実装(JSDoc付き) +└── test.ts # テスト(正常系・異常系) +``` + +## 🧪 テストとJSDocの必須ルール + +### 新規関数作成時の必須事項 + +**重要**: 新しい関数やモジュールを作成する際は、以下を**必ず実施**してください。 + +#### 1. JSDocコメントの追加 + +````typescript +/** + * 関数の説明(日本語で明確に) + * + * @param paramName - パラメータの説明 + * @returns 戻り値の説明 + * @throws {Error} エラーが発生する条件 + * + * @example + * ```typescript + * const result = await functionName(client, "value"); + * console.log(result); + * ``` + */ +export async function functionName(...): Promise { + // 実装 +} +```` + +#### 2. テストファイルの作成 + +- **配置**: 関数と同じディレクトリに`test.ts` +- **命名**: 日本語で明確に(例: "正常にデータを取得できる") +- **カバレッジ**: 正常系と異常系の両方をテスト + +```typescript +import { assertEquals, assertRejects } from "std/testing/asserts.ts"; + +Deno.test("正常にチャンネル情報を取得できる", async () => { + // Arrange: 準備 + const mockClient = createMockClient(); + + // Act: 実行 + const result = await retrieveChannelSummary(mockClient, "C12345"); + + // Assert: 検証 + assertEquals(result.id, "C12345"); +}); + +Deno.test("チャンネルIDが無効な場合はエラーを返す", async () => { + const mockClient = createErrorClient(); + + await assertRejects( + () => retrieveChannelSummary(mockClient, "invalid"), + Error, + "Expected error message", + ); +}); +``` + +#### 3. モックの使用 + +- **Slack API**: 必ずモックを使用 +- **外部依存**: テスト時は全て mock化 + +参考: `functions/example_function/test.ts` + +## 🌍 I18n(多言語対応)ルール + +**重要**: +エラーメッセージやユーザー向けメッセージは**必ず**多言語化してください。 + +### 必須事項 + +#### 1. 直接文字列を使わず `t()` 関数を使用 + +```typescript +// ❌ 悪い例 +throw new Error("Failed to load channel info"); + +// ✅ 良い例 +import { t } from "../../lib/i18n/mod.ts"; +throw new Error(t("errors.channel_not_found", { error })); +``` + +#### 2. 英語をベース言語として `locales/en.json` に追加 + +```json +{ + "errors": { + "user_not_found": "User not found: {userId}" + }, + "messages": { + "success": "Operation completed successfully" + }, + "logs": { + "starting": "Starting workflow..." + } +} +``` + +#### 3. プレースホルダーの使用 + +```typescript +// ✅ 良い例 +t("messages.channel_summary", { + name: channel.name, + count: memberCount, +}) + + // ❌ 悪い例 + `Channel: ${channel.name}, Members: ${memberCount}`; +``` + +### 対象範囲 + +**i18n化が必要:** + +- ✅ エラーメッセージ(`throw new Error()`) +- ✅ ログメッセージ(`console.log()`, `console.error()`) +- ✅ ユーザー向けメッセージ(Slack応答など) +- ✅ 関数の戻り値に含まれるメッセージ + +**i18n化不要:** + +- ❌ コメント(日本語可) +- ❌ 変数名、関数名 +- ❌ デバッグ用の一時的な出力 + +詳細: `docs/i18n-guide.md` + +## 🔄 開発ワークフロー + +### コミット前の必須チェック + +```bash +# 1. フォーマットチェック +deno fmt --check + +# 2. リントチェック +deno lint + +# 3. 全テスト実行 +deno test --allow-all + +# 4. I18n整合性チェック +deno task i18n:check +``` + +### Git Hooks + +プロジェクトではGit Hooksが設定されています: + +- **pre-commit**: フォーマット・Lint +- **pre-push**: フォーマット・Lint・テスト + +セットアップ: + +```bash +bash scripts/setup-git-hooks.sh +``` + +### コミットメッセージ + +Conventional Commits形式を使用: + +``` +feat: 新機能追加 +fix: バグ修正 +docs: ドキュメント更新 +test: テスト追加・修正 +chore: ビルド・設定変更 +refactor: リファクタリング +``` + +## 📦 便利なタスク + +```bash +# フォーマット +deno task fmt + +# リント +deno task lint + +# テスト +deno task test + +# I18n関連 +deno task i18n:check # 整合性チェック +deno task i18n:test # I18nテスト + +# 型チェック +deno task check + +# 全チェック(CI相当) +deno task cursor-ci +``` + +## 🚫 禁止事項 + +### 絶対にやってはいけないこと + +1. **直接文字列のハードコード**(i18n化必須) +2. **テストなしの関数追加** +3. **JSDocコメントなしの公開関数** +4. **バリデーションなしの入力処理**(Zod使用必須) +5. **`package.json`の作成**(Denoプロジェクトです) +6. **暗黙的な`any`の使用** +7. **インラインHTTPS imports**(`import_map.json`を使用) + +### CI/CDでの注意 + +- **Denoバージョン**: v2.x を使用 +- **ロックファイル**: `deno.lock` v5形式 + +## 🤖 Claude Code Action(GitHub自動化) + +このプロジェクトでは、GitHub上でClaude Codeを自動実行できます。 + +### Issue → PR 自動実装 + +Issueに `claude-ready` ラベルを付けると、Claudeが自動で: + +1. Issueの内容を分析 +2. 必要なコードを実装 +3. テストを追加 +4. PRを作成 + +**使い方:** + +1. Issueを作成(実装してほしい内容を詳しく記述) +2. `claude-ready` ラベルを付与 +3. Claudeが自動でブランチを作成してPRを提出 + +### @claude メンション + +IssueやPRのコメントで `@claude` とメンションすると、Claudeが応答します。 + +``` +@claude このエラーの原因を調べてください +@claude テストを追加してください +@claude コードレビューをお願いします +``` + +### PR 自動コードレビュー + +PRが作成・更新されると、Claudeが自動でコードレビューを実行します。 + +**レビュー観点:** + +- CLAUDE.md準拠 +- 型安全性 +- エラーハンドリング +- i18n対応 +- テストカバレッジ +- セキュリティ + +### セットアップ + +1. リポジトリのSecrets に `ANTHROPIC_API_KEY` を追加 +2. ワークフローファイルは既に設定済み(`.github/workflows/issue-to-pr.yml`, + `.github/workflows/claude-pr-review.yml`) + +詳細: +[anthropics/claude-code-action](https://github.com/anthropics/claude-code-action) + +## 📚 参考ドキュメント + +### プロジェクト内 + +- `README.md` - セットアップとデプロイ手順 +- `CONTRIBUTING.md` - 貢献ガイドライン +- `docs/testing-guide.md` - テストガイド +- `docs/i18n-guide.md` - 多言語化ガイド +- `docs/github-actions.md` - CI/CDガイド + +### 外部ドキュメント(Context7) + +- `/slackapi/deno-slack-sdk` - Slack Deno SDK +- `/websites/deno` - Deno公式ドキュメント +- `/denoland/std` - Deno標準ライブラリ +- `/colinhacks/zod` - Zodバリデーションライブラリ + +## 🎯 ベストプラクティス + +### コード品質 + +1. **関数は小さく保つ**: 1関数1責任 +2. **エラーハンドリング**: 全てのAPI呼び出しをtry-catch +3. **型安全**: `unknown`より`具体的な型`を使用 +4. **テスト駆動**: 機能実装前にテストを書く + +### パフォーマンス + +1. **非同期処理**: `Promise.all()`で並列化 +2. **メモリ管理**: 大きなデータは適切にストリーム処理 +3. **キャッシュ**: I18nロケールデータはキャッシュ済み + +### セキュリティ + +1. **環境変数**: 機密情報は`.env`で管理 +2. **入力検証**: 全てのユーザー入力を検証 +3. **パーミッション**: 必要最小限の`--allow-*`フラグを使用 + +## 🔒 バリデーション(Zod) + +このプロジェクトでは、型安全なバリデーションのために**Zod**を使用しています。 + +### Zodの使用ルール + +#### 必須事項 + +1. **全ての入力値をZodで検証** + - ユーザー入力 + - API入力 + - 環境変数 + - 外部データソース + +2. **共通スキーマを優先使用** + - `lib/validation/schemas.ts` の既存スキーマを使用 + - 新規スキーマは同ファイルに追加 + +3. **型推論を活用** + - `z.infer` で型を自動生成 + - 手動で型定義を重複させない + +#### 基本パターン + +```typescript +import { channelIdSchema } from "../../lib/validation/schemas.ts"; + +// パターン1: parse(エラー時は例外をthrow) +const channelId = channelIdSchema.parse(inputs.channel_id); + +// パターン2: safeParse(エラー時は結果オブジェクト) +const result = channelIdSchema.safeParse(inputs.channel_id); +if (!result.success) { + throw new Error(result.error.message); +} +``` + +### 利用可能なスキーマ + +```typescript +// Slackチャンネル ID +channelIdSchema; // 例: "C12345678" + +// Slackユーザー ID +userIdSchema; // 例: "U0812GLUZD2" または "W1234567890" + +// 空でない文字列 +nonEmptyStringSchema; // 最低1文字以上 +``` + +### 新規スキーマの追加 + +```typescript +// lib/validation/schemas.ts に追加 +/** + * メールアドレス スキーマ + */ +export const emailSchema = z.string() + .email("Invalid email format") + .toLowerCase(); + +export type Email = z.infer; +``` + +### テストの追加 + +新規スキーマには必ずテストを追加: + +```typescript +// lib/validation/test.ts に追加 +Deno.test("emailSchema: 正常なメールアドレスを検証", () => { + const result = emailSchema.safeParse("test@example.com"); + assertEquals(result.success, true); +}); +``` + +### エラーメッセージの多言語化(i18n) + +Zodのエラーメッセージは**動的に多言語化**されます。`.superRefine()`による実装により、バリデーション実行時に現在のロケールに応じたエラーメッセージが生成されます: + +```typescript +import { channelIdSchema } from "../../lib/validation/schemas.ts"; +import { setLocale } from "../../lib/i18n/mod.ts"; + +// 英語でバリデーション実行 +setLocale("en"); +const result1 = channelIdSchema.safeParse("invalid"); +// エラー: "Channel ID must start with 'C' followed by uppercase alphanumeric characters" + +// 同じスキーマインスタンスで日本語に切り替え +setLocale("ja"); +const result2 = channelIdSchema.safeParse("invalid"); +// エラー: "チャンネルIDは'C'で始まり、その後に大文字の英数字が続く必要があります" + +// 英語に戻す +setLocale("en"); +const result3 = channelIdSchema.safeParse("invalid"); +// エラー: "Channel ID must start with 'C' followed by uppercase alphanumeric characters" +``` + +**実装の特徴:** + +- **動的評価**: `.superRefine()`により、`t()`関数が検証時に毎回呼ばれます +- **デフォルトスキーマ対応**: `channelIdSchema`等もロケール変更に自動対応 +- **スキーマ再作成不要**: 同じインスタンスで異なるロケールに対応 +- **レビューフィードバック対応**: + エラーメッセージが検証時まで評価されないため、ロケール変更を正しく反映 + +**ファクトリー関数(オプション):** + +後方互換性のため、ファクトリー関数(`createChannelIdSchema()`等)も提供されていますが、 +デフォルトスキーマも動的に対応するため、使用は任意です。 + +**新規スキーマのi18n化例:** + +```typescript +// lib/validation/schemas.ts +import { z } from "zod"; +import { t } from "../i18n/mod.ts"; + +/** + * メールアドレス スキーマを生成(i18n対応) + */ +export function createEmailSchema() { + return z.string().superRefine((val, ctx) => { + // メールアドレス形式チェック + if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) { + ctx.addIssue({ + code: z.ZodIssueCode.invalid_string, + validation: "email", + message: t("errors.validation.email_format"), + }); + } + }).transform((val) => val.toLowerCase()); +} + +// デフォルトエクスポート(動的i18n対応) +export const emailSchema = createEmailSchema(); + +// 型推論 +export type Email = z.infer>; +``` + +そして、`locales/en.json`にエラーメッセージを追加: + +```json +{ + "errors": { + "validation": { + "email_format": "Invalid email format" + } + } +} +``` + +**ポイント:** + +- `.superRefine()`で検証ロジックを実装 +- `t()`関数は検証時に呼ばれるため、ロケール変更に自動対応 +- デフォルトエクスポートも動的に多言語化される + +## 🚨 例外処理ルール + +**重要**: API通信とバリデーションでは、必ず適切な例外処理を実装してください。 + +### API通信の例外処理 + +Slack +API呼び出しでは、必ず`response.ok`をチェックしてからデータにアクセスします。 + +**✅ 正しい例:** + +```typescript +/** + * Slack API呼び出しの例外処理 + */ +async function callSlackAPI(client: SlackAPIClient, channelId: string) { + const response = await client.conversations.info({ channel: channelId }); + + // ✅ 必須: response.okをチェック + if (!response.ok) { + // ✅ 必須: エラーメッセージをi18n化 + const errorCode = response.error ?? "unknown_error"; + throw new Error(t("errors.api_call_failed", { error: errorCode })); + } + + // ✅ 必須: データの存在チェック + if (!response.channel) { + throw new Error(t("errors.data_not_found")); + } + + return response.channel; +} +``` + +**理由:** + +- `response.ok`をチェックしないと、undefinedデータにアクセスしてしまう +- エラーメッセージは必ずi18n化(多言語対応) +- フォールバック値を提供(`??` 演算子使用) + +### バリデーションの例外処理 + +入力値は必ず型チェックとフォーマット検証を行います。 + +**✅ 正しい例:** + +```typescript +/** + * 入力値のバリデーション + */ +function validateInput(input: unknown): string { + // ✅ 必須: 型ガードを使用 + if (typeof input !== "string") { + throw new Error(t("errors.invalid_type", { + expected: "string", + actual: typeof input, + })); + } + + // ✅ 必須: 空文字チェック + if (input.trim().length === 0) { + throw new Error(t("errors.empty_value")); + } + + // ✅ 推奨: フォーマットチェック(必要に応じて) + if (!/^[A-Z0-9]+$/.test(input)) { + throw new Error(t("errors.invalid_format", { + field: "channel_id", + pattern: "uppercase alphanumeric", + })); + } + + return input; +} +``` + +### Slack関数のエラーハンドリング + +Slack関数全体をtry-catchでラップし、エラーを適切に処理します。 + +**✅ 正しい例:** + +```typescript +export default SlackFunction( + MyFunctionDefinition, + async ({ inputs, client }) => { + try { + // ✅ 必須: 入力値のバリデーション + const validatedInput = validateInput(inputs.channel_id); + + // ✅ 必須: API呼び出し + const result = await callSlackAPI(client, validatedInput); + + // ✅ 必須: 成功時はoutputsを返す + return { outputs: { result } }; + } catch (error) { + // ✅ 必須: エラーメッセージの型安全な取得 + const message = error instanceof Error ? error.message : String(error); + + // ✅ 必須: エラーをログ出力(デバッグ用) + console.error("Function error:", message); + + // ✅ 必須: errorプロパティで返す + return { error: message }; + } + }, +); +``` + +### 禁止事項 + +**❌ やってはいけないこと:** + +```typescript +// ❌ 文字列を直接throw +throw "Something went wrong"; + +// ❌ response.okをチェックせずにデータアクセス +const channel = response.channel.name; // response.okがfalseの場合、undefinedエラー + +// ❌ エラーメッセージをハードコード +throw new Error("Channel not found"); + +// ❌ エラーを無視 +try { + await client.api.call(); +} catch (error) { + // 何もしない - これは危険! +} + +// ❌ 汎用的すぎるエラー +throw new Error("Error"); +``` + +### エラーメッセージのi18n化 + +全てのエラーメッセージは`locales/en.json`に定義し、`t()`関数で取得します: + +```json +{ + "errors": { + "api_call_failed": "API call failed: {error}", + "data_not_found": "Required data not found", + "invalid_type": "Invalid type: expected {expected}, got {actual}", + "empty_value": "Value cannot be empty", + "invalid_format": "Invalid format for {field}: expected {pattern}" + } +} +``` + +使用例: + +```typescript +throw new Error(t("errors.api_call_failed", { error: errorCode })); +``` + +## 🤖 AI開発時の推奨フロー + +### 1. 調査フェーズ + +```typescript +// Serenaでコードベース内検索 +mcp_serena_find_symbol({ name_path: "関数名" }); +mcp_serena_search_for_pattern({ substring_pattern: "パターン" }); + +// Context7で外部ライブラリ調査 +mcp_Context7_resolve - library - id({ libraryName: "deno" }); +mcp_Context7_get - library - docs({ + context7CompatibleLibraryID: "/denoland/deno", + topic: "testing", +}); +``` + +### 2. 実装フェーズ + +1. JSDocコメントを書く +2. 関数のインターフェースを定義 +3. Zodスキーマを定義(バリデーション) +4. テストケースを書く +5. 実装する +6. I18n化する + +### 3. 検証フェーズ + +```bash +deno task cursor-ci # 全チェック実行 +``` + +## 🎊 完了時の音声フィードバック + +タスク完了時は、ずんだもんによる音声フィードバックを実行: + +```typescript +mcp_voicevox_speak({ + text: "完了内容を1-2文で説明する文章のだ!", + speaker: 3, // ずんだもん:ノーマル + immediate: true, + waitForStart: true, +}); +``` + +## 📝 最後に + +- **常にドキュメントを参照**: 不明点は`docs/`を確認 +- **Serenaで検索**: コードベース内の情報はSerenaで +- **Context7で調査**: 外部ライブラリはContext7で +- **テストは必須**: 全ての新機能にテストを追加 +- **I18nを忘れずに**: ユーザー向けメッセージは必ず多言語化 +- **Zodでバリデーション**: 全ての入力値を型安全に検証 + +**Happy Coding! 🚀** diff --git a/CLAUDE.md b/CLAUDE.md index ef746ea..43c994c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,822 +1 @@ -# Claude Rules for slack-utils - -このドキュメントはClaude/Cursor -AIがslack-utilsプロジェクトで作業する際のルールとガイドラインを定義します。 - -## 📋 プロジェクト概要 - -**slack-utils** は Slack Deno SDK v2.x -を使用したSlackアプリケーション開発テンプレートです。 - -- **言語**: TypeScript (Deno v2互換) -- **主な用途**: Slack Functions、Workflows、Triggersの管理 -- **デプロイ**: Slack CLI を使用 -- **ライセンス**: MIT - -### プロジェクト構成 - -``` -slack-utils/ -├── functions/ # Slack Functions(各関数にtest.tsを配置) -├── workflows/ # Slack Workflows -├── triggers/ # Slack Triggers -├── lib/ # 共通ライブラリ(i18nなど) -├── locales/ # 多言語対応(en.json, ja.json) -├── scripts/ # ビルド・デプロイスクリプト -├── docs/ # ドキュメント -├── .github/ # CI/CD設定 -├── deno.jsonc # Deno設定 -├── import_map.json # インポートマップ -└── manifest.ts # Slackアプリマニフェスト -``` - -## 🔍 コードベース検索・調査のルール - -### 必須ツールの使用 - -このプロジェクトでは以下のツールを**必ず使用**してください: - -#### 1. **Serena** - リポジトリ内コード検索 - -プロジェクト内のコード検索には`serena`を使用: - -```typescript -// ✅ 正しい使用例 -mcp_serena_find_symbol({ - name_path: "functionName", - relative_path: "functions/", -}); - -mcp_serena_search_for_pattern({ - substring_pattern: "エラーパターン", - relative_path: "lib/", -}); -``` - -**使用するシーン:** - -- 関数やクラスの定義を探す -- 特定のパターンやキーワードを検索 -- プロジェクト構造の理解 -- シンボルの参照箇所を調べる - -#### 2. **Context7** - モジュール・ライブラリ調査 - -外部モジュールやライブラリの調査には`context7`を使用: - -```typescript -// ✅ 正しい使用例 -// まずライブラリIDを解決 -mcp_Context7_resolve - library - id({ - libraryName: "deno slack sdk", -}); - -// 次にドキュメントを取得 -mcp_Context7_get - library - docs({ - context7CompatibleLibraryID: "/slackapi/deno-slack-sdk", - topic: "functions", -}); -``` - -**主要ライブラリ:** - -- `/slackapi/deno-slack-sdk` - Slack Deno SDK -- `/denoland/deno` - Deno runtime -- `/websites/deno` - Deno公式ドキュメント - -## 💻 技術スタック - -### Deno (v2.x) - -- 公式ドキュメント: `/websites/deno` -- セキュアなランタイム -- TypeScript標準サポート -- 組み込みテストランナー - -### Slack Deno SDK (v2.15.1+) - -- ライブラリID: `/slackapi/deno-slack-sdk` -- Slack Functions、Workflows、Triggersをサポート -- 最新のSlack Platform featuresに対応 - -## 📝 コーディング規約 - -### TypeScript - -- **Strictモード必須**: `deno.jsonc`で`strict: true`を設定 -- **暗黙的なanyを禁止**: 全ての型を明示的に定義 -- **JSDocコメント必須**: 全ての公開関数に追加 - -```typescript -/** - * Slackチャンネルの情報を取得します - * - * @param client - Slack APIクライアント - * @param channelId - 取得対象のチャンネルID - * @returns チャンネルの概要情報 - * @throws {Error} チャンネル情報の取得に失敗した場合 - */ -export async function retrieveChannelSummary( - client: SlackAPIClient, - channelId: string, -): Promise { - // 実装 -} -``` - -### インポート - -- **`import_map.json`を使用**: 相対パスを避ける -- **標準ライブラリ**: `std/` プレフィックスを使用 - -```typescript -// ✅ 良い例 -import { assertEquals } from "std/testing/asserts.ts"; -import { DefineFunction } from "deno-slack-sdk/mod.ts"; - -// ❌ 悪い例 -import { assertEquals } from "https://deno.land/std@0.200.0/testing/asserts.ts"; -``` - -### ファイル構造 - -``` -functions/example_function/ -├── mod.ts # 関数実装(JSDoc付き) -└── test.ts # テスト(正常系・異常系) -``` - -## 🧪 テストとJSDocの必須ルール - -### 新規関数作成時の必須事項 - -**重要**: 新しい関数やモジュールを作成する際は、以下を**必ず実施**してください。 - -#### 1. JSDocコメントの追加 - -````typescript -/** - * 関数の説明(日本語で明確に) - * - * @param paramName - パラメータの説明 - * @returns 戻り値の説明 - * @throws {Error} エラーが発生する条件 - * - * @example - * ```typescript - * const result = await functionName(client, "value"); - * console.log(result); - * ``` - */ -export async function functionName(...): Promise { - // 実装 -} -```` - -#### 2. テストファイルの作成 - -- **配置**: 関数と同じディレクトリに`test.ts` -- **命名**: 日本語で明確に(例: "正常にデータを取得できる") -- **カバレッジ**: 正常系と異常系の両方をテスト - -```typescript -import { assertEquals, assertRejects } from "std/testing/asserts.ts"; - -Deno.test("正常にチャンネル情報を取得できる", async () => { - // Arrange: 準備 - const mockClient = createMockClient(); - - // Act: 実行 - const result = await retrieveChannelSummary(mockClient, "C12345"); - - // Assert: 検証 - assertEquals(result.id, "C12345"); -}); - -Deno.test("チャンネルIDが無効な場合はエラーを返す", async () => { - const mockClient = createErrorClient(); - - await assertRejects( - () => retrieveChannelSummary(mockClient, "invalid"), - Error, - "Expected error message", - ); -}); -``` - -#### 3. モックの使用 - -- **Slack API**: 必ずモックを使用 -- **外部依存**: テスト時は全て mock化 - -参考: `functions/example_function/test.ts` - -## 🌍 I18n(多言語対応)ルール - -**重要**: -エラーメッセージやユーザー向けメッセージは**必ず**多言語化してください。 - -### 必須事項 - -#### 1. 直接文字列を使わず `t()` 関数を使用 - -```typescript -// ❌ 悪い例 -throw new Error("Failed to load channel info"); - -// ✅ 良い例 -import { t } from "../../lib/i18n/mod.ts"; -throw new Error(t("errors.channel_not_found", { error })); -``` - -#### 2. 英語をベース言語として `locales/en.json` に追加 - -```json -{ - "errors": { - "user_not_found": "User not found: {userId}" - }, - "messages": { - "success": "Operation completed successfully" - }, - "logs": { - "starting": "Starting workflow..." - } -} -``` - -#### 3. プレースホルダーの使用 - -```typescript -// ✅ 良い例 -t("messages.channel_summary", { - name: channel.name, - count: memberCount, -}) - - // ❌ 悪い例 - `Channel: ${channel.name}, Members: ${memberCount}`; -``` - -### 対象範囲 - -**i18n化が必要:** - -- ✅ エラーメッセージ(`throw new Error()`) -- ✅ ログメッセージ(`console.log()`, `console.error()`) -- ✅ ユーザー向けメッセージ(Slack応答など) -- ✅ 関数の戻り値に含まれるメッセージ - -**i18n化不要:** - -- ❌ コメント(日本語可) -- ❌ 変数名、関数名 -- ❌ デバッグ用の一時的な出力 - -詳細: `docs/i18n-guide.md` - -## 🔄 開発ワークフロー - -### コミット前の必須チェック - -```bash -# 1. フォーマットチェック -deno fmt --check - -# 2. リントチェック -deno lint - -# 3. 全テスト実行 -deno test --allow-all - -# 4. I18n整合性チェック -deno task i18n:check -``` - -### Git Hooks - -プロジェクトではGit Hooksが設定されています: - -- **pre-commit**: フォーマット・Lint -- **pre-push**: フォーマット・Lint・テスト - -セットアップ: - -```bash -bash scripts/setup-git-hooks.sh -``` - -### コミットメッセージ - -Conventional Commits形式を使用: - -``` -feat: 新機能追加 -fix: バグ修正 -docs: ドキュメント更新 -test: テスト追加・修正 -chore: ビルド・設定変更 -refactor: リファクタリング -``` - -## 📦 便利なタスク - -```bash -# フォーマット -deno task fmt - -# リント -deno task lint - -# テスト -deno task test - -# I18n関連 -deno task i18n:check # 整合性チェック -deno task i18n:test # I18nテスト - -# 型チェック -deno task check - -# 全チェック(CI相当) -deno task cursor-ci -``` - -## 🚫 禁止事項 - -### 絶対にやってはいけないこと - -1. **直接文字列のハードコード**(i18n化必須) -2. **テストなしの関数追加** -3. **JSDocコメントなしの公開関数** -4. **バリデーションなしの入力処理**(Zod使用必須) -5. **`package.json`の作成**(Denoプロジェクトです) -6. **暗黙的な`any`の使用** -7. **インラインHTTPS imports**(`import_map.json`を使用) - -### CI/CDでの注意 - -- **Denoバージョン**: v2.x を使用 -- **ロックファイル**: `deno.lock` v5形式 - -## 🤖 Claude Code Action(GitHub自動化) - -このプロジェクトでは、GitHub上でClaude Codeを自動実行できます。 - -### Issue → PR 自動実装 - -Issueに `claude-ready` ラベルを付けると、Claudeが自動で: - -1. Issueの内容を分析 -2. 必要なコードを実装 -3. テストを追加 -4. PRを作成 - -**使い方:** - -1. Issueを作成(実装してほしい内容を詳しく記述) -2. `claude-ready` ラベルを付与 -3. Claudeが自動でブランチを作成してPRを提出 - -### @claude メンション - -IssueやPRのコメントで `@claude` とメンションすると、Claudeが応答します。 - -``` -@claude このエラーの原因を調べてください -@claude テストを追加してください -@claude コードレビューをお願いします -``` - -### PR 自動コードレビュー - -PRが作成・更新されると、Claudeが自動でコードレビューを実行します。 - -**レビュー観点:** - -- CLAUDE.md準拠 -- 型安全性 -- エラーハンドリング -- i18n対応 -- テストカバレッジ -- セキュリティ - -### セットアップ - -1. リポジトリのSecrets に `ANTHROPIC_API_KEY` を追加 -2. ワークフローファイルは既に設定済み(`.github/workflows/issue-to-pr.yml`, - `.github/workflows/claude-pr-review.yml`) - -詳細: -[anthropics/claude-code-action](https://github.com/anthropics/claude-code-action) - -## 📚 参考ドキュメント - -### プロジェクト内 - -- `README.md` - セットアップとデプロイ手順 -- `CONTRIBUTING.md` - 貢献ガイドライン -- `docs/testing-guide.md` - テストガイド -- `docs/i18n-guide.md` - 多言語化ガイド -- `docs/github-actions.md` - CI/CDガイド - -### 外部ドキュメント(Context7) - -- `/slackapi/deno-slack-sdk` - Slack Deno SDK -- `/websites/deno` - Deno公式ドキュメント -- `/denoland/std` - Deno標準ライブラリ -- `/colinhacks/zod` - Zodバリデーションライブラリ - -## 🎯 ベストプラクティス - -### コード品質 - -1. **関数は小さく保つ**: 1関数1責任 -2. **エラーハンドリング**: 全てのAPI呼び出しをtry-catch -3. **型安全**: `unknown`より`具体的な型`を使用 -4. **テスト駆動**: 機能実装前にテストを書く - -### パフォーマンス - -1. **非同期処理**: `Promise.all()`で並列化 -2. **メモリ管理**: 大きなデータは適切にストリーム処理 -3. **キャッシュ**: I18nロケールデータはキャッシュ済み - -### セキュリティ - -1. **環境変数**: 機密情報は`.env`で管理 -2. **入力検証**: 全てのユーザー入力を検証 -3. **パーミッション**: 必要最小限の`--allow-*`フラグを使用 - -## 🔒 バリデーション(Zod) - -このプロジェクトでは、型安全なバリデーションのために**Zod**を使用しています。 - -### Zodの使用ルール - -#### 必須事項 - -1. **全ての入力値をZodで検証** - - ユーザー入力 - - API入力 - - 環境変数 - - 外部データソース - -2. **共通スキーマを優先使用** - - `lib/validation/schemas.ts` の既存スキーマを使用 - - 新規スキーマは同ファイルに追加 - -3. **型推論を活用** - - `z.infer` で型を自動生成 - - 手動で型定義を重複させない - -#### 基本パターン - -```typescript -import { channelIdSchema } from "../../lib/validation/schemas.ts"; - -// パターン1: parse(エラー時は例外をthrow) -const channelId = channelIdSchema.parse(inputs.channel_id); - -// パターン2: safeParse(エラー時は結果オブジェクト) -const result = channelIdSchema.safeParse(inputs.channel_id); -if (!result.success) { - throw new Error(result.error.message); -} -``` - -### 利用可能なスキーマ - -```typescript -// Slackチャンネル ID -channelIdSchema; // 例: "C12345678" - -// Slackユーザー ID -userIdSchema; // 例: "U0812GLUZD2" または "W1234567890" - -// 空でない文字列 -nonEmptyStringSchema; // 最低1文字以上 -``` - -### 新規スキーマの追加 - -```typescript -// lib/validation/schemas.ts に追加 -/** - * メールアドレス スキーマ - */ -export const emailSchema = z.string() - .email("Invalid email format") - .toLowerCase(); - -export type Email = z.infer; -``` - -### テストの追加 - -新規スキーマには必ずテストを追加: - -```typescript -// lib/validation/test.ts に追加 -Deno.test("emailSchema: 正常なメールアドレスを検証", () => { - const result = emailSchema.safeParse("test@example.com"); - assertEquals(result.success, true); -}); -``` - -### エラーメッセージの多言語化(i18n) - -Zodのエラーメッセージは**動的に多言語化**されます。`.superRefine()`による実装により、バリデーション実行時に現在のロケールに応じたエラーメッセージが生成されます: - -```typescript -import { channelIdSchema } from "../../lib/validation/schemas.ts"; -import { setLocale } from "../../lib/i18n/mod.ts"; - -// 英語でバリデーション実行 -setLocale("en"); -const result1 = channelIdSchema.safeParse("invalid"); -// エラー: "Channel ID must start with 'C' followed by uppercase alphanumeric characters" - -// 同じスキーマインスタンスで日本語に切り替え -setLocale("ja"); -const result2 = channelIdSchema.safeParse("invalid"); -// エラー: "チャンネルIDは'C'で始まり、その後に大文字の英数字が続く必要があります" - -// 英語に戻す -setLocale("en"); -const result3 = channelIdSchema.safeParse("invalid"); -// エラー: "Channel ID must start with 'C' followed by uppercase alphanumeric characters" -``` - -**実装の特徴:** - -- **動的評価**: `.superRefine()`により、`t()`関数が検証時に毎回呼ばれます -- **デフォルトスキーマ対応**: `channelIdSchema`等もロケール変更に自動対応 -- **スキーマ再作成不要**: 同じインスタンスで異なるロケールに対応 -- **レビューフィードバック対応**: - エラーメッセージが検証時まで評価されないため、ロケール変更を正しく反映 - -**ファクトリー関数(オプション):** - -後方互換性のため、ファクトリー関数(`createChannelIdSchema()`等)も提供されていますが、 -デフォルトスキーマも動的に対応するため、使用は任意です。 - -**新規スキーマのi18n化例:** - -```typescript -// lib/validation/schemas.ts -import { z } from "zod"; -import { t } from "../i18n/mod.ts"; - -/** - * メールアドレス スキーマを生成(i18n対応) - */ -export function createEmailSchema() { - return z.string().superRefine((val, ctx) => { - // メールアドレス形式チェック - if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val)) { - ctx.addIssue({ - code: z.ZodIssueCode.invalid_string, - validation: "email", - message: t("errors.validation.email_format"), - }); - } - }).transform((val) => val.toLowerCase()); -} - -// デフォルトエクスポート(動的i18n対応) -export const emailSchema = createEmailSchema(); - -// 型推論 -export type Email = z.infer>; -``` - -そして、`locales/en.json`にエラーメッセージを追加: - -```json -{ - "errors": { - "validation": { - "email_format": "Invalid email format" - } - } -} -``` - -**ポイント:** - -- `.superRefine()`で検証ロジックを実装 -- `t()`関数は検証時に呼ばれるため、ロケール変更に自動対応 -- デフォルトエクスポートも動的に多言語化される - -## 🚨 例外処理ルール - -**重要**: API通信とバリデーションでは、必ず適切な例外処理を実装してください。 - -### API通信の例外処理 - -Slack -API呼び出しでは、必ず`response.ok`をチェックしてからデータにアクセスします。 - -**✅ 正しい例:** - -```typescript -/** - * Slack API呼び出しの例外処理 - */ -async function callSlackAPI(client: SlackAPIClient, channelId: string) { - const response = await client.conversations.info({ channel: channelId }); - - // ✅ 必須: response.okをチェック - if (!response.ok) { - // ✅ 必須: エラーメッセージをi18n化 - const errorCode = response.error ?? "unknown_error"; - throw new Error(t("errors.api_call_failed", { error: errorCode })); - } - - // ✅ 必須: データの存在チェック - if (!response.channel) { - throw new Error(t("errors.data_not_found")); - } - - return response.channel; -} -``` - -**理由:** - -- `response.ok`をチェックしないと、undefinedデータにアクセスしてしまう -- エラーメッセージは必ずi18n化(多言語対応) -- フォールバック値を提供(`??` 演算子使用) - -### バリデーションの例外処理 - -入力値は必ず型チェックとフォーマット検証を行います。 - -**✅ 正しい例:** - -```typescript -/** - * 入力値のバリデーション - */ -function validateInput(input: unknown): string { - // ✅ 必須: 型ガードを使用 - if (typeof input !== "string") { - throw new Error(t("errors.invalid_type", { - expected: "string", - actual: typeof input, - })); - } - - // ✅ 必須: 空文字チェック - if (input.trim().length === 0) { - throw new Error(t("errors.empty_value")); - } - - // ✅ 推奨: フォーマットチェック(必要に応じて) - if (!/^[A-Z0-9]+$/.test(input)) { - throw new Error(t("errors.invalid_format", { - field: "channel_id", - pattern: "uppercase alphanumeric", - })); - } - - return input; -} -``` - -### Slack関数のエラーハンドリング - -Slack関数全体をtry-catchでラップし、エラーを適切に処理します。 - -**✅ 正しい例:** - -```typescript -export default SlackFunction( - MyFunctionDefinition, - async ({ inputs, client }) => { - try { - // ✅ 必須: 入力値のバリデーション - const validatedInput = validateInput(inputs.channel_id); - - // ✅ 必須: API呼び出し - const result = await callSlackAPI(client, validatedInput); - - // ✅ 必須: 成功時はoutputsを返す - return { outputs: { result } }; - } catch (error) { - // ✅ 必須: エラーメッセージの型安全な取得 - const message = error instanceof Error ? error.message : String(error); - - // ✅ 必須: エラーをログ出力(デバッグ用) - console.error("Function error:", message); - - // ✅ 必須: errorプロパティで返す - return { error: message }; - } - }, -); -``` - -### 禁止事項 - -**❌ やってはいけないこと:** - -```typescript -// ❌ 文字列を直接throw -throw "Something went wrong"; - -// ❌ response.okをチェックせずにデータアクセス -const channel = response.channel.name; // response.okがfalseの場合、undefinedエラー - -// ❌ エラーメッセージをハードコード -throw new Error("Channel not found"); - -// ❌ エラーを無視 -try { - await client.api.call(); -} catch (error) { - // 何もしない - これは危険! -} - -// ❌ 汎用的すぎるエラー -throw new Error("Error"); -``` - -### エラーメッセージのi18n化 - -全てのエラーメッセージは`locales/en.json`に定義し、`t()`関数で取得します: - -```json -{ - "errors": { - "api_call_failed": "API call failed: {error}", - "data_not_found": "Required data not found", - "invalid_type": "Invalid type: expected {expected}, got {actual}", - "empty_value": "Value cannot be empty", - "invalid_format": "Invalid format for {field}: expected {pattern}" - } -} -``` - -使用例: - -```typescript -throw new Error(t("errors.api_call_failed", { error: errorCode })); -``` - -## 🤖 AI開発時の推奨フロー - -### 1. 調査フェーズ - -```typescript -// Serenaでコードベース内検索 -mcp_serena_find_symbol({ name_path: "関数名" }); -mcp_serena_search_for_pattern({ substring_pattern: "パターン" }); - -// Context7で外部ライブラリ調査 -mcp_Context7_resolve - library - id({ libraryName: "deno" }); -mcp_Context7_get - library - docs({ - context7CompatibleLibraryID: "/denoland/deno", - topic: "testing", -}); -``` - -### 2. 実装フェーズ - -1. JSDocコメントを書く -2. 関数のインターフェースを定義 -3. Zodスキーマを定義(バリデーション) -4. テストケースを書く -5. 実装する -6. I18n化する - -### 3. 検証フェーズ - -```bash -deno task cursor-ci # 全チェック実行 -``` - -## 🎊 完了時の音声フィードバック - -タスク完了時は、ずんだもんによる音声フィードバックを実行: - -```typescript -mcp_voicevox_speak({ - text: "完了内容を1-2文で説明する文章のだ!", - speaker: 3, // ずんだもん:ノーマル - immediate: true, - waitForStart: true, -}); -``` - -## 📝 最後に - -- **常にドキュメントを参照**: 不明点は`docs/`を確認 -- **Serenaで検索**: コードベース内の情報はSerenaで -- **Context7で調査**: 外部ライブラリはContext7で -- **テストは必須**: 全ての新機能にテストを追加 -- **I18nを忘れずに**: ユーザー向けメッセージは必ず多言語化 -- **Zodでバリデーション**: 全ての入力値を型安全に検証 - -**Happy Coding! 🚀** +@AGENTS.md