a11y_agent 0.0.5.pre.alpha.4 → 0.0.12
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 +4 -4
- data/lib/a11y_agent/version.rb +1 -1
- data/lib/agents/a11y_agent.rb +60 -89
- data/lib/generators/confirmable_fix_generator.rb +56 -0
- data/lib/tasks/release.rake +3 -3
- data/package.json +12 -12
- data/yarn.lock +105 -2459
- metadata +60 -9
- data/fixtures/sample.tsx +0 -100
- data/lib/bin/axe.ts +0 -19
- data/lib/generators/hydrate_document_generator.rb +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a7493a6bb46ce2e52d663e2b1972ac0664339be3317604fb5fb8957ff209403
|
4
|
+
data.tar.gz: 77ad379603264914462dcb118b1082b03a7d88bf6f346d3feac341fc15ef37b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32fe6b574cc3d27f4e2d4aaa6e3ce7da83974c3a334e0df36e8458ca429bf5547c71a5169a723910bb7dfc2f74052945b9299d9189cda69a13aa92394eb5a644
|
7
|
+
data.tar.gz: 7fe424775861271b8bb689a0d4cd425153f8beb0933a9cdfe7cc16d6046ec619b332261742df9120019020351502216db69e29d105c921a4e1082123555a7121
|
data/lib/a11y_agent/version.rb
CHANGED
data/lib/agents/a11y_agent.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'axe/core'
|
4
|
+
require 'axe/api/run'
|
3
5
|
require 'dotenv/load'
|
4
6
|
require 'diffy'
|
5
7
|
require 'fileutils'
|
6
8
|
require 'json'
|
7
9
|
require 'open3'
|
8
10
|
require 'rainbow/refinement'
|
11
|
+
require 'selenium-webdriver'
|
9
12
|
require 'sublayer'
|
10
13
|
require 'tty-prompt'
|
11
|
-
require_relative '../generators/
|
12
|
-
require_relative '../generators/hydrate_document_generator'
|
14
|
+
require_relative '../generators/confirmable_fix_generator'
|
13
15
|
|
14
16
|
Diffy::Diff.default_format = :color
|
15
17
|
|
@@ -20,14 +22,7 @@ Diffy::Diff.default_format = :color
|
|
20
22
|
# Sublayer.configuration.ai_model = "gemini-1.5-flash-latest"
|
21
23
|
|
22
24
|
Sublayer.configuration.ai_provider = Sublayer::Providers::Claude
|
23
|
-
Sublayer.configuration.ai_model = 'claude-3-haiku-
|
24
|
-
|
25
|
-
CHOICES = [
|
26
|
-
{ key: 'y', name: 'approve and continue', value: :yes },
|
27
|
-
{ key: 'n', name: 'skip this change', value: :no },
|
28
|
-
{ key: 'r', name: 'retry with optional instructions', value: :retry },
|
29
|
-
{ key: 'q', name: 'quit; stop making changes', value: :quit }
|
30
|
-
].freeze
|
25
|
+
Sublayer.configuration.ai_model = 'claude-3-5-haiku-latest'
|
31
26
|
|
32
27
|
module Sublayer
|
33
28
|
module Agents
|
@@ -39,6 +34,7 @@ module Sublayer
|
|
39
34
|
@issue_types = []
|
40
35
|
@file = file
|
41
36
|
@file_contents = File.read(@file)
|
37
|
+
@source_code = @file_contents
|
42
38
|
@prompt = TTY::Prompt.new
|
43
39
|
end
|
44
40
|
|
@@ -47,7 +43,7 @@ module Sublayer
|
|
47
43
|
end
|
48
44
|
|
49
45
|
check_status do
|
50
|
-
load_issues
|
46
|
+
load_issues
|
51
47
|
end
|
52
48
|
|
53
49
|
goal_condition do
|
@@ -56,104 +52,79 @@ module Sublayer
|
|
56
52
|
end
|
57
53
|
|
58
54
|
step do
|
59
|
-
@accessibility_issues.each
|
55
|
+
@accessibility_issues.each do |issue|
|
56
|
+
puts issue.description
|
57
|
+
fix_issue_and_save(issue:)
|
58
|
+
end
|
59
|
+
|
60
60
|
exit 0
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
64
64
|
|
65
|
-
def run_axe(file: @file)
|
66
|
-
stdout, _stderr, _status = Open3.capture3("ts-node lib/bin/axe.ts #{file}")
|
67
|
-
JSON.parse(stdout)
|
68
|
-
end
|
69
|
-
|
70
65
|
def load_issues
|
71
66
|
Tempfile.create(['', File.extname(@file)]) do |tempfile|
|
72
|
-
tempfile.write(
|
67
|
+
tempfile.write(@file_contents)
|
73
68
|
tempfile.rewind
|
74
69
|
|
75
|
-
|
76
|
-
|
77
|
-
|
70
|
+
command = %(yarn --silent biome lint --reporter=json --only=a11y #{tempfile.path})
|
71
|
+
stdout, _stderr, _status = Open3.capture3(command, stdin_data: @file_contents)
|
72
|
+
|
73
|
+
@accessibility_issues = JSON.parse(stdout).fetch('diagnostics').map do |d|
|
74
|
+
OpenStruct.new(
|
75
|
+
description: d.fetch('description'),
|
76
|
+
location: d.fetch('location').fetch('span'),
|
77
|
+
snippet: d.fetch('location').fetch('sourceCode')[d.fetch('location').fetch('span')[0]..d.fetch('location').fetch('span')[1] - 1],
|
78
|
+
advice: d.fetch('advices').fetch('advices').map do |a|
|
79
|
+
a.fetch('log')[1][0].fetch('content') unless a.fetch('log', nil).nil?
|
80
|
+
end
|
81
|
+
)
|
78
82
|
end
|
79
83
|
end
|
80
84
|
|
81
85
|
puts "🚨 Found #{@accessibility_issues.length} accessibility issues" unless @accessibility_issues.empty?
|
82
86
|
end
|
83
87
|
|
84
|
-
def hydrated_file
|
85
|
-
puts "Loading fake data into #{@file}"
|
86
|
-
hydrated = HydrateDocumentGenerator.new(contents: @file_contents, extension: File.extname(@file)).generate
|
87
|
-
hydrated << "\n" until hydrated.end_with?("\n")
|
88
|
-
|
89
|
-
print_diff(contents: @file_contents, fixed: hydrated, message: '📊 Changes made:')
|
90
|
-
hydration_approved = @prompt.yes? 'Continue with updates?'
|
91
|
-
hydrated = @file_contents unless hydration_approved
|
92
|
-
hydrated
|
93
|
-
end
|
94
|
-
|
95
88
|
def fix_issue_and_save(issue:)
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
puts
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
when :quit
|
128
|
-
puts 'Quitting...'
|
129
|
-
exit 0
|
130
|
-
end
|
89
|
+
additional_instructions = nil
|
90
|
+
input = nil
|
91
|
+
|
92
|
+
until %i[fix skip].include?(input)
|
93
|
+
exit 0 if input == :exit
|
94
|
+
puts additional_instructions if additional_instructions
|
95
|
+
|
96
|
+
result = ConfirmableFixGenerator.new(lint_failure: issue, source_code: @source_code, additional_instructions:).generate
|
97
|
+
fixed = result.fixed + "\n"
|
98
|
+
puts Diffy::Diff.new(@source_code, fixed, context: 2).to_s(:color)
|
99
|
+
|
100
|
+
input = @prompt.expand('Apply the fix?', [
|
101
|
+
{ key: 'y', name: 'Apply the fix', value: :fix },
|
102
|
+
{ key: 'n', name: 'Skip the fix', value: :skip },
|
103
|
+
{ key: 'e', name: 'Explain why', value: :explain },
|
104
|
+
{ key: 'r', name: 'Retry with optional instructions', value: :retry },
|
105
|
+
{ key: 'q', name: 'Exit', value: :exit }
|
106
|
+
])
|
107
|
+
|
108
|
+
if input == :retry
|
109
|
+
additional_instructions = @prompt.ask('Provide additional instructions:')
|
110
|
+
next
|
111
|
+
elsif input == :explain
|
112
|
+
puts
|
113
|
+
puts 'Explanation'.bright
|
114
|
+
puts result.description
|
115
|
+
puts
|
116
|
+
|
117
|
+
continue = @prompt.yes? 'Continue with the fix?'
|
118
|
+
next unless continue
|
119
|
+
input = :fix
|
131
120
|
end
|
132
|
-
|
133
|
-
puts '📝 Saving changes...'
|
134
|
-
File.write(@file, fixed) if fixed
|
135
121
|
end
|
136
|
-
end
|
137
122
|
|
138
|
-
def print_diff(contents:, fixed:, message: '')
|
139
|
-
puts message
|
140
|
-
puts Diffy::Diff.new(contents, fixed)
|
141
|
-
end
|
142
123
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
print chunk.to_s.green
|
148
|
-
when /^-/
|
149
|
-
print chunk.to_s.red
|
150
|
-
else
|
151
|
-
lines = chunk.to_s.split("\n")
|
152
|
-
puts lines[0..2].join("\n")
|
153
|
-
puts '...'
|
154
|
-
puts lines[-3..].join("\n") if lines.length > 5
|
155
|
-
end
|
156
|
-
end
|
124
|
+
return unless input == :fix
|
125
|
+
puts '📝 Saving changes...'
|
126
|
+
File.write(@file, fixed)
|
127
|
+
@source_code = fixed
|
157
128
|
end
|
158
129
|
end
|
159
130
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'dotenv/load'
|
4
|
+
require 'sublayer'
|
5
|
+
|
6
|
+
class ConfirmableFixGenerator < Sublayer::Generators::Base
|
7
|
+
llm_output_adapter type: :named_strings,
|
8
|
+
name: 'lint_fix',
|
9
|
+
description: 'A fix for lint failures',
|
10
|
+
item_name: 'lint_fix',
|
11
|
+
attributes: [
|
12
|
+
{
|
13
|
+
name: 'description',
|
14
|
+
description: 'A brief description of the fix and why it is important'
|
15
|
+
},
|
16
|
+
{
|
17
|
+
name: 'impact',
|
18
|
+
description: 'A brief explanation of how the fix impacts assistive technologies'
|
19
|
+
},
|
20
|
+
{
|
21
|
+
name: 'fixed',
|
22
|
+
description: 'The complete source code with the fix applied'
|
23
|
+
}
|
24
|
+
]
|
25
|
+
|
26
|
+
def initialize(lint_failure:, source_code:, additional_instructions: nil)
|
27
|
+
super()
|
28
|
+
@source_code = source_code
|
29
|
+
@additional_instructions = additional_instructions
|
30
|
+
@failure_line = %(#{lint_failure.description} at span #{lint_failure.location[0]}:#{lint_failure.location[1]})
|
31
|
+
end
|
32
|
+
|
33
|
+
def prompt
|
34
|
+
<<-PROMPT
|
35
|
+
You are an expert at remediating lint errors in source code.
|
36
|
+
Generate a fix for the following lint failure in the provided source code.
|
37
|
+
Only fix one specified lint failure at a time, at the given position.
|
38
|
+
|
39
|
+
Source code:
|
40
|
+
#{@source_code}
|
41
|
+
|
42
|
+
Lint failure:
|
43
|
+
#{@failure_line}
|
44
|
+
|
45
|
+
Additional instructions (if any):
|
46
|
+
#{@additional_instructions}
|
47
|
+
|
48
|
+
For the fix provide:
|
49
|
+
- description: A brief description of the change and why it is important.
|
50
|
+
- fixed: the fixed source code with the issue resolved.
|
51
|
+
- impact: A description of how the fix impacts assistive technologies.
|
52
|
+
|
53
|
+
Provide your response is an object containing the above attributes.
|
54
|
+
PROMPT
|
55
|
+
end
|
56
|
+
end
|
data/lib/tasks/release.rake
CHANGED
@@ -54,8 +54,8 @@ namespace :release do
|
|
54
54
|
# Extract all files to libexec, which is a common Homebrew practice for third-party tools
|
55
55
|
libexec.install Dir["*"]
|
56
56
|
|
57
|
-
system "bundle", "install", "--
|
58
|
-
system "gem", "build", "#{spec.name}.gemspec"
|
57
|
+
system "bundle", "install", "--gemfile", libexec/"Gemfile"
|
58
|
+
system "gem", "build", libexec/"#{spec.name}.gemspec"
|
59
59
|
system "gem", "install", "--ignore-dependencies", "#{spec.name}-#{version}.gem"
|
60
60
|
|
61
61
|
bin.install libexec/"exe/#{spec.name}"
|
@@ -64,7 +64,7 @@ namespace :release do
|
|
64
64
|
|
65
65
|
test do
|
66
66
|
# Simple test to check the version or a help command
|
67
|
-
system "\#{bin}
|
67
|
+
system "\#{bin}/#{spec.name}", "--help"
|
68
68
|
end
|
69
69
|
end
|
70
70
|
RUBY
|
data/package.json
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
{
|
2
|
-
"
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
"typescript-eslint": "^7.17.0"
|
10
|
-
},
|
2
|
+
"name": "a11y-agent",
|
3
|
+
"version": "0.0.0",
|
4
|
+
"main": "index.js",
|
5
|
+
"repository": "git@github.com:AccessLint/a11y-agent.git",
|
6
|
+
"author": "Cameron Cundiff <github@ckundo.com>",
|
7
|
+
"license": "AGPL-3.0",
|
8
|
+
"private": true,
|
11
9
|
"dependencies": {
|
12
|
-
"
|
13
|
-
"
|
14
|
-
|
10
|
+
"@biomejs/biome": "1.9.4",
|
11
|
+
"eslint-plugin-vuejs-accessibility": "^2.4.1"
|
12
|
+
},
|
13
|
+
"devDependencies": {
|
14
|
+
"globals": "^15.9.0"
|
15
15
|
}
|
16
16
|
}
|