ukiryu 0.1.1 → 0.1.3

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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/release.yml +58 -14
  3. data/.gitignore +3 -0
  4. data/.rubocop_todo.yml +170 -79
  5. data/Gemfile +1 -1
  6. data/README.adoc +1603 -576
  7. data/docs/.gitignore +1 -0
  8. data/docs/Gemfile +10 -0
  9. data/docs/INDEX.adoc +261 -0
  10. data/docs/_config.yml +180 -0
  11. data/docs/advanced/custom-tool-classes.adoc +581 -0
  12. data/docs/advanced/index.adoc +20 -0
  13. data/docs/features/configuration.adoc +657 -0
  14. data/docs/features/index.adoc +31 -0
  15. data/docs/features/platform-support.adoc +488 -0
  16. data/docs/getting-started/core-concepts.adoc +666 -0
  17. data/docs/getting-started/index.adoc +36 -0
  18. data/docs/getting-started/installation.adoc +216 -0
  19. data/docs/getting-started/quick-start.adoc +258 -0
  20. data/docs/guides/env-var-sets.adoc +388 -0
  21. data/docs/guides/index.adoc +20 -0
  22. data/docs/interfaces/cli.adoc +609 -0
  23. data/docs/interfaces/index.adoc +153 -0
  24. data/docs/interfaces/ruby-api.adoc +538 -0
  25. data/docs/lychee.toml +49 -0
  26. data/docs/reference/configuration-options.adoc +720 -0
  27. data/docs/reference/error-codes.adoc +634 -0
  28. data/docs/reference/index.adoc +20 -0
  29. data/docs/reference/ruby-api.adoc +1217 -0
  30. data/docs/understanding/index.adoc +20 -0
  31. data/lib/ukiryu/cli.rb +43 -58
  32. data/lib/ukiryu/cli_commands/base_command.rb +16 -27
  33. data/lib/ukiryu/cli_commands/cache_command.rb +100 -0
  34. data/lib/ukiryu/cli_commands/commands_command.rb +8 -8
  35. data/lib/ukiryu/cli_commands/commands_command.rb.fixed +1 -1
  36. data/lib/ukiryu/cli_commands/config_command.rb +49 -7
  37. data/lib/ukiryu/cli_commands/definitions_command.rb +254 -0
  38. data/lib/ukiryu/cli_commands/describe_command.rb +13 -7
  39. data/lib/ukiryu/cli_commands/describe_command.rb.fixed +1 -1
  40. data/lib/ukiryu/cli_commands/docs_command.rb +148 -0
  41. data/lib/ukiryu/cli_commands/exec_inline_command.rb.fixed +1 -1
  42. data/lib/ukiryu/cli_commands/extract_command.rb +2 -2
  43. data/lib/ukiryu/cli_commands/info_command.rb +7 -7
  44. data/lib/ukiryu/cli_commands/lint_command.rb +167 -0
  45. data/lib/ukiryu/cli_commands/list_command.rb +6 -6
  46. data/lib/ukiryu/cli_commands/opts_command.rb +2 -2
  47. data/lib/ukiryu/cli_commands/opts_command.rb.fixed +1 -1
  48. data/lib/ukiryu/cli_commands/register_command.rb +144 -0
  49. data/lib/ukiryu/cli_commands/resolve_command.rb +124 -0
  50. data/lib/ukiryu/cli_commands/run_command.rb +38 -14
  51. data/lib/ukiryu/cli_commands/run_file_command.rb +2 -2
  52. data/lib/ukiryu/cli_commands/system_command.rb +50 -32
  53. data/lib/ukiryu/cli_commands/validate_command.rb +452 -51
  54. data/lib/ukiryu/cli_commands/which_command.rb +5 -5
  55. data/lib/ukiryu/command_builder.rb +81 -23
  56. data/lib/ukiryu/config/env_provider.rb +3 -3
  57. data/lib/ukiryu/config/env_schema.rb +6 -6
  58. data/lib/ukiryu/config.rb +11 -11
  59. data/lib/ukiryu/definition/definition_cache.rb +238 -0
  60. data/lib/ukiryu/definition/definition_composer.rb +257 -0
  61. data/lib/ukiryu/definition/definition_linter.rb +460 -0
  62. data/lib/ukiryu/definition/definition_validator.rb +320 -0
  63. data/lib/ukiryu/definition/discovery.rb +239 -0
  64. data/lib/ukiryu/definition/documentation_generator.rb +429 -0
  65. data/lib/ukiryu/definition/lint_issue.rb +168 -0
  66. data/lib/ukiryu/definition/loader.rb +139 -0
  67. data/lib/ukiryu/definition/metadata.rb +159 -0
  68. data/lib/ukiryu/definition/source.rb +87 -0
  69. data/lib/ukiryu/definition/sources/file.rb +138 -0
  70. data/lib/ukiryu/definition/sources/string.rb +88 -0
  71. data/lib/ukiryu/definition/validation_result.rb +158 -0
  72. data/lib/ukiryu/definition/version_resolver.rb +194 -0
  73. data/lib/ukiryu/definition.rb +40 -0
  74. data/lib/ukiryu/errors.rb +6 -0
  75. data/lib/ukiryu/execution_context.rb +11 -11
  76. data/lib/ukiryu/executor.rb +6 -0
  77. data/lib/ukiryu/extractors/extractor.rb +6 -5
  78. data/lib/ukiryu/extractors/help_parser.rb +13 -19
  79. data/lib/ukiryu/logger.rb +3 -1
  80. data/lib/ukiryu/models/command_definition.rb +3 -3
  81. data/lib/ukiryu/models/command_info.rb +1 -1
  82. data/lib/ukiryu/models/components.rb +1 -3
  83. data/lib/ukiryu/models/env_var_definition.rb +11 -3
  84. data/lib/ukiryu/models/flag_definition.rb +15 -0
  85. data/lib/ukiryu/models/option_definition.rb +7 -7
  86. data/lib/ukiryu/models/platform_profile.rb +6 -3
  87. data/lib/ukiryu/models/routing.rb +1 -1
  88. data/lib/ukiryu/models/tool_definition.rb +2 -4
  89. data/lib/ukiryu/models/tool_metadata.rb +6 -6
  90. data/lib/ukiryu/models/validation_result.rb +1 -1
  91. data/lib/ukiryu/models/version_compatibility.rb +6 -3
  92. data/lib/ukiryu/models/version_detection.rb +10 -1
  93. data/lib/ukiryu/{registry.rb → register.rb} +54 -38
  94. data/lib/ukiryu/register_auto_manager.rb +268 -0
  95. data/lib/ukiryu/schema_validator.rb +31 -10
  96. data/lib/ukiryu/shell/base.rb +18 -0
  97. data/lib/ukiryu/shell/bash.rb +19 -1
  98. data/lib/ukiryu/shell/cmd.rb +11 -1
  99. data/lib/ukiryu/shell/powershell.rb +11 -1
  100. data/lib/ukiryu/shell.rb +1 -1
  101. data/lib/ukiryu/tool.rb +107 -95
  102. data/lib/ukiryu/tool_index.rb +22 -22
  103. data/lib/ukiryu/tools/base.rb +12 -25
  104. data/lib/ukiryu/tools/generator.rb +7 -7
  105. data/lib/ukiryu/tools.rb +3 -3
  106. data/lib/ukiryu/type.rb +20 -5
  107. data/lib/ukiryu/version.rb +1 -1
  108. data/lib/ukiryu/version_detector.rb +21 -2
  109. data/lib/ukiryu.rb +6 -3
  110. data/ukiryu-proposal.md +41 -41
  111. data/ukiryu.gemspec +1 -0
  112. metadata +64 -8
  113. data/.gitmodules +0 -3
