commit 53f8448ca77b41d547a425783373e36f06e1f5f8 Author: Zhongwei Li Date: Sat Nov 29 18:17:37 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..396f493 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "dev-tools", + "description": "Development tools: agents for coding, refactoring, and architecture", + "version": "1.0.0", + "author": { + "name": "Danil Pismenny", + "email": "danilpismenny@gmail.com" + }, + "skills": [ + "./skills" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..f23a64c --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# dev-tools + +Development tools: agents for coding, refactoring, and architecture diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..9e7c71d --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,69 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:dapi/claude-code-marketplace:dev-tools", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "dfaee878adc4dd11d42fa70e6210d2fc70f61e06", + "treeHash": "3fc97ae028e5ec54bcc32647a973b966d83ad2c4acc9f20a1eeac20ae3827d2a", + "generatedAt": "2025-11-28T10:16:01.997856Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "dev-tools", + "description": "Development tools: agents for coding, refactoring, and architecture", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "59427e1d410bb8f3a76f36b89f5352e6ba743eb5302648235b5ff3e4863d4f38" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "614079dd673f259b91af147fa7fd0efb35a5657d649207a9d40d3e2bee23cb7b" + }, + { + "path": "skills/bugsnag/bugsnag_helper.rb", + "sha256": "ba6241914c3b207b49c82bf79e87643b05fa27799c972b092cca3bfde74cffb5" + }, + { + "path": "skills/bugsnag/TESTING.md", + "sha256": "06064b4f7bb41ce13a4dbfa3a658eb4ce8db4edb23b9fdf7985ea734cb371f01" + }, + { + "path": "skills/bugsnag/README.md", + "sha256": "9d88ea1d0effc7ccf0ffdfccbe7cd06465947b701d1025a8ff78b300534397f2" + }, + { + "path": "skills/bugsnag/bugsnag.rb", + "sha256": "3aacfe410a619d0bd1fe3d3f582d70cff9da9168a3a516593807b3ca39e65483" + }, + { + "path": "skills/bugsnag/TRIGGER_EXAMPLES.md", + "sha256": "8a90fdfcf4d273e1891aa3c878e624815be00dae95b4eb8184fadeba6bf7304d" + }, + { + "path": "skills/bugsnag/SKILL.md", + "sha256": "b8c077b62b5e00d980b618b5c88173ebe601bbdf2a667f7951e9bf2bef3df692" + }, + { + "path": "skills/bugsnag/bugsnag_api_client.rb", + "sha256": "8cf5544d18897b0383843ec86adcabf29bd828807aee8e01c6b5b740f475f115" + } + ], + "dirSha256": "3fc97ae028e5ec54bcc32647a973b966d83ad2c4acc9f20a1eeac20ae3827d2a" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/bugsnag/README.md b/skills/bugsnag/README.md new file mode 100644 index 0000000..6a3598c --- /dev/null +++ b/skills/bugsnag/README.md @@ -0,0 +1,97 @@ +# Bugsnag Skill + +Skill для интеграции с Bugsnag API, позволяющий просматривать и управлять ошибками в проекте. + +## Возможности + +- 🏢 **Управление организациями** - Просмотр списка доступных организаций в Bugsnag +- 📦 **Управление проектами** - Просмотр списка доступных проектов +- 📋 **Просмотр текущих ошибок** - Получение списка активных ошибок из Bugsnag +- 🔍 **Детальный контекст ошибки** - Просмотр полной информации об ошибке включая stack trace +- ✅ **Управление статусами** - Пометка ошибок как выполненные (resolved) +- 📈 **Анализ паттернов** - Автоматический анализ повторяющихся ошибок +- 🔐 **Безопасная авторизация** - Использование API ключей из переменных окружения + +## Использование + +### Быстрый доступ (Slash Commands) + +Рекомендуется для частых операций: + +```bash +# Работа с ошибками +/bugsnag:list # Список всех ошибок +/bugsnag:list --limit 50 # С фильтрами +/bugsnag:open # Только открытые ошибки +/bugsnag:details ERROR_ID # Детали ошибки + +# Комментарии +/bugsnag:comments ERROR_ID # Просмотр комментариев +/bugsnag:comment ERROR_ID "text" # Добавить комментарий + +# Управление +/bugsnag:fix ERROR_ID # Отметить ошибку как исправленную + +# Обзор +/bugsnag:projects # Список проектов +/bugsnag:orgs # Список организаций +``` + +### Естественный язык (Skill) + +Альтернативный способ через естественный язык: + +``` +"показать bugsnag ошибки" +"bugsnag открытые ошибки" +"bugsnag детали для error_123" +"показать комментарии для bugsnag ошибки abc123" +"добавить комментарий к bugsnag ошибке abc123: investigating" +"список проектов bugsnag" +"отметить ошибку ERROR_ID как исправленную" +"проанализируй bugsnag ошибки" +``` + +### Прямой вызов скрипта + +Для автоматизации и скриптинга: + +```bash +# Обзор +./bugsnag.rb organizations # Список организаций +./bugsnag.rb projects # Список проектов + +# Работа с ошибками +./bugsnag.rb list # Список всех ошибок +./bugsnag.rb open # Только открытые ошибки +./bugsnag.rb details ERROR_ID # Детали конкретной ошибки + +# Комментарии +./bugsnag.rb comments ERROR_ID # Показать комментарии +./bugsnag.rb comment ERROR_ID "message" # Добавить комментарий + +# Управление +./bugsnag.rb resolve ERROR_ID # Отметить как выполненную +./bugsnag.rb analyze # Анализ паттернов ошибок +``` + +### Настройка + +#### Как получить API ключ + +1. Перейдите в [Bugsnag Dashboard](https://app.bugsnag.com) +2. Настройки → Organization → API Authentication +3. Создайте Personal Access Token с правами на чтение ошибок +4. Получите ID проекта из настроек проекта + +#### Переменные окружения + +```bash +export BUGSNAG_DATA_API_KEY="your_api_key_here" +export BUGSNAG_PROJECT_ID="your_project_id_here" +``` + +## Подробная документация + +Полное описание команд и примеры использования см. в [SKILL.md](SKILL.md). + diff --git a/skills/bugsnag/SKILL.md b/skills/bugsnag/SKILL.md new file mode 100644 index 0000000..94a6d37 --- /dev/null +++ b/skills/bugsnag/SKILL.md @@ -0,0 +1,158 @@ +--- +name: bugsnag +description: | + **UNIVERSAL TRIGGER**: Use when user wants to GET/FETCH/RETRIEVE any data FROM Bugsnag. + + Common patterns: + - "get/show/list/display [something] from bugsnag" + - "получить/показать/вывести [что-то] из bugsnag" + - "bugsnag [organizations/projects/errors/details/events/comments/stats]" + - "what [data] in bugsnag", "check bugsnag [resource]" + + Specific data types supported: + + 📊 **Organizations & Projects**: + - "list bugsnag organizations/orgs", "show organizations" + - "list bugsnag projects", "available projects", "проекты bugsnag" + + 🐛 **Errors (viewing)**: + - "show/list bugsnag errors", "что в bugsnag", "check bugsnag" + - "open errors", "error list", "ошибки bugsnag", "открытые ошибки" + - "errors with severity error/warning", "filter bugsnag errors" + + 🔍 **Error Details**: + - "bugsnag details for ", "error details", "детали ошибки" + - "show stack trace", "error context", "what happened in error" + - "events for error", "error timeline", "события ошибки" + + 💬 **Comments**: + - "show comments for error", "error comments", "комментарии ошибки" + - "bugsnag discussion", "what comments on error" + + 📈 **Analysis**: + - "analyze bugsnag errors", "error patterns", "анализ ошибок" + - "bugsnag statistics", "error trends", "что происходит в bugsnag" + + ✅ **Management** (write operations): + - "mark as fixed/resolved", "fix error", "resolve error", "close error" + - "закрыть ошибку", "отметить как решенную", "исправить ошибку" + - "add comment to error", "comment on bugsnag error" + - NOTE: Fix/Resolve/Close are synonyms - all mark error as resolved in Bugsnag + + TRIGGERS: bugsnag, получить из bugsnag, показать bugsnag, список bugsnag, + bugsnag data, bugsnag info, check bugsnag, what in bugsnag, bugsnag status, + error tracking, error monitoring, production errors, stack trace, + bugsnag organizations, bugsnag projects, bugsnag errors, bugsnag details, + bugsnag events, bugsnag comments, bugsnag analysis, ошибки в bugsnag, + что в bugsnag, проверить bugsnag, данные bugsnag, fix error, resolve error, + close error, закрыть ошибку, исправить ошибку, отметить как решенную + + This skill provides complete Bugsnag API integration for viewing and managing + error tracking data via Ruby helper scripts. +allowed-tools: Bash, Read +--- + +Skill для интеграции с Bugsnag API, позволяющий просматривать и управлять ошибками в проекте. + +**⚠️ PATH RESOLUTION**: Этот skill использует Ruby скрипты из своей директории. +НИКОГДА не вызывай `./bugsnag.rb` из текущей рабочей директории пользователя. + +**ПРАВИЛЬНО**: `find ~ -name "bugsnag.rb" -type f | head -1 | xargs dirname` → получим путь к skill +**НЕПРАВИЛЬНО**: `./bugsnag.rb` из текущей директории → ошибка "no such file or directory" + +**ВАЖНО:** Используй ТОЛЬКО команды, описанные в этом файле. НЕ анализируй код bugsnag.rb - следуй документации ниже. + +Вызывает скрипт ./bugsnag.rb находающийся в каталого этого skill с целью выполнить пользовательский запрос. + +## Path Resolution для Claude + +**КРИТИЧЕСКИ ВАЖНО**: При выполнении команд НЕ ИСПОЛЬЗУЙ `./bugsnag.rb` из текущей директории пользователя. +Всегда определяй директорию где находится skill и выполняй скрипт оттуда. + +### Проблема +Когда пользователь говорит "покажи ошибки из bugsnag", выполнение `./bugsnag.rb` из текущей директории не работает. + +### Решение +1. Определи директорию skill +2. Перейди в неё +3. Выполни скрипт + +**Команда для Claude**: Используй `bash -c "cd [path_to_skill] && ./bugsnag.rb [args]"` + +Где `[path_to_skill]` - это путь к директории skill. Определи его с помощью: +1. `find ~/.claude -name "bugsnag.rb" -type f` (если установлен) +2. или используй `pwd` если ты уже в директории skill +3. или попробуй стандартные пути установки плагинов + +## Возможности + +- 🏢 **Управление организациями** - Просмотр списка доступных организаций в Bugsnag +- 📦 **Управление проектами** - Просмотр списка доступных проектов +- 📋 **Просмотр текущих ошибок** - Получение списка активных ошибок из Bugsnag +- 🔍 **Детальный контекст ошибки** - Просмотр полной информации об ошибке включая stack trace +- ✅ **Управление статусами** - Пометка ошибок как выполненные (resolved) +- 🔐 **Безопасная авторизация** - Использование API ключей из переменных окружения + +## Команды bugsnag.rb + +### Обзор +- `organizations` / `orgs` / `организации` - Список всех организаций +- `projects` / `проекты` - Список всех проектов + +### Просмотр ошибок +- `list` / `show` / `показать` - Список всех ошибок +- `open` / `открыть` / `открытые` - Только открытые ошибки +- `list --limit 50` - Показать до 50 ошибок +- `list --severity error` - Только ошибки (без предупреждений) + +### Детализация +- `details ` / `детали ` - Полная информация об ошибке +- `events [limit]` / `события [лимит]` - Показать события ошибки + +### Управление +- `resolve ` / `отметить ` - Отметить как выполненную + +### Анализ +- `analyze` / `анализ` - Анализ паттернов ошибок + +### Справка +- `help` / `помощь` / `h` - Показать справку + +## ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ + +**FOR CLAUDE**: Используй `bash -c "cd [skill_directory] && ./bugsnag.rb [command]"` + +```bash +# Показать организации +bash -c "cd [skill_dir] && ./bugsnag.rb organizations" + +# Показать проекты +bash -c "cd [skill_dir] && ./bugsnag.rb projects" + +# Показать открытые ошибки +bash -c "cd [skill_dir] && ./bugsnag.rb open --limit 20" + +# Показать все ошибки (лимит 50) +bash -c "cd [skill_dir] && ./bugsnag.rb list --limit 50" + +# Детали конкретной ошибки +bash -c "cd [skill_dir] && ./bugsnag.rb details ERROR_ID" + +# Показать справку +bash -c "cd [skill_dir] && ./bugsnag.rb help" +``` + +**Примечание**: `[skill_dir]` - это путь к директории где установлен skill bugsnag. + +## ЗАПРЕЩЕННЫЕ КОМАНДЫ + +❌ `list-errors` - такой команды НЕ существует +❌ `--help` - используется `help` без дефисов +�️ Использовать команды не описанные выше + +## Безопасность + +- API ключи хранятся только в переменных окружения +- Все запросы выполняются через HTTPS +- Минимальные необходимые права доступа к API +- Логирование чувствительных данных отключено diff --git a/skills/bugsnag/TESTING.md b/skills/bugsnag/TESTING.md new file mode 100644 index 0000000..546340e --- /dev/null +++ b/skills/bugsnag/TESTING.md @@ -0,0 +1,166 @@ +# Bugsnag Skill - Testing Guide + +## Auto-Activation Test Scenarios + +This document contains test scenarios to verify the bugsnag skill activates correctly based on user input. + +### ✅ SHOULD Activate + +These phrases should trigger automatic skill activation: + +1. **Direct Bugsnag mentions**: + - "показать ошибки в bugsnag" + - "show bugsnag errors" + - "check bugsnag" + - "what's in bugsnag?" + +2. **Error details requests**: + - "bugsnag details for ERROR_123" + - "show error stack trace for ERROR_456" + - "get bugsnag error context" + - "покажи стектрейс ошибки ERROR_789" + +3. **Error management**: + - "resolve bugsnag error ERROR_123" + - "mark bugsnag error as fixed" + - "close bugsnag error ERROR_456" + - "отметить ошибку ERROR_789 как решенную" + +4. **Error analysis**: + - "analyze bugsnag errors" + - "bugsnag error patterns" + - "проанализировать ошибки в bugsnag" + - "what error patterns in production?" + +5. **Generic error monitoring mentions**: + - "check production errors" + - "show error tracking" + - "what's happening in error monitoring?" + +### ❌ Should NOT Activate + +These phrases should NOT trigger bugsnag skill: + +1. **Code errors (not monitoring)**: + - "найти ошибку в коде" + - "this code has a bug" + - "review this function for errors" + +2. **Application logs (not Bugsnag)**: + - "показать логи приложения" + - "show server logs" + - "check nginx logs" + +3. **Generic debugging**: + - "debug this issue" + - "why is this not working?" + - "help me fix this" + +4. **Other monitoring tools**: + - "check sentry errors" + - "show datadog alerts" + - "rollbar notifications" + +## Testing Procedure + +### 1. Install Plugin Locally + +```bash +# From repository root +/plugin marketplace add /home/danil/code/claude-code-marketplace +/plugin install dev-tools@dapi +``` + +### 2. Verify Skill Discovery + +```bash +# Check skill is registered +/skills list +# Should show: bugsnag (dev-tools) +``` + +### 3. Test Auto-Activation + +Start new conversation and try phrases from "SHOULD Activate" section: + +``` +User: "show bugsnag errors" +Expected: Claude should mention using bugsnag skill or invoke ./bugsnag.rb +``` + +``` +User: "bugsnag details for ERROR_123" +Expected: Claude should invoke ./bugsnag.rb details ERROR_123 +``` + +### 4. Test Non-Activation + +Try phrases from "Should NOT Activate" section: + +``` +User: "найти ошибку в коде" +Expected: Claude uses code analysis, NOT bugsnag skill +``` + +## Environment Setup for Testing + +Before testing, ensure environment variables are set: + +```bash +export BUGSNAG_DATA_API_KEY='your_actual_api_key' +export BUGSNAG_PROJECT_ID='your_actual_project_id' +``` + +To get these values: +1. Visit https://app.bugsnag.com +2. Settings → Organization → API Authentication +3. Create Personal Access Token +4. Get Project ID from project settings + +## Expected Behavior + +### Correct Activation Flow + +1. User mentions "bugsnag errors" +2. Claude recognizes trigger keywords +3. Claude invokes bugsnag skill +4. Skill executes `./bugsnag.rb ` +5. Results displayed to user + +### Correct Non-Activation Flow + +1. User asks about code errors (no "bugsnag" mention) +2. Claude uses native code analysis +3. Bugsnag skill does NOT activate +4. Standard debugging workflow proceeds + +## Success Criteria + +- ✅ Skill activates for all "SHOULD Activate" scenarios +- ✅ Skill does NOT activate for "Should NOT Activate" scenarios +- ✅ Commands execute correctly when activated +- ✅ Environment variables validated before execution +- ✅ Error messages are clear when API keys missing +- ✅ Help command works: `./bugsnag.rb help` + +## Troubleshooting + +### Skill Not Activating + +1. Check skill is installed: `/skills list` +2. Verify SKILL.md frontmatter has proper YAML +3. Check description contains trigger keywords +4. Restart Claude Code session + +### Commands Not Working + +1. Verify script is executable: `chmod +x bugsnag.rb` +2. Check environment variables are set +3. Test script directly: `./bugsnag.rb help` +4. Check Ruby dependencies installed + +### Permission Errors + +1. Verify API key has correct permissions in Bugsnag +2. Check project ID is correct +3. Test API access with curl first diff --git a/skills/bugsnag/TRIGGER_EXAMPLES.md b/skills/bugsnag/TRIGGER_EXAMPLES.md new file mode 100644 index 0000000..d5d2f0f --- /dev/null +++ b/skills/bugsnag/TRIGGER_EXAMPLES.md @@ -0,0 +1,175 @@ +# Bugsnag Skill Trigger Examples + +Примеры запросов, которые **должны активировать** bugsnag skill. + +## ✅ Универсальные паттерны (ДОЛЖНЫ СРАБОТАТЬ) + +### Английский +- "get data from bugsnag" +- "show me bugsnag information" +- "list bugsnag resources" +- "retrieve bugsnag data" +- "display bugsnag status" +- "what's in bugsnag" +- "check bugsnag" +- "fetch bugsnag info" + +### Русский +- "получить данные из bugsnag" +- "показать информацию bugsnag" +- "вывести список bugsnag" +- "что в bugsnag" +- "проверить bugsnag" +- "данные из bugsnag" + +--- + +## ✅ Организации и проекты (ДОЛЖНЫ СРАБОТАТЬ) + +### Организации +- "list bugsnag organizations" +- "show bugsnag orgs" +- "get organizations from bugsnag" +- "список организаций bugsnag" +- "организации в bugsnag" +- "показать организации bugsnag" + +### Проекты +- "list bugsnag projects" +- "show available projects in bugsnag" +- "get bugsnag projects" +- "выведи список доступных проектов в bugsnag" ← **твой исходный запрос** +- "список проектов bugsnag" +- "проекты в bugsnag" +- "показать проекты bugsnag" + +--- + +## ✅ Ошибки - просмотр (ДОЛЖНЫ СРАБОТАТЬ) + +### Общий список +- "show bugsnag errors" +- "list errors from bugsnag" +- "what errors in bugsnag" +- "показать ошибки bugsnag" +- "список ошибок bugsnag" +- "что в bugsnag" +- "ошибки в bugsnag" + +### Открытые ошибки +- "show open bugsnag errors" +- "list open errors" +- "открытые ошибки bugsnag" +- "показать открытые ошибки" + +### С фильтрацией +- "show errors with severity error" +- "list bugsnag warnings" +- "filter bugsnag errors by severity" + +--- + +## ✅ Детали ошибки (ДОЛЖНЫ СРАБОТАТЬ) + +### Детальная информация +- "bugsnag details for ERROR_123" +- "show error details ERROR_123" +- "get error information ERROR_123" +- "детали ошибки ERROR_123" +- "показать детали ошибки ERROR_123" + +### Stack trace +- "show stack trace for error ERROR_123" +- "error context ERROR_123" +- "what happened in error ERROR_123" +- "stack trace ошибки ERROR_123" + +### События +- "show events for error ERROR_123" +- "error timeline ERROR_123" +- "события ошибки ERROR_123" +- "timeline для ошибки ERROR_123" + +--- + +## ✅ Комментарии (ДОЛЖНЫ СРАБОТАТЬ) + +- "show comments for error ERROR_123" +- "list bugsnag comments ERROR_123" +- "error discussion ERROR_123" +- "комментарии ошибки ERROR_123" +- "показать комментарии ERROR_123" +- "что говорят об ошибке ERROR_123" + +--- + +## ✅ Анализ и статистика (ДОЛЖНЫ СРАБОТАТЬ) + +- "analyze bugsnag errors" +- "show error patterns in bugsnag" +- "bugsnag statistics" +- "error trends in bugsnag" +- "what's happening in bugsnag" +- "анализ ошибок bugsnag" +- "паттерны ошибок в bugsnag" +- "статистика bugsnag" +- "что происходит в bugsnag" + +--- + +## ✅ Управление (ДОЛЖНЫ СРАБОТАТЬ) + +### Пометка как исправлено (fix/resolve/close - синонимы) +- "mark bugsnag error ERROR_123 as fixed" +- "fix error ERROR_123" +- "resolve error ERROR_123" +- "close bugsnag error ERROR_123" +- "отметить ошибку ERROR_123 как решенную" +- "закрыть ошибку ERROR_123" +- "исправить ошибку ERROR_123" +- NOTE: Fix, Resolve, Close - всё это одна операция в Bugsnag + +### Добавление комментария +- "add comment to bugsnag error ERROR_123" +- "comment on error ERROR_123" +- "добавить комментарий к ошибке ERROR_123" + +--- + +## ❌ НЕ должны активировать (другие контексты) + +- "create a bug tracking system" (создание системы, не использование bugsnag) +- "what is bugsnag" (общий вопрос, не запрос данных) +- "install bugsnag" (установка, не получение данных) +- "bugsnag pricing" (коммерческий вопрос) +- "compare bugsnag with sentry" (сравнение продуктов) + +--- + +## 🎯 Ключевые триггерные слова + +### Действия (verbs) +**EN**: get, show, list, display, retrieve, fetch, check, analyze, view +**RU**: получить, показать, вывести, список, проверить, анализ, посмотреть + +### Типы данных (nouns) +**EN**: organizations, orgs, projects, errors, details, events, comments, analysis, patterns, trends, statistics +**RU**: организации, проекты, ошибки, детали, события, комментарии, анализ, паттерны, статистика + +### Контекст +**EN**: from bugsnag, in bugsnag, bugsnag [noun] +**RU**: из bugsnag, в bugsnag, bugsnag [существительное] + +--- + +## 🧪 Тестирование + +Для проверки активации skill попробуйте: + +1. **Минимальный запрос**: "check bugsnag" +2. **Специфичный**: "list bugsnag projects" +3. **Русский**: "что в bugsnag" +4. **С ID**: "details for error 12345" +5. **Аналитический**: "analyze error patterns in bugsnag" + +Каждый из этих запросов **должен активировать** bugsnag skill автоматически. diff --git a/skills/bugsnag/bugsnag.rb b/skills/bugsnag/bugsnag.rb new file mode 100755 index 0000000..af2fa2a --- /dev/null +++ b/skills/bugsnag/bugsnag.rb @@ -0,0 +1,250 @@ +#!/usr/bin/env ruby + +require_relative 'bugsnag_helper' + +class BugsnagCLI + def initialize + @helper = BugsnagHelper.new + rescue StandardError => e + puts "❌ Ошибка инициализации: #{e.message}" + puts "" + puts "Убедитесь что установлены переменные окружения:" + puts "export BUGSNAG_DATA_API_KEY='your_api_key' # Обязательно для всех команд" + puts "export BUGSNAG_PROJECT_ID='your_project_id' # Обязательно только для команд работы с ошибками" + puts "" + puts "💡 Команды 'organizations' и 'projects' работают без BUGSNAG_PROJECT_ID" + exit 1 + end + + def run(args = []) + if args.empty? + puts show_help + return + end + + command = args[0].downcase + case command + when 'list', 'errors', 'show', 'показать', 'список' + list_errors(args[1..-1]) + when 'open', 'открыть', 'opened', 'открытые' + show_open_errors(args[1..-1]) + when 'details', 'error', 'детали' + show_error_details(args[1]) + when 'resolve', 'close', 'resolve-error', 'отметить', 'решить' + resolve_error(args[1]) + when 'events', 'события' + show_events(args[1], args[2]) + when 'analyze', 'analysis', 'анализ', 'проанализировать' + analyze_errors + when 'organizations', 'orgs', 'организации' + list_organizations + when 'projects', 'проекты' + list_projects + when 'comment', 'комментарий' + add_error_comment(args[1], args[2..-1].join(' ')) + when 'comments', 'комментарии' + show_error_comments(args[1]) + when 'help', 'помощь', 'h' + puts show_help + else + puts "❌ Неизвестная команда: #{command}" + puts show_help + end + rescue StandardError => e + puts "❌ Ошибка выполнения: #{e.message}" + end + + private + + def list_errors(options = []) + limit = extract_option('--limit', options) || 20 + status = extract_option('--status', options) + severity = extract_option('--severity', options) + + puts "📋 Получение списка ошибок..." + puts "" + result = @helper.list_errors(limit: limit.to_i, status: status, severity: severity) + puts result + end + + def show_open_errors(options = []) + limit = extract_option('--limit', options) || 20 + severity = extract_option('--severity', options) + + puts "📋 Получение списка **открытых** ошибок..." + puts "" + result = @helper.list_errors(limit: limit.to_i, status: 'open', severity: severity) + puts result + end + + def show_error_details(error_id) + unless error_id + puts "❌ Укажите ID ошибки" + puts "Пример: bugsnag-lookuper details 5f8a9b2c" + return + end + + puts "🔍 Получение деталей ошибки #{error_id}..." + puts "" + result = @helper.get_error_details(error_id) + puts result + + # Также покажем последние события + puts "" + puts "📊 **Последние события:**" + events_result = @helper.get_error_events(error_id, 3) + puts events_result + end + + def resolve_error(error_id) + unless error_id + puts "❌ Укажите ID ошибки для пометки как выполненной" + puts "Пример: bugsnag-lookuper resolve 5f8a9b2c" + return + end + + puts "🔄 Пометка ошибки #{error_id} как выполненной..." + result = @helper.resolve_error(error_id) + puts result + end + + def show_events(error_id, limit = nil) + unless error_id + puts "❌ Укажите ID ошибки" + puts "Пример: bugsnag-lookuper events 5f8a9b2c 5" + return + end + + event_limit = limit&.to_i || 10 + puts "📊 Получение событий ошибки #{error_id} (лимит: #{event_limit})..." + puts "" + result = @helper.get_error_events(error_id, limit: event_limit) + puts result + end + + def analyze_errors + puts "📈 Анализ ошибок в проекте..." + puts "" + result = @helper.analyze_errors + puts result + end + + def list_organizations + puts "🏢 Получение списка организаций..." + puts "" + result = @helper.list_organizations + puts result + end + + def list_projects + puts "📦 Получение списка проектов..." + puts "" + result = @helper.list_projects + puts result + end + + def add_error_comment(error_id, message) + unless error_id && !message.empty? + puts "❌ Укажите ID ошибки и текст комментария" + puts "Пример: bugsnag.rb comment 5f8a9b2c 'Investigating this issue'" + return + end + + puts "💬 Добавление комментария к ошибке #{error_id}..." + result = @helper.add_comment(error_id, message) + puts result + end + + def show_error_comments(error_id) + unless error_id + puts "❌ Укажите ID ошибки" + puts "Пример: bugsnag.rb comments 5f8a9b2c" + return + end + + puts "💬 Получение комментариев для ошибки #{error_id}..." + puts "" + result = @helper.list_comments(error_id) + puts result + end + + def show_help + <<~HELP + 🚀 **Bugsnag** - Инструмент для работы с Bugsnag API + + **Использование:** + `skill: "bugsnag" "<команда> [аргументы]"` + + **Команды:** + + 📋 **Просмотр ошибок:** + • `list` / `show` / `показать` - Список всех ошибок + • `open` / `открыть` / `открытые` - Только **открытые** ошибки + • `list --limit 50` - Показать до 50 ошибок + • `list --status open` - Только открытые ошибки + • `list --severity error` - Только ошибки (не предупреждения) + + 🔍 **Детали ошибки:** + • `details ` / `детали ` - Полная информация об ошибке + • Пример: `details 5f8a9b2c` + + ✅ **Управление статусами:** + • `resolve ` / `resolve-error ` / `отметить ` - Отметить как выполненную + • Пример: `resolve 5f8a9b2c` + + 📊 **События ошибки:** + • `events [limit]` / `события [лимит]` - Показать события + • Пример: `events 5f8a9b2c 5` + + 📈 **Анализ:** + • `analyze` / `analysis` / `анализ` - Анализ паттернов ошибок + + 💬 **Комментарии:** + • `comment "message"` / `комментарий "текст"` - Добавить комментарий + • `comments ` / `комментарии ` - Показать все комментарии + + 🏢 **Организации:** + • `organizations` / `orgs` / `организации` - Список всех организаций + + 📦 **Проекты:** + • `projects` / `проекты` - Список всех проектов + + ❓ **Справка:** + • `help` / `помощь` / `h` - Показать эту справку + + **Настройка:** + ```bash + export BUGSNAG_DATA_API_KEY="your_api_key" # Обязательно для всех команд + export BUGSNAG_PROJECT_ID="your_project_id" # Обязательно для команд работы с ошибками + ``` + + 💡 **Важно:** Команды `organizations` и `projects` работают **БЕЗ** BUGSNAG_PROJECT_ID. + Используйте их для получения списка доступных проектов и их ID. + + HELP + end + + def extract_option(option_name, options) + index = options.find_index { |opt| opt.start_with?(option_name) } + return nil unless index + + option = options[index] + value = nil + + if option.include?('=') + value = option.split('=', 2)[1] + options.delete_at(index) + elsif options[index + 1] && !options[index + 1].start_with?('--') + value = options.delete_at(index + 1) + options.delete_at(index) + else + options.delete_at(index) + end + + value + end +end + +# Handle execution through MCP or direct CLI +cli = BugsnagCLI.new +cli.run(ARGV) \ No newline at end of file diff --git a/skills/bugsnag/bugsnag_api_client.rb b/skills/bugsnag/bugsnag_api_client.rb new file mode 100644 index 0000000..1ae433c --- /dev/null +++ b/skills/bugsnag/bugsnag_api_client.rb @@ -0,0 +1,370 @@ +#!/usr/bin/env ruby + +require 'bugsnag/api' + +class BugsnagApiClient + def initialize + @api_key = ENV.fetch('BUGSNAG_DATA_API_KEY') + @project_id = ENV['BUGSNAG_PROJECT_ID'] # Optional - needed only for error-specific commands + + validate_api_key + configure_api + end + + def list_errors(limit: 20, status: nil, severity: nil) + errors_data = fetch_errors(limit: limit, status: status, severity: severity) + format_errors_list(errors_data) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении списка ошибок") + end + + def get_error_details(error_id) + require_project_id! + + response = Bugsnag::Api.client.error(@project_id, error_id) + format_error_details(response) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении деталей ошибки") + end + + def resolve_error(error_id) + require_project_id! + + # Try to resolve via API first + begin + Bugsnag::Api.client.update_errors(@project_id, [error_id], "resolve") + "✅ Ошибка `#{error_id}` успешно отмечена как выполненная!" + rescue Bugsnag::Api::Error => e + # Fallback to adding a resolution comment + begin + comment_text = "🔧 **MARKED AS RESOLVED** - Эта ошибка была помечена как выполненная через Bugsnag skill." + Bugsnag::Api.client.create_comment(@project_id, error_id, comment_text) + "✅ Ошибка `#{error_id}` помечена как выполненная через комментарий. Пожалуйста, закройте ошибку вручную в Bugsnag dashboard." + rescue Bugsnag::Api::Error => comment_error + handle_api_error(comment_error, "пометки ошибки как выполненной") + end + end + end + + def add_comment(error_id, message) + require_project_id! + + Bugsnag::Api.client.create_comment(@project_id, error_id, message) + "✅ Комментарий успешно добавлен к ошибке `#{error_id}`" + rescue Bugsnag::Api::Error => e + handle_api_error(e, "добавлении комментария") + end + + def get_error_events(error_id, limit: 10) + require_project_id! + + options = {} + options[:limit] = limit if limit + + response = Bugsnag::Api.client.error_events(@project_id, error_id, options) + format_events_list(response) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении событий ошибки") + end + + def analyze_errors + errors_data = fetch_errors(limit: 50) + errors = normalize_errors_array(errors_data) + analyze_error_patterns(errors) + end + + def list_organizations + response = Bugsnag::Api.client.organizations + format_organizations_list(response) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении списка организаций") + end + + def list_projects + # Get all organizations first + orgs = Bugsnag::Api.client.organizations + all_projects = [] + + # Get projects for each organization + orgs.each do |org| + org_projects = Bugsnag::Api.client.projects(org['id']) + all_projects.concat(org_projects) + rescue Bugsnag::Api::Error + # Skip this org if error, continue with others + next + end + + format_projects_list(all_projects) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении списка проектов") + end + + def list_comments(error_id) + require_project_id! + + response = Bugsnag::Api.client.comments(@project_id, error_id) + format_comments_list(response) + rescue Bugsnag::Api::Error => e + handle_api_error(e, "получении комментариев") + end + + private + + def fetch_errors(limit: 20, status: nil, severity: nil) + require_project_id! + + options = {} + options[:limit] = limit if limit + options[:status] = status if status + options[:severity] = severity if severity + + Bugsnag::Api.client.errors(@project_id, nil, options) + end + + def normalize_errors_array(errors_data) + errors_data.is_a?(Array) ? errors_data : errors_data['errors'] || [] + end + + def validate_api_key + unless @api_key + raise "Missing required environment variable: BUGSNAG_DATA_API_KEY" + end + end + + def require_project_id! + unless @project_id + raise "Missing required environment variable: BUGSNAG_PROJECT_ID\n\n" \ + "This command requires a project ID. Set it with:\n" \ + "export BUGSNAG_PROJECT_ID='your_project_id'\n\n" \ + "Use 'projects' command to list available project IDs." + end + end + + def configure_api + Bugsnag::Api.configure do |config| + config.auth_token = @api_key + end + end + + def handle_api_error(error, operation) + case error + when Bugsnag::Api::ClientError + "❌ Ошибка при #{operation}: ошибка клиента API - #{error.message}" + when Bugsnag::Api::ServerError + "❌ Ошибка при #{operation}: ошибка сервера Bugsnag - #{error.message}" + when Bugsnag::Api::InternalServerError + "❌ Ошибка при #{operation}: внутренняя ошибка сервера Bugsnag - #{error.message}" + else + "❌ Ошибка при #{operation}: #{error.message}" + end + end + + def format_errors_list(errors_data) + errors = errors_data.is_a?(Array) ? errors_data : errors_data['errors'] || [] + + output = ["📋 Найдено ошибок: #{errors.length}\n"] + + errors.each do |error| + status_emoji = case error['status'] + when 'open' then '❌' + when 'resolved' then '✅' + when 'ignored' then '🚫' + else '❓' + end + + output << "#{status_emoji} **#{error['error_class']}** (#{error['events']} событий)" + output << " ID: `#{error['id']}`" + output << " Severity: #{error['severity']}" + output << " Первое появление: #{error['first_seen']}" + output << " Последнее: #{error['last_seen']}" + output << " URL: #{error['url']}" if error['url'] + output << "" + end + + output.join("\n") + end + + def format_error_details(error_data) + error = error_data['error'] || error_data + + output = [] + output << "🔍 **Детали ошибки:** #{error['error_class']}" + output << "" + output << "**Основная информация:**" + output << "• ID: `#{error['id']}`" + output << "• Статус: #{error['status']}" + output << "• Критичность: #{error['severity']}" + output << "• Событий: #{error['events']}" + output << "• Пользователи затронуто: #{error['users']}" + output << "" + + if error['first_seen'] && error['last_seen'] + output << "**Временные рамки:**" + output << "• Первое появление: #{error['first_seen']}" + output << "• Последнее: #{error['last_seen']}" + output << "" + end + + output << "**Контекст:**" + output << "• App Version: #{error.dig('app', 'version') || 'N/A'}" + output << "• Release Stage: #{error.dig('app', 'releaseStage') || 'N/A'}" + output << "• Language: #{error['language'] || 'N/A'}" + output << "• Framework: #{error['framework'] || 'N/A'}" + output << "" + + if error['url'] + output << "**URL:** #{error['url']}" + output << "" + end + + if error['message'] + output << "**Сообщение:**" + output << "```" + output << error['message'] + output << "```" + output << "" + end + + output + end + + def format_events_list(events_data) + events = events_data['events'] || [] + + output = ["📊 События ошибки (#{events.length}):\n"] + + events.each_with_index do |event, index| + output << "**Событие #{index + 1}:**" + output << "• ID: `#{event['id']}`" + output << "• Время: #{event['receivedAt']}" + output << "• App Version: #{event['app']['releaseStage'] || 'N/A'}" + output << "• OS: #{event['device']['osName'] || 'N/A'} #{event['device']['osVersion'] || ''}" + + if event['user'] + output << "• Пользователь: #{event['user']['name'] || event['user']['id'] || 'N/A'}" + end + + if event['message'] + output << "• Сообщение: #{event['message']}" + end + + output << "" + end + + output.join("\n") + end + + def analyze_error_patterns(errors) + critical_errors = errors.select { |e| e['severity'] == 'error' && e['status'] == 'open' } + warnings = errors.select { |e| e['severity'] == 'warning' && e['status'] == 'open' } + + output = ["📈 **Анализ ошибок в проекте:**\n"] + + output << "🔴 **Критичные ошибки (#{critical_errors.length}):**" + if critical_errors.any? + critical_errors.first(5).each do |error| + events_count = error['events'] || error['events_count'] || 0 + output << "• #{error['error_class']} - #{events_count} событий (ID: #{error['id']})" + end + else + output << "• Нет критичных ошибок!" + end + output << "" + + output << "🟡 **Предупреждения (#{warnings.length}):**" + if warnings.any? + warnings.first(5).each do |error| + events_count = error['events'] || error['events_count'] || 0 + output << "• #{error['error_class']} - #{events_count} событий (ID: #{error['id']})" + end + else + output << "• Нет предупреждений!" + end + output << "" + + # Частые паттерны ошибок + error_classes = errors.group_by { |e| e['error_class'] } + frequent_errors = error_classes.select { |klass, errs| errs.length > 1 } + + if frequent_errors.any? + output << "🔄 **Повторяющиеся паттерны:**" + frequent_errors.each do |error_class, errors| + total_events = errors.sum { |e| e['events'] || e['events_count'] || 0 } + output << "• #{error_class}: #{errors.length} экземпляров, #{total_events} событий" + end + end + + output.join("\n") + end + + def format_organizations_list(orgs_data) + orgs = orgs_data.is_a?(Array) ? orgs_data : orgs_data['organizations'] || [] + + output = ["🏢 Доступные организации: #{orgs.length}\n"] + + orgs.each_with_index do |org, index| + output << "#{index + 1}. **#{org['name']}** (ID: `#{org['id']}`)" + output << " Создана: #{org['created_at']}" if org['created_at'] + output << " Коллабораторов: #{org['collaborators_count']}" if org['collaborators_count'] + output << " Проектов: #{org['projects_count']}" if org['projects_count'] + output << " URL: #{org['url']}" if org['url'] + output << "" + end + + output.join("\n") + end + + def format_projects_list(projects_data) + projects = projects_data.is_a?(Array) ? projects_data : projects_data['projects'] || [] + + output = ["📦 Доступные проекты: #{projects.length}\n"] + + projects.each_with_index do |project, index| + output << "#{index + 1}. **#{project['name']}** (ID: `#{project['id']}`)" + output << " Тип: #{project['type']}" if project['type'] + output << " Открытых ошибок: #{project['open_error_count']}" if project['open_error_count'] + output << " Коллабораторов: #{project['collaborators_count']}" if project['collaborators_count'] + + if project['release_stages'] && project['release_stages'].any? + output << " Стадии: #{project['release_stages'].join(', ')}" + end + + output << " URL: #{project['url']}" if project['url'] + output << "" + end + + output.join("\n") + end + + def format_comments_list(comments_data) + comments = comments_data.is_a?(Array) ? comments_data : comments_data['comments'] || [] + + output = ["💬 Комментарии (#{comments.length}):\n"] + + if comments.empty? + output << "Нет комментариев для этой ошибки." + return output.join("\n") + end + + comments.each_with_index do |comment, index| + output << "**Комментарий #{index + 1}:**" + output << "• ID: `#{comment['id']}`" + + # Author info + author = if comment['user'] && comment['user']['name'] + comment['user']['name'] + elsif comment['user'] && comment['user']['email'] + comment['user']['email'] + else + 'Unknown' + end + output << "• Автор: #{author}" + + output << "• Время: #{comment['created_at']}" if comment['created_at'] + output << "• Текст: #{comment['message']}" if comment['message'] + output << "" + end + + output.join("\n") + end +end \ No newline at end of file diff --git a/skills/bugsnag/bugsnag_helper.rb b/skills/bugsnag/bugsnag_helper.rb new file mode 100755 index 0000000..54961d0 --- /dev/null +++ b/skills/bugsnag/bugsnag_helper.rb @@ -0,0 +1,45 @@ +#!/usr/bin/env ruby + +require_relative 'bugsnag_api_client' + +class BugsnagHelper + def initialize + @client = BugsnagApiClient.new + end + + def list_errors(limit: 20, status: nil, severity: nil) + @client.list_errors(limit: limit, status: status, severity: severity) + end + + def get_error_details(error_id) + @client.get_error_details(error_id) + end + + def resolve_error(error_id) + @client.resolve_error(error_id) + end + + def get_error_events(error_id, limit: 10) + @client.get_error_events(error_id, limit: limit) + end + + def analyze_errors + @client.analyze_errors + end + + def list_organizations + @client.list_organizations + end + + def list_projects + @client.list_projects + end + + def add_comment(error_id, message) + @client.add_comment(error_id, message) + end + + def list_comments(error_id) + @client.list_comments(error_id) + end +end \ No newline at end of file