rubycli 0.1.1 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9809e4d2f9c1ddb0b97f7c7cede2d665888726952e96ed5b14584e9230c617b
4
- data.tar.gz: 538af61eb924c66fdd0bf26df5bdcfaba66fbec76daa70ddbfcef943d2cfa654
3
+ metadata.gz: a71a438637363ea54e22377b645dc99a26a0c91692571bf57a68368403e32308
4
+ data.tar.gz: 367b354b2ffde5ee80e7ee3ee5b2858f15cbf8097cc8346fd812b780f41443e2
5
5
  SHA512:
6
- metadata.gz: efc21b5eddfb00f727ae0a056c7507ee36e27cc3da47c7bad0d68f310862f6cb3f7511328ec9bc2ac40f1493fa9b340892482a858623c57ec6ff8a9069ea9f73
7
- data.tar.gz: 1f21f11831d6b90b4981034827c33ac9a27a48385fae73cf22e806b7d5728835209f231977c1ca6f609cecd7bdfe12cae30d74adeaf409e0231e616262ced993
6
+ metadata.gz: 5eb055a5cbaa3daf0d311b67922a0c9bb7dbb8a75c2844524dc21f5faed232f560c8f1aaea6caf96737fea0b979625813b1ea5cc6c589ecee405b994b07ee16f
7
+ data.tar.gz: f64107c28083a96674970f8414090e47b77a749f8d3c9393ff9ca972a747ebe36a5da128d1c75a313ca570631436509392985477d1eefe271722eb4b046c69a8
data/CHANGELOG.md CHANGED
@@ -1,6 +1,47 @@
1
1
  # Changelog
2
2
 
3
- ## [0.1.0] - Unreleased
3
+ ## [0.1.4] - 2025-11-08
4
+
5
+ ### Changed
6
+ - Re-cut the release that was briefly published as 0.1.3 (and yanked) so RubyGems now hosts the complete set of changes listed below.
7
+
8
+ > _Note:_ 0.1.3 was yanked before general availability; consumers should upgrade directly to 0.1.4.
9
+
10
+ ## [0.1.3] - 2025-11-08
11
+
12
+ ### Added
13
+ - TracePoint-backed constant capture so Rubycli can detect CLI classes/modules even when they are defined indirectly; bundled example and tests illustrate the behavior.
14
+ - Strict/auto constant selection modes with the new `--auto-target` / `-a` flag so single callable constants are picked automatically when requested.
15
+ - `--eval-lax` / `-E` argument mode that evaluates Ruby inputs but gracefully falls back to raw strings on parse failures.
16
+
17
+ ### Changed
18
+ - Argument parsing internals were modularized so JSON/eval coercion now flows through a dedicated controller, simplifying future extensions.
19
+ - `rubycli` now returns explicit status codes for success and failure, improving scriptability.
20
+ - CLI constant selection errors provide clearer guidance, document the `--new` behavior, and consistently refer to the `--auto-target` flag.
21
+
22
+ ### Documentation
23
+ - Clarified the authoritative source for documentation comments, reorganized helper logic in the showcase example, and expanded guidance around constant modes.
24
+
25
+ ### Fixed
26
+ - CLI no longer dumps a Ruby backtrace when `Rubycli::Runner` reports user-facing errors; only the curated guidance is shown.
27
+
28
+ ## [0.1.2] - 2025-11-06
29
+
30
+ ### Added
31
+ - Example `examples/documentation_style_showcase.rb` covering all supported documentation notations.
32
+ - Parenthesised `(Type)` and `(type: Type)` annotations for positional and option documentation.
33
+
34
+ ### Documentation
35
+ - Linked the showcase example from the English and Japanese READMEs to highlight the new syntax.
36
+ - Clarified that optional arguments do not require brackets in comments and noted the current comma-delimited behaviour for repeated values.
37
+ - Documented the refined literal parsing guard rails so only structured values auto-coerce while plain strings stay untouched unless type hints request otherwise.
38
+
39
+ ### Fixed
40
+ - Restored uppercase positional placeholders in the generated help output so the default style stays consistent.
41
+ - Help renderer now preserves documented placeholder casing instead of wrapping everything in `<...>`.
42
+ - Default literal parsing now skips generic strings to avoid collapsing comma-separated inputs, while still supporting array coercion when documentation specifies list types.
43
+
44
+ ## [0.1.1] - 2025-11-01
4
45
 
5
46
  ### Added
6
47
  - Initial public release of Rubycli with the `rubycli` executable for running documented Ruby classes and modules.
data/README.ja.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Rubycli — Python Fire 風の Ruby 向け CLI
2
2
 
3
- Rubycli は Ruby のクラス/モジュールに書いたコメントから CLI を自動生成する小さなフレームワークです。Python Fire にインスパイアされていますが、互換や公式ポートを目指すものではありません。Ruby のコメント記法と型アノテーションに合わせて設計しています。
3
+ ![Rubycli ロゴ](assets/rubycli-logo.png)
4
+
5
+ Rubycli は Ruby のクラス/モジュールにある公開メソッドの定義と、そのメソッドに付けたドキュメントコメントから CLI を自動生成する小さなフレームワークです。Python Fire にインスパイアされていますが、互換や公式ポートを目指すものではありません。Ruby のコメント記法と型アノテーションに合わせて設計しており、コメントに書いた型ヒントや繰り返し指定が CLI の引数解釈もコントロールします。
4
6
 
5
7
  > English guide is available in [README.md](README.md).
