rails-informant 0.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +340 -0
- data/Rakefile +10 -0
- data/VERSION +1 -0
- data/app/controllers/rails_informant/api/base_controller.rb +62 -0
- data/app/controllers/rails_informant/api/errors_controller.rb +72 -0
- data/app/controllers/rails_informant/api/occurrences_controller.rb +14 -0
- data/app/controllers/rails_informant/api/status_controller.rb +29 -0
- data/app/jobs/rails_informant/application_job.rb +4 -0
- data/app/jobs/rails_informant/notify_job.rb +34 -0
- data/app/jobs/rails_informant/purge_job.rb +29 -0
- data/app/models/rails_informant/application_record.rb +5 -0
- data/app/models/rails_informant/error_group.rb +175 -0
- data/app/models/rails_informant/occurrence.rb +22 -0
- data/config/routes.rb +14 -0
- data/db/migrate/20260227000000_create_informant_tables.rb +65 -0
- data/exe/informant-mcp +27 -0
- data/lib/generators/rails_informant/devin/templates/error-triage.devin.md +48 -0
- data/lib/generators/rails_informant/devin_generator.rb +12 -0
- data/lib/generators/rails_informant/install_generator.rb +20 -0
- data/lib/generators/rails_informant/skill/templates/SKILL.md +168 -0
- data/lib/generators/rails_informant/skill_generator.rb +12 -0
- data/lib/generators/rails_informant/templates/create_informant_tables.rb.erb +55 -0
- data/lib/generators/rails_informant/templates/initializer.rb.erb +33 -0
- data/lib/rails_informant/breadcrumb_buffer.rb +30 -0
- data/lib/rails_informant/breadcrumb_subscriber.rb +51 -0
- data/lib/rails_informant/configuration.rb +51 -0
- data/lib/rails_informant/context_builder.rb +142 -0
- data/lib/rails_informant/context_filter.rb +45 -0
- data/lib/rails_informant/current.rb +5 -0
- data/lib/rails_informant/engine.rb +86 -0
- data/lib/rails_informant/error_recorder.rb +47 -0
- data/lib/rails_informant/error_subscriber.rb +17 -0
- data/lib/rails_informant/fingerprint.rb +23 -0
- data/lib/rails_informant/mcp/base_tool.rb +38 -0
- data/lib/rails_informant/mcp/client.rb +123 -0
- data/lib/rails_informant/mcp/configuration.rb +90 -0
- data/lib/rails_informant/mcp/server.rb +29 -0
- data/lib/rails_informant/mcp/tools/annotate_error.rb +25 -0
- data/lib/rails_informant/mcp/tools/delete_error.rb +25 -0
- data/lib/rails_informant/mcp/tools/get_error.rb +24 -0
- data/lib/rails_informant/mcp/tools/get_informant_status.rb +22 -0
- data/lib/rails_informant/mcp/tools/ignore_error.rb +24 -0
- data/lib/rails_informant/mcp/tools/list_environments.rb +20 -0
- data/lib/rails_informant/mcp/tools/list_errors.rb +32 -0
- data/lib/rails_informant/mcp/tools/list_occurrences.rb +27 -0
- data/lib/rails_informant/mcp/tools/mark_duplicate.rb +25 -0
- data/lib/rails_informant/mcp/tools/mark_fix_pending.rb +27 -0
- data/lib/rails_informant/mcp/tools/reopen_error.rb +24 -0
- data/lib/rails_informant/mcp/tools/resolve_error.rb +24 -0
- data/lib/rails_informant/mcp.rb +22 -0
- data/lib/rails_informant/middleware/error_capture.rb +28 -0
- data/lib/rails_informant/middleware/rescued_exception_interceptor.rb +16 -0
- data/lib/rails_informant/notifiers/devin.rb +61 -0
- data/lib/rails_informant/notifiers/notification_policy.rb +85 -0
- data/lib/rails_informant/notifiers/slack.rb +77 -0
- data/lib/rails_informant/notifiers/webhook.rb +31 -0
- data/lib/rails_informant/structured_event_subscriber.rb +14 -0
- data/lib/rails_informant/version.rb +3 -0
- data/lib/rails_informant.rb +147 -0
- data/lib/tasks/rails_informant.rake +30 -0
- metadata +177 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module RailsInformant
|
|
2
|
+
module Notifiers
|
|
3
|
+
class Slack
|
|
4
|
+
include NotificationPolicy
|
|
5
|
+
|
|
6
|
+
def notify(error_group, occurrence)
|
|
7
|
+
post_json \
|
|
8
|
+
url: RailsInformant.slack_webhook_url,
|
|
9
|
+
body: build_payload(error_group, occurrence),
|
|
10
|
+
label: "Slack webhook"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def regression?(error_group)
|
|
16
|
+
error_group.status == "unresolved" && error_group.fix_deployed_at.present?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def build_payload(error_group, occurrence)
|
|
20
|
+
regression_tag = regression?(error_group) ? " [REGRESSION]" : ""
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
blocks: [
|
|
24
|
+
{
|
|
25
|
+
type: "header",
|
|
26
|
+
text: {
|
|
27
|
+
type: "plain_text",
|
|
28
|
+
text: "#{error_group.error_class}#{regression_tag}",
|
|
29
|
+
emoji: true
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
type: "section",
|
|
34
|
+
fields: [
|
|
35
|
+
{ type: "mrkdwn", text: "*Message:*\n#{error_group.message.to_s.truncate(200)}" },
|
|
36
|
+
{ type: "mrkdwn", text: "*Status:*\n#{error_group.status}" },
|
|
37
|
+
{ type: "mrkdwn", text: "*Occurrences:*\n#{error_group.total_occurrences}" },
|
|
38
|
+
{ type: "mrkdwn", text: "*First seen:*\n#{error_group.first_seen_at&.iso8601}" }
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: "section",
|
|
43
|
+
fields: [
|
|
44
|
+
location_field(error_group),
|
|
45
|
+
{ type: "mrkdwn", text: "*Severity:*\n#{error_group.severity}" }
|
|
46
|
+
].compact
|
|
47
|
+
},
|
|
48
|
+
context_block(occurrence)
|
|
49
|
+
].compact
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def location_field(error_group)
|
|
54
|
+
location = error_group.controller_action || error_group.job_class || error_group.first_backtrace_line
|
|
55
|
+
return unless location
|
|
56
|
+
|
|
57
|
+
{ type: "mrkdwn", text: "*Location:*\n`#{location.to_s.truncate(100)}`" }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def context_block(occurrence)
|
|
61
|
+
return unless occurrence
|
|
62
|
+
|
|
63
|
+
elements = []
|
|
64
|
+
if occurrence.git_sha
|
|
65
|
+
elements << { type: "mrkdwn", text: "Deploy: `#{occurrence.git_sha[0, 7]}`" }
|
|
66
|
+
end
|
|
67
|
+
if occurrence.environment_context&.dig("hostname")
|
|
68
|
+
elements << { type: "mrkdwn", text: "Host: `#{occurrence.environment_context["hostname"]}`" }
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
return if elements.empty?
|
|
72
|
+
|
|
73
|
+
{ type: "context", elements: elements }
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module RailsInformant
|
|
2
|
+
module Notifiers
|
|
3
|
+
class Webhook
|
|
4
|
+
include NotificationPolicy
|
|
5
|
+
|
|
6
|
+
def notify(error_group, occurrence)
|
|
7
|
+
post_json \
|
|
8
|
+
url: RailsInformant.webhook_url,
|
|
9
|
+
body: build_payload(error_group, occurrence),
|
|
10
|
+
label: "Webhook"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def build_payload(error_group, occurrence)
|
|
16
|
+
{
|
|
17
|
+
error_class: error_group.error_class,
|
|
18
|
+
fingerprint: error_group.fingerprint,
|
|
19
|
+
message: error_group.message,
|
|
20
|
+
severity: error_group.severity,
|
|
21
|
+
status: error_group.status,
|
|
22
|
+
total_occurrences: error_group.total_occurrences,
|
|
23
|
+
controller_action: error_group.controller_action,
|
|
24
|
+
job_class: error_group.job_class,
|
|
25
|
+
first_seen_at: error_group.first_seen_at&.iso8601,
|
|
26
|
+
last_seen_at: error_group.last_seen_at&.iso8601
|
|
27
|
+
}
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module RailsInformant
|
|
2
|
+
class StructuredEventSubscriber
|
|
3
|
+
def emit(event)
|
|
4
|
+
return unless RailsInformant.initialized?
|
|
5
|
+
|
|
6
|
+
BreadcrumbBuffer.current.record(
|
|
7
|
+
category: event[:name],
|
|
8
|
+
message: event[:name],
|
|
9
|
+
metadata: event[:payload].is_a?(Hash) ? RailsInformant::ContextFilter.filter(event[:payload]) : {},
|
|
10
|
+
duration: nil
|
|
11
|
+
)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
require "rails_informant/version"
|
|
2
|
+
require "rails_informant/configuration"
|
|
3
|
+
|
|
4
|
+
module RailsInformant
|
|
5
|
+
InvalidParameterError = Class.new(StandardError)
|
|
6
|
+
NotifierError = Class.new(StandardError)
|
|
7
|
+
|
|
8
|
+
IGNORED_EXCEPTIONS_DEFAULT = %w[
|
|
9
|
+
AbstractController::ActionNotFound
|
|
10
|
+
ActionController::BadRequest
|
|
11
|
+
ActionController::InvalidAuthenticityToken
|
|
12
|
+
ActionController::InvalidCrossOriginRequest
|
|
13
|
+
ActionController::MethodNotAllowed
|
|
14
|
+
ActionController::NotImplemented
|
|
15
|
+
ActionController::ParameterMissing
|
|
16
|
+
ActionController::RoutingError
|
|
17
|
+
ActionController::UnknownAction
|
|
18
|
+
ActionController::UnknownFormat
|
|
19
|
+
ActionController::UnknownHttpMethod
|
|
20
|
+
ActionController::UrlGenerationError
|
|
21
|
+
ActionDispatch::Http::MimeNegotiation::InvalidType
|
|
22
|
+
ActiveRecord::RecordNotFound
|
|
23
|
+
CGI::Session::CookieStore::TamperedWithCookie
|
|
24
|
+
Mime::Type::InvalidMimeType
|
|
25
|
+
Rack::QueryParser::InvalidParameterError
|
|
26
|
+
Rack::QueryParser::ParameterTypeError
|
|
27
|
+
Rack::Utils::InvalidParameterError
|
|
28
|
+
SignalException
|
|
29
|
+
SystemExit
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
GIT_SHA_SOURCES = %w[GIT_SHA REVISION KAMAL_VERSION].freeze
|
|
33
|
+
|
|
34
|
+
autoload :BreadcrumbBuffer, "rails_informant/breadcrumb_buffer"
|
|
35
|
+
autoload :BreadcrumbSubscriber, "rails_informant/breadcrumb_subscriber"
|
|
36
|
+
autoload :ContextBuilder, "rails_informant/context_builder"
|
|
37
|
+
autoload :ContextFilter, "rails_informant/context_filter"
|
|
38
|
+
autoload :Current, "rails_informant/current"
|
|
39
|
+
autoload :ErrorRecorder, "rails_informant/error_recorder"
|
|
40
|
+
autoload :ErrorSubscriber, "rails_informant/error_subscriber"
|
|
41
|
+
autoload :Fingerprint, "rails_informant/fingerprint"
|
|
42
|
+
autoload :StructuredEventSubscriber, "rails_informant/structured_event_subscriber"
|
|
43
|
+
|
|
44
|
+
module Middleware
|
|
45
|
+
autoload :ErrorCapture, "rails_informant/middleware/error_capture"
|
|
46
|
+
autoload :RescuedExceptionInterceptor, "rails_informant/middleware/rescued_exception_interceptor"
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
module Notifiers
|
|
50
|
+
autoload :Devin, "rails_informant/notifiers/devin"
|
|
51
|
+
autoload :NotificationPolicy, "rails_informant/notifiers/notification_policy"
|
|
52
|
+
autoload :Slack, "rails_informant/notifiers/slack"
|
|
53
|
+
autoload :Webhook, "rails_informant/notifiers/webhook"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
mattr_accessor :config
|
|
57
|
+
self.config = Configuration.new
|
|
58
|
+
|
|
59
|
+
class << self
|
|
60
|
+
delegate :api_token,
|
|
61
|
+
:capture_errors,
|
|
62
|
+
:capture_user_email,
|
|
63
|
+
:devin_api_key,
|
|
64
|
+
:devin_playbook_id,
|
|
65
|
+
:ignored_exceptions,
|
|
66
|
+
:notifiers,
|
|
67
|
+
:retention_days,
|
|
68
|
+
:slack_webhook_url,
|
|
69
|
+
:webhook_url,
|
|
70
|
+
to: :config
|
|
71
|
+
|
|
72
|
+
def configure
|
|
73
|
+
yield config
|
|
74
|
+
reset_caches!
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def reset_caches!
|
|
78
|
+
@_initialized = nil
|
|
79
|
+
@_ignored_set = nil
|
|
80
|
+
config.reset_notifiers!
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def initialized?
|
|
84
|
+
return @_initialized if defined?(@_initialized) && @_initialized
|
|
85
|
+
|
|
86
|
+
@_initialized = capture_errors && defined?(ActiveRecord::Base) && ActiveRecord::Base.connection_pool.connected?
|
|
87
|
+
rescue ActiveRecord::ConnectionNotEstablished
|
|
88
|
+
false
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def ignored_exception?(exception)
|
|
92
|
+
ignored = ignored_exception_set
|
|
93
|
+
exception.class.ancestors.each do |ancestor|
|
|
94
|
+
name = ancestor.name or next
|
|
95
|
+
return true if ignored.include?(name)
|
|
96
|
+
end
|
|
97
|
+
false
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def current_git_sha
|
|
101
|
+
@_current_git_sha ||= resolve_git_sha
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def already_captured?(error)
|
|
105
|
+
error.instance_variable_get(:@__rails_informant_captured)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def capture(exception, context: {}, request: nil)
|
|
109
|
+
return if already_captured?(exception)
|
|
110
|
+
mark_captured!(exception)
|
|
111
|
+
ErrorRecorder.record exception, severity: "error", context:, env: request&.env
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def mark_captured!(error)
|
|
115
|
+
error.instance_variable_set(:@__rails_informant_captured, true) unless error.frozen?
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def server_mode?
|
|
119
|
+
defined?(Rails::Server)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def ignored_exception_set
|
|
125
|
+
@_ignored_set ||= Set.new(IGNORED_EXCEPTIONS_DEFAULT + Array(ignored_exceptions)).freeze
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def resolve_git_sha
|
|
129
|
+
GIT_SHA_SOURCES.each do |key|
|
|
130
|
+
return ENV[key] if ENV[key].present?
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
head = Rails.root.join(".git", "HEAD").read.strip
|
|
134
|
+
if head.start_with?("ref: ")
|
|
135
|
+
ref = head.delete_prefix("ref: ")
|
|
136
|
+
raise ArgumentError if ref.include?("..") # path traversal guard
|
|
137
|
+
Rails.root.join(".git", ref).read.strip
|
|
138
|
+
else
|
|
139
|
+
head
|
|
140
|
+
end
|
|
141
|
+
rescue Errno::ENOENT, Errno::EACCES, ArgumentError
|
|
142
|
+
nil
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
require "rails_informant/engine"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
namespace :informant do
|
|
2
|
+
desc "Purge resolved errors older than retention_days"
|
|
3
|
+
task purge: :environment do
|
|
4
|
+
RailsInformant::PurgeJob.perform_now
|
|
5
|
+
puts "Purge complete"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
desc "Show error monitoring statistics"
|
|
9
|
+
task stats: :environment do
|
|
10
|
+
groups = RailsInformant::ErrorGroup
|
|
11
|
+
total = groups.count
|
|
12
|
+
unresolved = groups.where(status: "unresolved").count
|
|
13
|
+
fix_pending = groups.where(status: "fix_pending").count
|
|
14
|
+
resolved = groups.where(status: "resolved").count
|
|
15
|
+
ignored = groups.where(status: "ignored").count
|
|
16
|
+
duplicates = groups.where(status: "duplicate").count
|
|
17
|
+
occurrences = RailsInformant::Occurrence.count
|
|
18
|
+
|
|
19
|
+
puts "Rails Informant Statistics"
|
|
20
|
+
puts "-" * 30
|
|
21
|
+
puts "Error groups: #{total}"
|
|
22
|
+
puts " Unresolved: #{unresolved}"
|
|
23
|
+
puts " Fix pending: #{fix_pending}"
|
|
24
|
+
puts " Resolved: #{resolved}"
|
|
25
|
+
puts " Ignored: #{ignored}"
|
|
26
|
+
puts " Duplicates: #{duplicates}"
|
|
27
|
+
puts "Occurrences: #{occurrences}"
|
|
28
|
+
puts "Deploy SHA: #{RailsInformant.current_git_sha || 'unknown'}"
|
|
29
|
+
end
|
|
30
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-informant
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Daniel López Prat
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: actionpack
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.1'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.1'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: activejob
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '8.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - ">="
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '8.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: activerecord
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '8.1'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '8.1'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: activesupport
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - ">="
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '8.1'
|
|
61
|
+
type: :runtime
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '8.1'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: railties
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '8.1'
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '8.1'
|
|
82
|
+
description: Rails Engine that captures exceptions, stores them in your database with
|
|
83
|
+
rich context, and exposes error data via a bundled MCP server so AI agents can triage,
|
|
84
|
+
resolve, and fix errors autonomously.
|
|
85
|
+
email:
|
|
86
|
+
- daniel@6temes.cat
|
|
87
|
+
executables:
|
|
88
|
+
- informant-mcp
|
|
89
|
+
extensions: []
|
|
90
|
+
extra_rdoc_files: []
|
|
91
|
+
files:
|
|
92
|
+
- LICENSE
|
|
93
|
+
- README.md
|
|
94
|
+
- Rakefile
|
|
95
|
+
- VERSION
|
|
96
|
+
- app/controllers/rails_informant/api/base_controller.rb
|
|
97
|
+
- app/controllers/rails_informant/api/errors_controller.rb
|
|
98
|
+
- app/controllers/rails_informant/api/occurrences_controller.rb
|
|
99
|
+
- app/controllers/rails_informant/api/status_controller.rb
|
|
100
|
+
- app/jobs/rails_informant/application_job.rb
|
|
101
|
+
- app/jobs/rails_informant/notify_job.rb
|
|
102
|
+
- app/jobs/rails_informant/purge_job.rb
|
|
103
|
+
- app/models/rails_informant/application_record.rb
|
|
104
|
+
- app/models/rails_informant/error_group.rb
|
|
105
|
+
- app/models/rails_informant/occurrence.rb
|
|
106
|
+
- config/routes.rb
|
|
107
|
+
- db/migrate/20260227000000_create_informant_tables.rb
|
|
108
|
+
- exe/informant-mcp
|
|
109
|
+
- lib/generators/rails_informant/devin/templates/error-triage.devin.md
|
|
110
|
+
- lib/generators/rails_informant/devin_generator.rb
|
|
111
|
+
- lib/generators/rails_informant/install_generator.rb
|
|
112
|
+
- lib/generators/rails_informant/skill/templates/SKILL.md
|
|
113
|
+
- lib/generators/rails_informant/skill_generator.rb
|
|
114
|
+
- lib/generators/rails_informant/templates/create_informant_tables.rb.erb
|
|
115
|
+
- lib/generators/rails_informant/templates/initializer.rb.erb
|
|
116
|
+
- lib/rails_informant.rb
|
|
117
|
+
- lib/rails_informant/breadcrumb_buffer.rb
|
|
118
|
+
- lib/rails_informant/breadcrumb_subscriber.rb
|
|
119
|
+
- lib/rails_informant/configuration.rb
|
|
120
|
+
- lib/rails_informant/context_builder.rb
|
|
121
|
+
- lib/rails_informant/context_filter.rb
|
|
122
|
+
- lib/rails_informant/current.rb
|
|
123
|
+
- lib/rails_informant/engine.rb
|
|
124
|
+
- lib/rails_informant/error_recorder.rb
|
|
125
|
+
- lib/rails_informant/error_subscriber.rb
|
|
126
|
+
- lib/rails_informant/fingerprint.rb
|
|
127
|
+
- lib/rails_informant/mcp.rb
|
|
128
|
+
- lib/rails_informant/mcp/base_tool.rb
|
|
129
|
+
- lib/rails_informant/mcp/client.rb
|
|
130
|
+
- lib/rails_informant/mcp/configuration.rb
|
|
131
|
+
- lib/rails_informant/mcp/server.rb
|
|
132
|
+
- lib/rails_informant/mcp/tools/annotate_error.rb
|
|
133
|
+
- lib/rails_informant/mcp/tools/delete_error.rb
|
|
134
|
+
- lib/rails_informant/mcp/tools/get_error.rb
|
|
135
|
+
- lib/rails_informant/mcp/tools/get_informant_status.rb
|
|
136
|
+
- lib/rails_informant/mcp/tools/ignore_error.rb
|
|
137
|
+
- lib/rails_informant/mcp/tools/list_environments.rb
|
|
138
|
+
- lib/rails_informant/mcp/tools/list_errors.rb
|
|
139
|
+
- lib/rails_informant/mcp/tools/list_occurrences.rb
|
|
140
|
+
- lib/rails_informant/mcp/tools/mark_duplicate.rb
|
|
141
|
+
- lib/rails_informant/mcp/tools/mark_fix_pending.rb
|
|
142
|
+
- lib/rails_informant/mcp/tools/reopen_error.rb
|
|
143
|
+
- lib/rails_informant/mcp/tools/resolve_error.rb
|
|
144
|
+
- lib/rails_informant/middleware/error_capture.rb
|
|
145
|
+
- lib/rails_informant/middleware/rescued_exception_interceptor.rb
|
|
146
|
+
- lib/rails_informant/notifiers/devin.rb
|
|
147
|
+
- lib/rails_informant/notifiers/notification_policy.rb
|
|
148
|
+
- lib/rails_informant/notifiers/slack.rb
|
|
149
|
+
- lib/rails_informant/notifiers/webhook.rb
|
|
150
|
+
- lib/rails_informant/structured_event_subscriber.rb
|
|
151
|
+
- lib/rails_informant/version.rb
|
|
152
|
+
- lib/tasks/rails_informant.rake
|
|
153
|
+
homepage: https://github.com/6temes/rails-informant
|
|
154
|
+
licenses:
|
|
155
|
+
- MIT
|
|
156
|
+
metadata:
|
|
157
|
+
bug_tracker_uri: https://github.com/6temes/rails-informant/issues
|
|
158
|
+
rubygems_mfa_required: 'true'
|
|
159
|
+
source_code_uri: https://github.com/6temes/rails-informant
|
|
160
|
+
rdoc_options: []
|
|
161
|
+
require_paths:
|
|
162
|
+
- lib
|
|
163
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
164
|
+
requirements:
|
|
165
|
+
- - ">="
|
|
166
|
+
- !ruby/object:Gem::Version
|
|
167
|
+
version: '4.0'
|
|
168
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
169
|
+
requirements:
|
|
170
|
+
- - ">="
|
|
171
|
+
- !ruby/object:Gem::Version
|
|
172
|
+
version: '0'
|
|
173
|
+
requirements: []
|
|
174
|
+
rubygems_version: 4.0.6
|
|
175
|
+
specification_version: 4
|
|
176
|
+
summary: Self-hosted error monitoring for Rails with MCP server for agentic workflows
|
|
177
|
+
test_files: []
|