ui_guardrails 1.0.0 → 1.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.
@@ -107,19 +107,106 @@ namespace :guardrails do
107
107
  }
108
108
  $stdout.puts JSON.pretty_generate(payload)
109
109
  else
110
+ # Run each sub-audit against an in-memory sink first so we can
111
+ # render the top-of-report summary before the per-category
112
+ # details (the summary needs every detector's count). Then
113
+ # write the sink + per-category sections out together.
114
+ #
115
+ # Important: detectors write into `sink` (StringIO) but make
116
+ # ANSI-color decisions against `$stdout`. If we let each
117
+ # detector instantiate its own Style bound to the sink, every
118
+ # color() call would be false (StringIO isn't a TTY) and the
119
+ # body of the report would print plain even on a real terminal,
120
+ # while the top/bottom summary printed straight to $stdout
121
+ # would still be colored. Pre-build one Style here and thread
122
+ # it through every detector so the whole report tracks the
123
+ # real terminal's TTY/NO_COLOR signal consistently.
124
+ require "guardrails/report/summary"
125
+ require "guardrails/report/style"
126
+ sink = StringIO.new
127
+ report_style = Guardrails::Report::Style.new(io: $stdout)
110
128
  violations = Guardrails::Audit.new(
111
- root: root, suggest: suggest, apply: apply, format: :text
129
+ root: root, output: sink, suggest: suggest, apply: apply, format: :text,
130
+ style: report_style
112
131
  ).run
113
- stimulus = Guardrails::StimulusAudit.new(root: root).run
132
+ stimulus = Guardrails::StimulusAudit.new(root: root, output: sink, style: report_style).run
133
+ similarity_opts[:output] = sink
134
+ similarity_opts[:style] = report_style
114
135
  similarity = Guardrails::PartialSimilarity.new(**similarity_opts).run
115
- vc = Guardrails::ViewComponentAudit.new(root: root).run
116
- a11y = Guardrails::A11yAudit.new(root: root).run
136
+ vc = Guardrails::ViewComponentAudit.new(root: root, output: sink, style: report_style).run
137
+ a11y = Guardrails::A11yAudit.new(root: root, output: sink, style: report_style).run
138
+ pattern_opts[:output] = sink
139
+ pattern_opts[:style] = report_style
117
140
  patterns = Guardrails::CrossCodebasePatterns.new(**pattern_opts).run
141
+ classitis_opts[:output] = sink
142
+ classitis_opts[:style] = report_style
118
143
  classitis = Guardrails::ClassItis.new(**classitis_opts).run
119
- a11y_deep_runner = axe_json_path ? Guardrails::A11yDeep.new(input: axe_json_path) : nil
144
+ a11y_deep_runner = axe_json_path ? Guardrails::A11yDeep.new(input: axe_json_path, output: sink, style: report_style) : nil
120
145
  a11y_deep = a11y_deep_runner&.run || []
121
- visual_diff_runner = visual_diff_on ? Guardrails::VisualDiff.new(root: root) : nil
146
+ visual_diff_runner = visual_diff_on ? Guardrails::VisualDiff.new(root: root, output: sink, style: report_style) : nil
122
147
  visual_diff = visual_diff_runner&.run || []