6
8
 
@@ -28,7 +30,7 @@ Usage: hello_app.rb COMMAND [arguments]
28
30
 
29
31
  Available commands:
30
32
  Class methods:
31
- greet <name>
33
+ greet <NAME>
32
34
 
33
35
  Detailed command help: hello_app.rb COMMAND help
34
36
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -40,10 +42,10 @@ rubycli examples/hello_app.rb greet
40
42
 
41
43
  ```text
42
44
  Error: wrong number of arguments (given 0, expected 1)
43
- Usage: hello_app.rb greet <NAME>
45
+ Usage: hello_app.rb greet NAME
44
46
 
45
47
  Positional arguments:
46
- NAME
48
+ NAME required
47
49
  ```
48
50
 
49
51
  ```bash
@@ -102,7 +104,7 @@ Usage: hello_app_with_docs.rb COMMAND [arguments]
102
104
 
103
105
  Available commands:
104
106
  Class methods:
105
- greet <name> [--shout=<value>]
107
+ greet <NAME> [--shout]
106
108
 
107
109
  Detailed command help: hello_app_with_docs.rb COMMAND help
108
110
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -113,13 +115,13 @@ rubycli examples/hello_app_with_docs.rb greet --help
113
115
  ```
114
116
 
115
117
  ```text
116
- Usage: hello_app_with_docs.rb greet <NAME> [--shout]
118
+ Usage: hello_app_with_docs.rb greet NAME [--shout]
117
119
 
118
120
  Positional arguments:
119
- NAME [String] 挨拶対象
121
+ NAME [String] required 挨拶対象
120
122
 
121
123
  Options:
122
- --shout [Boolean] 大文字で出力 (default: false)
124
+ --shout [Boolean] optional 大文字で出力 (default: false)
123
125
  ```
124
126
 
125
127
  ```bash
@@ -145,18 +147,31 @@ end
145
147
 
146
148
  `ruby hello_app.rb ...` の形で呼び出したい場合だけ `require "rubycli"` を追加し、`Rubycli.run` に制御を渡します(後述のクイックスタート参照)。
147
149
 
150
+ ## 定数解決モード
151
+
152
+ Rubycli は「ファイル名を CamelCase にした定数」を公開対象だと想定しています。ファイル名とクラス/モジュール名が一致しない場合は、次のモードで挙動を切り替えられます。
153
+
154
+ | モード | 有効化方法 | 挙動 |
155
+ | --- | --- | --- |
156
+ | `strict`(デフォルト) | 何もしない / `RUBYCLI_AUTO_TARGET=strict` | CamelCase が一致しないとエラーになります。検出した定数一覧と再実行コマンド例を表示します。 |
157
+ | `auto` | `--auto-target`(短縮 `-a`) または `RUBYCLI_AUTO_TARGET=auto` | ファイル内で CLI として実行できる定数が 1 つだけなら自動選択します。複数あれば従来通りエラーで案内します。 |
158
+
159
+ 大規模なコードベースでも安全側を保ちながら、どうしても自動選択したいときだけ 1 フラグで切り替えられます。
160
+
161
+ > **インスタンスメソッド専用のクラスについて** – 公開メソッドがインスタンス側(`attr_reader` や `def greet`)にしか無い場合は、`--new` を付けて事前にインスタンス化しないと CLI から呼び出せません。クラスメソッドを 1 つ用意するか、`--new` を明示して実行してください。
162
+
148
163
  ## 開発方針
149
164
 
150
165
  - **便利さが最優先** – 既存の Ruby スクリプトを最小の手間で CLI 化できることを目的にしており、Python Fire の完全移植は目指していません。
151
166
  - **インスパイアであってポートではない** – アイデアの出自は Fire ですが、同等機能を揃える予定は基本的にありません。Fire 由来の未実装機能は仕様です。
152
- - **コードが一次情報、コメントは補助**メソッド定義こそが真実であり、コメントはヘルプを豊かにする付加情報です。コメントと実装のズレを観測したいときだけ `RUBYCLI_STRICT=ON` で厳格モードを有効化し、警告を受け取ります。
167
+ - **メソッド定義が土台、コメントが挙動を補強**公開メソッドのシグネチャが CLI に露出する範囲と必須/任意を決めますが、コメントに `TAG...` や `[Integer]` を書くと同じ引数でも配列化や型変換が行われます。コメントと実装のズレを観測したいときだけ `RUBYCLI_STRICT=ON` で厳格モードを有効化し、警告を受け取ります。
153
168
  - **軽量メンテナンス** – 実装の多くは AI 支援で作られており、深い Ruby メタプログラミングを伴う大規模拡張は想定外です。Fire 互換を求める PR は事前相談をお願いします。
154
169
 
155
170
  ## 特徴
156
171
 
157
172
  - コメントベースで CLI オプションやヘルプを自動生成
158
173
  - YARD 形式と `NAME [Type] 説明…` の簡潔記法を同時サポート
159
- - `--json-args` で渡された引数を自動的に JSON パース
174
+ - 引数はデフォルトで安全なリテラルとして解釈し、必要に応じて厳格 JSON モードや Ruby eval モードを切り替え可能
160
175
  - `--pre-script`(エイリアス: `--init`)で任意の Ruby コードを評価し、その結果オブジェクトを公開
161
176
  - `RUBYCLI_STRICT=ON` で有効化できる厳格モードにより、コメントとシグネチャの矛盾を警告として検知可能
162
177
 
@@ -170,28 +185,24 @@ end
170
185
  | 機能 | Python Fire | Rubycli |
171
186
  | ---- | ----------- | -------- |
172
187
  | 属性の辿り方 | オブジェクトを辿ってプロパティ/属性を自動公開 | 対象オブジェクトの公開メソッドをそのまま公開(暗黙の辿りは無し) |
173
- | クラス初期化 | `__init__` 引数を CLI で自動受け取りインスタンス化 | `--new` を指定した明示的な初期化のみ。引数はコメントで宣言 |
188
+ | クラス初期化 | `__init__` 引数を CLI で自動受け取りインスタンス化 | `--new` 指定時だけ引数なしで明示的に初期化(初期化引数の CLI 受け渡しは未対応なので、必要なら pre-script や自前ファクトリで注入) |
174
189
  | インタラクティブシェル | コマンド未指定時に Fire REPL を提供 | インタラクティブモード無し。コマンド実行専用 |
175
190
  | 情報源 | 反射で引数・プロパティを解析 | ライブなメソッド定義を基点にしつつコメントをヘルプへ反映 |
176
191
  | 辞書/配列 | dict/list を自動でサブコマンド化 | クラス/モジュールのメソッドに特化(辞書自動展開なし) |
177
192
 
178
193
  ## インストール
179
194
 
180
- まだ RubyGems で公開していません。リポジトリをクローンしてローカルパスを Bundler に指定するか、`.gemspec` 追加後に `gem build` で `.gem` を作成してインストールしてください。
195
+ Rubycli RubyGems からインストールできます。
181
196
 
182
197
  ```bash
