docscribe 1.4.1 → 1.5.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +588 -104
  3. data/lib/docscribe/cli/check_for_comments.rb +183 -0
  4. data/lib/docscribe/cli/config_builder.rb +180 -36
  5. data/lib/docscribe/cli/formatters/json.rb +294 -0
  6. data/lib/docscribe/cli/formatters/sarif.rb +235 -0
  7. data/lib/docscribe/cli/formatters/text.rb +208 -0
  8. data/lib/docscribe/cli/formatters.rb +26 -0
  9. data/lib/docscribe/cli/generate.rb +296 -125
  10. data/lib/docscribe/cli/init.rb +58 -14
  11. data/lib/docscribe/cli/options.rb +410 -133
  12. data/lib/docscribe/cli/rbs_gen.rb +529 -0
  13. data/lib/docscribe/cli/run.rb +503 -189
  14. data/lib/docscribe/cli/sigs.rb +366 -0
  15. data/lib/docscribe/cli/update_types.rb +103 -0
  16. data/lib/docscribe/cli.rb +35 -9
  17. data/lib/docscribe/config/defaults.rb +16 -12
  18. data/lib/docscribe/config/emit.rb +18 -0
  19. data/lib/docscribe/config/filtering.rb +37 -31
  20. data/lib/docscribe/config/loader.rb +20 -13
  21. data/lib/docscribe/config/plugin.rb +2 -1
  22. data/lib/docscribe/config/rbs.rb +68 -27
  23. data/lib/docscribe/config/sorbet.rb +40 -17
  24. data/lib/docscribe/config/sorting.rb +2 -1
  25. data/lib/docscribe/config/template.rb +10 -1
  26. data/lib/docscribe/config/utils.rb +12 -9
  27. data/lib/docscribe/config.rb +3 -4
  28. data/lib/docscribe/infer/ast_walk.rb +1 -1
  29. data/lib/docscribe/infer/constants.rb +15 -0
  30. data/lib/docscribe/infer/literals.rb +39 -26
  31. data/lib/docscribe/infer/names.rb +24 -16
  32. data/lib/docscribe/infer/params.rb +57 -13
  33. data/lib/docscribe/infer/raises.rb +23 -15
  34. data/lib/docscribe/infer/returns.rb +784 -199
  35. data/lib/docscribe/infer.rb +28 -28
  36. data/lib/docscribe/inline_rewriter/collector.rb +816 -430
  37. data/lib/docscribe/inline_rewriter/doc_block.rb +323 -150
  38. data/lib/docscribe/inline_rewriter/doc_builder.rb +1837 -648
  39. data/lib/docscribe/inline_rewriter/source_helpers.rb +119 -71
  40. data/lib/docscribe/inline_rewriter/tag_sorter.rb +165 -107
  41. data/lib/docscribe/inline_rewriter.rb +1144 -727
  42. data/lib/docscribe/parsing.rb +29 -10
  43. data/lib/docscribe/plugin/base/collector_plugin.rb +3 -3
  44. data/lib/docscribe/plugin/base/tag_plugin.rb +1 -2
  45. data/lib/docscribe/plugin/context.rb +28 -18
  46. data/lib/docscribe/plugin/registry.rb +49 -23
  47. data/lib/docscribe/plugin/tag.rb +9 -14
  48. data/lib/docscribe/plugin.rb +54 -22
  49. data/lib/docscribe/types/provider_chain.rb +4 -2
  50. data/lib/docscribe/types/rbs/collection_loader.rb +2 -3
  51. data/lib/docscribe/types/rbs/provider.rb +127 -62
  52. data/lib/docscribe/types/rbs/type_formatter.rb +286 -77
  53. data/lib/docscribe/types/signature.rb +22 -42
  54. data/lib/docscribe/types/sorbet/base_provider.rb +51 -27
  55. data/lib/docscribe/types/sorbet/rbi_provider.rb +3 -3
  56. data/lib/docscribe/types/sorbet/source_provider.rb +3 -2
  57. data/lib/docscribe/types/yard/formatter.rb +100 -0
  58. data/lib/docscribe/types/yard/parser.rb +240 -0
  59. data/lib/docscribe/types/yard/types.rb +52 -0
  60. data/lib/docscribe/version.rb +1 -1
  61. metadata +34 -2
