bugsage 0.2.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 (69) hide show
  1. checksums.yaml +7 -0
  2. data/ARCHITECTURE.md +442 -0
  3. data/CHANGELOG.md +28 -0
  4. data/CODE_OF_CONDUCT.md +10 -0
  5. data/CONTRIBUTING.md +301 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +344 -0
  8. data/ROADMAP.md +217 -0
  9. data/SECURITY.md +83 -0
  10. data/docs/AI.md +167 -0
  11. data/docs/Configuration.md +168 -0
  12. data/docs/GettingStarted.md +146 -0
  13. data/docs/RELEASE_CHECKLIST.md +346 -0
  14. data/docs/Troubleshooting.md +181 -0
  15. data/docs/images/BadRequest.png +0 -0
  16. data/docs/images/BadRequest2.png +0 -0
  17. data/docs/images/BugSage_Logo.png +0 -0
  18. data/docs/images/BugSage_Social_Preview.png +0 -0
  19. data/docs/images/NoMethodError.png +0 -0
  20. data/docs/images/NoMethodError2.png +0 -0
  21. data/docs/images/README.md +38 -0
  22. data/docs/releases/README.md +21 -0
  23. data/docs/releases/v0.2.0.md +40 -0
  24. data/exe/bugsage +7 -0
  25. data/lib/bugsage/ai_analyzer.rb +145 -0
  26. data/lib/bugsage/ai_chat.rb +388 -0
  27. data/lib/bugsage/ai_context.rb +69 -0
  28. data/lib/bugsage/ai_panel.rb +708 -0
  29. data/lib/bugsage/ai_support.rb +36 -0
  30. data/lib/bugsage/auto_configurator.rb +97 -0
  31. data/lib/bugsage/cli.rb +59 -0
  32. data/lib/bugsage/code_context.rb +81 -0
  33. data/lib/bugsage/code_patch.rb +147 -0
  34. data/lib/bugsage/configuration.rb +149 -0
  35. data/lib/bugsage/console_context.rb +51 -0
  36. data/lib/bugsage/cursor_client.rb +165 -0
  37. data/lib/bugsage/dashboard.rb +627 -0
  38. data/lib/bugsage/editor_links.rb +34 -0
  39. data/lib/bugsage/error_page.rb +298 -0
  40. data/lib/bugsage/exception_handler.rb +66 -0
  41. data/lib/bugsage/exception_support.rb +95 -0
  42. data/lib/bugsage/exceptions_app.rb +31 -0
  43. data/lib/bugsage/fix_applicator.rb +107 -0
  44. data/lib/bugsage/formatter.rb +37 -0
  45. data/lib/bugsage/http_error_capture.rb +104 -0
  46. data/lib/bugsage/http_response_error.rb +14 -0
  47. data/lib/bugsage/inline_console.rb +226 -0
  48. data/lib/bugsage/installation.rb +94 -0
  49. data/lib/bugsage/installer.rb +66 -0
  50. data/lib/bugsage/json_endpoint.rb +34 -0
  51. data/lib/bugsage/locales/en.yml +439 -0
  52. data/lib/bugsage/middleware.rb +152 -0
  53. data/lib/bugsage/openai_client.rb +103 -0
  54. data/lib/bugsage/page_actions.rb +255 -0
  55. data/lib/bugsage/railtie.rb +49 -0
  56. data/lib/bugsage/request_context.rb +56 -0
  57. data/lib/bugsage/rule.rb +271 -0
  58. data/lib/bugsage/session_clear.rb +35 -0
  59. data/lib/bugsage/store.rb +60 -0
  60. data/lib/bugsage/suggestion.rb +58 -0
  61. data/lib/bugsage/trace_cleaner.rb +24 -0
  62. data/lib/bugsage/translations.rb +85 -0
  63. data/lib/bugsage/version.rb +5 -0
  64. data/lib/bugsage.rb +65 -0
  65. data/lib/generators/bugsage/install/install_generator.rb +21 -0
  66. data/lib/generators/bugsage/install/templates/bugsage.rb +22 -0
  67. data/scripts/publish-github-release.sh +38 -0
  68. data/sig/bugsage.rbs +4 -0
  69. metadata +157 -0
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+
5
+ module Bugsage
6
+ class Store
7
+ MAX_ENTRIES = 100
8
+
9
+ def self.add(suggestion, context = {}, metadata = {})
10
+ event = build_event(suggestion, context, metadata)
11
+ entries.unshift(event)
12
+ entries.pop if entries.size > MAX_ENTRIES
13
+ end
14
+
15
+ def self.all
16
+ entries
17
+ end
18
+
19
+ def self.clear!
20
+ @entries = []
21
+ end
22
+
23
+ def self.entries
24
+ @entries ||= []
25
+ end
26
+
27
+ def self.update_at(index, suggestion, ai_error: nil)
28
+ event = entries[index]
29
+ return unless event
30
+
31
+ event[:root_cause] = suggestion.root_cause
32
+ event[:fixes] = suggestion.fixes
33
+ event[:confidence] = suggestion.confidence
34
+ event[:source] = suggestion.source
35
+ event[:ai_notes] = suggestion.ai_notes
36
+ event[:code_patch] = suggestion.code_patch
37
+ event[:ai_error] = ai_error
38
+ end
39
+
40
+ def self.build_event(suggestion, context, metadata = {})
41
+ exception = metadata[:exception]
42
+
43
+ {
44
+ issue: suggestion.issue,
45
+ location: suggestion.location,
46
+ root_cause: suggestion.root_cause,
47
+ fixes: suggestion.fixes,
48
+ confidence: suggestion.confidence,
49
+ source: suggestion.source,
50
+ ai_notes: suggestion.ai_notes,
51
+ code_patch: suggestion.code_patch,
52
+ ai_error: metadata[:ai_error],
53
+ exception_class: exception&.class&.name,
54
+ exception_message: exception&.message.to_s,
55
+ context: context,
56
+ timestamp: Time.now.utc.iso8601
57
+ }
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bugsage
4
+ class Suggestion
5
+ SOURCES = %i[rules ai hybrid].freeze
6
+
7
+ attr_reader :issue, :location, :root_cause, :fixes, :confidence, :source, :ai_notes, :code_patch
8
+
9
+ def initialize(issue:, location:, root_cause:, fixes:, confidence:, source: :rules, ai_notes: nil, code_patch: nil,
10
+ code_fix: nil)
11
+ @issue = issue
12
+ @location = location
13
+ @root_cause = root_cause
14
+ @fixes = fixes
15
+ @confidence = confidence
16
+ @source = normalize_source(source)
17
+ @ai_notes = ai_notes
18
+ @code_patch = normalize_code_patch(code_patch, code_fix)
19
+ end
20
+
21
+ def code_fix
22
+ CodePatch.preview_for(@code_patch)
23
+ end
24
+
25
+ def ai_enhanced?
26
+ %i[hybrid ai].include?(source)
27
+ end
28
+
29
+ def with_ai_enhancement(root_cause:, fixes:, confidence:, ai_notes: nil, code_patch: nil)
30
+ self.class.new(
31
+ issue: issue,
32
+ location: location,
33
+ root_cause: root_cause,
34
+ fixes: fixes,
35
+ confidence: confidence,
36
+ source: :hybrid,
37
+ ai_notes: ai_notes,
38
+ code_patch: code_patch || @code_patch
39
+ )
40
+ end
41
+
42
+ private
43
+
44
+ def normalize_source(source)
45
+ symbol = source.to_sym
46
+ SOURCES.include?(symbol) ? symbol : :rules
47
+ end
48
+
49
+ def normalize_code_patch(code_patch, legacy_code_fix)
50
+ return code_patch if code_patch.is_a?(Hash) && !code_patch.empty?
51
+ return nil if legacy_code_fix.to_s.strip.empty?
52
+
53
+ _, line_number = CodeContext.extract_location(location)
54
+ legacy = CodePatch.from_legacy(legacy_code_fix, line_number || 1)
55
+ legacy&.to_h
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bugsage
4
+ class TraceCleaner
5
+ FRAMEWORK_PATTERNS = [
6
+ %r{/gems/},
7
+ %r{/ruby/\d}
8
+ ].freeze
9
+
10
+ def self.clean(backtrace)
11
+ return [] unless backtrace
12
+
13
+ backtrace.reject { |line| framework_line?(line) }
14
+ end
15
+
16
+ def self.first_application_frame(backtrace)
17
+ clean(backtrace).first
18
+ end
19
+
20
+ def self.framework_line?(line)
21
+ FRAMEWORK_PATTERNS.any? { |pattern| line.match?(pattern) }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+
5
+ module Bugsage
6
+ module Translations
7
+ LOCALE_DIR = File.expand_path("locales", __dir__)
8
+
9
+ module_function
10
+
11
+ def load!
12
+ return if @loaded
13
+
14
+ paths = Dir[File.join(LOCALE_DIR, "*.yml")]
15
+ if defined?(::I18n)
16
+ ::I18n.load_path |= paths
17
+ else
18
+ @fallback_translations = {}
19
+ paths.each do |path|
20
+ data = YAML.safe_load_file(path, aliases: true)
21
+ deep_merge!(@fallback_translations, data)
22
+ end
23
+ end
24
+
25
+ @loaded = true
26
+ end
27
+
28
+ def t(key, **)
29
+ load!
30
+ full_key = key.to_s.start_with?("bugsage.") ? key : "bugsage.#{key}"
31
+
32
+ if defined?(::I18n)
33
+ ::I18n.t(full_key, **)
34
+ else
35
+ translate_fallback(full_key, **)
36
+ end
37
+ end
38
+
39
+ def translate_fallback(key, **options)
40
+ keys = key.split(".")
41
+ value = dig_fallback(keys)
42
+ return options[:default] if value.nil? && options.key?(:default)
43
+
44
+ case value
45
+ when Array
46
+ value
47
+ when String
48
+ interpolate(value, options)
49
+ else
50
+ options.fetch(:default, missing_translation(key))
51
+ end
52
+ end
53
+
54
+ def dig_fallback(keys)
55
+ locale = @fallback_translations["en"] || @fallback_translations[:en] || {}
56
+ keys.reduce(locale) do |node, segment|
57
+ break nil unless node.is_a?(Hash)
58
+
59
+ node[segment] || node[segment.to_sym]
60
+ end
61
+ end
62
+
63
+ def deep_merge!(base, other)
64
+ other.each do |key, value|
65
+ key = key.to_s
66
+ if base[key].is_a?(Hash) && value.is_a?(Hash)
67
+ deep_merge!(base[key], value)
68
+ else
69
+ base[key] = value
70
+ end
71
+ end
72
+ end
73
+
74
+ def interpolate(string, options)
75
+ string.gsub(/%\{(\w+)\}/) do
76
+ symbol = Regexp.last_match(1).to_sym
77
+ options.key?(symbol) ? options[symbol].to_s : "%{#{symbol}}"
78
+ end
79
+ end
80
+
81
+ def missing_translation(key)
82
+ key
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Bugsage
4
+ VERSION = "0.2.0"
5
+ end
data/lib/bugsage.rb ADDED
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "bugsage/version"
4
+ require_relative "bugsage/configuration"
5
+ require_relative "bugsage/suggestion"
6
+ require_relative "bugsage/trace_cleaner"
7
+ require_relative "bugsage/exception_support"
8
+ require_relative "bugsage/http_response_error"
9
+ require_relative "bugsage/http_error_capture"
10
+ require_relative "bugsage/code_context"
11
+ require_relative "bugsage/rule"
12
+ require_relative "bugsage/json_endpoint"
13
+ require_relative "bugsage/request_context"
14
+ require_relative "bugsage/ai_support"
15
+ require_relative "bugsage/openai_client"
16
+ require_relative "bugsage/cursor_client"
17
+ require_relative "bugsage/ai_analyzer"
18
+ require_relative "bugsage/formatter"
19
+ require_relative "bugsage/store"
20
+ require_relative "bugsage/console_context"
21
+ require_relative "bugsage/ai_context"
22
+ require_relative "bugsage/inline_console"
23
+ require_relative "bugsage/ai_panel"
24
+ require_relative "bugsage/ai_chat"
25
+ require_relative "bugsage/code_patch"
26
+ require_relative "bugsage/editor_links"
27
+ require_relative "bugsage/fix_applicator"
28
+ require_relative "bugsage/session_clear"
29
+ require_relative "bugsage/page_actions"
30
+ require_relative "bugsage/translations"
31
+ require_relative "bugsage/exception_handler"
32
+ require_relative "bugsage/error_page"
33
+ require_relative "bugsage/dashboard"
34
+ require_relative "bugsage/middleware"
35
+ require_relative "bugsage/auto_configurator"
36
+ require_relative "bugsage/installation"
37
+ require_relative "bugsage/installer"
38
+ require_relative "bugsage/cli"
39
+ require_relative "bugsage/exceptions_app"
40
+
41
+ require_relative "bugsage/railtie" if defined?(Rails::Railtie)
42
+
43
+ Bugsage::Translations.load!
44
+
45
+ module Bugsage
46
+ class Error < StandardError; end
47
+
48
+ class << self
49
+ def configuration
50
+ @configuration ||= Configuration.new
51
+ end
52
+
53
+ def t(key, **)
54
+ Translations.t(key, **)
55
+ end
56
+
57
+ def configure
58
+ yield(configuration)
59
+ end
60
+
61
+ def reset_configuration!
62
+ @configuration = Configuration.new
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module Bugsage
6
+ module Generators
7
+ class InstallGenerator < Rails::Generators::Base
8
+ desc "Install BugSage configuration (optional initializer)"
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ def copy_initializer
12
+ template "bugsage.rb", "config/initializers/bugsage.rb"
13
+ end
14
+
15
+ def show_summary
16
+ config = Bugsage::AutoConfigurator.apply!
17
+ say "\n#{Bugsage::AutoConfigurator.summary(config).join("\n")}\n", :green
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ # BugSage — Rails installation
4
+ #
5
+ # Quick start (zero-config):
6
+ # 1. Add to Gemfile: gem "bugsage"
7
+ # 2. Run: bundle install
8
+ # 3. Run: bin/rails server
9
+ # 4. Trigger an error in development to see the BugSage error page
10
+ # 5. Visit: http://localhost:3000/bugsage
11
+ #
12
+ # Optional:
13
+ # - bundle exec rails generate bugsage:install (commented initializer)
14
+ # - export OPENAI_API_KEY=sk-... (auto-enables OpenAI)
15
+ # - export CURSOR_API_KEY=crsr_... (auto-enables Cursor)
16
+ # - export BUGSAGE_ENABLED_ENVIRONMENTS=development,test,staging
17
+ #
18
+ # BugSage auto-wires middleware, /bugsage, and error pages via Bugsage::Railtie.
19
+ # You do not need to edit config/routes.rb or add middleware manually.
20
+ #
21
+ # See Bugsage::Installation for the full step list, or run:
22
+ # bundle exec bugsage install
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env bash
2
+ # Publish / refresh GitHub release notes for a tagged version.
3
+ # Usage: scripts/publish-github-release.sh [vX.Y.Z]
4
+ set -euo pipefail
5
+
6
+ ROOT="$(cd "$(dirname "$0")/.." && pwd)"
7
+ cd "$ROOT"
8
+
9
+ VERSION="${1:-v0.2.0}"
10
+ NOTES_FILE="docs/releases/${VERSION}.md"
11
+
12
+ if [[ ! -f "$NOTES_FILE" ]]; then
13
+ echo "Missing notes file: $NOTES_FILE" >&2
14
+ exit 1
15
+ fi
16
+
17
+ if ! command -v gh >/dev/null 2>&1; then
18
+ echo "Install GitHub CLI: https://cli.github.com/" >&2
19
+ exit 1
20
+ fi
21
+
22
+ TITLE="BugSage ${VERSION}"
23
+
24
+ if gh release view "$VERSION" --repo MONARCHKOLI/bugsage >/dev/null 2>&1; then
25
+ echo "Updating existing release $VERSION…"
26
+ gh release edit "$VERSION" \
27
+ --repo MONARCHKOLI/bugsage \
28
+ --title "$TITLE" \
29
+ --notes-file "$NOTES_FILE"
30
+ else
31
+ echo "Creating release $VERSION…"
32
+ gh release create "$VERSION" \
33
+ --repo MONARCHKOLI/bugsage \
34
+ --title "$TITLE" \
35
+ --notes-file "$NOTES_FILE"
36
+ fi
37
+
38
+ echo "Done: https://github.com/MONARCHKOLI/bugsage/releases/tag/${VERSION}"
data/sig/bugsage.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Bugsage
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bugsage
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Monarch Koli
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: i18n
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '1.14'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '1.14'
26
+ - !ruby/object:Gem::Dependency
27
+ name: pastel
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '0.8'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.8'
40
+ - !ruby/object:Gem::Dependency
41
+ name: thor
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.3'
54
+ description: BugSage watches your Rails application's logs and exceptions, classifies
55
+ errors, and suggests likely root causes and fixes — using deterministic rules first,
56
+ with optional AI-powered analysis.
57
+ email:
58
+ - monarchkoli12@gmail.com
59
+ executables:
60
+ - bugsage
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ARCHITECTURE.md
65
+ - CHANGELOG.md
66
+ - CODE_OF_CONDUCT.md
67
+ - CONTRIBUTING.md
68
+ - LICENSE.txt
69
+ - README.md
70
+ - ROADMAP.md
71
+ - SECURITY.md
72
+ - docs/AI.md
73
+ - docs/Configuration.md
74
+ - docs/GettingStarted.md
75
+ - docs/RELEASE_CHECKLIST.md
76
+ - docs/Troubleshooting.md
77
+ - docs/images/BadRequest.png
78
+ - docs/images/BadRequest2.png
79
+ - docs/images/BugSage_Logo.png
80
+ - docs/images/BugSage_Social_Preview.png
81
+ - docs/images/NoMethodError.png
82
+ - docs/images/NoMethodError2.png
83
+ - docs/images/README.md
84
+ - docs/releases/README.md
85
+ - docs/releases/v0.2.0.md
86
+ - exe/bugsage
87
+ - lib/bugsage.rb
88
+ - lib/bugsage/ai_analyzer.rb
89
+ - lib/bugsage/ai_chat.rb
90
+ - lib/bugsage/ai_context.rb
91
+ - lib/bugsage/ai_panel.rb
92
+ - lib/bugsage/ai_support.rb
93
+ - lib/bugsage/auto_configurator.rb
94
+ - lib/bugsage/cli.rb
95
+ - lib/bugsage/code_context.rb
96
+ - lib/bugsage/code_patch.rb
97
+ - lib/bugsage/configuration.rb
98
+ - lib/bugsage/console_context.rb
99
+ - lib/bugsage/cursor_client.rb
100
+ - lib/bugsage/dashboard.rb
101
+ - lib/bugsage/editor_links.rb
102
+ - lib/bugsage/error_page.rb
103
+ - lib/bugsage/exception_handler.rb
104
+ - lib/bugsage/exception_support.rb
105
+ - lib/bugsage/exceptions_app.rb
106
+ - lib/bugsage/fix_applicator.rb
107
+ - lib/bugsage/formatter.rb
108
+ - lib/bugsage/http_error_capture.rb
109
+ - lib/bugsage/http_response_error.rb
110
+ - lib/bugsage/inline_console.rb
111
+ - lib/bugsage/installation.rb
112
+ - lib/bugsage/installer.rb
113
+ - lib/bugsage/json_endpoint.rb
114
+ - lib/bugsage/locales/en.yml
115
+ - lib/bugsage/middleware.rb
116
+ - lib/bugsage/openai_client.rb
117
+ - lib/bugsage/page_actions.rb
118
+ - lib/bugsage/railtie.rb
119
+ - lib/bugsage/request_context.rb
120
+ - lib/bugsage/rule.rb
121
+ - lib/bugsage/session_clear.rb
122
+ - lib/bugsage/store.rb
123
+ - lib/bugsage/suggestion.rb
124
+ - lib/bugsage/trace_cleaner.rb
125
+ - lib/bugsage/translations.rb
126
+ - lib/bugsage/version.rb
127
+ - lib/generators/bugsage/install/install_generator.rb
128
+ - lib/generators/bugsage/install/templates/bugsage.rb
129
+ - scripts/publish-github-release.sh
130
+ - sig/bugsage.rbs
131
+ homepage: https://github.com/MONARCHKOLI/bugsage
132
+ licenses:
133
+ - MIT
134
+ metadata:
135
+ allowed_push_host: https://rubygems.org
136
+ homepage_uri: https://github.com/MONARCHKOLI/bugsage
137
+ source_code_uri: https://github.com/MONARCHKOLI/bugsage
138
+ changelog_uri: https://github.com/MONARCHKOLI/bugsage/blob/main/CHANGELOG.md
139
+ rubygems_mfa_required: 'true'
140
+ rdoc_options: []
141
+ require_paths:
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 3.2.0
148
+ required_rubygems_version: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ requirements: []
154
+ rubygems_version: 3.6.9
155
+ specification_version: 4
156
+ summary: AI-powered debugging assistant for Ruby on Rails
157
+ test_files: []