183
- git clone https://github.com/inakaegg/rubycli.git
184
- cd rubycli
185
- # gem build rubycli.gemspec
186
- gem build rubycli.gemspec
187
- gem install rubycli-<version>.gem
198
+ gem install rubycli
188
199
  ```
189
200
 
190
201
  Bundler 例:
191
202
 
192
203
  ```ruby
193
204
  # Gemfile
194
- gem "rubycli", path: "path/to/rubycli"
205
+ gem "rubycli"
195
206
  ```
196
207
 
197
208
  ## クイックスタート(Rubycli をスクリプトに組み込む)
@@ -247,22 +258,46 @@ rubycli scripts/multi_runner.rb Admin::Runner list --active
247
258
 
248
259
  ## コメント記法
249
260
 
250
- | 用途 | YARD 互換 | 簡潔記法 |
251
- | ---- | --------- | -------- |
252
- | 位置引数 | `@param name [Type] 説明` | `NAME [Type] 説明`(NAME は大文字) |
253
- | キーワード引数 | 同上 | `--flag -f FLAG [Type] 説明` |
261
+ | 用途 | YARD 互換 | Rubycli 標準 |
262
+ | ---- | --------- | ----------- |
263
+ | 位置引数 | `@param name [Type] 説明` | `NAME [Type] 説明` |
264
+ | キーワード引数 | 同上 | `--flag -f VALUE [Type] 説明` |
254
265
  | 戻り値 | `@return [Type] 説明` | `=> [Type] 説明` |
255
266
 
256
- 型は `String` `Integer` のほか、`String[]` や `Array<String>`, `String | nil` なども利用できます。`[VALUE]` や `[VALUE...]` といったプレースホルダ表現で、真偽値や可変長引数を推論させられます。型名を省略した大文字プレースホルダ(例: `--quiet`)は自動的に Boolean フラグとして扱われます。
267
+ 短いオプション(`-f` など)は任意で、登場順も自由です。Rubycli 標準の書き方では次の例が同義になります。
268
+
269
+ - `--flag -f VALUE [Type] 説明`
270
+ - `--flag VALUE [Type] 説明`
271
+ - `-f --flag VALUE [Type] 説明`
272
+
273
+ README のサンプルは既定スタイルとして大文字プレースホルダ(`NAME`, `VALUE` など)を使用しています。次項以降の表記揺れは、必要に応じて選べる追加記法です。
274
+
275
+ ### 互換プレースホルダ表記
276
+
277
+ コメントやヘルプ出力では次の表記も同じ意味として解釈されます。
278
+
279
+ - 山括弧で値を明示: `--flag <value>`, `NAME [<value>]`
280
+ - ロングオプションの `=` 付き表記: `--flag=<value>`
281
+ - 繰り返し指定: `VALUE...`, `<value>...`
282
+
283
+ 実行時には `--flag VALUE`, `--flag <value>`, `--flag=<value>` のどれで入力しても同じ扱いです。プロジェクトで読みやすいスタイルを選択してください。`[VALUE]` や `[VALUE...]` のような表記を使うと、真偽値・任意値・リストなどの推論が働きます。値プレースホルダを省略したオプション(例: `--quiet`)は自動で Boolean フラグとして扱われます。
284
+
285
+ > 補足: コメント内で任意引数を角括弧で表す必要はありません。Ruby 側のメソッドシグネチャから必須/任意は自動判定され、ヘルプ出力では Rubycli が適切に角括弧を追加します。
286
+
287
+ 型ヒントは `[String]`, `(String)`, `(type: String)` のように角括弧・丸括弧・`type:` プレフィックスのいずれでも指定できます。複数型は `(String, nil)` や `(type: String, nil)` のように列挙してください。
288
+
289
+ `VALUE...` のような繰り返し指定(`TAG...` など)や、`[String[]]` / `Array<String>` といった配列型の注釈が付いたオプションは配列として扱われます。JSON/YAML 形式のリスト(例: `--tags '["build","test"]'`)を渡すか、カンマ区切り文字列(`--tags "build,test"`)を渡すことで配列に変換されます。スペース区切りの複数値入力(`--tags build test`)にはまだ対応しておらず、繰り返し注記のないオプションは従来どおりスカラーとして扱われます。
257
290
 
258
291
  代表的な推論例:
259
292
 
260
- - `ARG1` のように型ラベルを省略した大文字プレースホルダは既定で `String` として扱われます。
261
- - `--name ARG1` のようにオプションへ大文字プレースホルダだけを指定しても同じく `String` が推論されます。
293
+ - `ARG1` のように型ラベルを省略したプレースホルダは既定で `String` として扱われます。
294
+ - `--name ARG1` のようにオプションへプレースホルダだけを指定しても同じく `String` が推論されます。
262
295
  - `--verbose` のように値プレースホルダを省略したオプションは Boolean フラグとして扱われます。
263
296
 
264
297
  `@example` や `@raise`, `@see`, `@deprecated` などその他の YARD タグは、現状ヘルプ出力には反映されません。
265
298
 
299
+ > すべての記法をまとめて試したい場合は `rubycli examples/documentation_style_showcase.rb canonical --help` や `... angled --help` などを実行してみてください。
300
+
266
301
  従来の `@param` 記法も既定で利用できます。簡潔なプレースホルダ記法だけに限定したい場合は `RUBYCLI_ALLOW_PARAM_COMMENT=OFF` を設定してください(厳格モードでの検証は継続されます)。
267
302
 
268
303
  ### コメントが不足している場合のフォールバック
@@ -293,7 +328,7 @@ Usage: fallback_example.rb COMMAND [arguments]
293
328
 
294
329
  Available commands:
295
330
  Class methods:
296
- scale <amount> [<factor>] [--clamp=<value>] [--notify=<value>]
331
+ scale AMOUNT [<FACTOR>] [--clamp=<value>] [--notify]
297
332
 
298
333
  Detailed command help: fallback_example.rb COMMAND help
299
334
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -304,15 +339,15 @@ rubycli examples/fallback_example.rb scale --help
304
339
  ```