@@ -4,6 +4,7 @@ require 'optparse'
4
4
 
5
5
  module Docscribe
6
6
  module CLI
7
+ # CLI option parsing and defaults.
7
8
  module Options
8
9
  DEFAULT = {
9
10
  stdin: false,
@@ -11,20 +12,74 @@ module Docscribe
11
12
  strategy: :safe, # :safe, :aggressive
12
13
  verbose: false,
13
14
  explain: false,
15
+ quiet: false,
16
+ format: :text,
14
17
  config: nil,
15
- include: [],
16
- exclude: [],
17
- include_file: [],
18
- exclude_file: [],
18
+ include: [], #: Array[String]
19
+ exclude: [], #: Array[String]
20
+ include_file: [], #: Array[String]
21
+ exclude_file: [], #: Array[String]
19
22
  rbs: false,
20
- sig_dirs: [],
23
+ sig_dirs: [], #: Array[String]
21
24
  sorbet: false,
22
- rbi_dirs: [],
23
- rbs_collection: false
25
+ rbi_dirs: [], #: Array[String]
26
+ rbs_collection: false,
27
+ keep_descriptions: false,
28
+ no_boilerplate: false,
29
+ progress: false
24
30
  }.freeze
25
31
 
26
32
  module_function
27
33
 
34
+ BANNER = <<~TEXT
35
+ Usage: docscribe [options] [files...]
36
+ docscribe init [options]
37
+ docscribe generate <type> <name> [options]
38
+ docscribe sigs [options] [files...]
39
+ docscribe rbs [options] [files...]
40
+ docscribe update_types [directory]
41
+ docscribe check_for_comments [paths...]
42
+
43
+ Default behavior:
44
+ Inspect files and report what safe doc updates would be applied.
45
+
46
+ Autocorrect:
47
+ -a, --autocorrect Apply safe doc updates in place
48
+ (insert missing docs, merge existing doc-like blocks,
49
+ normalize tag order)
50
+ -A, --autocorrect-all Apply aggressive doc updates in place
51
+ (rebuild existing doc blocks)
52
+
53
+ Input / config:
54
+ --stdin Read code from STDIN and print rewritten output
55
+ -C, --config PATH Path to config YAML (default: docscribe.yml)
56
+
57
+ Type information:
58
+ --rbs Use RBS signatures for @param/@return when available
59
+ --sig-dir DIR Add an RBS signature directory (repeatable). Implies `--rbs`.
60
+ --sorbet Use Sorbet signatures from inline sigs / RBI files when available
61
+ --rbi-dir DIR Add a Sorbet RBI directory (repeatable). Implies --sorbet.
62
+ --rbs-collection Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.
63
+
64
+ Filtering:
65
+ --include PATTERN Include PATTERN (method id or file path; glob or /regex/)
66
+ --exclude PATTERN Exclude PATTERN (method id or file path; glob or /regex/)
67
+ --include-file PATTERN Only process files matching PATTERN (glob or /regex/)
68
+ --exclude-file PATTERN Skip files matching PATTERN (glob or /regex/)
69
+
70
+ Output:
71
+ --verbose Print per-file actions
72
+ --progress Show progress [N/total] per file
73
+ -e, --explain Show detailed reasons for changes (default)
74
+ -q, --quiet Only show status, no details
75
+ --format FORMAT Output format: text (default), json, or sarif
76
+
77
+
78
+ Other:
79
+ -v, --version Print version and exit
80
+ -h, --help Show this help
81
+ TEXT
82
+
28
83
  # Parse CLI arguments into normalized Docscribe runtime options.
29
84
  #
30
85
  # CLI behavior model:
@@ -36,131 +91,355 @@ module Docscribe
36
91
  # Filtering, config, verbosity, and external type options are applied