148
+
149
+ summary_entries = [
150
+ Guardrails::Report::Summary::Entry.new(
151
+ category: "raw_color", count: violations.count { |v| v.type == :raw_color },
152
+ severity: :error, auto_fix: true
153
+ ),
154
+ Guardrails::Report::Summary::Entry.new(
155
+ category: "tailwind_arbitrary", count: violations.count { |v| v.type == :tailwind_arbitrary },
156
+ severity: :error, auto_fix: true
157
+ ),
158
+ Guardrails::Report::Summary::Entry.new(
159
+ category: "inline_style", count: violations.count { |v| v.type == :inline_style },
160
+ severity: :warning
161
+ ),
162
+ Guardrails::Report::Summary::Entry.new(
163
+ category: "helper_recommended", count: violations.count { |v| v.type == :helper_recommended },
164
+ severity: :warning
165
+ ),
166
+ Guardrails::Report::Summary::Entry.new(
167
+ category: "a11y (static)", count: a11y.length, severity: :error
168
+ ),
169
+ Guardrails::Report::Summary::Entry.new(
170
+ category: "a11y (deep)", count: a11y_deep.length, severity: :error
171
+ ),
172
+ Guardrails::Report::Summary::Entry.new(
173
+ category: "stimulus orphaned", count: stimulus.orphaned.length, severity: :warning
174
+ ),
175
+ Guardrails::Report::Summary::Entry.new(
176
+ category: "stimulus dead", count: stimulus.dead.length, severity: :warning
177
+ ),
178
+ Guardrails::Report::Summary::Entry.new(
179
+ category: "missing previews", count: vc.missing_previews.length, severity: :warning
180
+ ),
181
+ Guardrails::Report::Summary::Entry.new(
182
+ category: "orphan slots", count: vc.orphan_slots.length, severity: :warning
183
+ ),
184
+ Guardrails::Report::Summary::Entry.new(
185
+ category: "visual diff", count: visual_diff.length, severity: :error
186
+ ),
187
+ Guardrails::Report::Summary::Entry.new(
188
+ category: "similar partials", count: similarity.length, severity: :suggestion,
189
+ unit: "pairs", action: "consider deduplicating"
190
+ ),
191
+ Guardrails::Report::Summary::Entry.new(
192
+ category: "cross-codebase patterns", count: patterns.length, severity: :suggestion,
193
+ unit: "candidates", action: "consider extracting partials"
194
+ ),
195
+ Guardrails::Report::Summary::Entry.new(
196
+ category: "class-itis", count: classitis.length, severity: :suggestion,
197
+ unit: "clusters", action: "consider extracting component / @apply"
198
+ )
199
+ ]
200
+
201
+ # Render the summary twice — once at the top so the reader
202
+ # knows what to expect, once at the bottom as a recap so they
203
+ # don't have to scroll back up after the per-detector dump.
204
+ # On a long output (Patchvault has 981 findings) the bottom
205
+ # recap is the load-bearing one.
206
+ summary = Guardrails::Report::Summary.new(entries: summary_entries, output: $stdout, style: report_style)
207
+ summary.render
208
+ $stdout.write sink.string
209
+ summary.render(recap: true)
123
210
  end
124
211
 
125
212
  # Deep a11y findings only fail the audit when their impact crosses
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ui_guardrails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Athayde
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-05-11 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: railties
@@ -16,14 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '7.1'
18
+ version: '7.2'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '7.1'
25
+ version: '7.2'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: herb
29
28
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +79,7 @@ files:
80
79
  - doc/LOOKBOOK.md
81
80
  - doc/PRD.md
82
81
  - doc/PUBLISHING.md
82
+ - doc/RESEARCH-visual-diff.md
83
83
  - doc/ROADMAP.md
84
84
  - doc/UPSTREAM-snap_diff-issue-draft.md
85
85
  - doc/VISUAL-DIFF.md
@@ -105,6 +105,8 @@ files:
105
105
  - lib/guardrails/lookbook/views/lookbook_panels/_guardrails.html.erb
106
106
  - lib/guardrails/partial_similarity.rb
107
107
  - lib/guardrails/railtie.rb
108
+ - lib/guardrails/report/style.rb
109
+ - lib/guardrails/report/summary.rb
108
110
  - lib/guardrails/stimulus_audit.rb
109
111
  - lib/guardrails/token_matcher.rb
110
112
  - lib/guardrails/tokens.rb
@@ -123,7 +125,6 @@ metadata:
123
125
  source_code_uri: https://github.com/meticulous/guardrails
124
126
  changelog_uri: https://github.com/meticulous/guardrails/blob/main/CHANGELOG.md
125
127
  rubygems_mfa_required: 'true'
126
- post_install_message:
127
128
  rdoc_options: []
128
129
  require_paths:
129
130
  - lib
@@ -131,15 +132,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
131
132
  requirements:
132
133
  - - ">="
133
134
  - !ruby/object:Gem::Version
134
- version: 3.2.0
135
+ version: 3.4.0
135
136
  required_rubygems_version: !ruby/object:Gem::Requirement
136
137
  requirements:
137
138
  - - ">="
138
139
  - !ruby/object:Gem::Version
139
140
  version: '0'
140
141
  requirements: []
141
- rubygems_version: 3.4.19
142
- signing_key:
142
+ rubygems_version: 3.6.9
143
143
  specification_version: 4
144
144
  summary: Rails toolset for preventing UI drift in AI-assisted applications.
145
145
  test_files: []