305
340
 
306
341
  ```text
307
- Usage: fallback_example.rb scale <AMOUNT> [<FACTOR>] [--clamp=<value>] [--notify]
342
+ Usage: fallback_example.rb scale AMOUNT [FACTOR] [--clamp=<CLAMP>] [--notify]
308
343
 
309
344
  Positional arguments:
310
- AMOUNT [Integer] 処理対象の数値
311
- [FACTOR] (default: 2)
345
+ AMOUNT [Integer] required 処理対象の数値
346
+ FACTOR optional (default: 2)
312
347
 
313
348
  Options:
314
- --clamp CLAMP (type: String) (default: nil)
315
- --notify (type: Boolean) (default: false)
349
+ --clamp=<CLAMP> [String] optional (default: nil)
350
+ --notify [Boolean] optional (default: false)
316
351
  ```
317
352
 
318
353
  `AMOUNT` だけがドキュメント化されていますが、`factor` や `clamp`, `notify` も自動的に補完され、既定値や型が推論されていることがわかります。コメントとシグネチャの矛盾を早期に検知したい場合は `RUBYCLI_STRICT=ON` で厳格モードを有効化してください。
@@ -332,27 +367,29 @@ Options:
332
367
  - `@param` の行に続く箇条書きや補足行は CLI の自動生成には使われません。補足情報を表示したい場合は、`--flag ...` 行の説明に含めるか、README など別のドキュメントで扱ってください。
333
368
  - `RUBYCLI_ALLOW_PARAM_COMMENT=OFF` にすると `@param`/`@return` などのタグは警告扱いになります。プロジェクト内で簡潔記法へ統一するときはこの環境変数で段階的に移行できます。
334
369
 
335
- ## JSON モード
370
+ ## 引数解析モード
336
371
 
337
- CLI 実行時に `--json-args` を付けると、後続の引数が JSON として解釈され Ruby オブジェクトに変換されます。
372
+ ### 既定のリテラル解析
338
373
 
339
- ```bash
340
- rubycli --json-args my_cli.rb MyCLI run '["--config", "{\"foo\":1}"]'
341
- ```
342
-
343
- プログラム側では `Rubycli.with_json_mode(true) { … }` で同じ効果を得られます。
374
+ Rubycli は `{` や `[`、クォート、YAML の先頭記号といった「構造化リテラルらしい」形の引数に対して `Psych.safe_load` を試み、成功すれば Ruby の配列/ハッシュ/真偽値に変換してからメソッドへ渡します。たとえば `--names='["Alice","Bob"]'` や `--config='{foo: 1}'` のような値は追加フラグ無しでネイティブな配列・ハッシュとして届きます。一方、プレーンな `1,2,3` のような文字列はこの段階ではそのまま維持されます(コメントで `String[]` や `TAG...` と宣言されている場合は後段で配列に整形されます)。扱えない形式は自動的に文字列へフォールバックするため、`"2024-01-01"` のような値もそのまま文字列で受け取れますし、構文が崩れていても CLI 全体が落ちることはありません。
344
375
 
345
- ## Eval モード
376
+ ### JSON モード
346
377
 