37
92
  # orthogonally.
38
93
  #
39
- # @note module_function: when included, also defines #parse! (instance visibility: private)
94
+ # @note module_function: defines #parse! (visibility: private)
40
95
  # @param [Array<String>] argv raw CLI arguments
41
- # @return [Hash] normalized runtime options
96
+ # @return [Docscribe::CLI::Formatters::opts] normalized runtime options
42
97
  def parse!(argv)
43
98
  options = Marshal.load(Marshal.dump(DEFAULT))
44
- autocorrect_mode = nil
45
-
46
- parser = OptionParser.new do |opts|
47
- opts.banner = <<~TEXT
48
- Usage: docscribe [options] [files...]
49
-
50
- Default behavior:
51
- Inspect files and report what safe doc updates would be applied.
52
-
53
- Autocorrect:
54
- -a, --autocorrect Apply safe doc updates in place
55
- (insert missing docs, merge existing doc-like blocks,
56
- normalize tag order)
57
- -A, --autocorrect-all Apply aggressive doc updates in place
58
- (rebuild existing doc blocks)
59
-
60
- Input / config:
61
- --stdin Read code from STDIN and print rewritten output
62
- -C, --config PATH Path to config YAML (default: docscribe.yml)
63
-
64
- Type information:
65
- --rbs Use RBS signatures for @param/@return when available
66
- --sig-dir DIR Add an RBS signature directory (repeatable). Implies `--rbs`.
67
- --sorbet Use Sorbet signatures from inline sigs / RBI files when available
68
- --rbi-dir DIR Add a Sorbet RBI directory (repeatable). Implies --sorbet.
69
- --rbs-collection Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.
70
-
71
- Filtering:
72
- --include PATTERN Include PATTERN (method id or file path; glob or /regex/)
73
- --exclude PATTERN Exclude PATTERN (method id or file path; glob or /regex/)
74
- --include-file PATTERN Only process files matching PATTERN (glob or /regex/)
75
- --exclude-file PATTERN Skip files matching PATTERN (glob or /regex/)
76
-
77
- Output:
78
- --verbose Print per-file actions
79
- -e, --explain Show detailed reasons for changes
80
-
81
- Other:
82
- -v, --version Print version and exit
83
- -h, --help Show this help
84
- TEXT
85
-
86
- opts.on('-a', '--autocorrect', 'Apply safe doc updates in place') do
87
- autocorrect_mode = :safe
88
- end
89
-
90
- opts.on('-A', '--autocorrect-all', 'Apply aggressive doc updates in place') do
91
- autocorrect_mode = :aggressive
92
- end
93
-
94
- opts.on('--stdin', 'Read code from STDIN and print rewritten output') do
95
- options[:stdin] = true
96
- end
97
-
98
- opts.on('-C', '--config PATH', 'Path to config YAML (default: docscribe.yml)') do |v|
99
- options[:config] = v
100
- end
101
-
102
- opts.on('--rbs', 'Use RBS signatures for @param/@return when available (falls back to inference)') do
103
- options[:rbs] = true
104
- end
105
-
106
- opts.on('--sig-dir DIR', 'Add an RBS signature directory (repeatable). Implies --rbs.') do |v|
107
- options[:rbs] = true
108
- options[:sig_dirs] << v
109
- end
110
-
111
- opts.on('--sorbet', 'Use Sorbet signatures from inline sigs / RBI files when available') do
112
- options[:sorbet] = true
113
- end
114
-
115
- opts.on('--rbi-dir DIR', 'Add a Sorbet RBI directory (repeatable). Implies --sorbet.') do |v|
116
- options[:sorbet] = true
117
- options[:rbi_dirs] << v
118
- end
119
-
120
- opts.on('--rbs-collection', 'Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.') do
121
- options[:rbs] = true
122
- options[:rbs_collection] = true
123
- end
124
-
125
- opts.on('--include PATTERN', 'Include PATTERN (method id or file path; glob or /regex/)') do |v|
126
- route_include_exclude(options, :include, v)
127
- end
128
-
129
- opts.on('--exclude PATTERN',
130
- 'Exclude PATTERN (method id or file path; glob or /regex/). Exclude wins.') do |v|
131
- route_include_exclude(options, :exclude, v)
132
- end
133
-
134
- opts.on('--include-file PATTERN', 'Only process files matching PATTERN (glob or /regex/)') do |v|
135
- options[:include_file] << v
136
- end
137
-
138
- opts.on('--exclude-file PATTERN', 'Skip files matching PATTERN (glob or /regex/). Exclude wins.') do |v|
139
- options[:exclude_file] << v
140
- end
141
-
142
- opts.on('--verbose', 'Print per-file actions') do
143
- options[:verbose] = true
144
- end
145
-
146
- opts.on('-e', '--explain', 'Show detailed reasons for changes') do
147
- options[:explain] = true
148
- end
149
-
150
- opts.on('-v', '--version', 'Print version and exit') do
151
- require 'docscribe/version'
152
- puts Docscribe::VERSION
153
- exit 0
154
- end
155
-
156
- opts.on('-h', '--help', 'Show this help') do
157
- puts opts
158
- exit 0
159
- end
160
- end
161
-
162
- parser.parse!(argv)
99
+ autocorrect = { mode: nil }
100
+
101
+ build_option_parser(options, autocorrect).parse!(argv)
102
+ resolve_mode_and_strategy!(options, autocorrect[:mode])
103
+ options
104
+ end
105
+
106
+ # Build the OptionParser instance and register all CLI option groups.
107
+ #
108
+ # @note module_function: defines #build_option_parser (visibility: private)
109
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
110
+ # @param [Hash<Symbol, Symbol, nil>] autocorrect mutable container for autocorrect mode
111
+ # @return [OptionParser]
112
+ def build_option_parser(options, autocorrect)
113
+ OptionParser.new do |opts|
114
+ opts.banner = BANNER
115
+ define_autocorrect_options(opts, autocorrect)
116
+ define_input_options(opts, options)
117
+ define_type_options(opts, options)
118
+ define_filter_options(opts, options)
119
+ define_output_options(opts, options)
120
+ define_misc_options(opts)
121
+ end
122
+ end
123
+
124
+ # Define autocorrect options
125
+ #
126
+ # @note module_function: defines #define_autocorrect_options (visibility: private)
127
+ # @param [OptionParser] opts the option parser to configure
128
+ # @param [Hash<Symbol, Symbol, nil>] autocorrect mutable container for autocorrect mode (:safe, :aggressive, nil)
129
+ # @return [void]
130
+ def define_autocorrect_options(opts, autocorrect)
131
+ opts.on('-a', '--autocorrect', 'Apply safe doc updates in place') do
132
+ autocorrect[:mode] = :safe
133
+ end
134
+
135
+ opts.on('-A', '--autocorrect-all', 'Apply aggressive doc updates in place') do
136
+ autocorrect[:mode] = :aggressive
137
+ end
138
+ end
139
+
140
+ # Define input options
141
+ #
142
+ # @note module_function: defines #define_input_options (visibility: private)
143
+ # @param [OptionParser] opts the option parser to configure
144
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
145
+ # @return [void]
146
+ def define_input_options(opts, options)
147
+ define_stdin_option(opts, options)
148
+ define_config_option(opts, options)
149
+ end
150
+
151
+ # Define stdin option
152
+ #
153
+ # @note module_function: defines #define_stdin_option (visibility: private)
154
+ # @param [OptionParser] opts the option parser to configure
155
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
156
+ # @return [void]
157
+ def define_stdin_option(opts, options)
158
+ opts.on('--stdin', 'Read code from STDIN and print rewritten output') do
159
+ options[:stdin] = true
160
+ end
161
+ end
162
+
163
+ # Define config option
164
+ #
165
+ # @note module_function: defines #define_config_option (visibility: private)
166
+ # @param [OptionParser] opts the option parser to configure
167
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
168
+ # @return [void]
169
+ def define_config_option(opts, options)
170
+ opts.on('-C', '--config PATH', 'Path to config YAML (default: docscribe.yml)') do |v|
171
+ options[:config] = v
172
+ end
173
+ end
174
+
175
+ # Define type options
176
+ #
177
+ # @note module_function: defines #define_type_options (visibility: private)
178
+ # @param [OptionParser] opts the option parser to configure
179
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
180
+ # @return [void]
181
+ def define_type_options(opts, options)
182
+ define_rbs_option(opts, options)
183
+ define_sig_dir_option(opts, options)
184
+ define_sorbet_option(opts, options)
185
+ define_rbi_dir_option(opts, options)
186
+ define_rbs_collection_option(opts, options)
187
+ end
188
+
189
+ # Define rbs option
190
+ #
191
+ # @note module_function: defines #define_rbs_option (visibility: private)
192
+ # @param [OptionParser] opts the option parser to configure
193
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
194
+ # @return [void]
195
+ def define_rbs_option(opts, options)
196
+ opts.on('--rbs', 'Use RBS signatures for @param/@return when available (falls back to inference)') do
197
+ options[:rbs] = true
198
+ end
199
+ end
200
+
201
+ # Define sig dir option
202
+ #
203
+ # @note module_function: defines #define_sig_dir_option (visibility: private)
204
+ # @param [OptionParser] opts the option parser to configure
205
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
206
+ # @return [void]
207
+ def define_sig_dir_option(opts, options)
208
+ opts.on('--sig-dir DIR', 'Add an RBS signature directory (repeatable). Implies --rbs.') do |v|
209
+ options[:rbs] = true
210
+ options[:sig_dirs] << v
211
+ end
212
+ end
213
+
214
+ # Define sorbet option
215
+ #
216
+ # @note module_function: defines #define_sorbet_option (visibility: private)
217
+ # @param [OptionParser] opts the option parser to configure
218
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
219
+ # @return [void]
220
+ def define_sorbet_option(opts, options)
221
+ opts.on('--sorbet', 'Use Sorbet signatures from inline sigs / RBI files when available') do
222
+ options[:sorbet] = true
223
+ end
224
+ end
225
+
226
+ # Define rbi dir option
227
+ #
228
+ # @note module_function: defines #define_rbi_dir_option (visibility: private)
229
+ # @param [OptionParser] opts the option parser to configure
230
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
231
+ # @return [void]
232
+ def define_rbi_dir_option(opts, options)
233
+ opts.on('--rbi-dir DIR', 'Add a Sorbet RBI directory (repeatable). Implies --sorbet.') do |v|
234
+ options[:sorbet] = true
235
+ options[:rbi_dirs] << v
236
+ end
237
+ end
238
+
239
+ # Define rbs collection option
240
+ #
241
+ # @note module_function: defines #define_rbs_collection_option (visibility: private)
242
+ # @param [OptionParser] opts the option parser to configure
243
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
244
+ # @return [void]
245
+ def define_rbs_collection_option(opts, options)
246
+ opts.on('--rbs-collection', 'Auto-discover RBS collection from rbs_collection.lock.yaml. Implies --rbs.') do
247
+ options[:rbs] = true
248
+ options[:rbs_collection] = true
249
+ end
250
+ end
251
+
252
+ # Define filter options
253
+ #
254
+ # @note module_function: defines #define_filter_options (visibility: private)
255
+ # @param [OptionParser] opts the option parser to configure
256
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
257
+ # @return [void]
258
+ def define_filter_options(opts, options)
259
+ define_include_option(opts, options)
260
+ define_exclude_option(opts, options)
261
+ define_include_file_option(opts, options)
262
+ define_exclude_file_option(opts, options)
263
+ end
264
+
265
+ # Define include option
266
+ #
267
+ # @note module_function: defines #define_include_option (visibility: private)
268
+ # @param [OptionParser] opts the option parser to configure
269
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
270
+ # @return [void]
271
+ def define_include_option(opts, options)
272
+ opts.on('--include PATTERN', 'Include PATTERN (method id or file path; glob or /regex/)') do |v|
273
+ route_include_exclude(options, :include, v)
274
+ end
275
+ end
276
+
277
+ # Define exclude option
278
+ #
279
+ # @note module_function: defines #define_exclude_option (visibility: private)
280
+ # @param [OptionParser] opts the option parser to configure
281
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
282
+ # @return [void]
283
+ def define_exclude_option(opts, options)
284
+ opts.on('--exclude PATTERN',
285
+ 'Exclude PATTERN (method id or file path; glob or /regex/). Exclude wins.') do |v|
286
+ route_include_exclude(options, :exclude, v)
287
+ end
288
+ end
289
+
290
+ # Define include file option
291
+ #
292
+ # @note module_function: defines #define_include_file_option (visibility: private)
293
+ # @param [OptionParser] opts the option parser to configure
294
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
295
+ # @return [void]
296
+ def define_include_file_option(opts, options)
297
+ opts.on('--include-file PATTERN', 'Only process files matching PATTERN (glob or /regex/)') do |v|
298
+ options[:include_file] << v
299
+ end
300
+ end
301
+
302
+ # Define exclude file option
303
+ #
304
+ # @note module_function: defines #define_exclude_file_option (visibility: private)
305
+ # @param [OptionParser] opts the option parser to configure
306
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
307
+ # @return [void]
308
+ def define_exclude_file_option(opts, options)
309
+ opts.on('--exclude-file PATTERN', 'Skip files matching PATTERN (glob or /regex/). Exclude wins.') do |v|
310
+ options[:exclude_file] << v
311
+ end
312
+ end
313
+
314
+ # Define output options
315
+ #
316
+ # @note module_function: defines #define_output_options (visibility: private)
317
+ # @param [OptionParser] opts the option parser to configure
318
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
319
+ # @return [void]
320
+ def define_output_options(opts, options)
321
+ define_verbose_option(opts, options)
322
+ define_progress_option(opts, options)
323
+ define_explain_option(opts, options)
324
+ define_quiet_option(opts, options)
325
+ define_format_option(opts, options)
326
+ define_keep_descriptions_option(opts, options)
327
+ define_no_boilerplate_option(opts, options)
328
+ end
329
+
330
+ # Define verbose option
331
+ #
332
+ # @note module_function: defines #define_verbose_option (visibility: private)
333
+ # @param [OptionParser] opts the option parser to configure
334
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
335
+ # @return [void]
336
+ def define_verbose_option(opts, options)
337
+ opts.on('--verbose', 'Print per-file actions') do
338
+ options[:verbose] = true
339
+ options[:progress] = true
340
+ end
341
+ end
342
+
343
+ # Define progress option
344
+ #
345
+ # @note module_function: defines #define_progress_option (visibility: private)
346
+ # @param [OptionParser] opts the option parser to configure
347
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
348
+ # @return [void]
349
+ def define_progress_option(opts, options)
350
+ opts.on('--progress', 'Show progress [N/total] per file') do
351
+ options[:progress] = true
352
+ end
353
+ end
354
+
355
+ # Define explain option
356
+ #
357
+ # @note module_function: defines #define_explain_option (visibility: private)
358
+ # @param [OptionParser] opts the option parser to configure
359
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
360
+ # @return [void]
361
+ def define_explain_option(opts, options)
362
+ opts.on('-e', '--explain', 'Show detailed reasons for changes') do
363
+ options[:explain] = true
364
+ end
365
+ end
366
+
367
+ # Define quiet option
368
+ #
369
+ # @note module_function: defines #define_quiet_option (visibility: private)
370
+ # @param [OptionParser] opts the option parser to configure
371
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
372
+ # @return [void]
373
+ def define_quiet_option(opts, options)
374
+ opts.on('-q', '--quiet', 'Only show status, no details') do
375
+ options[:quiet] = true
376
+ end
377
+ end
378
+
379
+ # Define format option
380
+ #
381
+ # @note module_function: defines #define_format_option (visibility: private)
382
+ # @param [OptionParser] opts the option parser to configure
383
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
384
+ # @return [void]
385
+ def define_format_option(opts, options)
386
+ opts.on('--format FORMAT', %i[text json sarif], # steep:ignore
387
+ 'Output format: text (default), json, or sarif') do |v|
388
+ options[:format] = v
389
+ end
390
+ end
391
+
392
+ # Define keep descriptions option
393
+ #
394
+ # @note module_function: defines #define_keep_descriptions_option (visibility: private)
395
+ # @param [OptionParser] opts the option parser to configure
396
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
397
+ # @return [void]
398
+ def define_keep_descriptions_option(opts, options)
399
+ opts.on('-k', '--keep-descriptions',
400
+ 'Preserve existing @param/@return descriptions in aggressive mode') do
401
+ options[:keep_descriptions] = true
402
+ end
403
+ end
163
404
 
