docscribe 1.4.2 → 1.5.1

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +601 -139
  3. data/exe/docscribe-client +105 -0
  4. data/lib/docscribe/cli/check_for_comments.rb +183 -0
  5. data/lib/docscribe/cli/config_builder.rb +107 -53
  6. data/lib/docscribe/cli/formatters/json.rb +294 -0
  7. data/lib/docscribe/cli/formatters/sarif.rb +235 -0
  8. data/lib/docscribe/cli/formatters/text.rb +208 -0
  9. data/lib/docscribe/cli/formatters.rb +26 -0
  10. data/lib/docscribe/cli/generate.rb +56 -62
  11. data/lib/docscribe/cli/init.rb +14 -6
  12. data/lib/docscribe/cli/options.rb +206 -89
  13. data/lib/docscribe/cli/rbs_gen.rb +529 -0
  14. data/lib/docscribe/cli/run.rb +433 -154
  15. data/lib/docscribe/cli/server.rb +135 -0
  16. data/lib/docscribe/cli/sigs.rb +366 -0
  17. data/lib/docscribe/cli/update_types.rb +103 -0
  18. data/lib/docscribe/cli.rb +21 -24
  19. data/lib/docscribe/config/defaults.rb +7 -2
  20. data/lib/docscribe/config/emit.rb +17 -0
  21. data/lib/docscribe/config/filtering.rb +17 -24
  22. data/lib/docscribe/config/loader.rb +19 -17
  23. data/lib/docscribe/config/plugin.rb +1 -1
  24. data/lib/docscribe/config/rbs.rb +39 -7
  25. data/lib/docscribe/config/sorbet.rb +22 -16
  26. data/lib/docscribe/config/sorting.rb +1 -1
  27. data/lib/docscribe/config/template.rb +10 -1
  28. data/lib/docscribe/config/utils.rb +11 -9
  29. data/lib/docscribe/config.rb +10 -6
  30. data/lib/docscribe/infer/ast_walk.rb +1 -1
  31. data/lib/docscribe/infer/literals.rb +6 -11
  32. data/lib/docscribe/infer/names.rb +2 -3
  33. data/lib/docscribe/infer/params.rb +14 -16
  34. data/lib/docscribe/infer/raises.rb +3 -5
  35. data/lib/docscribe/infer/returns.rb +615 -151
  36. data/lib/docscribe/infer.rb +29 -26
  37. data/lib/docscribe/inline_rewriter/collector.rb +159 -164
  38. data/lib/docscribe/inline_rewriter/doc_block.rb +145 -115
  39. data/lib/docscribe/inline_rewriter/doc_builder.rb +1032 -723
  40. data/lib/docscribe/inline_rewriter/source_helpers.rb +48 -48
  41. data/lib/docscribe/inline_rewriter/tag_sorter.rb +82 -85
  42. data/lib/docscribe/inline_rewriter.rb +485 -488
  43. data/lib/docscribe/lru_cache.rb +49 -0
  44. data/lib/docscribe/parsing.rb +28 -9
  45. data/lib/docscribe/plugin/base/collector_plugin.rb +2 -1
  46. data/lib/docscribe/plugin/base/tag_plugin.rb +0 -1
  47. data/lib/docscribe/plugin/context.rb +28 -18
  48. data/lib/docscribe/plugin/registry.rb +25 -26
  49. data/lib/docscribe/plugin/tag.rb +9 -14
  50. data/lib/docscribe/plugin.rb +17 -16
  51. data/lib/docscribe/server.rb +608 -0
  52. data/lib/docscribe/types/provider_chain.rb +4 -2
  53. data/lib/docscribe/types/rbs/collection_loader.rb +2 -2
  54. data/lib/docscribe/types/rbs/provider.rb +177 -51
  55. data/lib/docscribe/types/rbs/type_formatter.rb +224 -83
  56. data/lib/docscribe/types/signature.rb +22 -42
  57. data/lib/docscribe/types/sorbet/base_provider.rb +29 -21
  58. data/lib/docscribe/types/sorbet/rbi_provider.rb +6 -5
  59. data/lib/docscribe/types/sorbet/source_provider.rb +6 -4
  60. data/lib/docscribe/types/yard/formatter.rb +100 -0
  61. data/lib/docscribe/types/yard/parser.rb +240 -0
  62. data/lib/docscribe/types/yard/types.rb +52 -0
  63. data/lib/docscribe/version.rb +1 -1
  64. metadata +38 -1