347
- `--eval-args` を使うと、後続の引数を Ruby コードとして評価した結果を CLI に渡せます。JSON では表現しづらいオブジェクトを扱いたいときに便利です。
378
+ CLI 実行時に `--json-args`(短縮形 `-j`)を付けると、後続の引数が厳格に JSON として解釈されます。
348
379
 
349
380
  ```bash
350
- rubycli --eval-args scripts/data_cli.rb DataCLI run '(1..10).to_a'
381
+ rubycli -j my_cli.rb MyCLI run '["--config", "{\"foo\":1}"]'
351
382
  ```
352
383
 
353
- 評価は `Object.new.instance_eval { binding }` に対して行われるため、信頼できる環境でのみ利用してください。プログラム側からは `Rubycli.with_eval_mode(true) { … }` で有効化できます。
384
+ YAML 固有の書き方は拒否され、無効な JSON であれば `JSON::ParserError` が発生するため、入力の妥当性を強く保証したいときに便利です。プログラム側では `Rubycli.with_json_mode(true) { … }` で有効化できます。
385
+
386
+ ### Eval モード
387
+
388
+ `--eval-args`(短縮形 `-e`)を使うと、後続の引数を Ruby コードとして評価した結果を CLI に渡せます。JSON や YAML では表現しづらいオブジェクトを扱いたいときに便利ですが、評価は `Object.new.instance_eval { binding }` 上で行われるため、信頼できる入力に限定してください。コード内では `Rubycli.with_eval_mode(true) { … }` で切り替えられます。
389
+
390
+ Ruby 評価を使いつつ、構文エラーが出たときは元の文字列にフォールバックさせたい場合は `--eval-lax`(短縮形 `-E`)を指定します。`--eval-args` と同じく eval モードを有効にしますが、Ruby として解釈できなかったトークン(例: 素の `https://example.com`)は警告を出した上でそのまま渡すため、`60*60*24*14` のような式と文字列を気軽に混在させられます。
354
391
 
355
- `--eval-args` `--json-args` は同時指定できません。両方付けた場合はエラーになります。
392
+ `--json-args`/`-j` `--eval-args`/`-e` および `--eval-lax`/`-E` と同時指定できません。どのモードも既定のリテラル解析を拡張する位置づけなので、用途に応じて厳格な JSON か Ruby eval(通常/lax)のいずれかを選択してください。
356
393
 
357
394
  ## Pre-script ブートストラップ
358
395
 
data/README.md CHANGED
@@ -1,9 +1,13 @@
1
1
  # Rubycli — Python Fire-inspired CLI for Ruby
2
2
 