405
+ # Define no boilerplate option
406
+ #
407
+ # @note module_function: defines #define_no_boilerplate_option (visibility: private)
408
+ # @param [OptionParser] opts the option parser to configure
409
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
410
+ # @return [void]
411
+ def define_no_boilerplate_option(opts, options)
412
+ opts.on('-B', '--no-boilerplate',
413
+ "Don't insert template text when generating documentation") do
414
+ options[:no_boilerplate] = true
415
+ end
416
+ end
417
+
418
+ # Define misc options
419
+ #
420
+ # @note module_function: defines #define_misc_options (visibility: private)
421
+ # @param [OptionParser] opts the option parser to configure
422
+ # @return [void]
423
+ def define_misc_options(opts)
424
+ opts.on('-v', '--version', 'Print version and exit') do
425
+ require 'docscribe/version'
426
+ puts Docscribe::VERSION
427
+ exit 0
428
+ end
429
+
430
+ opts.on('-h', '--help', 'Show this help') do
431
+ puts opts
432
+ exit 0
433
+ end
434
+ end
435
+
436
+ # Set the runtime mode and strategy after all options have been parsed.
437
+ #
438
+ # @note module_function: defines #resolve_mode_and_strategy! (visibility: private)
439
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
440
+ # @param [Symbol, nil] autocorrect_mode autocorrect mode selected (:safe, :aggressive, or nil)
441
+ # @return [void]
442
+ def resolve_mode_and_strategy!(options, autocorrect_mode)
164
443
  if options[:stdin]
