bug_bunny 4.8.0 → 4.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/skills/documentation-writer/SKILL.md +45 -0
  3. data/.agents/skills/gem-release/SKILL.md +116 -0
  4. data/.agents/skills/quality-code/SKILL.md +51 -0
  5. data/.agents/skills/sentry/SKILL.md +135 -0
  6. data/.agents/skills/sentry/references/api-endpoints.md +147 -0
  7. data/.agents/skills/sentry/scripts/sentry.rb +194 -0
  8. data/.agents/skills/skill-builder/SKILL.md +293 -0
  9. data/.agents/skills/skill-manager/SKILL.md +225 -0
  10. data/.agents/skills/skill-manager/scripts/sync.rb +356 -0
  11. data/.agents/skills/yard/SKILL.md +311 -0
  12. data/.agents/skills/yard/references/tipos.md +144 -0
  13. data/CHANGELOG.md +14 -0
  14. data/CLAUDE.md +28 -225
  15. data/README.md +5 -3
  16. data/lib/bug_bunny/consumer.rb +21 -5
  17. data/lib/bug_bunny/otel.rb +47 -0
  18. data/lib/bug_bunny/producer.rb +13 -4
  19. data/lib/bug_bunny/request.rb +14 -2
  20. data/lib/bug_bunny/version.rb +1 -1
  21. data/lib/bug_bunny.rb +1 -0
  22. data/skill/SKILL.md +253 -0
  23. data/skill/references/client-middleware.md +161 -0
  24. data/skill/references/consumer.md +122 -0
  25. data/skill/references/controller.md +105 -0
  26. data/skill/references/errores.md +97 -0
  27. data/skill/references/resource.md +116 -0
  28. data/skill/references/routing.md +82 -0
  29. data/skill/references/testing.md +138 -0
  30. data/skills.lock +30 -0
  31. data/skills.yml +40 -0
  32. data/spec/integration/consumer_middleware_spec.rb +23 -2
  33. data/spec/unit/consumer_spec.rb +138 -6
  34. data/spec/unit/otel_spec.rb +54 -0
  35. data/spec/unit/producer_spec.rb +187 -0
  36. data/spec/unit/request_spec.rb +51 -0
  37. metadata +28 -29
  38. data/.agents/skills/rabbitmq-expert/SKILL.md +0 -1555
  39. data/.claude/commands/gem-ai-setup.md +0 -174
  40. data/.claude/commands/pr.md +0 -53
  41. data/.claude/commands/release.md +0 -52
  42. data/.claude/commands/rubocop.md +0 -22
  43. data/.claude/commands/service-ai-setup.md +0 -168
  44. data/.claude/commands/test.md +0 -28
  45. data/.claude/commands/yard.md +0 -46
  46. data/docs/_index.md +0 -50
  47. data/docs/ai/_index.md +0 -56
  48. data/docs/ai/antipatterns.md +0 -166
  49. data/docs/ai/api.md +0 -251
  50. data/docs/ai/architecture.md +0 -92
  51. data/docs/ai/errors.md +0 -158
  52. data/docs/ai/faq_external.md +0 -133
  53. data/docs/ai/faq_internal.md +0 -86
  54. data/docs/ai/glossary.md +0 -45
  55. data/docs/concepts.md +0 -140
  56. data/docs/howto/controller.md +0 -194
  57. data/docs/howto/middleware_client.md +0 -119
  58. data/docs/howto/middleware_consumer.md +0 -127
  59. data/docs/howto/rails.md +0 -214
  60. data/docs/howto/resource.md +0 -200
  61. data/docs/howto/routing.md +0 -133
  62. data/docs/howto/testing.md +0 -259
  63. data/docs/howto/tracing.md +0 -119
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Helper para interactuar con la API de Sentry self-hosted de Wispro.
5
+ # Uso: ruby sentry.rb <comando> [opciones]
6
+ #
7
+ # Comandos:
8
+ # projects — Lista todos los proyectos
9
+ # issues <project_slug> [opciones] — Lista issues de un proyecto
10
+ # issue <issue_id> — Detalle de un issue
11
+ # events <issue_id> [--full] — Eventos de un issue
12
+ # search <project_slug> <query> — Busca issues por texto
13
+ # resolve <issue_id> — Resuelve un issue
14
+ # ignore <issue_id> — Ignora un issue
15
+ # assign <issue_id> <username> — Asigna un issue
16
+
17
+ require 'net/http'
18
+ require 'uri'
19
+ require 'json'
20
+ require 'openssl'
21
+
22
+ module Sentry
23
+ URL = ENV['SENTRY_URL'] || 'https://sentry.cloud.wispro.co'
24
+ TOKEN = ENV['SENTRY_TOKEN']
25
+ ORG = ENV['SENTRY_ORG'] || 'wispro'
26
+
27
+ class << self
28
+ def run(args)
29
+ unless TOKEN
30
+ puts "ERROR: SENTRY_TOKEN no configurado en el entorno."
31
+ exit 1
32
+ end
33
+
34
+ command = args.shift
35
+ case command
36
+ when 'projects' then projects
37
+ when 'issues' then issues(args)
38
+ when 'issue' then issue(args.first)
39
+ when 'events' then events(args)
40
+ when 'search' then search(args)
41
+ when 'resolve' then update_status(args.first, 'resolved')
42
+ when 'ignore' then update_status(args.first, 'ignored')
43
+ when 'assign' then assign(args[0], args[1])
44
+ else
45
+ puts USAGE
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ USAGE = <<~TEXT
52
+ Uso: ruby sentry.rb <comando> [opciones]
53
+
54
+ Comandos:
55
+ projects Lista todos los proyectos
56
+ issues <project_slug> [--period=24h] Lista issues (default: 24h, unresolved)
57
+ issue <issue_id> Detalle de un issue
58
+ events <issue_id> [--full] Eventos de un issue
59
+ search <project_slug> <query> Busca issues por texto
60
+ resolve <issue_id> Resuelve un issue
61
+ ignore <issue_id> Ignora un issue
62
+ assign <issue_id> <username> Asigna un issue
63
+ TEXT
64
+
65
+ def projects
66
+ data = get("/organizations/#{ORG}/projects/")
67
+ data.each do |p|
68
+ puts " #{p['slug'].ljust(30)} #{p['name']}"
69
+ end
70
+ end
71
+
72
+ def issues(args)
73
+ slug = args.shift
74
+ period = extract_flag(args, '--period') || '24h'
75
+
76
+ data = get("/projects/#{ORG}/#{slug}/issues/?statsPeriod=#{period}&query=is:unresolved")
77
+ print_issues(data)
78
+ end
79
+
80
+ def issue(issue_id)
81
+ data = get("/organizations/#{ORG}/issues/#{issue_id}/")
82
+ puts " ##{data['shortId']} — #{data['title']}"
83
+ puts " Level: #{data['level']} | Count: #{data['count']} | Users: #{data['userCount']}"
84
+ puts " First: #{data['firstSeen']} | Last: #{data['lastSeen']}"
85
+ puts " Status: #{data['status']}"
86
+ puts " Assigned: #{data.dig('assignedTo', 'name') || 'nadie'}"
87
+ puts " Link: #{data['permalink']}"
88
+ end
89
+
90
+ def events(args)
91
+ issue_id = args.shift
92
+ full = args.include?('--full')
93
+ params = full ? '?full=true&limit=3' : '?limit=5'
94
+
95
+ data = get("/organizations/#{ORG}/issues/#{issue_id}/events/#{params}")
96
+ data.each do |event|
97
+ puts "\n Event #{event['eventID'][0..7]} — #{event['dateCreated']}"
98
+ puts " #{event['title']}"
99
+
100
+ next unless full && event['entries']
101
+
102
+ event['entries'].each do |entry|
103
+ next unless entry['type'] == 'exception'
104
+
105
+ entry.dig('data', 'values')&.each do |exc|
106
+ puts " Exception: #{exc['type']}: #{exc['value']}"
107
+ frames = exc.dig('stacktrace', 'frames') || []
108
+ frames.select { |f| f['inApp'] }.last(5).each do |frame|
109
+ puts " #{frame['filename']}:#{frame['lineNo']} in #{frame['function']}"
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ def search(args)
117
+ slug = args.shift
118
+ query = args.join(' ')
119
+ data = get("/projects/#{ORG}/#{slug}/issues/?query=#{URI.encode_www_form_component(query)}&statsPeriod=24h")
120
+ print_issues(data)
121
+ end
122
+
123
+ def update_status(issue_id, status)
124
+ data = put("/organizations/#{ORG}/issues/#{issue_id}/", { status: status })
125
+ puts " Issue ##{issue_id} → #{data['status']}"
126
+ end
127
+
128
+ def assign(issue_id, username)
129
+ data = put("/organizations/#{ORG}/issues/#{issue_id}/", { assignedTo: username })
130
+ puts " Issue ##{issue_id} → asignado a #{data.dig('assignedTo', 'name') || username}"
131
+ end
132
+
133
+ def print_issues(data)
134
+ if data.empty?
135
+ puts " Sin issues encontrados."
136
+ return
137
+ end
138
+
139
+ data.each do |i|
140
+ level = i['level'].upcase.ljust(7)
141
+ count = "x#{i['count']}".ljust(6)
142
+ puts " #{level} #{count} ##{i['shortId'].ljust(15)} #{i['title'][0..80]}"
143
+ end
144
+ end
145
+
146
+ # --- HTTP ---
147
+
148
+ def get(path)
149
+ request(:get, path)
150
+ end
151
+
152
+ def put(path, body)
153
+ request(:put, path, body)
154
+ end
155
+
156
+ def request(method, path, body = nil)
157
+ uri = URI.parse("#{URL}/api/0#{path}")
158
+ http = Net::HTTP.new(uri.host, uri.port)
159
+ http.use_ssl = uri.scheme == 'https'
160
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
161
+
162
+ req = case method
163
+ when :get then Net::HTTP::Get.new(uri)
164
+ when :put then Net::HTTP::Put.new(uri)
165
+ end
166
+
167
+ req['Authorization'] = "Bearer #{TOKEN}"
168
+ req['Content-Type'] = 'application/json'
169
+ req.body = JSON.generate(body) if body
170
+
171
+ response = http.request(req)
172
+
173
+ unless response.code.start_with?('2')
174
+ puts " ERROR: HTTP #{response.code} — #{response.body[0..200]}"
175
+ exit 1
176
+ end
177
+
178
+ JSON.parse(response.body)
179
+ rescue StandardError => e
180
+ puts " ERROR: #{e.message}"
181
+ exit 1
182
+ end
183
+
184
+ def extract_flag(args, flag)
185
+ idx = args.index { |a| a.start_with?(flag) }
186
+ return nil unless idx
187
+
188
+ value = args.delete_at(idx)
189
+ value.include?('=') ? value.split('=', 2).last : args.delete_at(idx)
190
+ end
191
+ end
192
+ end
193
+
194
+ Sentry.run(ARGV.dup) if __FILE__ == $PROGRAM_NAME
@@ -0,0 +1,293 @@
1
+ ---
2
+ name: skill-builder
3
+ description: Genera o actualiza la skill empaquetada (`skill/`) para cualquier proyecto Ruby (gema o servicio). Detecta automáticamente el tipo de proyecto y analiza el código correspondiente. Soporta desde un SKILL.md simple hasta skills con references y scripts. Es invocada por `skill-manager`, `gem-release` (regeneración completa) y `quality-code` (actualización incremental).
4
+ ---
5
+
6
+ # Skill Builder
7
+
8
+ Sos un experto en crear skills de conocimiento para proyectos Ruby. Tu objetivo es generar o actualizar `skill/SKILL.md` — un artefacto autocontenido que permite a cualquier agente de IA responder preguntas sobre integración, arquitectura, API, errores y antipatrones del proyecto.
9
+
10
+ ## Detección de tipo de proyecto
11
+
12
+ - **Gema**: existe `.gemspec` en la raíz.
13
+ - **Servicio**: existe `config/application.rb`.
14
+ - Si ambos existen, priorizar gema.
15
+
16
+ **Distribución en gemas:** La skill queda en `skill/` en la raíz del repositorio. Al publicar con `gem-release`, `skill/` se empaqueta en el `.gem`, de modo que los consumidores acceden a la skill directamente desde la gema instalada (`Gem.loaded_specs["[name]"].gem_dir + "/skill/"`) sin descargas adicionales.
17
+
18
+ **Distribución en servicios:** La skill queda en `skill/` en la raíz del repositorio. Los consumidores la descargan vía `skill-manager sync` desde GitHub.
19
+
20
+ ## Escenarios de Complejidad
21
+
22
+ La estructura de `skill/` depende de la complejidad del proyecto. `skill-manager` determina el escenario inicial, pero puede evolucionar con el tiempo:
23
+
24
+ ### Escenario 1 — Proyecto simple
25
+ ```
26
+ skill/
27
+ SKILL.md
28
+ ```
29
+ La skill entera cabe en un solo archivo. Secciones autocontenidas de ≤400 tokens.
30
+
31
+ ### Escenario 2 — Con referencias
32
+ ```
33
+ skill/
34
+ SKILL.md
35
+ references/
36
+ api-detallada.md
37
+ errores.md
38
+ ```
39
+ Cuando la API o contratos son extensos, o el catálogo de errores es grande.
40
+
41
+ ### Escenario 3 — Con scripts
42
+ ```
43
+ skill/
44
+ SKILL.md
45
+ scripts/
46
+ diagnostico.rb
47
+ ```
48
+ Cuando el proyecto necesita herramientas ejecutables (diagnóstico, migración, validación).
49
+
50
+ ### Escenario 4 — Completa
51
+ ```
52
+ skill/
53
+ SKILL.md
54
+ references/
55
+ api-detallada.md
56
+ errores.md
57
+ scripts/
58
+ diagnostico.rb
59
+ migration_helper.rb
60
+ ```
61
+
62
+ ### Criterios para escalar
63
+
64
+ - **→ references/** cuando una sección de `SKILL.md` supera los 400 tokens y es conocimiento de referencia (API, contratos, errores, catálogos).
65
+ - **→ scripts/** cuando el proyecto necesita herramientas ejecutables para diagnóstico, migración o validación que complementen el conocimiento.
66
+
67
+ ## Modos de Ejecución
68
+
69
+ - **Completo** (invocado por `gem-release` o manualmente): Regenera la skill desde cero analizando todo el código.
70
+ - **Incremental** (invocado por `quality-code`): Actualiza solo las secciones afectadas por el diff del PR.
71
+
72
+ ---
73
+
74
+ ## Paso 1 — Descubrir el estado actual
75
+
76
+ Leer en orden:
77
+ 1. `skill/SKILL.md` — si existe, es la skill actual.
78
+ 2. `skill/references/` — referencias existentes.
79
+ 3. `skill/scripts/` — scripts existentes.
80
+ 4. `CLAUDE.md` — propósito del artefacto, arquitectura, decisiones clave.
81
+
82
+ Según el tipo de proyecto:
83
+
84
+ **Si es gema:**
85
+ 5. `.gemspec` — nombre, descripción, dependencias.
86
+ 6. `lib/` — API pública, responsabilidades de clases, firmas de métodos.
87
+ 7. `spec/` o `test/` — patrones de uso, casos borde, errores esperados.
88
+
89
+ **Si es servicio:**
90
+ 5. `config/application.rb` — nombre, configuración.
91
+ 6. `config/routes.rb` — endpoints HTTP expuestos.
92
+ 7. `app/` — controllers, models, services, jobs.
93
+ 8. Queues/mensajería — contratos de eventos (Sidekiq, ActiveJob, etc.).
94
+ 9. `db/migrate/` — esquema y evolución del modelo de datos.
95
+ 10. `spec/` o `test/` — patrones de uso, casos borde, errores esperados.
96
+
97
+ En modo **incremental**, analizar también `git diff [PRIMARY_BRANCH]...HEAD` para identificar qué cambió.
98
+
99
+ ---
100
+
101
+ ## Paso 2 — Analizar el código y determinar escenario
102
+
103
+ **Común a ambos tipos:**
104
+ - **Arquitectura**: Flujo de datos, componentes core, thread safety, dependencias.
105
+ - **Uso**: Ejemplos de integración, patrones comunes.
106
+ - **Errores**: Excepciones personalizadas, causas, resoluciones.
107
+ - **Antipatrones**: Usos incorrectos identificados en tests o código.
108
+ - **Scripts necesarios**: ¿Hay flujos de diagnóstico, migración o validación que justifiquen un script?
109
+
110
+ **Específico de gemas:**
111
+ - **API Pública**: Bloque de configuración, clases principales, métodos públicos, parámetros.
112
+
113
+ **Específico de servicios:**
114
+ - **Contratos**: Endpoints HTTP, queues, eventos publicados/consumidos, webhooks.
115
+ - **Infraestructura**: Variables de entorno, servicios externos, bases de datos.
116
+
117
+ Con base en el análisis, determiná qué escenario de complejidad corresponde (1-4). Si la skill ya existe, evaluá si el escenario debe escalar.
118
+
119
+ ---
120
+
121
+ ## Paso 3 — Generar la Skill
122
+
123
+ Generar `skill/SKILL.md` (y `references/` o `scripts/` si el escenario lo requiere).
124
+
125
+ ### Reglas de escritura (RAG-optimized)
126
+
127
+ 1. **Idioma: español** — Todo el contenido en español. Mantener términos técnicos estándar (ej: "middleware", "routing").
128
+ 2. **Cada sección <= 400 tokens** — Autocontenida, sin asumir contexto de otras secciones. Si una sección supera este límite, extraerla a `references/`.
129
+ 3. **Sin prosa introductoria** — Ir directo al contenido técnico.
130
+ 4. **Sin frontmatter complejo** — No incluir version, profile o audiences.
131
+ 5. **Sin duplicación** — Si un tema está en `references/`, la skill solo lo referencia.
132
+
133
+ ### Estructura de SKILL.md
134
+
135
+ #### Titulo y Descripcion
136
+
137
+ ```markdown
138
+ # [Nombre] Expert
139
+
140
+ Skill de conocimiento completo sobre [Nombre]. Consultame para cualquier pregunta sobre integración, arquitectura, API, errores y antipatrones.
141
+ ```
142
+
143
+ #### Glosario
144
+
145
+ Términos del dominio. Formato: `**Término** — Definición concisa (1-3 líneas).`
146
+
147
+ #### Arquitectura
148
+
149
+ - Responsabilidad core.
150
+ - Mapa de componentes (diagrama ASCII).
151
+ - Flujo en runtime y decisiones de diseño.
152
+
153
+ **Formato de diagramas ASCII:**
154
+ - Cajas con `┌─┐└─┘`, flechas con `────>` (horizontal) y `│▼` (vertical).
155
+ - Máximo 4-5 componentes por diagrama. Si hay más, dividir en sub-diagramas por capa.
156
+ - Sin texto decorativo fuera de las cajas.
157
+
158
+ Ejemplo:
159
+ ```
160
+ ┌──────────┐ ┌──────────┐ ┌──────────┐
161
+ │ Request │────>│ Middleware│────>│ Handler │
162
+ └──────────┘ └──────────┘ └──────────┘
163
+
164
+
165
+ ┌──────────┐
166
+ │ Cache │
167
+ └──────────┘
168
+ ```
169
+
170
+ #### API Publica (gemas)
171
+
172
+ - Bloque de configuración (tipos, defaults, restricciones).
173
+ - Clases y métodos (firma, parámetros, retorno).
174
+ - Ejemplos de código para operaciones principales.
175
+ - Si la API es extensa, mantener un resumen acá y extraer el detalle a `references/api-detallada.md`.
176
+
177
+ #### Contratos (servicios)
178
+
179
+ - Endpoints HTTP (método, path, parámetros, respuesta).
180
+ - Queues y eventos (nombre, payload, productor/consumidor).
181
+ - Webhooks (si aplica).
182
+ - Si los contratos son extensos, extraer a `references/contratos.md`.
183
+
184
+ #### FAQ
185
+
186
+ Formato Q&A estricto. H3 para la pregunta, respuesta <= 150 palabras. Sin preámbulo.
187
+
188
+ #### Antipatrones
189
+
190
+ Qué NO hacer. Por cada uno: nombre, código incorrecto, razón y alternativa.
191
+
192
+ #### Errores
193
+
194
+ Catálogo de excepciones. Por cada una: nombre, causa, reproducción y resolución.
195
+ Si el catálogo es extenso, mantener los errores más comunes acá y extraer el catálogo completo a `references/errores.md`.
196
+
197
+ #### Referencias (si existen)
198
+
199
+ Índice de archivos en `references/` y `scripts/` con descripción de una línea.
200
+
201
+ ```markdown
202
+ ## Referencias
203
+
204
+ - [API Detallada](references/api-detallada.md) — Documentación completa de clases y métodos
205
+ - [Catálogo de Errores](references/errores.md) — Todas las excepciones con resolución
206
+ - [Diagnóstico](scripts/diagnostico.rb) — Script para verificar configuración en runtime
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Paso 4 — Actualizar README.md
212
+
213
+ Invocá la skill `documentation-writer` para auditar y actualizar el README.
214
+
215
+ **Regla fundamental:** El README es para **humanos** (devs que usan la gema/servicio). La skill (`skill/`) es para **agentes**. Son audiencias distintas. **Nunca referenciar `skill/` desde el README.**
216
+
217
+ ### README de una gema (máx 150 líneas)
218
+
219
+ ```markdown
220
+ # [Nombre de la gema]
221
+
222
+ Descripción en una línea.
223
+
224
+ ## Instalación
225
+
226
+ gem 'nombre', '~> X.X'
227
+
228
+ ## Quick Start
229
+
230
+ [Ejemplo mínimo funcional — copiar, pegar, funciona]
231
+
232
+ ## Uso
233
+
234
+ [Ejemplos de las operaciones principales]
235
+
236
+ ## Configuración
237
+
238
+ [Bloque de configuración con opciones, defaults y descripción]
239
+
240
+ ## Contribuir
241
+
242
+ [Cómo correr tests, linting, etc.]
243
+ ```
244
+
245
+ ### README de un servicio (máx 150 líneas)
246
+
247
+ ```markdown
248
+ # [Nombre del servicio]
249
+
250
+ Descripción en una línea.
251
+
252
+ ## Setup
253
+
254
+ [Pasos para levantar el servicio localmente: bin/setup, docker, etc.]
255
+
256
+ ## Endpoints / Contratos
257
+
258
+ [Resumen de los endpoints o queues principales]
259
+
260
+ ## Variables de entorno
261
+
262
+ [Lista de env vars necesarias]
263
+
264
+ ## Testing
265
+
266
+ [Cómo correr tests]
267
+
268
+ ## Deploy
269
+
270
+ [Cómo se despliega: branch, tag, Codefresh]
271
+ ```
272
+
273
+ ### Qué NO poner en el README
274
+ - Links a `skill/` ni a `skill/SKILL.md`
275
+ - Documentación interna para agentes
276
+ - Diagramas ASCII extensos (esos van en la skill)
277
+ - Catálogo completo de errores (eso va en la skill)
278
+ - FAQ técnico detallado (eso va en la skill)
279
+
280
+ ---
281
+
282
+ ## Paso 5 — Mostrar resumen y esperar aprobación
283
+
284
+ Mostrá un resumen de los cambios realizados:
285
+ - Tipo de proyecto detectado (gema o servicio).
286
+ - Escenario de complejidad determinado (y si escaló respecto al anterior).
287
+ - Secciones nuevas, modificadas o eliminadas en `SKILL.md`.
288
+ - Archivos nuevos o actualizados en `references/` o `scripts/`.
289
+ - Cambios en README.md.
290
+
291
+ Preguntá: "¿Querés ver el diff completo antes de escribir los archivos?"
292
+
293
+ **Esperá la aprobación explícita del desarrollador** antes de persistir los cambios.