@@ -0,0 +1,429 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ukiryu
4
+ module Definition
5
+ # Generate documentation from tool definitions
6
+ #
7
+ # This class generates human-readable documentation from tool definitions
8
+ # in various formats (Markdown, AsciiDoc).
9
+ class DocumentationGenerator
10
+ # Supported formats
11
+ FORMATS = %i[markdown asciidoc md].freeze
12
+
13
+ class << self
14
+ # Generate documentation for a definition
15
+ #
16
+ # @param definition [Hash] the definition
17
+ # @param format [Symbol] output format (:markdown, :asciidoc)
18
+ # @return [String] generated documentation
19
+ def generate(definition, format: :markdown)
20
+ format = normalize_format(format)
21
+
22
+ case format
23
+ when :markdown
24
+ generate_markdown(definition)
25
+ when :asciidoc
26
+ generate_asciidoc(definition)
27
+ else
28
+ raise ArgumentError, "Unsupported format: #{format}"
29
+ end
30
+ end
31
+
32
+ # Generate documentation to a file
33
+ #
34
+ # @param definition [Hash] the definition
35
+ # @param file_path [String] output file path
36
+ # @param format [Symbol] output format
37
+ def generate_to_file(definition, file_path, format: :markdown)
38
+ content = generate(definition, format: format)
39
+ File.write(file_path, content)
40
+ end
41
+
42
+ # Generate documentation for a specific command
43
+ #
44
+ # @param command_name [String] the command name
45
+ # @param command_def [Hash] the command definition
46
+ # @param format [Symbol] output format
47
+ # @return [String] generated command documentation
48
+ def generate_command_docs(command_name, command_def, format: :markdown)
49
+ format = normalize_format(format)
50
+
51
+ case format
52
+ when :markdown
53
+ generate_command_markdown(command_name, command_def)
54
+ when :asciidoc
55
+ generate_command_asciidoc(command_name, command_def)
56
+ else
57
+ raise ArgumentError, "Unsupported format: #{format}"
58
+ end
59
+ end
60
+
61
+ private
62
+
63
+ # Normalize format
64
+ #
65
+ # @param format [Symbol] the format
66
+ # @return [Symbol] normalized format
67
+ def normalize_format(format)
68
+ return :markdown if format == :md
69
+
70
+ format
71
+ end
72
+
73
+ # Generate Markdown documentation
74
+ #
75
+ # @param definition [Hash] the definition
76
+ # @return [String] Markdown documentation
77
+ def generate_markdown(definition)
78
+ output = []
79
+ tool_name = definition[:name] || 'Unknown'
80
+ version = definition[:version]
81
+
82
+ # Title
83
+ output << "# #{tool_name}"
84
+ output << '' if version
85
+ output << "Version: #{version}" if version
86
+ output << ''
87
+
88
+ # Description
89
+ if definition[:description]
90
+ output << definition[:description]
91
+ output << ''
92
+ end
93
+
94
+ # Metadata
95
+ output << '## Overview'
96
+ output << ''
97
+ output << '| Property | Value |'
98
+ output << '|----------|-------|'
99
+ output << "| Name | `#{tool_name}` |"
100
+ output << "| Version | `#{version}` |" if version
101
+ output << "| Homepage | #{definition[:homepage]} |" if definition[:homepage]
102
+ output << ''
103
+
104
+ # Platforms
105
+ platforms = extract_platforms(definition)
106
+ if platforms.any?
107
+ output << '### Supported Platforms'
108
+ output << ''
109
+ platforms.each do |platform|
110
+ shells = extract_shells_for_platform(definition, platform)
111
+ output << "- **#{platform.to_s.capitalize}**: #{shells.join(', ')}"
112
+ end
113
+ output << ''
114
+ end
115
+
116
+ # Installation
117
+ if definition[:install]
118
+ output << '## Installation'
119
+ output << ''
120
+ output << render_installation_markdown(definition[:install])
121
+ output << ''
122
+ end
123
+
124
+ # Commands
125
+ commands = extract_commands(definition)
126
+ if commands.any?
127
+ output << '## Commands'
128
+ output << ''
129
+ commands.each do |cmd_name, cmd_def|
130
+ output << generate_command_markdown(cmd_name, cmd_def)
131
+ end
132
+ end
133
+
134
+ # Options reference
135
+ if commands.any?
136
+ output << '## Options Reference'
137
+ output << ''
138
+ commands.each do |cmd_name, cmd_def|
139
+ options = cmd_def[:options] || []
140
+ flags = cmd_def[:flags] || []
141
+
142
+ next unless options.any? || flags.any?
143
+
144
+ output << "### `#{cmd_name}`"
145
+ output << ''
146
+
147
+ if options.any?
148
+ output << '#### Options'
149
+ output << ''
150
+ options.each do |opt|
151
+ output << render_option_markdown(opt)
152
+ end
153
+ end
154
+
155
+ if flags.any?
156
+ output << '#### Flags'
157
+ output << ''
158
+ flags.each do |flag|
159
+ output << render_flag_markdown(flag)
160
+ end
161
+ end
162
+
163
+ output << ''
164
+ end
165
+ end
166
+
167
+ output.join("\n")
168
+ end
169
+
170
+ # Generate command documentation in Markdown
171
+ #
172
+ # @param command_name [String] the command name
173
+ # @param command_def [Hash] the command definition
174
+ # @return [String] command documentation
175
+ def generate_command_markdown(command_name, command_def)
176
+ output = []
177
+ output << "### `#{command_name}`"
178
+ output << ''
179
+
180
+ if command_def[:description]
181
+ output << command_def[:description]
182
+ output << ''
183
+ end
184
+
185
+ # Arguments
186
+ if command_def[:arguments]
187
+ output << '#### Arguments'
188
+ output << ''
189
+ command_def[:arguments].each do |arg|
190
+ output << render_argument_markdown(arg)
191
+ end
192
+ output << ''
193
+ end
194
+
195
+ output.join("\n")
196
+ end
197
+
198
+ # Render option in Markdown
199
+ #
200
+ # @param opt [Hash] the option
201
+ # @return [String] rendered option
202
+ def render_option_markdown(opt)
203
+ cli = opt[:cli] || opt[:name]
204
+ required = opt[:required] ? '**(required)**' : '(optional)'
205
+ desc = opt[:description] || ''
206
+
207
+ output = "- `#{cli}` #{required}"
208
+ output << " - #{desc}" if desc
209
+ output << "\n\n Type: `#{opt[:type]}`" if opt[:type]
210
+ output
211
+ end
212
+
213
+ # Render flag in Markdown
214
+ #
215
+ # @param flag [Hash] the flag
216
+ # @return [String] rendered flag
217
+ def render_flag_markdown(flag)
218
+ cli = flag[:cli] || flag[:name]
219
+ desc = flag[:description] || ''
220
+
221
+ output = "- `#{cli}`"
222
+ output << " - #{desc}" if desc
223
+ output
224
+ end
225
+
226
+ # Render argument in Markdown
227
+ #
228
+ # @param arg [Hash] the argument
229
+ # @return [String] rendered argument
230
+ def render_argument_markdown(arg)
231
+ name = arg[:name]
232
+ type = arg[:type] || 'any'
233
+ desc = arg[:description] || ''
234
+ required = arg[:required] ? '**required**' : 'optional'
235
+
236
+ output = "- **`#{name}`** (#{type}, #{required})"
237
+ output << " - #{desc}" if desc
238
+ output
239
+ end
240
+
241
+ # Render installation instructions in Markdown
242
+ #
243
+ # @param install [Hash] installation instructions
244
+ # @return [String] rendered installation
245
+ def render_installation_markdown(install)
246
+ output = []
247
+
248
+ if install[:macos]
249
+ output << '#### macOS'
250
+ output << '```bash'
251
+ output << install[:macos]
252
+ output << '```'
253
+ output << ''
254
+ end
255
+
256
+ if install[:linux]
257
+ output << '#### Linux'
258
+ output << '```bash'
259
+ output << install[:linux]
260
+ output << '```'
261
+ output << ''
262
+ end
263
+
264
+ if install[:windows]
265
+ output << '#### Windows'
266
+ output << '```powershell'
267
+ output << install[:windows]
268
+ output << '```'
269
+ output << ''
270
+ end
271
+
272
+ output.join("\n")
273
+ end
274
+
275
+ # Generate AsciiDoc documentation
276
+ #
277
+ # @param definition [Hash] the definition
278
+ # @return [String] AsciiDoc documentation
279
+ def generate_asciidoc(definition)
280
+ output = []
281
+ tool_name = definition[:name] || 'Unknown'
282
+ version = definition[:version]
283
+
284
+ # Title
285
+ output << "= #{tool_name}"
286
+ output << '' if version
287
+ output << ':toc:' if definition[:profiles] # Only add TOC if there's content
288
+ output << ''
289
+
290
+ if version
291
+ output << "Version: #{version}"
292
+ output << ''
293
+ end
294
+
295
+ # Description
296
+ if definition[:description]
297
+ output << definition[:description]
298
+ output << ''
299
+ end
300
+
301
+ # Overview
302
+ output << '== Overview'
303
+ output << ''
304
+ output << '[cols="1,2"]'
305
+ output << '|==='
306
+ output << '| Property | Value'
307
+ output << "| Name | `#{tool_name}`"
308
+ output << "| Version | `#{version}`" if version
309
+ output << "| Homepage | #{definition[:homepage]}" if definition[:homepage]
310
+ output << '|==='
311
+ output << ''
312
+
313
+ # Platforms
314
+ platforms = extract_platforms(definition)
315
+ if platforms.any?
316
+ output << '=== Supported Platforms'
317
+ output << ''
318
+ platforms.each do |platform|
319
+ shells = extract_shells_for_platform(definition, platform)
320
+ output << "* *#{platform.to_s.capitalize}*: #{shells.join(', ')}"
321
+ end
322
+ output << ''
323
+ end
324
+
325
+ # Commands
326
+ commands = extract_commands(definition)
327
+ if commands.any?
328
+ output << '== Commands'
329
+ output << ''
330
+ commands.each do |cmd_name, cmd_def|
331
+ output << generate_command_asciidoc(cmd_name, cmd_def)
332
+ end
333
+ end
334
+
335
+ output.join("\n")
336
+ end
337
+
338
+ # Generate command documentation in AsciiDoc
339
+ #
340
+ # @param command_name [String] the command name
341
+ # @param command_def [Hash] the command definition
342
+ # @return [String] command documentation
343
+ def generate_command_asciidoc(command_name, command_def)
344
+ output = []
345
+ output << "=== `#{command_name}`"
346
+ output << ''
347
+
348
+ if command_def[:description]
349
+ output << command_def[:description]
350
+ output << ''
351
+ end
352
+
353
+ if command_def[:arguments]
354
+ output << '==== Arguments'
355
+ output << ''
356
+ command_def[:arguments].each do |arg|
357
+ name = arg[:name]
358
+ type = arg[:type] || 'any'
359
+ desc = arg[:description] || ''
360
+ required = arg[:required] ? '*required*' : 'optional'
361
+
362
+ output << "* **`#{name}`** (#{type}, #{required})"
363
+ output << ": #{desc}" if desc
364
+ end
365
+ output << ''
366
+ end
367
+
368
+ output.join("\n")
369
+ end
370
+
371
+ # Extract all platforms from definition
372
+ #
373
+ # @param definition [Hash] the definition
374
+ # @return [Array<Symbol>] platforms
375
+ def extract_platforms(definition)
376
+ platforms = Set.new
377
+ return platforms.to_a unless definition[:profiles]
378
+
379
+ definition[:profiles].each do |profile|
380
+ next unless profile[:platforms]
381
+
382
+ profile[:platforms].each { |p| platforms.add(p) }
383
+ end
384
+
385
+ platforms.to_a
386
+ end
387
+
388
+ # Extract shells for a specific platform
389
+ #
390
+ # @param definition [Hash] the definition
391
+ # @param platform [Symbol] the platform
392
+ # @return [Array<Symbol>] shells
393
+ def extract_shells_for_platform(definition, platform)
394
+ shells = Set.new
395
+ return shells.to_a unless definition[:profiles]
396
+
397
+ definition[:profiles].each do |profile|
398
+ next unless profile[:platforms]&.include?(platform)
399
+ next unless profile[:shells]
400
+
401
+ profile[:shells].each { |s| shells.add(s) }
402
+ end
403
+
404
+ shells.to_a.sort
405
+ end
406
+
407
+ # Extract all commands from definition
408
+ #
409
+ # @param definition [Hash] the definition
410
+ # @return [Hash] commands
411
+ def extract_commands(definition)
412
+ commands = {}
413
+
414
+ return commands unless definition[:profiles]
415
+
416
+ definition[:profiles].each do |profile|
417
+ next unless profile[:commands]
418
+
419
+ profile[:commands].each do |name, cmd_def|
420
+ commands[name] = cmd_def unless commands.key?(name)
421
+ end
422
+ end
423
+
424
+ commands
425
+ end
426
+ end
427
+ end
428
+ end
429
+ end
@@ -0,0 +1,168 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ukiryu
4
+ module Definition
5
+ # A linting issue found in a definition
6
+ #
7
+ # This class represents a single linting issue with severity,
8
+ # message, location, and optional suggestion.
9
+ class LintIssue
10
+ # Severity levels
11
+ SEVERITY_ERROR = :error
12
+ SEVERITY_WARNING = :warning
13
+ SEVERITY_INFO = :info
14
+ SEVERITY_STYLE = :style
15
+
16
+ attr_reader :severity, :message, :location, :suggestion, :rule_id
17
+
18
+ def initialize(severity:, message:, location: nil, suggestion: nil, rule_id: nil)
19
+ @severity = severity
20
+ @message = message
21
+ @location = location
22
+ @suggestion = suggestion
23
+ @rule_id = rule_id
24
+ end
25
+
26
+ # Check if this is an error
27
+ #
28
+ # @return [Boolean] true if severity is error
29
+ def error?
30
+ @severity == SEVERITY_ERROR
31
+ end
32
+
33
+ # Check if this is a warning
34
+ #
35
+ # @return [Boolean] true if severity is warning
36
+ def warning?
37
+ @severity == SEVERITY_WARNING
38
+ end
39
+
40
+ # Check if this is info
41
+ #
42
+ # @return [Boolean] true if severity is info
43
+ def info?
44
+ @severity == SEVERITY_INFO
45
+ end
46
+
47
+ # Check if this is style
48
+ #
49
+ # @return [Boolean] true if severity is style
50
+ def style?
51
+ @severity == SEVERITY_STYLE
52
+ end
53
+
54
+ # Check if this issue has a suggestion
55
+ #
56
+ # @return [Boolean] true if suggestion is present
57
+ def has_suggestion?
58
+ !@suggestion.nil? && !@suggestion.empty?
59
+ end
60
+
61
+ # Check if this issue has a location
62
+ #
63
+ # @return [Boolean] true if location is present
64
+ def has_location?
65
+ !@location.nil? && !@location.empty?
66
+ end
67
+
68
+ # Get severity as a readable string
69
+ #
70
+ # @return [String] severity string
71
+ def severity_string
72
+ @severity.to_s.upcase
73
+ end
74
+
75
+ # Convert to hash
76
+ #
77
+ # @return [Hash] hash representation
78
+ def to_h
79
+ {
80
+ severity: @severity,
81
+ severity_string: severity_string,
82
+ message: @message,
83
+ location: @location,
84
+ suggestion: @suggestion,
85
+ rule_id: @rule_id
86
+ }
87
+ end
88
+
89
+ # Format as string
90
+ #
91
+ # @return [String] formatted issue
92
+ def to_s
93
+ output = "[#{severity_string}] #{@message}"
94
+ output += " (at #{@location})" if has_location?
95
+ output += "\n Suggestion: #{@suggestion}" if has_suggestion?
96
+ output
97
+ end
98
+
99
+ # Create an error issue
100
+ #
101
+ # @param message [String] error message
102
+ # @param location [String, nil] issue location
103
+ # @param suggestion [String, nil] fix suggestion
104
+ # @param rule_id [String, nil] rule identifier
105
+ # @return [LintIssue] error issue
106
+ def self.error(message, location: nil, suggestion: nil, rule_id: nil)
107
+ new(
108
+ severity: SEVERITY_ERROR,
109
+ message: message,
110
+ location: location,
111
+ suggestion: suggestion,
112
+ rule_id: rule_id
113
+ )
114
+ end
115
+
116
+ # Create a warning issue
117
+ #
118
+ # @param message [String] warning message
119
+ # @param location [String, nil] issue location
120
+ # @param suggestion [String, nil] fix suggestion
121
+ # @param rule_id [String, nil] rule identifier
122
+ # @return [LintIssue] warning issue
123
+ def self.warning(message, location: nil, suggestion: nil, rule_id: nil)
124
+ new(
125
+ severity: SEVERITY_WARNING,
126
+ message: message,
127
+ location: location,
128
+ suggestion: suggestion,
129
+ rule_id: rule_id
130
+ )
131
+ end
132
+
133
+ # Create an info issue
134
+ #
135
+ # @param message [String] info message
136
+ # @param location [String, nil] issue location
137
+ # @param suggestion [String, nil] fix suggestion
138
+ # @param rule_id [String, nil] rule identifier
139
+ # @return [LintIssue] info issue
140
+ def self.info(message, location: nil, suggestion: nil, rule_id: nil)
141
+ new(
142
+ severity: SEVERITY_INFO,
143
+ message: message,
144
+ location: location,
145
+ suggestion: suggestion,
146
+ rule_id: rule_id
147
+ )
148
+ end
149
+
150
+ # Create a style issue
151
+ #
152
+ # @param message [String] style message
153
+ # @param location [String, nil] issue location
154
+ # @param suggestion [String, nil] fix suggestion
155
+ # @param rule_id [String, nil] rule identifier
156
+ # @return [LintIssue] style issue
157
+ def self.style(message, location: nil, suggestion: nil, rule_id: nil)
158
+ new(
159
+ severity: SEVERITY_STYLE,
160
+ message: message,
161
+ location: location,
162
+ suggestion: suggestion,
163
+ rule_id: rule_id
164
+ )
165
+ end
166
+ end
167
+ end
168
+ end