165
444
  options[:mode] = :stdin
166
445
  options[:strategy] = autocorrect_mode || :safe
@@ -171,8 +450,6 @@ module Docscribe
171
450
  options[:mode] = :check
172
451
  options[:strategy] = :safe
173
452
  end
174
-
175
- options
176
453
  end
177
454
 
178
455
  # Route an include/exclude pattern into method filters or file filters.
@@ -180,8 +457,8 @@ module Docscribe
180
457
  # Regex-looking patterns (`/…/`) are treated as method-id filters.
181
458
  # File-like patterns are routed into `*_file`.
182
459
  #
183
- # @note module_function: when included, also defines #route_include_exclude (instance visibility: private)
184
- # @param [Hash] options mutable parsed options hash
460
+ # @note module_function: defines #route_include_exclude (visibility: private)
461
+ # @param [Hash<Symbol, Object>] options mutable parsed options hash
185
462
  # @param [Symbol] kind either :include or :exclude
186
463
  # @param [String] value raw pattern from the CLI
187
464
  # @return [void]
@@ -198,7 +475,7 @@ module Docscribe
198
475
  # Regex syntax (`/.../`) is intentionally treated as a method-id pattern,
199
476
  # not a file pattern.
200
477
  #
201
- # @note module_function: when included, also defines #looks_like_file_pattern? (instance visibility: private)
478
+ # @note module_function: defines #looks_like_file_pattern? (visibility: private)
202
479
  # @param [String] pat pattern passed via CLI
203
480
  # @return [Boolean]
204
481
  def looks_like_file_pattern?(pat)