data/README.md CHANGED
@@ -1,10 +1,18 @@
1
- # Docscribe
2
-
3
- [![Gem Version](https://img.shields.io/gem/v/docscribe.svg)](https://rubygems.org/gems/docscribe)
4
- [![RubyGems Downloads](https://img.shields.io/gem/dt/docscribe.svg)](https://rubygems.org/gems/docscribe)
5
- [![CI](https://github.com/unurgunite/docscribe/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/unurgunite/docscribe/actions/workflows/ci.yml)
6
- [![License](https://img.shields.io/github/license/unurgunite/docscribe.svg)](https://github.com/unurgunite/docscribe/blob/master/LICENSE.txt)
7
- [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%202.7-blue.svg)](#installation)
1
+ <p align="center">
2
+ <img src="assets/icons/icon_128x128.png" alt="Docscribe logo" width="128">
3
+ </p>
4
+
5
+ <h1 align="center">DocScribe</h1>
6
+
7
+ <p align="center">
8
+ <a href="https://rubygems.org/gems/docscribe"><img src="https://img.shields.io/gem/v/docscribe.svg" alt="Gem Version"></a>
9
+ <a href="https://rubygems.org/gems/docscribe"><img src="https://img.shields.io/gem/dt/docscribe.svg" alt="RubyGems Downloads"></a>
10
+ <a href="https://github.com/unurgunite/docscribe/actions/workflows/ci.yml"><img src="https://github.com/unurgunite/docscribe/actions/workflows/ci.yml/badge.svg?branch=master" alt="CI"></a>
11
+ <a href="https://github.com/unurgunite/docscribe/blob/master/LICENSE.txt"><img src="https://img.shields.io/github/license/unurgunite/docscribe.svg" alt="License"></a>
12
+ <a href="#installation"><img src="https://img.shields.io/badge/ruby-%3E%3D%202.7-blue.svg" alt="Ruby"></a>
13
+ <a href="https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode"><img src="https://img.shields.io/badge/VS%20Code-plugin-blue?logo=visualstudiocode" alt="VS Code"></a>
14
+ <a href="https://plugins.jetbrains.com/plugin/32349-docscribe"><img src="https://img.shields.io/badge/RubyMine-plugin-green?logo=jetbrains" alt="RubyMine"></a>
15
+ </p>
8
16
 
9
17
  ![Docscribe before/after demo](docs/image.png)
10
18
 
@@ -29,6 +37,10 @@ returns), and respects Ruby visibility semantics — without using YARD to parse
29
37
  - `attr_reader` / `attr_writer` / `attr_accessor`;
30
38
  - `Struct.new` declarations in both constant-assigned and class-based styles.
31
39
 
40
+ > [!NOTE]
41
+ > Docscribe is under **active development**. If you run into any edge cases or have ideas for improvement, feel free to
42
+ > [open an issue](https://github.com/unurgunite/docscribe/issues/new) or submit a pull request.
43
+
32
44
  Common workflows:
33
45
 
34
46
  - Inspect what safe doc updates would be applied: `docscribe lib`
@@ -38,21 +50,49 @@ Common workflows:
38
50
  - Use RBS signatures when available: `docscribe -a --rbs --sig-dir sig lib`
39
51
  - Use Sorbet signatures when available: `docscribe -a --sorbet --rbi-dir sorbet/rbi lib`
40
52
 
53
+ ## Quick start
54
+
55
+ ```shell
56
+ # Check what safe doc updates would be applied
57
+ docscribe lib
58
+
59
+ # Apply safe updates (insert missing docs, merge existing)
60
+ docscribe -a lib
61
+
62
+ # Rebuild all doc blocks aggressively
63
+ docscribe -A lib
64
+ ```
65
+
66
+ > [!TIP]
67
+ > See [CLI](#cli) for all options and [Update strategies](#update-strategies) for the
68
+ > difference between safe and aggressive modes.
69
+ >
70
+ > Want IDE integration? Check out
71
+ > the [VS Code](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
72
+ > and [RubyMine](https://plugins.jetbrains.com/plugin/32349-docscribe) plugins.
73
+
41
74
  ## Contents
42
75
 
43
76
  * [Docscribe](#docscribe)
77
+ * [Quick start](#quick-start)
44
78
  * [Contents](#contents)
45
79
  * [Installation](#installation)
46
- * [Quick start](#quick-start)
47
80
  * [Architecture](#architecture)
48
81
  * [Data flow](#data-flow)
49
82
  * [CLI](#cli)
83
+ * [Exit codes](#exit-codes)
50
84
  * [Options](#options)
51
85
  * [Examples](#examples)
86
+ * [`docscribe sigs` — check RBS signature coverage](#docscribe-sigs--check-rbs-signature-coverage)
87
+ * [`docscribe rbs` — generate RBS from YARD](#docscribe-rbs--generate-rbs-from-yard)
88
+ * [`docscribe update_types` — two-pass type-aware documentation update](#docscribe-update_types--two-pass-type-aware-documentation-update)
89
+ * [`docscribe check_for_comments` — find placeholder documentation](#docscribe-check_for_comments--find-placeholder-documentation)
90
+ * [`docscribe server` — persistent daemon mode](#docscribe-server--persistent-daemon-mode)
52
91
  * [Update strategies](#update-strategies)
53
92
  * [Safe strategy](#safe-strategy)
54
93
  * [Aggressive strategy](#aggressive-strategy)
55
94
  * [Output markers](#output-markers)
95
+ * [Tips & tricks](#tips--tricks)
56
96
  * [Parser backend (Parser gem vs Prism)](#parser-backend-parser-gem-vs-prism)
57
97
  * [External type integrations (optional)](#external-type-integrations-optional)
58
98
  * [RBS](#rbs)
@@ -77,6 +117,7 @@ Common workflows:
77
117
  * [Idempotency](#idempotency)
78
118
  * [Plugin examples](#plugin-examples)
79
119
  * [Configuration](#configuration)
120
+ * [Anonymous block parameters](#anonymous-block-parameters)
80
121
  * [Filtering](#filtering)
81
122
  * [`attr_*` example](#attr_-example)
82
123
  * [`Struct.new` examples](#structnew-examples)
@@ -86,14 +127,17 @@ Common workflows:
86
127
  * [Param tag style](#param-tag-style)
87
128
  * [Create a starter config](#create-a-starter-config)
88
129
  * [Generate a plugin skeleton](#generate-a-plugin-skeleton)
130
+ * [Full configuration reference](#full-configuration-reference)
89
131
  * [CI integration](#ci-integration)
90
132
  * [Comparison to YARD's parser](#comparison-to-yards-parser)
91
- * [Mermaid Architecture Reference](#mermaid-architecture-reference)
92
- * [Data flow](#data-flow)
93
133
  * [Limitations](#limitations)
94
134
  * [Roadmap](#roadmap)
135
+ * [Editor Integration](#editor-integration)
136
+ * [VS Code](#vs-code)
137
+ * [RubyMine](#rubymine)
95
138
  * [Contributing](#contributing)
96
139
  * [Discussion & Community](#discussion--community)
140
+ * [Logo Attribution](#logo-attribution)
97
141
  * [License](#license)
98
142
 
99
143
  ## Installation
@@ -118,102 +162,16 @@ gem install docscribe
118
162
 
119
163
  Requires Ruby 2.7+.
120
164
 
121
- ## Quick start
122
-
123
- Given code:
124
-
125
- ```ruby
126
- class Demo
127
- def foo(a, options: {})
128
- 42
129
- end
130
-
131
- def bar(verbose: true)
132
- 123
133
- end
134
-
135
- private
136
-
137
- def self.bump
138
- :ok
139
- end
140
-
141
- class << self
142
- private
143
-
144
- def internal; end
145
- end
146
- end
147
- ```
148
-
149
- Run:
150
-
151
- ```shell
152
- echo "...code above..." | docscribe --stdin
153
- ```
154
-
155
- Output:
156
-
157
- ```ruby
158
- class Demo
159
- # +Demo#foo+ -> Integer
160
- #
161
- # Method documentation.
162
- #
163
- # @param [Object] a Param documentation.
164
- # @param [Hash] options Param documentation.
165
- # @return [Integer]
166
- def foo(a, options: {})
167
- 42
168
- end
169
-
170
- # +Demo#bar+ -> Integer
171
- #
172
- # Method documentation.
173
- #
174
- # @param [Boolean] verbose Param documentation.
175
- # @return [Integer]
176
- def bar(verbose: true)
177
- 123
178
- end
179
-
180
- private
181
-
182
- # +Demo.bump+ -> Symbol
183
- #
184
- # Method documentation.
185
- #
186
- # @return [Symbol]
187
- def self.bump
188
- :ok
189
- end
190
-
191
- class << self
192
- private
193
-
194
- # +Demo.internal+ -> Object
195
- #
196
- # Method documentation.
197
- #
198
- # @private
199
- # @return [Object]
200
- def internal; end
201
- end
202
- end
203
- ```
204
-
205
- > [!NOTE]
206
- > - The tool inserts doc headers before method headers and preserves everything else.
207
- > - For methods with a leading Sorbet `sig`, docs are inserted above the first `sig`.
208
- > - Class methods show with a dot (`+Demo.bump+`, `+Demo.internal+`).
209
- > - Methods inside `class << self` under `private` are marked `@private`.
210
-
211
165
  ## Architecture
212
166
 
213
- Docscribe is organized into seven subsystems. The CLI layer receives user input and orchestrates configuration loading,
214
- then delegates to the core engine which parses source code, collects methods (using an AST walker), builds YARD doc
215
- lines — combining heuristic type inference, external RBS/Sorbet signatures, and plugin output — and finally rewrites the
216
- source via a strategy (safe merge or aggressive replace).
167
+ Docscribe is organized into several subsystems. The CLI layer receives user input and orchestrates configuration
168
+ loading, then delegates to the core engine which parses source code, collects methods (using an AST walker), builds YARD
169
+ doc lines — combining heuristic type inference, external RBS/Sorbet signatures, and plugin output — and finally rewrites
170
+ the source via a strategy (safe merge or aggressive replace).
171
+
172
+ In server mode, a persistent daemon (`docscribe server`) keeps the runtime loaded and caches parsed results across
173
+ invocations via an LRU cache, enabling near-instant repeated checks for IDE plugins. A thin client (`docscribe-client`)
174
+ provides minimal-overhead socket communication without loading the full gem.
217
175
 
218
176
  ```mermaid
219
177
  flowchart TB
@@ -223,6 +181,9 @@ flowchart TB
223
181
  Options["CLI::Options\nARGV parsing\n(mode, strategy,\nfilters, flags)"]
224
182
  InitCmd["CLI::Init\ndocscribe init\nGenerate config"]
225
183
  GenCmd["CLI::Generate\ndocscribe generate\nScaffold plugins"]
184
+ SigsCmd["CLI::Sigs\ndocscribe sigs\nCheck RBS coverage"]
185
+ RbsGenCmd["CLI::RbsGen\ndocscribe rbs\nGenerate RBS from YARD"]
186
+ SarifFormatter["CLI::Formatters::Sarif\nSARIF 2.1 JSON\nCode Scanning"]
226
187
  ConfigBuilder["CLI::ConfigBuilder\nApply CLI overrides\nto config"]
227
188
  end
228
189
 
@@ -244,10 +205,10 @@ flowchart TB
244
205
  end
245
206
 
246
207
  subgraph Core["Core Engine"]
247
- InlineRewriter["InlineRewriter\n· parse collect\n· deduplicate dispatch\n· rewrite"]
208
+ InlineRewriter["InlineRewriter\n· parse -> collect\n· deduplicate -> dispatch\n· rewrite"]
248
209
  Collector["Collector\n< Parser::AST::Processor\nAST walker\n· find methods/attrs\n· track visibility\n· track containers"]
249
210
  DocBuilder["DocBuilder\nGenerate YARD doc lines\n· combine inference\n· external signatures\n· plugin tags"]
250
- DocBlock["DocBlock\nSafe strategy:\nparse merge sort\nexisting doc blocks"]
211
+ DocBlock["DocBlock\nSafe strategy:\nparse -> merge -> sort\nexisting doc blocks"]
251
212
  SourceHelpers["SourceHelpers\nPosition/range\nutilities"]
252
213
  end
253
214
 
@@ -256,36 +217,57 @@ flowchart TB
256
217
  Params["Infer::Params\nParameter type\nfrom name + default"]
257
218
  Returns["Infer::Returns\nReturn type\nfrom method body"]
258
219
  Raises["Infer::Raises\n@raise tags\nfrom raise/rescue"]
259
- Literals["Infer::Literals\nAST literal →\ntype string"]
260
- Names["Infer::Names\n:const node →\nFQN string"]
220
+ Literals["Infer::Literals\nAST literal ->\ntype string"]
221
+ Names["Infer::Names\n:const node ->\nFQN string"]
261
222
  ASTWalk["Infer::ASTWalk\nRecursive DFS\nAST traversal"]
262
223
  end
263
224
 
264
225
  subgraph Plugins["Plugin System"]
265
226
  PluginModule["Plugin\nTag/Collector\ndispatch"]
266
- Registry["Plugin::Registry\nGlobal registry\n· register route\n· tag_entries\n· collector_entries"]
267
- TagPlugin["Base::TagPlugin\nOverride #call(context)\n Array<Tag>"]
268
- CollectorPlugin["Base::CollectorPlugin\nOverride #collect(ast, buffer)\n Array<Hash>"]
227
+ Registry["Plugin::Registry\nGlobal registry\n· register -> route\n· tag_entries\n· collector_entries"]
228
+ TagPlugin["Base::TagPlugin\nOverride #call(context)\n-> Array<Tag>"]
229
+ CollectorPlugin["Base::CollectorPlugin\nOverride #collect(ast, buffer)\n-> Array<Hash>"]
269
230
  TagValue["Plugin::Tag\nStruct (name, text, types)"]
270
231
  Context["Plugin::Context\nMethod snapshot struct"]
271
232
  end
272
233
 
273
234
  subgraph Types["External Type System"]
274
235
  ProviderChain["ProviderChain\nComposite:\nquery in order\nfirst match wins"]
275
- RBSProvider["RBS::Provider\n.rbs files\n RBS lib"]
276
- RBSFormatter["RBS::TypeFormatter\nRBS type →\nYARD type string"]
236
+ RBSProvider["RBS::Provider\n.rbs files\n-> RBS lib"]
237
+ RBSFormatter["RBS::TypeFormatter\nRBS type ->\nYARD type string"]
277
238
  RBSCollection["RBS::CollectionLoader\nrbs_collection\n.lock.yaml"]
278
239
  SorbetBase["Sorbet::BaseProvider\nRBS::Prototype::RBI\nbridge"]
279
240
  SorbetSource["Sorbet::SourceProvider\nInline sig{}\ndeclarations"]
280
241
  SorbetRBI["Sorbet::RBIProvider\n.rbi files\ndirectories"]
281
242
  end
282
243
 
244
+ subgraph YardTypes["YARD Type Parser"]
245
+ YParser["Yard::Parser\nParse YARD type\nstrings -> AST"]
246
+ YFormatter["Yard::Formatter\nYARD AST ->\nRBS string"]
247
+ YTypes["Yard::Types\n9 AST node types\n(Named, Generic, etc.)"]
248
+ end
249
+
250
+ subgraph Server["Server / Daemon"]
251
+ ThinClient["exe/docscribe-client\nThin client\n· socket send/receive\n· no gem load"]
252
+ ServerDaemon["Server::Daemon\nSocket listener\n· check / fix / shutdown\n· JSON-RPC 2.0"]
253
+ Cache["Docscribe::LRUCache\nFile result cache\n(max 1000, by mtime)"]
254
+ end
255
+
283
256
  Exe --> Run
257
+ Exe --> ServerDaemon
258
+ ThinClient --> ServerDaemon
259
+ ServerDaemon --> ConfigClass
260
+ ServerDaemon --> Cache
261
+ ServerDaemon --> InlineRewriter
284
262
  Run --> Options
285
263
  Run --> InitCmd
286
264
  Run --> GenCmd
265
+ Run --> SigsCmd
266
+ Run --> RbsGenCmd
267
+ Run --> SarifFormatter
287
268
  Run --> ConfigBuilder
288
269
  ConfigBuilder --> ConfigClass
270
+ ConfigBuilder --> ServerDaemon
289
271
  ConfigClass --> Defaults
290
272
  ConfigClass --> Loader
291
273
  ConfigClass --> Emit
@@ -325,13 +307,35 @@ flowchart TB
325
307
  TagPlugin --> Context
326
308
  InlineRewriter --> DocBlock
327
309
  InlineRewriter --> SourceHelpers
310
+ RbsGenCmd --> ParsingModule
311
+ RbsGenCmd --> YParser
312
+ YParser --> YTypes
313
+ YParser --> YFormatter
328
314
  ```
329
315
 
330
316
  ### Data flow
331
317
 
332
318
  ```mermaid
333
319
  flowchart LR
334
- Input["Source files\n(.rb)"] --> Parse["Parsing.parse_buffer\nParser gem / Prism"]
320
+ subgraph Entry["Entry Points"]
321
+ Direct["docscribe lib\n(no --server)"]
322
+ ViaServer["docscribe --server\nor docscribe-client"]
323
+ end
324
+
325
+ subgraph Daemon["Server Daemon (Unix Socket)"]
326
+ Socket["Daemon#listen_loop\nJSON-RPC 2.0 dispatch"]
327
+ CacheCheck{"File cached &\nmtime fresh?"}
328
+ CacheStorage["LRUCache\n(1000 entries)"]
329
+ ApplyOverrides["apply_cli_overrides\n(reset on nil)"]
330
+ end
331
+
332
+ ViaServer --> Socket
333
+ Socket --> ApplyOverrides
334
+ ApplyOverrides --> CacheCheck
335
+ CacheCheck -->|Hit| Socket
336
+ CacheCheck -->|Miss| Parse
337
+ Direct --> Parse
338
+ Parse["Parsing.parse_buffer\nParser gem / Prism"]
335
339
  Parse --> AST["AST + Comments"]
336
340
  AST --> Collect["Collector.process\n· Find methods\n· Track visibility\n· Find attr_*"]
337
341
  AST --> CollectPlugins["CollectorPlugin#collect\n· Custom AST walks\n· Non-standard constructs"]
@@ -348,25 +352,42 @@ flowchart LR
348
352
  ResultDoc --> Strategy{"Strategy?"}
349
353
  Strategy -->|Safe| Merge["DocBlock.merge\npreserve + append + sort"]
350
354
  Strategy -->|Aggressive| Replace["Replace entirely"]
351
- Merge --> Rewritten["Rewriter#process\n rewritten source"]
355
+ Merge --> Rewritten["Rewriter#process\n-> rewritten source"]
352
356
  Replace --> Rewritten
353
- Rewritten --> Output["Modified .rb file\n/ STDOUT"]
357
+ Rewritten --> Result["Result / response"]
358
+ Rewritten --> CacheStorage
359
+ CacheStorage --> Socket
360
+ Result -->|Direct mode| Output["Modified .rb file / STDOUT"]
361
+ Result -->|Server mode| Socket
354
362
  ```
355
363
 
356
364
  ## CLI
357
365
 
358
366
  ```shell
359
367
  docscribe [options] [files...]
368
+ docscribe init [options]
369
+ docscribe generate [type] [name] [options]
370
+ docscribe sigs [options] [files...]
371
+ docscribe rbs [options] [files...]
372
+ docscribe update_types [directory]
373
+ docscribe check_for_comments [paths...]
374
+ docscribe server [start|status|stop] [options]
360
375
  ```
361
376
 
362
377
  Docscribe has three main ways to run:
363
378
 
364
- - **Inspect mode** (default): checks what safe doc updates would be applied and exits non-zero if files need changes.
379
+ - **Inspect mode** (default): checks what safe doc updates would be applied and exits 1 if files need changes.
365
380
  - **Safe autocorrect** (`-a`, `--autocorrect`): writes safe, non-destructive updates in place.
366
381
  - **Aggressive autocorrect** (`-A`, `--autocorrect-all`): rewrites existing doc blocks more aggressively.
367
382
  - **STDIN mode** (`--stdin`): reads Ruby source from STDIN and prints rewritten source to STDOUT.
368
383
 
369
- If you pass no files and dont use `--stdin`, Docscribe processes the current directory recursively.
384
+ If you pass no files and don't use `--stdin`, Docscribe processes the current directory recursively.
385
+
386
+ ### Exit codes
387
+
388
+ - **0** — all files are up to date (no changes needed)
389
+ - **1** — some files need documentation updates
390
+ - **2** — execution error (parse error, missing files, etc.)
370
391
 
371
392
  ### Options
372
393
 
@@ -385,10 +406,32 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
385
406
  Read source from STDIN and print rewritten output.
386
407
 
387
408
  - `--verbose`
388
- Print per-file actions.
409
+ Print per-file actions. Also enables `--progress`.
410
+
411
+ - `--progress`
412
+ Show progress (`[N/total] path/to/file.rb`) on stderr as each file is processed.
413
+
414
+ - `--quiet` (`-q`)
415
+ Only show status, no details (suppresses change reasons).
416
+ Overrides the default detailed output.
389
417
 
390
418
  - `--explain`
391
- Show detailed reasons for each file that would change.
419
+ Show detailed reasons for each file (default; no-op for compatibility).
420
+
421
+ - `--server`
422
+ Run via a persistent daemon (Unix socket). Speeds up repeated invocations
423
+ by keeping the Ruby runtime loaded and caching results.
424
+
425
+ - `-k`, `--keep-descriptions`
426
+ Preserve existing documentation text when rebuilding doc blocks in aggressive mode.
427
+
428
+ - `-B`, `--no-boilerplate`
429
+ Suppress boilerplate text (`Method documentation.`, `Param documentation.`) in output.
430
+ Equivalent to `emit.include_default_message: false` and `emit.include_param_documentation: false` in config.
431
+
432
+ - `--format FORMAT`
433
+ Output format: `text` (default, human-readable), `json` (machine-readable, RuboCop-compatible), or `sarif` (SARIF 2.1
434
+ JSON, compatible with GitHub Code Scanning).
392
435
 
393
436
  - `--rbs`
394
437
  Use RBS signatures for `@param`/`@return` when available (falls back to inference).
@@ -396,6 +439,12 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
396
439
  - `--sig-dir DIR`
397
440
  Add an RBS signature directory (repeatable). Implies `--rbs`.
398
441
 
442
+ - `--sorbet`
443
+ Use Sorbet signatures for `@param`/`@return` when available (falls back to inference).
444
+
445
+ - `--rbi-dir DIR`
446
+ Add an Sorbet RBI directory (repeatable). Implies `--sorbet`.
447
+
399
448
  - `--include PATTERN`
400
449
  Include PATTERN (method id or file path; glob or `/regex/`).
401
450
 
@@ -456,9 +505,224 @@ If you pass no files and don’t use `--stdin`, Docscribe processes the current
456
505
 
457
506
  - Show detailed reasons for files that would change:
458
507
  ```shell
459
- docscribe --verbose --explain lib
508
+ docscribe --verbose lib
460
509
  ```
461
510
 
511
+ ### `docscribe sigs` — check RBS signature coverage
512
+
513
+ > [!WARNING]
514
+ > `docscribe sigs` requires **Ruby 3.0+** and the `rbs` gem. On Ruby 2.7 it will print an error and exit with code 2.
515
+
516
+ `docscribe sigs` parses Ruby files, extracts method definitions, and checks each
517
+ method against the configured RBS signature directories. Reports methods that lack RBS type signatures.
518
+
519
+ ```shell
520
+ # Check lib/ against sig/ signatures
521
+ docscribe sigs lib
522
+
523
+ # Use RBS collection
524
+ docscribe sigs --rbs-collection lib
525
+
526
+ # Multiple signature directories
527
+ docscribe sigs -s sig -s vendor/sigs lib
528
+
529
+ # Verbose output (also prints methods that have signatures)
530
+ docscribe sigs --verbose lib
531
+ ```
532
+
533
+ **Flags:**
534
+
535
+ - `-s`, `--sig-dir DIR` — add an RBS signature directory (repeatable). Default: `sig`.
536
+ - `--rbs-collection` — use RBS collection (reads `rbs_collection.lock.yaml`).
537
+ - `--verbose` — also print methods that have signatures.
538
+ - `-h`, `--help` — show help.
539
+
540
+ **Exit codes:**
541
+
542
+ - `0` — all methods have RBS signatures.
543
+ - `1` — some methods lack RBS signatures.
544
+ - `2` — error occurred.
545
+
546
+ ### `docscribe rbs` — generate RBS from YARD
547
+
548
+ > [!WARNING]
549
+ > `docscribe rbs` requires **Ruby 3.0+** and the `rbs` gem. On Ruby 2.7 it will print an error and exit with code 2.
550
+
551
+ `docscribe rbs` parses YARD comments (`@param`, `@return`, `@option`) from Ruby files
552
+ and generates corresponding `.rbs` signature files.
553
+
554
+ ```shell
555
+ # Generate RBS files in sig/
556
+ docscribe rbs lib
557
+
558
+ # Print to stdout (no files written)
559
+ docscribe rbs -n lib
560
+
561
+ # Specify output directory
562
+ docscribe rbs -o sig/gen lib
563
+
564
+ # Overwrite existing files
565
+ docscribe rbs -f lib
566
+ ```
567
+
568
+ **Flags:**
569
+
570
+ - `-o`, `--output DIR` — output directory (default: `sig`).
571
+ - `-n`, `--dry-run` — print generated RBS to stdout, do not write files.
572
+ - `-f`, `--force` — overwrite existing files.
573
+ - `-h`, `--help` — show help.
574
+
575
+ **Exit codes:**
576
+
577
+ - `0` — success.
578
+ - `1` — errors occurred during processing.
579
+ - `2` — execution error (no files found).
580
+
581
+ ```ruby
582
+ # Example: lib/user.rb
583
+ class User
584
+ # @param [String] name
585
+ # @param [Integer] age
586
+ # @return [User]
587
+ def initialize(name, age)
588
+ @name = name
589
+ @age = age
590
+ end
591
+ end
592
+ ```
593
+
594
+ ```shell
595
+ docscribe rbs lib
596
+ # Generated sig/user.rbs
597
+ ```
598
+
599
+ ```ruby.rbs
600
+ # sig/user.rbs
601
+ class User
602
+ def initialize: (String name, Integer age) -> User
603
+ end
604
+ ```
605
+
606
+ ### `docscribe update_types` — two-pass type-aware documentation update
607
+
608
+ > [!NOTE]
609
+ > `docscribe update_types` is a convenience alias for the two-pass workflow above. It requires Ruby 3.0+ and the `rbs`
610
+ > gem (because of `--rbs-collection`). The RBS collection must be set up first with
611
+ > `bundle exec rbs collection install`. Type accuracy depends on your RBS signatures — if signatures are incomplete or
612
+ > missing, types will fall back to AST inference.
613
+
614
+ `docscribe update_types` runs two passes to bring both docs and RBS signatures up to date:
615
+
616
+ 1. **Pass 1** — `docscribe -AkB --rbs-collection <dir>`: aggressively rebuilds doc blocks, preserves existing
617
+ descriptions, suppresses boilerplate, uses RBS collection types.
618
+ 2. **Pass 2** — `docscribe -aB --rbs-collection <dir>`: safe merge cleanup with no boilerplate.
619
+
620
+ ```shell
621
+ # Update docs in lib/ using RBS collection
622
+ docscribe update_types lib
623
+
624
+ # Defaults to current directory
625
+ docscribe update_types
626
+ ```
627
+
628
+ ### `docscribe check_for_comments` — find placeholder documentation
629
+
630
+ `docscribe check_for_comments` scans Ruby source files for YARD comments that still contain default placeholder
631
+ text. Reads the configured placeholder messages from `docscribe.yml` (or built-in defaults). Useful in CI to catch
632
+ files where auto-generated text was never replaced with real documentation.
633
+
634
+ ```shell
635
+ # Scan entire project
636
+ docscribe check_for_comments
637
+
638
+ # Scan specific directories
639
+ docscribe check_for_comments lib app
640
+ ```
641
+
642
+ Exit code `0` if no placeholders found, `1` if any are detected.
643
+
644
+ **Flags:**
645
+
646
+ - `-h`, `--help` — show help.
647
+
648
+ ### `docscribe server` — persistent daemon mode
649
+
650
+ > [!NOTE]
651
+ > Server mode requires **Ruby 3.1+** (`Process.fork` for background daemon).
652
+
653
+ `docscribe server` starts a background daemon that keeps the Ruby runtime loaded and caches parsed ASTs across
654
+ invocations. Subsequent `docscribe` calls with `--server` communicate with the daemon over a Unix socket instead of
655
+ reloading the entire toolchain.
656
+
657
+ ```shell
658
+ # Start the daemon (auto-detached in background)
659
+ docscribe server start
660
+
661
+ # Check if the daemon is running
662
+ docscribe server status
663
+
664
+ # Stop the daemon
665
+ docscribe server stop
666
+
667
+ # Use the daemon for file checks (much faster on repeated calls)
668
+ docscribe --server lib
669
+ docscribe -a --server lib
670
+ ```
671
+
672
+ **How it works:**
673
+
674
+ 1. `docscribe server start` forks a background process that listens on a Unix socket in the system temp directory.
675
+ 2. The socket path is derived from the project root, `Gemfile.lock` mtime, and `rbs_collection.lock.yaml` mtime — so any
676
+ environment change spawns a fresh daemon automatically.
677
+ 3. Client requests (`docscribe --server`) send JSON-RPC 2.0 messages over the socket: `check` (inspect), `fix` (apply
678
+ changes), `shutdown`, and `ping` (health/version info).
679
+ 4. The daemon holds a reusable `Docscribe::Config` instance and an LRU file cache (bounded at 1000 entries), so repeated
680
+ checks on the same files are nearly instant.
681
+ 5. After 5 minutes of inactivity the daemon shuts down automatically.
682
+
683
+ **Thin client (`docscribe-client`):**
684
+
685
+ For IDE plugins and CI systems that need minimal startup overhead, a standalone thin client is available as
686
+ `exe/docscribe-client`. It connects to the daemon via the same Unix socket protocol without loading the full docscribe
687
+ gem:
688
+
689
+ ```shell
690
+ # Check a file via the daemon
691
+ docscribe-client --check lib/user.rb
692
+
693
+ # Apply fixes via the daemon
694
+ docscribe-client --fix lib/user.rb
695
+
696
+ # Check if the daemon is running
697
+ docscribe-client --status
698
+
699
+ # Get version, pid, uptime from the daemon
700
+ docscribe-client --ping
701
+
702
+ # Stop the daemon
703
+ docscribe-client --shutdown
704
+ ```
705
+
706
+ The thin client is used automatically by
707
+ the [VS Code](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
708
+ and [RubyMine](https://plugins.jetbrains.com/plugin/32349-docscribe) plugins when the daemon is running.
709
+
710
+ **Environment invalidation:**
711
+
712
+ The daemon's socket path includes a hash of:
713
+
714
+ - `Gemfile.lock` mtime — gem changes that may affect RBS signatures.
715
+ - `rbs_collection.lock.yaml` mtime — RBS collection signature updates.
716
+
717
+ If any of these files change, the next `docscribe --server` call will start a new daemon automatically. The old daemon
718
+ is left to idle-timeout on its own.
719
+
720
+ **CLI override handling:**
721
+
722
+ When CLI overrides (`-C`, `--include`, `--exclude`, etc.) change between requests, the daemon resets its effective
723
+ configuration, clears the file cache, and applies the new overrides before processing. If no overrides are passed, the
724
+ cached state from the initial load is used, preserving performance.
725
+
462
726
  ## Update strategies
463
727
 
464
728
  Docscribe supports two update strategies: **safe** and **aggressive**.
@@ -493,6 +757,9 @@ Aggressive strategy:
493
757
 
494
758
  Use it when you want to rebaseline or regenerate docs wholesale.
495
759
 
760
+ > [!CAUTION]
761
+ > Aggressive mode rewrites existing doc blocks entirely. Review changes with `git diff` before committing.
762
+
496
763
  ### Output markers
497
764
 
498
765
  In inspect mode, Docscribe prints one character per file:
@@ -500,14 +767,20 @@ In inspect mode, Docscribe prints one character per file:
500
767
  - `.` = file is up to date
501
768
  - `F` = file would change
502
769
  - `E` = file had an error
770
+ - `M` = type mismatch detected (external RBS/Sorbet signature differs from inferred type)
503
771
 
504
772
  In write modes:
505
773
 
506
774
  - `.` = file already OK
507
775
  - `C` = file was updated
508
776
  - `E` = file had an error
777
+ - `M` = file updated but type mismatch remains
778
+
779
+ > [!IMPORTANT]
780
+ > The `M` marker only appears when `--rbs` or `--sorbet` is enabled, since type mismatches are detected by
781
+ > comparing external signatures against inferred types.
509
782
 
510
- With `--verbose`, Docscribe prints per-file statuses instead.
783
+ With `--verbose`, Docscribe prints per-file statuses instead; type mismatches show as `MT` with the specific difference.
511
784
 
512
785
  With `--explain`, Docscribe also prints detailed reasons, such as:
513
786
 
@@ -516,6 +789,27 @@ With `--explain`, Docscribe also prints detailed reasons, such as:
516
789
  - missing module_function note
517
790
  - unsorted tags
518
791
 
792
+ Use `--quiet` to suppress these details and show only file names and the summary line.
793
+
794
+ ## Tips & tricks
795
+
796
+ Useful flag combinations for common workflows:
797
+
798
+ - `docscribe -aB lib` — safe autocorrect without boilerplate. Uses safe mode but suppresses default text, leaving only
799
+ type tags (`@param`, `@return`). Clean minimal docs.
800
+ - `docscribe -AkB lib` — aggressive autocorrect with preserved descriptions and no boilerplate. Rebuilds doc blocks,
801
+ keeps your hand-written descriptions, suppresses default text. Best for rebaselining.
802
+ - `docscribe -a --rbs-collection lib` — safe autocorrect using RBS gem collection signatures. Recommended for Rails
803
+ projects.
804
+ - `docscribe -a --sorbet --rbi-dir sorbet/rbi lib` — safe autocorrect using Sorbet RBI signatures.
805
+ - `docscribe update_types lib` — two-pass type-aware update: aggressively rebuilds docs with kept descriptions and RBS
806
+ collection, then safe-merges to clean up.
807
+ See [docscribe update_types](#docscribe-update_types--two-pass-type-aware-documentation-update).
808
+
809
+ > [!NOTE]
810
+ > `docscribe update_types` is a convenient shortcut, but be aware it uses `--rbs-collection` under the hood.
811
+ > If your RBS signatures are incomplete, types may fall back to AST inference.
812
+
519
813
  ## Parser backend (Parser gem vs Prism)
520
814
 
521
815
  Docscribe internally works with `parser`-gem-compatible AST nodes and `Parser::Source::*` objects (so it can use
@@ -524,6 +818,11 @@ Docscribe internally works with `parser`-gem-compatible AST nodes and `Parser::S
524
818
  - On Ruby **<= 3.3**, Docscribe parses using the `parser` gem.
525
819
  - On Ruby **>= 3.4**, Docscribe parses using **Prism** and translates the tree into the `parser` gem's AST.
526
820
 
821
+ > [!NOTE]
822
+ > Prism support is automatic on Ruby 3.4+. On earlier Rubies, the `parser` gem is always used.
823
+ > You can override with `DOCSCRIBE_PARSER_BACKEND=prism` on Ruby 3.1+ if you have the `prism` gem installed,
824
+ > but this is not recommended for production use.
825
+
527
826
  You can force a backend with an environment variable:
528
827
 
529
828
  ```shell
@@ -537,16 +836,33 @@ Docscribe can improve generated `@param` and `@return` types by reading external
537
836
  AST inference.
538
837
 
539
838
  > [!IMPORTANT]
540
- > When external type information is available, Docscribe resolves signatures in this order:
541
- > - inline Sorbet `sig` declarations in the current Ruby source;
542
- > - Sorbet RBI files;
543
- > - RBS files;
544
- > - AST inference fallback.
839
+ > Docscribe resolves types in a two-level chain. For **documentation tags** (`@param`, `@return`), the priority is:
545
840
  >
546
- > If an external signature cannot be loaded or parsed, Docscribe falls back to normal inference instead of failing.
841
+ > | Priority | Source | When active |
842
+ > |----------|-------------------------------------------------------------|----------------------------------------------------------|
843
+ > | **1** | Inline Sorbet `sig { ... }` in current file | `--sorbet` or `sorbet.enabled: true` |
844
+ > | **2** | Sorbet RBI files (`.rbi`) | `--sorbet --rbi-dir` or `sorbet.rbi_dirs` |
845
+ > | **3** | RBS files (sig_dirs + collection, loaded into one env) | `--rbs --sig-dir` / `--rbs-collection` or `rbs.*` config |
846
+ > | **3a** | └─ Fallback: sig_dirs only (collection dropped on conflict) | Automatic if priority 3 env load fails |
847
+ > | **4** | AST inference (fallback) | Always active |
848
+ >
849
+ > For **intra-method body inference** (e.g. resolving `Integer#positive?` -> `Boolean`), a separate core RBS provider
850
+ > loads only stdlib/built-in signatures and is active automatically when the `rbs` gem is available — even without
851
+ > `--rbs`.
852
+ >
853
+ > If an external signature cannot be loaded or parsed, Docscribe falls back to the next source in the chain instead of
854
+ > failing.
547
855
 
548
856
  ### RBS
549
857
 
858
+ > [!IMPORTANT]
859
+ > All RBS features require the `rbs` gem. Add `gem "rbs"` to your Gemfile and run `bundle install`,
860
+ > or install it globally with `gem install rbs`.
861
+ >
862
+ > On Ruby 2.7 the `rbs` gem cannot load at all — `--rbs`, `--sig-dir`, `--rbs-collection`,
863
+ > `docscribe sigs`, and `docscribe rbs` will print a warning and fall back to AST-only inference.
864
+ > On Ruby 3.0+, if the gem is missing, Docscribe silently falls back to inference when you pass RBS flags.
865
+
550
866
  Docscribe can read method signatures from `.rbs` files and use them to generate more accurate parameter and return
551
867
  types.
552
868
 
@@ -566,12 +882,23 @@ Config:
566
882
 
567
883
  ```yaml
568
884
  rbs:
569
- enabled: true
885
+ enabled: false
570
886
  sig_dirs:
571
887
  - sig
888
+ collection: false
572
889
  collapse_generics: false
890
+ collapse_object_generics: false
891
+ warn_missing_collection: true
573
892
  ```
574
893
 
894
+ - `collection` — enable auto-discovery of RBS collection from `rbs_collection.lock.yaml`. Pass `--rbs-collection` on the
895
+ CLI.
896
+ - `collapse_generics` — strip all generic type arguments (e.g. `Array<String>` -> `Array`).
897
+ - `collapse_object_generics` — only strip generic arguments when all are `Object` (e.g. `Hash<Object, Object>` ->
898
+ `Hash`, but `Hash<Symbol, Object>` stays).
899
+ - `warn_missing_collection` — warn on stderr when `rbs_collection.lock.yaml` is found but collection is not enabled. Set
900
+ to `false` to suppress.
901
+
575
902
  Example:
576
903
 
577
904
  ```ruby
@@ -794,24 +1121,29 @@ end
794
1121
 
795
1122
  ### Generic type formatting
796
1123
 
797
- Both RBS and Sorbet integrations support `collapse_generics`.
1124
+ Both RBS and Sorbet integrations support generic type collapsing.
798
1125
 
799
- When disabled:
1126
+ **`collapse_generics`** — strips all generic type arguments.
1127
+ **`collapse_object_generics`** — only strips arguments when all are `Object` (e.g. `Hash<Object, Object>` -> `Hash`, but
1128
+ `Hash<Symbol, Object>` stays).
1129
+
1130
+ When both disabled:
800
1131
 
801
1132
  ```yaml
802
1133
  rbs:
803
1134
  collapse_generics: false
1135
+ collapse_object_generics: false
804
1136
 
805
1137
  sorbet:
806
1138
  collapse_generics: false
807
1139
  ```
808
1140
 
809
- Docscribe preserves generic container details where possible, for example:
1141
+ Docscribe preserves generic container details, for example:
810
1142
 
811
1143
  - `Array<String>`
812
1144
  - `Hash<Symbol, Integer>`
813
1145
 
814
- When enabled:
1146
+ When `collapse_generics` is enabled:
815
1147
 
816
1148
  ```yaml
817
1149
  rbs:
@@ -821,7 +1153,7 @@ sorbet:
821
1153
  collapse_generics: true
822
1154
  ```
823
1155
 
824
- Docscribe simplifies container types to their outer names, for example:
1156
+ Docscribe simplifies all container types to their outer names, for example:
825
1157
 
826
1158
  - `Array`
827
1159
  - `Hash`
@@ -856,6 +1188,11 @@ Return values:
856
1188
  - For simple bodies, Docscribe looks at the last expression or explicit `return`.
857
1189
  - Unions with `nil` become optional types (e.g. `String` or `nil` -> `String?`).
858
1190
  - For control flow (`if`/`case`), it unifies branches conservatively.
1191
+
1192
+ > [!TIP]
1193
+ > Docscribe resolves return types for core Ruby methods (`Integer#positive?`, `String#upcase`, etc.)
1194
+ > **even without `--rbs`** — the core RBS provider is always active when the `rbs` gem is available.
1195
+
859
1196
  - **RBS core type inference**: when `--rbs` is enabled, Docscribe resolves return types for method calls on core types
860
1197
  from their RBS definitions:
861
1198
  - `arg.positive?` (`arg = 1`) -> `Boolean` (from `Integer#positive?`)
@@ -1168,6 +1505,19 @@ Sample plugins available at [examples](examples/plugins):
1168
1505
 
1169
1506
  Docscribe can be configured via a YAML file (`docscribe.yml` by default, or pass `--config PATH`).
1170
1507
 
1508
+ ### Anonymous block parameters
1509
+
1510
+ Ruby 3.2+ allows anonymous block arguments (`def foo(&)`). By default, Docscribe generates `@param [Proc] block` for
1511
+ these — but since the parameter has no name, the tag is misleading.
1512
+
1513
+ To suppress the `@param` tag for anonymous block arguments:
1514
+
1515
+ ```yaml
1516
+ skip_anonymous_block_params: true
1517
+ ```
1518
+
1519
+ When `false` (default), anonymous block params generate `@param [Proc] block`.
1520
+
1171
1521
  ### Filtering
1172
1522
 
1173
1523
  Docscribe can filter both *files* and *methods*.
@@ -1391,6 +1741,7 @@ docscribe generate tag SincePlugin --stdout
1391
1741
  ```
1392
1742
 
1393
1743
  The generated file contains:
1744
+
1394
1745
  - the correct base class (`Base::TagPlugin` or `Base::CollectorPlugin`)
1395
1746
  - inline comments describing every available `Context` attribute (TagPlugin)
1396
1747
  or the expected return shape (CollectorPlugin)
@@ -1401,6 +1752,58 @@ The generated file contains:
1401
1752
  > The class name must be a valid Ruby constant (`MyPlugin`, `My::Plugin`).
1402
1753
  > The output filename is the snake_case equivalent (`my_plugin.rb`, `my/plugin.rb`).
1403
1754
 
1755
+ ### Full configuration reference
1756
+
1757
+ <details>
1758
+ <summary>Click to expand — 43 config keys</summary>
1759
+
1760
+ | Key | Type | Default | Description |
1761
+ |-------------------------------------------|------------|----------------------------------------------------------------------------------------------|----------------------------------------------------------------------------|
1762
+ | `keep_descriptions` | `bool` | `false` | Preserve existing doc text in aggressive mode |
1763
+ | `skip_anonymous_block_params` | `bool` | `false` | Skip `@param [Proc] block` for anonymous `&` params |
1764
+ | `emit.header` | `bool` | `false` | Generate method header line (`+#foo+ -> ...`) |
1765
+ | `emit.include_default_message` | `bool` | `true` | Insert default message (`Method documentation.`) |
1766
+ | `emit.include_param_documentation` | `bool` | `true` | Insert param description text (`Param documentation.`) |
1767
+ | `emit.param_tags` | `bool` | `true` | Generate `@param` tags |
1768
+ | `emit.return_tag` | `bool` | `true` | Generate `@return` tag |
1769
+ | `emit.visibility_tags` | `bool` | `true` | Generate `@private`/`@protected` tags |
1770
+ | `emit.raise_tags` | `bool` | `true` | Generate `@raise` tags (for `raise` in method body) |
1771
+ | `emit.rescue_conditional_returns` | `bool` | `true` | Consider `rescue` branches in return type inference |
1772
+ | `emit.attributes` | `bool` | `false` | Generate `@!attribute` for `attr_*` and `Struct.new` |
1773
+ | `doc.default_message` | `string` | `"Method documentation."` | Default text for method description |
1774
+ | `doc.param_documentation` | `string` | `"Param documentation."` | Default text for param description |
1775
+ | `doc.sort_tags` | `bool` | `true` | Sort tags according to `tag_order` |
1776
+ | `doc.tag_order` | `string[]` | `["todo","note","api","private","protected","param","option","yieldparam","raise","return"]` | Tag sort order |
1777
+ | `doc.param_tag_style` | `string` | `"type_name"` | `@param` style: `type_name` (`[Type] name`) or `name_type` (`name [Type]`) |
1778
+ | `inference.fallback_type` | `string` | `"Object"` | Fallback type when inference yields no result |
1779
+ | `inference.nil_as_optional` | `bool` | `true` | Treat `nil` as an optional type |
1780
+ | `inference.treat_options_keyword_as_hash` | `bool` | `true` | Treat `**options` as `Hash` in `@option` tags |
1781
+ | `filter.include` | `string[]` | `[]` | Only include methods matching pattern |
1782
+ | `filter.exclude` | `string[]` | `[]` | Exclude methods matching pattern |
1783
+ | `filter.visibilities` | `string[]` | `["public","protected","private"]` | Visibilities to process |
1784
+ | `filter.scopes` | `string[]` | `["instance","class"]` | Scopes to process |
1785
+ | `filter.files.include` | `string[]` | `[]` | Only process files matching pattern |
1786
+ | `filter.files.exclude` | `string[]` | `["spec"]` | Skip files matching pattern |
1787
+ | `methods.instance.public` | `object` | `{}` | Overrides for instance public methods |
1788
+ | `methods.instance.protected` | `object` | `{}` | Overrides for instance protected methods |
1789
+ | `methods.instance.private` | `object` | `{}` | Overrides for instance private methods |
1790
+ | `methods.class.public` | `object` | `{}` | Overrides for class public methods |
1791
+ | `methods.class.protected` | `object` | `{}` | Overrides for class protected methods |
1792
+ | `methods.class.private` | `object` | `{}` | Overrides for class private methods |
1793
+ | `rbs.enabled` | `bool` | `false` | Enable RBS integration |
1794
+ | `rbs.collection` | `bool` | `false` | Auto-discover RBS collection from `rbs_collection.lock.yaml` |
1795
+ | `rbs.sig_dirs` | `string[]` | `["sig"]` | RBS signature directories |
1796
+ | `rbs.collection_dirs` | `string[]` | `[]` | RBS collection directories (set automatically) |
1797
+ | `rbs.collapse_generics` | `bool` | `false` | Strip generic arguments (`Array<String>` -> `Array`) |
1798
+ | `rbs.collapse_object_generics` | `bool` | `false` | Strip generics only when all are `Object` |
1799
+ | `rbs.warn_missing_collection` | `bool` | `true` | Warn when `rbs_collection.lock.yaml` found but collection not enabled |
1800
+ | `sorbet.enabled` | `bool` | `false` | Enable Sorbet integration |
1801
+ | `sorbet.rbi_dirs` | `string[]` | `["sorbet/rbi","rbi"]` | RBI file directories |
1802
+ | `sorbet.collapse_generics` | `bool` | `false` | Strip generic arguments from Sorbet signatures |
1803
+ | `plugins.require` | `string[]` | `[]` | Plugin paths for `require` |
1804
+
1805
+ </details>
1806
+
1404
1807
  ## CI integration
1405
1808
 
1406
1809
  Fail the build if files would need safe updates:
@@ -1417,11 +1820,33 @@ Apply safe fixes before the test stage:
1417
1820
  run: docscribe -a lib
1418
1821
  ```
1419
1822
 
1420
- Aggressively rebuild docs:
1823
+ Rebuild docs aggressively, preserve descriptions, suppress boilerplate, with verbose output:
1421
1824
 
1422
1825
  ```yaml
1423
1826
  - name: Rebuild inline docs
1424
- run: docscribe -A lib
1827
+ run: docscribe -AkB --verbose
1828
+ ```
1829
+
1830
+ Run static type checking with Steep (requires Ruby ≥ 3.2):
1831
+
1832
+ ```yaml
1833
+ - name: Steep (type check)
1834
+ if: ${{ matrix.ruby >= 3.2 }}
1835
+ run: bundle exec steep check
1836
+ ```
1837
+
1838
+ Fail the build if any generated docs still contain default placeholder text:
1839
+
1840
+ ```yaml
1841
+ - name: Check for placeholder docs
1842
+ run: docscribe check_for_comments lib/
1843
+ ```
1844
+
1845
+ For stricter validation, check for empty doc blocks:
1846
+
1847
+ ```yaml
1848
+ - name: Check for empty doc blocks
1849
+ run: "! grep -rn '^ # $' lib/"
1425
1850
  ```
1426
1851
 
1427
1852
  ## Comparison to YARD's parser
@@ -1450,13 +1875,42 @@ yard doc -o docs
1450
1875
  ## Roadmap
1451
1876
 
1452
1877
  - Method behavior inference from AST;
1453
- - YAML-based plugin configuration;
1878
+ - Deeper YARD integration — parse `.c` source comments and generate docs for C-extensions;
1879
+ - Custom parser plugins — support non-Ruby languages (Crystal, etc.) via the plugin system;
1454
1880
  - Effective config dump;
1455
- - JSON output;
1456
1881
  - Overload-aware signature selection;
1457
1882
  - Manual `@!attribute` merge policy;
1458
1883
  - Richer inference for common APIs;
1459
- - Editor integration.
1884
+ - Documentation coverage report — percentage of documented methods, params, returns;
1885
+ - Pre-commit hook auto-install (`docscribe init --pre-commit`);
1886
+ - Parallel processing for large codebases.
1887
+
1888
+ ## Editor Integration
1889
+
1890
+ Docscribe provides IDE plugins for a better development experience:
1891
+
1892
+ ### VS Code
1893
+
1894
+ [![VS Code](https://img.shields.io/badge/VS%20Code-plugin-blue?logo=visualstudiocode)](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
1895
+
1896
+ - **Repository:** [github.com/FlorexLabs/docscribe-vscode](https://github.com/FlorexLabs/docscribe-vscode)
1897
+ - **Marketplace:** [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=unurgunite.docscribe-vscode)
1898
+ - **Features:** inline diagnostics, quick-fix, auto-check on save, status bar
1899
+
1900
+ ### RubyMine
1901
+
1902
+ [![RubyMine](https://img.shields.io/badge/RubyMine-plugin-green?logo=jetbrains)](https://plugins.jetbrains.com/plugin/32349-docscribe)
1903
+
1904
+ - **Repository:** [github.com/FlorexLabs/docscribe-rubymine](https://github.com/FlorexLabs/docscribe-rubymine)
1905
+ - **Marketplace:** [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/32349-docscribe)
1906
+ - **Features:** ExternalAnnotator, AnAction, IntentionAction, Settings UI
1907
+
1908
+ > [!NOTE]
1909
+ > Both plugins require **docscribe >= 1.5.0**.
1910
+ >
1911
+ > For optimal performance, both plugins use the `docscribe-client` thin client
1912
+ > to communicate with the docscribe daemon (see [server mode](#docscribe-server--persistent-daemon-mode)).
1913
+ > Start the daemon with `docscribe server start` for near-instant diagnostics.
1460
1914
 
1461
1915
  ## Contributing
1462
1916
 
@@ -1471,6 +1925,14 @@ bundle exec rubocop
1471
1925
  - [Dev.to article](https://dev.to/unurgunite)
1472
1926
  - [GitHub Discussions](https://github.com/unurgunite/docscribe/discussions)
1473
1927
 
1928
+ ## Logo Attribution
1929
+
1930
+ The Docscribe logo uses the Ruby gem icon by [FlorexLabs](https://github.com/FlorexLabs).
1931
+
1932
+ <p>
1933
+ <img src="assets/icons/icon_128x128.png" alt="Docscribe logo">
1934
+ </p>
1935
+
1474
1936
  ## License
1475
1937
 
1476
1938
  MIT