3
- Rubycli turns existing Ruby classes and modules into CLIs by reading their documentation comments. It is inspired by [Python Fire](https://github.com/google/python-fire) but is not a drop-in port or an official project; the focus here is Ruby’s documentation conventions and type annotations.
3
+ ![Rubycli logo](assets/rubycli-logo.png)
4
+
5
+ Rubycli turns existing Ruby classes and modules into CLIs by inspecting their public method definitions and the doc comments attached to those methods. It is inspired by [Python Fire](https://github.com/google/python-fire) but is not a drop-in port or an official project; the focus here is Ruby’s documentation conventions and type annotations, and those annotations can actively change how a CLI argument is coerced (for example, `TAG... [String[]]` forces array parsing).
4
6
 
5
7
  > 🇯🇵 Japanese documentation is available in [README.ja.md](README.ja.md).
6
8
 
9
+ ![Rubycli demo showing generated commands and invocation](assets/rubycli-demo.gif)
10
+
7
11
  ### 1. Existing Ruby script (Rubycli unaware)
8
12
 
9
13
  ```ruby
@@ -28,7 +32,7 @@ Usage: hello_app.rb COMMAND [arguments]
28
32
 
29
33
  Available commands:
30
34
  Class methods:
31
- greet <name>
35
+ greet <NAME>
32
36
 
33
37
  Detailed command help: hello_app.rb COMMAND help
34
38
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -40,10 +44,10 @@ rubycli examples/hello_app.rb greet
40
44
 
41
45
  ```text
42
46
  Error: wrong number of arguments (given 0, expected 1)
43
- Usage: hello_app.rb greet <NAME>
47
+ Usage: hello_app.rb greet NAME
44
48
 
45
49
  Positional arguments:
46
- NAME
50
+ NAME required
47
51
  ```
48
52
 
49
53
  ```bash
@@ -102,7 +106,7 @@ Usage: hello_app_with_docs.rb COMMAND [arguments]
102
106
 
103
107
  Available commands:
104
108
  Class methods:
105
- greet <name> [--shout=<value>]
109
+ greet <NAME> [--shout]
106
110
 
107
111
  Detailed command help: hello_app_with_docs.rb COMMAND help
108
112
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -113,13 +117,13 @@ rubycli examples/hello_app_with_docs.rb greet --help
113
117
  ```
114
118
 
115
119
  ```text
116
- Usage: hello_app_with_docs.rb greet <NAME> [--shout]
120
+ Usage: hello_app_with_docs.rb greet NAME [--shout]
117
121
 
118
122
  Positional arguments:
119
- NAME [String] Name to greet
123
+ NAME [String] required Name to greet
120
124
 
121
125
  Options:
122
- --shout [Boolean] Print in uppercase (default: false)
126
+ --shout [Boolean] optional Print in uppercase (default: false)
123
127
  ```
124
128
 
125
129
  ```bash
@@ -143,20 +147,38 @@ end
143
147
 
144
148
  ### 3. (Optional) Embed the runner inside your script
145
149
 
146
- Prefer to launch via `ruby hello_app.rb ...`? Require the gem and delegate to `Rubycli.run` (see Quick start below).
150
+ Prefer to launch via `ruby ...` directly? Require the gem and delegate to `Rubycli.run` (see Quick start below for `examples/hello_app_with_require.rb`).
151
+
152
+ ```bash
153
+ ruby examples/hello_app_with_require.rb greet Hanako --shout
154
+ #=> HELLO, HANAKO!
155
+ ```
156
+
157
+ ## Constant resolution modes
158
+
159
+ Rubycli assumes that the file name (CamelCased) matches the class or module you want to expose. When that is not the case you can choose how eagerly Rubycli should pick a constant:
160
+
161
+ | Mode | How to enable | Behaviour |
162
+ | --- | --- | --- |
163
+ | `strict` (default) | do nothing / `RUBYCLI_AUTO_TARGET=strict` | Fails unless the CamelCase name matches. The error lists the detected constants and gives explicit rerun instructions. |
164
+ | `auto` | `--auto-target`, `-a`, or `RUBYCLI_AUTO_TARGET=auto` | If exactly one constant in that file defines CLI-callable methods, Rubycli auto-selects it; otherwise you still get the friendly error message. |
165
+
166
+ This keeps large projects safe by default but still provides a one-flag escape hatch when you prefer the fully automatic behaviour.
167
+
168
+ > **Instance-only classes** – If a class only defines public *instance* methods (for example, it exposes functionality via `attr_reader` or `def greet` on the instance), you must run Rubycli with `--new` so the class is instantiated before commands are resolved. Otherwise Rubycli cannot see any CLI-callable methods. Add at least one public class method when you do not want to rely on `--new`.
147
169
 
148
170
  ## Project Philosophy
149
171
 
150
172
  - **Convenience first** – The goal is to wrap existing Ruby scripts in a CLI with almost no manual plumbing. Fidelity with Python Fire is not a requirement.
151
173
  - **Inspired, not a port** – We borrow ideas from Python Fire, but we do not aim for feature parity. Missing Fire features are generally “by design.”
152
- - **Comments enrich, code decides** – Method signatures remain the source of truth; optional documentation comments add richer help output and can surface warnings when you opt into strict mode (`RUBYCLI_STRICT=ON`).
174
+ - **Method definitions first, comments augment behavior** – Public method signatures determine what gets exposed (and which arguments are required), while doc comments like `TAG...` or `[Integer]` can turn the very same CLI value into arrays, integers, booleans, etc. Enable strict mode (`RUBYCLI_STRICT=ON`) when you want warnings about mismatches.
153
175
  - **Lightweight maintenance** – Much of the implementation was generated with AI assistance; contributions that diverge into deep Ruby metaprogramming are out of scope. Please discuss expectations before opening parity PRs.
154
176
 
155
177
  ## Features
156
178
 
157
179
  - Comment-aware CLI generation with both YARD-style tags and concise placeholders
158
180
  - Automatic option signature inference (`NAME [Type] Description…`) without extra DSLs
159
- - Optional JSON coercion for arguments passed via `--json-args`
181
+ - Safe literal parsing out of the box (arrays / hashes / booleans) with opt-in strict JSON and Ruby eval modes
160
182
  - Optional pre-script hook (`--pre-script` / `--init`) to evaluate Ruby and expose the resulting object
161
183
  - Opt-in strict mode (`RUBYCLI_STRICT=ON`) that emits warnings whenever comments contradict method signatures
162
184
 
@@ -170,36 +192,32 @@ Prefer to launch via `ruby hello_app.rb ...`? Require the gem and delegate to `R
170
192
  | Capability | Python Fire | Rubycli |
171
193
  | ---------- | ----------- | -------- |
172
194
  | Attribute traversal | Recursively exposes attributes/properties on demand | Exposes public methods defined on the target; no implicit traversal |
173
- | Constructor handling | Automatically prompts for `__init__` args when instantiating classes | Requires explicit `--new` plus comment docs; no automatic prompting |
195
+ | Constructor handling | Automatically prompts for `__init__` args when instantiating classes | `--new` simply instantiates without passing CLI args (use pre-scripts or your own factories if you need injected dependencies) |
174
196
  | Interactive shell | Offers Fire-specific REPL when invoked without command | No interactive shell mode; strictly command execution |
175
197
  | Input discovery | Pure reflection, no doc comments required | Doc comments drive option names, placeholders, and validation |
176
198
  | Data structures | Dictionaries / lists become subcommands by default | Focused on class or module methods; no automatic dict/list expansion |
177
199
 
178
200
  ## Installation
179
201
 
180
- The library is not published on RubyGems yet. Clone the repository and point Bundler to the local path, or build a `.gem` once the `.gemspec` is added.
202
+ Rubycli is published on RubyGems.
181
203
 
182
204
  ```bash
183
- git clone https://github.com/inakaegg/rubycli.git
184
- cd rubycli
185
- # gem build rubycli.gemspec
186
- gem build rubycli.gemspec
187
- gem install rubycli-<version>.gem
205
+ gem install rubycli
188
206
  ```
189
207
 
190
208
  Bundler example:
191
209
 
192
210
  ```ruby
193
211
  # Gemfile
194
- gem "rubycli", path: "path/to/rubycli"
212
+ gem "rubycli"
195
213
  ```
196
214
 
197
215
  ## Quick start (embed Rubycli in the script)
198
216
 
199
- Step 3 adds `require "rubycli"` so the script can invoke the CLI directly:
217
+ Step 3 adds `require "rubycli"` so the script can invoke the CLI directly (see `examples/hello_app_with_require.rb`):
200
218
 
201
219
  ```ruby
202
- # hello_app.rb
220
+ # hello_app_with_require.rb
203
221
  require "rubycli"
204
222
 
205
223
  module HelloApp
@@ -222,10 +240,10 @@ Rubycli.run(HelloApp)
222
240
  Run it:
223
241
 
224
242
  ```bash
225
- ruby hello_app.rb greet Taro
243
+ ruby examples/hello_app_with_require.rb greet Taro
226
244
  #=> Hello, Taro!
227
245
 
228
- ruby hello_app.rb greet Taro --shout
246
+ ruby examples/hello_app_with_require.rb greet Taro --shout
229
247
  #=> HELLO, TARO!
230
248
  ```
231
249
 
@@ -249,22 +267,46 @@ This is useful when a file defines multiple candidates or when you want a nested
249
267
 
250
268
  Rubycli parses a hybrid format – you can stick to familiar YARD tags or use short forms.
251
269
 
252
- | Purpose | YARD-compatible | Concise form |
253
- | ------- | --------------- | ------------ |
254
- | Positional argument | `@param name [Type] Description` | `NAME [Type] Description` (`NAME` must be uppercase) |
255
- | Keyword option | Same as above | `--flag -f FLAG [Type] Description` |
270
+ | Purpose | YARD-compatible | Rubycli style |
271
+ | ------- | --------------- | ------------- |
272
+ | Positional argument | `@param name [Type] Description` | `NAME [Type] Description` |
273
+ | Keyword option | Same as above | `--flag -f VALUE [Type] Description` |
256
274
  | Return value | `@return [Type] Description` | `=> [Type] Description` |
257
275
 
258
- Types accept `String`, `Integer`, `String[]`, `Array<String>`, union `String | nil`, etc. Optional placeholders like `[VALUE]` or `[VALUE...]` let Rubycli infer boolean flags, optional values, and list coercion. When you omit the type on an uppercase placeholder (for example `--quiet`), Rubycli infers a Boolean flag automatically.
276
+ Short options are optional and order-independent, so the following examples are equivalent in Rubycli’s default style:
277
+
278
+ - `--flag -f VALUE [Type] Description`
279
+ - `--flag VALUE [Type] Description`
280
+ - `-f --flag VALUE [Type] Description`
281
+
282
+ Our examples keep the classic uppercase placeholders (`NAME`, `VALUE`) as the canonical style; the variations below are optional sugar.
283
+
284
+ ### Alternate placeholder notations
285
+
286
+ Rubycli also understands these syntaxes when parsing comments and rendering help:
287
+
288
+ - Angle brackets for user input: `--flag <value>` or `NAME [<value>]`
289
+ - Inline equals for long options: `--flag=<value>`
290
+ - Trailing ellipsis for repeated values: `VALUE...` or `<value>...`
291
+
292
+ The CLI treats `--flag VALUE`, `--flag <value>`, and `--flag=<value>` identically at runtime—document with whichever variant your team prefers. Optional placeholders like `[VALUE]` or `[VALUE...]` let Rubycli infer boolean flags, optional values, and list coercion. When you omit the placeholder entirely (for example `--quiet`), Rubycli infers a Boolean flag automatically.
293
+
294
+ > Tip: You do not need to wrap optional arguments in brackets inside the comment. Rubycli already knows which parameters are optional from the Ruby signature and will introduce the brackets in generated help.
295
+
296
+ You can annotate types using `[String]`, `(String)`, or `(type: String)`—they all convey the same hint, and you can list multiple types such as `(String, nil)` or `(type: String, nil)`.
297
+
298
+ Repeated values (`VALUE...`) now materialize as arrays automatically whenever the option is documented with an ellipsis (for example `TAG...`) or an explicit array type hint (`[String[]]`, `Array<String>`). Supply either JSON/YAML list syntax (`--tags "[\"build\",\"test\"]"`) or a comma-delimited string (`--tags "build,test"`); Rubycli will coerce both forms to arrays. Space-separated multi-value flags (`--tags build test`) are still not supported, and options without a repeated/array hint continue to be parsed as scalars.
259
299
 
260
300
  Common inference rules:
261
301
 
262
- - Writing a bare uppercase placeholder such as `ARG1` (without `[String]`) makes Rubycli treat it as a `String`.
302
+ - Writing a placeholder such as `ARG1` (without `[String]`) makes Rubycli treat it as a `String`.
263
303
  - Using that placeholder in an option line (`--name ARG1`) also infers a `String`.
264
304
  - Omitting the placeholder entirely (`--verbose`) produces a Boolean flag.
265
305
 
266
306
  Other YARD tags such as `@example`, `@raise`, `@see`, and `@deprecated` are currently ignored by the CLI renderer.
267
307
 
308
+ > Want to explore every notation in a single script? Try `rubycli examples/documentation_style_showcase.rb canonical --help`, `... angled --help`, or the other showcase commands.
309
+
268
310
  YARD-style `@param` annotations continue to work out of the box. If you want to enforce the concise placeholder syntax exclusively, set `RUBYCLI_ALLOW_PARAM_COMMENT=OFF` (strict mode still applies either way).
269
311
 
270
312
  ### When docs are missing or incomplete
@@ -295,7 +337,7 @@ Usage: fallback_example.rb COMMAND [arguments]
295
337
 
296
338
  Available commands:
297
339
  Class methods:
298
- scale <amount> [<factor>] [--clamp=<value>] [--notify=<value>]
340
+ scale AMOUNT [<FACTOR>] [--clamp=<value>] [--notify]
299
341
 
300
342
  Detailed command help: fallback_example.rb COMMAND help
301
343
  Enable debug logging: --debug or RUBYCLI_DEBUG=true
@@ -306,15 +348,15 @@ rubycli examples/fallback_example.rb scale --help
306
348
  ```
307
349
 
308
350
  ```text
309
- Usage: fallback_example.rb scale <AMOUNT> [<FACTOR>] [--clamp=<value>] [--notify]
351
+ Usage: fallback_example.rb scale AMOUNT [FACTOR] [--clamp=<CLAMP>] [--notify]
310
352
 
311
353
  Positional arguments:
312
- AMOUNT [Integer] Base amount to process
313
- [FACTOR] (default: 2)
354
+ AMOUNT [Integer] required Base amount to process
355
+ FACTOR optional (default: 2)
314
356
 
315
357
  Options:
316
- --clamp CLAMP (type: String) (default: nil)
317
- --notify (type: Boolean) (default: false)
358
+ --clamp=<CLAMP> [String] optional (default: nil)
359
+ --notify [Boolean] optional (default: false)
318
360
  ```
319
361
 
320
362
  Here only `AMOUNT` is documented, yet `factor`, `clamp`, and `notify` are still presented with sensible defaults and inferred types. Enable strict mode (`RUBYCLI_STRICT=ON`) if you want mismatches between comments and signatures to surface as warnings during development.
@@ -327,27 +369,35 @@ Here only `AMOUNT` is documented, yet `factor`, `clamp`, and `notify` are still
327
369
 
328
370
  In short, comments never add live parameters by themselves; they enrich or describe what your method already supports.
329
371
 
330
- ## JSON mode
372
+ ## Argument parsing modes
373
+
374
+ ### Default literal parsing
331
375
 
332
- Supply `--json-args` when invoking the runner and Rubycli will parse subsequent arguments as JSON before passing them to your method:
376
+ Rubycli tries to interpret arguments that look like structured literals (values starting with `{`, `[`, quotes, or YAML front matter) using `Psych.safe_load` before handing them to your code. That means values such as `--names='["Alice","Bob"]'` or `--config='{foo: 1}'` arrive as native arrays / hashes without any extra flags. Plain strings like `1,2,3` stay untouched at this stage (if the documentation declares `String[]` or `TAG...`, a later pass still normalises them into arrays), and unsupported constructs fall back to the original text, so `"2024-01-01"` remains a string and malformed payloads still reach your method instead of killing the run.
377
+
378
+ ### JSON mode
379
+
380
+ Supply `--json-args` (or the shorthand `-j`) when invoking the runner and Rubycli will parse subsequent arguments strictly as JSON before passing them to your method:
333
381
 
334
382
  ```bash
335
- rubycli --json-args my_cli.rb MyCLI run '["--config", "{\"foo\":1}"]'
383
+ rubycli -j my_cli.rb MyCLI run '["--config", "{\"foo\":1}"]'
336
384
  ```
337
385
 
338
- Programmatically you can call `Rubycli.with_json_mode(true) { … }`.
386
+ This mode rejects YAML-only syntax and raises `JSON::ParserError` when the payload is invalid, which is handy for callers who want explicit failures instead of silent fallbacks. Programmatically you can call `Rubycli.with_json_mode(true) { … }`.
339
387
 
340
388
  ## Eval mode
341
389
 
342
- Use `--eval-args` to evaluate Ruby expressions before they are forwarded to your CLI. This is handy when you want to pass rich objects that are awkward to express as JSON:
390
+ Use `--eval-args` (or the shorthand `-e`) to evaluate Ruby expressions before they are forwarded to your CLI. This is handy when you want to pass rich objects that are awkward to express as JSON:
343
391
 
344
392
  ```bash
345
- rubycli --eval-args scripts/data_cli.rb DataCLI run '(1..10).to_a'
393
+ rubycli -e scripts/data_cli.rb DataCLI run '(1..10).to_a'
346
394
  ```
347
395
 
348
396
  Under the hood Rubycli evaluates each argument inside an isolated binding (`Object.new.instance_eval { binding }`). Treat this as unsafe input: do not enable it for untrusted callers. The mode can also be toggled programmatically via `Rubycli.with_eval_mode(true) { … }`.
349
397
 
350
- `--eval-args` and `--json-args` are mutually exclusive; Rubycli will raise an error if both are present.
398
+ Need Ruby evaluation plus a safety net? Pass `--eval-lax` (or `-E`). It flips on eval mode just like `--eval-args`, but if Ruby fails to parse a token (for example, a bare `https://example.com`), Rubycli emits a warning and forwards the original string unchanged. This lets you mix inline math (`60*60*24*14`) with literal values without constantly juggling quotes.
399
+
400
+ `--json-args`/`-j` cannot be combined with either `--eval-args`/`-e` or `--eval-lax`/`-E`; Rubycli will raise an error if both are present. Both modes augment the default literal parsing, so you can pick either strict JSON or one of the Ruby eval variants when the defaults are not enough.
351
401
 
352
402
  ## Pre-script bootstrap
353
403