inquirex-tty 0.5.0 → 0.9.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: 5d2a11293b1ac438c2ccd3b25cf1b18782e9b49e9b3e1d4d344e84216403cca9
4
- data.tar.gz: d4001771880c748982dbaa765a390f910d4d0e71772259981bb317af0c2696c7
3
+ metadata.gz: b0f91188b8315da187113b379d8a265d91b231c8e6a4a8309dc7cc41febafe9c
4
+ data.tar.gz: f8b109d024b9cd492e3f71f1e04f5cd2fcefa6673e5debc47c19bf9b8df87fdc
5
5
  SHA512:
6
- metadata.gz: '085bb14fd6cd73b341108763e61989b882ad4936f7b70ffd2a45089bd5bc930887ab7e7d85b8e2c15e682c77bdb8ba7384971a102199ae74038f2696c0af71dd'
7
- data.tar.gz: c6c92ee9cd25e7b23b1a0d6a839f7df0f92bc6a6a36c385a33ee2be4bd01004261c476bb4cf2a5689a2eca10afca1cfd23334546c4c12da7c4e89e42602ebc8f
6
+ metadata.gz: ad0e8b2efc4eac5ee7c45c1615f8d1e3910ab9a66c608c73651e8eb087e38761ae8e68014bc8c6ed119369e9c4635c97a7f3ec3cbff8319f9181750330d9c3d1
7
+ data.tar.gz: 922fe5ee727a6392ae73ce6d893a4ab99610dffef64c918ad8e4cc74dabae103e60644d587d94670be506d309823ab755a4e7c20d5ca170ece91edea86512fee
@@ -0,0 +1,5 @@
1
+ user=inquirex
2
+ project=inquirex-tty
3
+ exclude-labels=duplicate,question,invalid,wontfix
4
+ issues=false
5
+ add-sections={"security":{"prefix":"### Security","labels":["security"]}}
data/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
- ## [Unreleased]
1
+ # Changelog
2
2
 
3
- ## [0.1.0] - 2026-04-13
3
+ ## [Unreleased](https://github.com/inquirex/inquirex-tty/tree/HEAD)
4
4
 
5
- - Initial release
5
+ [Full Changelog](https://github.com/inquirex/inquirex-tty/compare/v0.6.0...HEAD)
6
+
7
+ **Merged pull requests:**
8
+
9
+ - Fix CI: broken Gemfile path override, RuboCop scanning vendor/ [\#9](https://github.com/inquirex/inquirex-tty/pull/9) ([kigster](https://github.com/kigster))
10
+
11
+ ## [v0.6.0](https://github.com/inquirex/inquirex-tty/tree/v0.6.0) (2026-07-20)
12
+
13
+ [Full Changelog](https://github.com/inquirex/inquirex-tty/compare/v0.5.0...v0.6.0)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - Document lib in YARD style to 100% coverage [\#8](https://github.com/inquirex/inquirex-tty/pull/8) ([kigster](https://github.com/kigster))
18
+ - Use $stderr.puts for CLI messages, not warn [\#7](https://github.com/inquirex/inquirex-tty/pull/7) ([kigster](https://github.com/kigster))
19
+
20
+ ## [v0.5.0](https://github.com/inquirex/inquirex-tty/tree/v0.5.0) (2026-07-16)
21
+
22
+ [Full Changelog](https://github.com/inquirex/inquirex-tty/compare/v0.4.0...v0.5.0)
23
+
24
+ ## [v0.4.0](https://github.com/inquirex/inquirex-tty/tree/v0.4.0) (2026-07-14)
25
+
26
+ [Full Changelog](https://github.com/inquirex/inquirex-tty/compare/971751bdfcbde3f1fd694cfb38d00bc3205b3e12...v0.4.0)
27
+
28
+ **Merged pull requests:**
29
+
30
+ - Raise inquirex-tty test coverage from 79% to 99% [\#5](https://github.com/inquirex/inquirex-tty/pull/5) ([kigster](https://github.com/kigster))
31
+ - Update bundled dependencies [\#4](https://github.com/inquirex/inquirex-tty/pull/4) ([kigster](https://github.com/kigster))
32
+ - Integrate inquirex-llm, drop inquirex-ui dep, add LLM-assisted tax example [\#3](https://github.com/inquirex/inquirex-tty/pull/3) ([kigster](https://github.com/kigster))
33
+ - Add inquirex export command and shared OutputPath helper [\#2](https://github.com/inquirex/inquirex-tty/pull/2) ([kigster](https://github.com/kigster))
34
+ - Simplify graph command: merge open-graph, add "both" format [\#1](https://github.com/inquirex/inquirex-tty/pull/1) ([kigster](https://github.com/kigster))
35
+
36
+
37
+
38
+ \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)*
data/README.md CHANGED
@@ -4,7 +4,11 @@
4
4
 
5
5
  Terminal adapter for the [Inquirex](https://github.com/inquirex/inquirex) questionnaire engine. Renders flow definitions as interactive ANSI terminal wizards using [tty-prompt](https://github.com/piotrmurach/tty-prompt), with ASCII-art headers, styled boxes, and automatic widget selection based on data types.
6
6
 
7
- Ships as a CLI (`inquirex`) with commands to run flows interactively, validate definitions, and export Mermaid diagrams.
7
+ Ships as a CLI (`inquirex-tty`) with commands to run flows interactively, validate definitions, and export Mermaid diagrams.
8
+
9
+ ## Inquirex CLI Shorthand Alias
10
+
11
+ This gem also installs a shorter command `inqt` which you can run in place of `inquirex-tty`.
8
12
 
9
13
  ## Installation
10
14
 
@@ -12,7 +16,7 @@ Ships as a CLI (`inquirex`) with commands to run flows interactively, validate d
12
16
  gem "inquirex-tty"
13
17
  ```
14
18
 
15
- The gem installs an `inquirex` executable.
19
+ The gem installs an `inquirex-tty` executable.
16
20
 
17
21
  ## Quick Start
18
22
 
@@ -47,30 +51,30 @@ end
47
51
  Run it:
48
52
 
49
53
  ```bash
50
- inquirex run my_flow.rb
54
+ inqt run my_flow.rb
51
55
  ```
52
56
 
53
57
  The CLI walks the user through each step, selecting the appropriate TTY widget for each data type, and prints collected answers as JSON when the flow completes.
54
58
 
55
59
  ## CLI Commands
56
60
 
57
- ### `inquirex run <flow_file>`
61
+ ### `inquirex-tty run <flow_file>`
58
62
 
59
63
  Execute a flow interactively. Each step is rendered with the appropriate tty-prompt widget based on the node's data type and widget hints.
60
64
 
61
65
  ```bash
62
66
  # run it and then dump answers as json to stdout
63
- inquirex run examples/08_tax_preparer.rb
67
+ inqt run examples/08_tax_preparer.rb
64
68
 
65
69
  # run it and save the anawers to a json file
66
- inquirex run examples/08_tax_preparer.rb \
70
+ inqt run examples/08_tax_preparer.rb \
67
71
  --output answers.json
68
72
  ```
69
73
 
70
74
  Options:
71
75
 
72
- | Flag | Description |
73
- |------|-------------|
76
+ | Flag | Description |
77
+ | ---------------- | ---------------------------------------------- |
74
78
  | `--output`, `-o` | Write JSON results to a file instead of stderr |
75
79
 
76
80
  On completion, outputs a JSON summary:
@@ -98,7 +102,7 @@ On completion, outputs a JSON summary:
98
102
  }
99
103
  ```
100
104
 
101
- ### `inquirex validate <flow_file>`
105
+ ### `inquirex-tty validate <flow_file>`
102
106
 
103
107
  Check that a flow definition is well-formed without running it. Validates:
104
108
 
@@ -107,49 +111,49 @@ Check that a flow definition is well-formed without running it. Validates:
107
111
  - All steps are reachable from the start step (detects orphans)
108
112
 
109
113
  ```bash
110
- inquirex validate examples/08_tax_preparer.rb
114
+ inqt validate examples/08_tax_preparer.rb
111
115
  ```
112
116
 
113
- ### `inquirex graph <flow_file>`
117
+ ### `inquirex-tty graph <flow_file>`
114
118
 
115
119
  Export the flow as a [Mermaid](https://mermaid.js.org/) diagram source, an image, or both.
116
120
 
117
121
  ```bash
118
- inquirex graph examples/08_tax_preparer.rb # Mermaid source to stdout
119
- inquirex graph examples/08_tax_preparer.rb --output flow.mmd # write source to a file
120
- inquirex graph examples/08_tax_preparer.rb --format image -o flow.svg # SVG via mmdc
121
- inquirex graph examples/08_tax_preparer.rb --format both --output ~/Desktop # source + image into a directory
122
- inquirex graph examples/08_tax_preparer.rb --format image --open # SVG + open in viewer
122
+ inqt graph examples/08_tax_preparer.rb # Mermaid source to stdout
123
+ inqt graph examples/08_tax_preparer.rb --output flow.mmd # write source to a file
124
+ inqt graph examples/08_tax_preparer.rb --format image -o flow.svg # SVG via mmdc
125
+ inqt graph examples/08_tax_preparer.rb --format both --output ~/Desktop # source + image into a directory
126
+ inqt graph examples/08_tax_preparer.rb --format image --open # SVG + open in viewer
123
127
  ```
124
128
 
125
129
  Options:
126
130
 
127
- | Flag | Description |
128
- |------|-------------|
129
- | `--output`, `-o` | Output file or directory (default: stdout) |
130
- | `--format`, `-f` | `source` (default), `image` (SVG via `mmdc`), or `both` |
131
- | `--open`, `-p` | Open the generated image in the system viewer (default: false) |
131
+ | Flag | Description |
132
+ | ---------------- | -------------------------------------------------------------- |
133
+ | `--output`, `-o` | Output file or directory (default: stdout) |
134
+ | `--format`, `-f` | `source` (default), `image` (SVG via `mmdc`), or `both` |
135
+ | `--open`, `-p` | Open the generated image in the system viewer (default: false) |
132
136
 
133
137
  Image generation requires [mermaid-cli](https://github.com/mermaid-js/mermaid-cli) (`npm install -g @mermaid-js/mermaid-cli`). The command attempts to install it automatically if `mmdc` is not on your `PATH`.
134
138
 
135
- ### `inquirex export <flow_file>`
139
+ ### `inquirex-tty export <flow_file>`
136
140
 
137
141
  Export the flow definition as JSON or YAML. Useful for serving flows to frontend adapters (the JS widget, Rails API, etc.) or for inspecting the wire format.
138
142
 
139
143
  ```bash
140
- inquirex export examples/08_tax_preparer.rb # pretty JSON to stdout
141
- inquirex export examples/08_tax_preparer.rb -f yml # YAML to stdout
142
- inquirex export examples/08_tax_preparer.rb -o . # write 08_tax_preparer.json to cwd
143
- inquirex export examples/08_tax_preparer.rb -f yml -o ~/flows # write 08_tax_preparer.yml to ~/flows
144
- inquirex export examples/08_tax_preparer.rb -o out.json # write to named file
145
- inquirex export examples/08_tax_preparer.rb -f yml -o out # appends .yml → out.yml
144
+ inqt export examples/08_tax_preparer.rb # pretty JSON to stdout
145
+ inqt export examples/08_tax_preparer.rb -f yml # YAML to stdout
146
+ inqt export examples/08_tax_preparer.rb -o . # write 08_tax_preparer.json to cwd
147
+ inqt export examples/08_tax_preparer.rb -f yml -o ~/flows # write 08_tax_preparer.yml to ~/flows
148
+ inqt export examples/08_tax_preparer.rb -o out.json # write to named file
149
+ inqt export examples/08_tax_preparer.rb -f yml -o out # appends .yml → out.yml
146
150
  ```
147
151
 
148
152
  Options:
149
153
 
150
- | Flag | Description |
151
- |------|-------------|
152
- | `--format`, `-f` | `json` (default), `yaml`, or `yml` |
154
+ | Flag | Description |
155
+ | ---------------- | ------------------------------------------ |
156
+ | `--format`, `-f` | `json` (default), `yaml`, or `yml` |
153
157
  | `--output`, `-o` | Output file or directory (default: stdout) |
154
158
 
155
159
  Output path rules:
@@ -158,12 +162,12 @@ Output path rules:
158
162
  - `--output <dir>` (existing directory) → write `<flow-basename>.<ext>` inside it
159
163
  - `--output <file>` → use that filename; if the extension is missing or mismatched, the appropriate one (`.json`/`.yml`) is substituted
160
164
 
161
- ### `inquirex version`
165
+ ### `inquirex-tty version`
162
166
 
163
167
  Print version information for the TTY adapter and its dependencies.
164
168
 
165
169
  ```bash
166
- inquirex version
170
+ inqt version
167
171
  ```
168
172
 
169
173
  ## Example Session
@@ -171,7 +175,7 @@ inquirex version
171
175
  Running the tax preparation intake example:
172
176
 
173
177
  ```
174
- $ inquirex run examples/08_tax_preparer.rb
178
+ $ inquirex-tty run examples/08_tax_preparer.rb
175
179
 
176
180
  _____ _ __ __ ____ ____ _____ ____ _ ____ _ _____ ___ ___ _ _ ___ _ _ _____ _ _ __ _____
177
181
  |_ _| / \ \ \/ / | _ \ | _ \ | ____| | _ \ / \ | _ \ / \ |_ _| |_ _| / _ \ | \ | | |_ _| | \ | | |_ _| / \ | |/ / | ____|
@@ -226,17 +230,17 @@ Press any key to continue...
226
230
 
227
231
  The renderer selects a tty-prompt method for each node based on the `:tty` widget hint from `WidgetRegistry`:
228
232
 
229
- | Widget Hint | tty-prompt Method | Used For |
230
- |-------------|-------------------|----------|
231
- | `text_input` | `prompt.ask` | `:string`, `:date`, `:email`, `:phone` |
232
- | `multiline` | `prompt.multiline` | `:text` |
233
- | `number_input` | `prompt.ask` (with `convert:`) | `:integer`, `:decimal`, `:currency` |
234
- | `yes_no` | `prompt.yes?` | `:boolean` / `confirm` |
235
- | `select` | `prompt.select` | `:enum` |
236
- | `multi_select` | `prompt.multi_select` | `:multi_enum` |
237
- | `enum_select` | `prompt.enum_select` | Numbered menu variant |
238
- | `mask` | `prompt.mask` | Password/hidden input |
239
- | `slider` | `prompt.slider` | Numeric range |
233
+ | Widget Hint | tty-prompt Method | Used For |
234
+ | -------------- | ------------------------------ | -------------------------------------- |
235
+ | `text_input` | `prompt.ask` | `:string`, `:date`, `:email`, `:phone` |
236
+ | `multiline` | `prompt.multiline` | `:text` |
237
+ | `number_input` | `prompt.ask` (with `convert:`) | `:integer`, `:decimal`, `:currency` |
238
+ | `yes_no` | `prompt.yes?` | `:boolean` / `confirm` |
239
+ | `select` | `prompt.select` | `:enum` |
240
+ | `multi_select` | `prompt.multi_select` | `:multi_enum` |
241
+ | `enum_select` | `prompt.enum_select` | Numbered menu variant |
242
+ | `mask` | `prompt.mask` | Password/hidden input |
243
+ | `slider` | `prompt.slider` | Numeric range |
240
244
 
241
245
  You can override the default by setting an explicit `:tty` widget hint in the DSL:
242
246
 
@@ -252,49 +256,37 @@ end
252
256
 
253
257
  ## Display Verbs
254
258
 
255
- | Verb | Rendering |
256
- |------|-----------|
257
- | `header` | Large ASCII-art text via TTY::Font (falls back to TTY::Box) |
258
- | `say` | Plain text with "Press any key to continue..." |
259
- | `btw` | Info-styled box (blue border) |
260
- | `warning` | Warning-styled box (yellow/red) |
259
+ | Verb | Rendering |
260
+ | --------- | ----------------------------------------------------------- |
261
+ | `header` | Large ASCII-art text via TTY::Font (falls back to TTY::Box) |
262
+ | `say` | Plain text with "Press any key to continue..." |
263
+ | `btw` | Info-styled box (blue border) |
264
+ | `warning` | Warning-styled box (yellow/red) |
261
265
 
262
266
  ## LLM Integration
263
267
 
264
- When a flow contains [inquirex-llm](../inquirex-llm) verbs (`clarify`,
265
- `describe`, `summarize`, `detour`), the `inquirex run` command automatically:
266
-
267
- 1. Loads `.env` files, walking up from the current directory and the flow
268
- file's directory. Shell-set values take precedence; empty-string keys are
269
- treated as unset.
270
- 1. Picks an adapter based on available credentials (see "Adapter selection"
271
- below).
272
- 1. Shows a `🧠 Thinking — asking <provider> to extract structured data…`
273
- banner while the LLM call is in flight.
274
- 1. For `clarify` steps, splats the extracted fields into the engine's
275
- top-level answers via `Engine#prefill!`, so any downstream step with
276
- `skip_if not_empty(:field)` is auto-skipped.
277
- 1. Prints a `✅` / `❓` extraction table showing which fields the LLM filled
278
- in vs. which ones will still be asked.
268
+ When a flow contains [inquirex-llm](../inquirex-llm) verbs (`clarify`, `describe`, `summarize`, `detour`), the `inquirex-tty run` command automatically:
269
+
270
+ 1. Loads `.env` files, walking up from the current directory and the flow file's directory. Shell-set values take precedence; empty-string keys are treated as unset.
271
+ 1. Picks an adapter based on available credentials (see "Adapter selection" below).
272
+ 1. Shows a `🧠 Thinking asking <provider> to extract structured data…` banner while the LLM call is in flight.
273
+ 1. For `clarify` steps, splats the extracted fields into the engine's top-level answers via `Engine#prefill!`, so any downstream step with `skip_if not_empty(:field)` is auto-skipped.
274
+ 1. Prints a `✅` / `❓` extraction table showing which fields the LLM filled in vs. which ones will still be asked.
279
275
 
280
276
  ### Adapter selection (first match wins)
281
277
 
282
- | Condition | Adapter used |
283
- |---------------------------------------------|--------------------------------------|
284
- | `INQUIREX_LLM_ADAPTER=null` | `Inquirex::LLM::NullAdapter` |
285
- | `INQUIREX_LLM_ADAPTER=anthropic` | `Inquirex::LLM::AnthropicAdapter` |
286
- | `INQUIREX_LLM_ADAPTER=openai` | `Inquirex::LLM::OpenAIAdapter` |
287
- | `ANTHROPIC_API_KEY` is set | `Inquirex::LLM::AnthropicAdapter` |
288
- | `OPENAI_API_KEY` is set | `Inquirex::LLM::OpenAIAdapter` |
289
- | nothing set | `Inquirex::LLM::NullAdapter` (demo) |
278
+ | Condition | Adapter used |
279
+ | -------------------------------- | ----------------------------------- |
280
+ | `INQUIREX_LLM_ADAPTER=null` | `Inquirex::LLM::NullAdapter` |
281
+ | `INQUIREX_LLM_ADAPTER=anthropic` | `Inquirex::LLM::AnthropicAdapter` |
282
+ | `INQUIREX_LLM_ADAPTER=openai` | `Inquirex::LLM::OpenAIAdapter` |
283
+ | `ANTHROPIC_API_KEY` is set | `Inquirex::LLM::AnthropicAdapter` |
284
+ | `OPENAI_API_KEY` is set | `Inquirex::LLM::OpenAIAdapter` |
285
+ | nothing set | `Inquirex::LLM::NullAdapter` (demo) |
290
286
 
291
287
  ### End-to-end example
292
288
 
293
- `examples/09_tax_preparer_llm.rb` is a complete LLM-assisted tax intake. The
294
- user types one free-text description of their tax situation; the LLM extracts
295
- `filing_status`, `dependents`, `income_types`, `state_filing`; the wizard only
296
- asks for whatever the LLM couldn't determine, then runs a final `summarize`
297
- for a complexity / fee-estimate write-up.
289
+ `examples/09_tax_preparer_llm.rb` is a complete LLM-assisted tax intake. The user types one free-text description of their tax situation; the LLM extracts `filing_status`, `dependents`, `income_types`, `state_filing`; the wizard only asks for whatever the LLM couldn't determine, then runs a final `summarize` for a complexity / fee-estimate write-up.
298
290
 
299
291
  ```ruby
300
292
  # examples/09_tax_preparer_llm.rb (excerpt)
@@ -363,7 +355,7 @@ Run it:
363
355
  # Put your key in any .env up the tree — OPENAI_API_KEY or ANTHROPIC_API_KEY
364
356
  echo 'OPENAI_API_KEY=sk-…' >> ../.env
365
357
 
366
- inquirex run examples/09_tax_preparer_llm.rb
358
+ inqt run examples/09_tax_preparer_llm.rb
367
359
  ```
368
360
 
369
361
  A typical session (input shortened):
@@ -384,52 +376,45 @@ A typical session (input shortened):
384
376
 
385
377
  ### Troubleshooting
386
378
 
387
- - **"the null adapter"** shown in the thinking banner → no API key was found.
388
- Check `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` in your shell or in a `.env`
389
- up the directory tree.
390
- - **`NoMethodError: undefined method 'prefill!'`** → you're running the
391
- `inquirex` binary against an older rubygems-installed copy of the core gem.
392
- `exe/inquirex` now bootstraps Bundler against the repo's Gemfile
393
- automatically, but a global `rake install`'d copy will still be
394
- stale — run `rake install` in `../inquirex` to update, or run from a
395
- checkout so the shim's Bundler bootstrap takes over.
379
+ - **"the null adapter"** shown in the thinking banner → no API key was found. Check `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` in your shell or in a `.env` up the directory tree.
380
+ - **`NoMethodError: undefined method 'prefill!'`** → you're running the `inquirex-tty` binary against an older rubygems-installed copy of the core gem. `exe/inquirex-tty` now bootstraps Bundler against the repo's Gemfile automatically, but a global `rake install`'d copy will still be stale — run `rake install` in `../inquirex` to update, or run from a checkout so the shim's Bundler bootstrap takes over.
396
381
 
397
382
  ## Examples
398
383
 
399
384
  The gem ships with 10 examples of increasing complexity:
400
385
 
401
- | Example | Description | Steps | Features |
402
- |---------|-------------|-------|----------|
403
- | `01_hello_world.rb` | Minimal flow | 3 | String and integer input |
404
- | `02_yes_or_no.rb` | Boolean branching | 3 | `confirm`, `equals` rule |
405
- | `03_food_preferences.rb` | Multi-select branching | 6 | `multi_enum`, `contains` rule |
406
- | `04_event_registration.rb` | Two-level branching | 9 | Nested conditionals |
407
- | `05_job_application.rb` | Composed rules | 13 | `all()`, `any()`, `greater_than` |
408
- | `06_health_assessment.rb` | Three-level branching | 18 | Complex composed rules |
409
- | `07_loan_application.rb` | Real-world loan intake | 20+ | Currency, 3-level branching |
410
- | `08_tax_preparer.rb` | Full tax preparation wizard | 18+ | All data types, deep branching |
411
- | `09_tax_preparer_llm.rb` | **LLM-assisted tax intake** | 9 | `clarify` + `summarize`, `skip_if not_empty`, auto-prefill |
412
- | `10_real_tax_preparer.rb` | Realistic tax preparer flow | 20+ | Full intake variant |
386
+ | Example | Description | Steps | Features |
387
+ | -------------------------- | --------------------------- | ----- | ---------------------------------------------------------- |
388
+ | `01_hello_world.rb` | Minimal flow | 3 | String and integer input |
389
+ | `02_yes_or_no.rb` | Boolean branching | 3 | `confirm`, `equals` rule |
390
+ | `03_food_preferences.rb` | Multi-select branching | 6 | `multi_enum`, `contains` rule |
391
+ | `04_event_registration.rb` | Two-level branching | 9 | Nested conditionals |
392
+ | `05_job_application.rb` | Composed rules | 13 | `all()`, `any()`, `greater_than` |
393
+ | `06_health_assessment.rb` | Three-level branching | 18 | Complex composed rules |
394
+ | `07_loan_application.rb` | Real-world loan intake | 20+ | Currency, 3-level branching |
395
+ | `08_tax_preparer.rb` | Full tax preparation wizard | 18+ | All data types, deep branching |
396
+ | `09_tax_preparer_llm.rb` | **LLM-assisted tax intake** | 9 | `clarify` + `summarize`, `skip_if not_empty`, auto-prefill |
397
+ | `10_real_tax_preparer.rb` | Realistic tax preparer flow | 20+ | Full intake variant |
413
398
 
414
399
  Run any example:
415
400
 
416
401
  ```bash
417
- inquirex run examples/01_hello_world.rb
418
- inquirex run examples/08_tax_preparer.rb
419
- inquirex run examples/09_tax_preparer_llm.rb
402
+ inqt run examples/01_hello_world.rb
403
+ inqt run examples/08_tax_preparer.rb
404
+ inqt run examples/09_tax_preparer_llm.rb
420
405
  ```
421
406
 
422
407
  Validate all examples:
423
408
 
424
409
  ```bash
425
- for f in examples/*.rb; do inquirex validate "$f"; done
410
+ for f in examples/*.rb; do inquirex-tty validate "$f"; done
426
411
  ```
427
412
 
428
413
  ## Architecture
429
414
 
430
415
  ```
431
416
  inquirex-tty/
432
- ├── exe/inquirex # CLI entry point (dry-cli)
417
+ ├── exe/inquirex-tty # CLI entry point (dry-cli)
433
418
  └── lib/inquirex/tty/
434
419
  ├── commands/
435
420
  │ ├── run.rb # Interactive flow execution
data/Rakefile CHANGED
@@ -19,8 +19,11 @@ task gem: [:build] do
19
19
  end
20
20
 
21
21
  task permissions: [:clean] do
22
- shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
23
- shell("find . -type d -exec chmod o+x,g+x {} \\;")
22
+ # One traversal replaces a six-level glob chain that printed "No such file
23
+ # or directory" for every level this project does not have, skipped dotfiles
24
+ # entirely, and silently stopped at depth six. .git is pruned — its objects
25
+ # have no business being group-readable.
26
+ shell("find . -path ./.git -prune -o -type d -exec chmod o+rx,g+rx {} + -o -type f -exec chmod o+r,g+r {} +")
24
27
  end
25
28
 
26
29
  task build: :permissions
@@ -15,7 +15,7 @@
15
15
  <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
16
16
  <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
17
17
  <text x="31.5" y="14">coverage</text>
18
- <text x="80" y="15" fill="#010101" fill-opacity=".3">99%</text>
19
- <text x="80" y="14">99%</text>
18
+ <text x="80" y="15" fill="#010101" fill-opacity=".3">98%</text>
19
+ <text x="80" y="14">98%</text>
20
20
  </g>
21
21
  </svg>
@@ -49,6 +49,37 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
49
49
  "Please do NOT enter any personal information: no names,\n" \
50
50
  "SSN, ITIN, addresses, or account numbers. We only need\n" \
51
51
  "enough to quote a fee."
52
+ transition to: :describe
53
+ end
54
+
55
+ # -----------------------------------------------------------------------
56
+ # Opening
57
+ # -----------------------------------------------------------------------
58
+
59
+ ask :describe do
60
+ type :text
61
+ question "Describe your last year's tax situation, your residency/citizenship status, your filing status, any dependents, various income types, which state(s) you are filing in,and whether you have your prior year returns available. If you also participate in businesses, please describe their tax status (S-Corp, LLC, etc), and if you have any foreign investments or property."
62
+ transition to: :summary
63
+ end
64
+
65
+ extract :summary do
66
+ from :describe
67
+ prompt "Extract the filer's tax intake details for scoping: filing status, " \
68
+ "dependents, income types, states filed in, residency status, prior " \
69
+ "return availability, and any business entities owned."
70
+
71
+ # Schema derived from the referenced questions: each symbol resolves to
72
+ # that ask step's type, and enum/multi_enum option values are folded in
73
+ # automatically — no duplication of the option lists here.
74
+ schema :filing_status,
75
+ :dependents,
76
+ :income_types,
77
+ :state_filing,
78
+ :residency_status,
79
+ :prior_return_available,
80
+ :business_entities
81
+
82
+ model :claude_sonnet
52
83
  transition to: :residency_status
53
84
  end
54
85
 
@@ -59,6 +90,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
59
90
  ask :residency_status do
60
91
  type :enum
61
92
  question "Which best describes your US tax residency for 2025?"
93
+ skip_if not_empty(:residency_status)
62
94
  options({
63
95
  "us_person" => "US citizen or permanent resident",
64
96
  "resident" => "Resident alien (substantial presence)",
@@ -74,6 +106,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
74
106
  ask :prior_return_available do
75
107
  type :enum
76
108
  question "Do you have a copy of your most recent tax return?"
109
+ skip_if not_empty(:prior_return_available)
77
110
  options({
78
111
  "yes_last_year" => "Yes, last year's return",
79
112
  "yes_older" => "Yes, but older than last year",
@@ -88,6 +121,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
88
121
  ask :filing_status do
89
122
  type :enum
90
123
  question "What is your filing status for 2025?"
124
+ skip_if not_empty(:filing_status)
91
125
  options({
92
126
  "single" => "Single",
93
127
  "married_filing_jointly" => "Married Filing Jointly",
@@ -98,12 +132,13 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
98
132
  widget target: :tty, type: :select
99
133
  widget target: :desktop, type: :radio_group, columns: 1
100
134
  widget target: :mobile, type: :dropdown
101
- transition to: :dependents_band
135
+ transition to: :dependents
102
136
  end
103
137
 
104
- ask :dependents_band do
138
+ ask :dependents do
105
139
  type :enum
106
140
  question "How many dependents will you claim?"
141
+ skip_if not_empty(:dependents)
107
142
  options %w[0 1 2 3 4+]
108
143
  widget target: :tty, type: :select
109
144
  widget target: :desktop, type: :radio_group, columns: 5
@@ -118,6 +153,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
118
153
  ask :income_types do
119
154
  type :multi_enum
120
155
  question "Select every type of income you had in 2025."
156
+ skip_if not_empty(:income_types)
121
157
  options({
122
158
  "W2" => "W-2 wages",
123
159
  "1099_nec" => "1099-NEC (contractor)",
@@ -166,6 +202,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
166
202
  ask :business_entities do
167
203
  type :multi_enum
168
204
  question "Which business entity types do you own or partner in?"
205
+ skip_if not_empty(:business_entities)
169
206
  options({
170
207
  "sole_prop" => "Sole Proprietor / Schedule C",
171
208
  "single_llc" => "Single-member LLC",
@@ -435,6 +472,7 @@ Inquirex.define id: "tax-preparer-2025", version: "2.0.0" do
435
472
  ask :state_filing do
436
473
  type :multi_enum
437
474
  question "Select every state (and DC) you need to file a return in."
475
+ skip_if not_empty(:state_filing)
438
476
  options US_STATES
439
477
  widget target: :tty, type: :multi_select
440
478
  widget target: :desktop, type: :checkbox_group, columns: 10, layout: :grid
@@ -0,0 +1,52 @@
1
+ Inquirex.define do
2
+ meta title: "Coolness Questionnaire",
3
+ subtitle: "Let's figure out how cool you are :)"
4
+
5
+ start :welcome
6
+
7
+ say :welcome do
8
+ text "Hello there! This is a demo of the Qualified.At DSL."
9
+ transition to: :loves_to_dance
10
+ end
11
+
12
+ ask :loves_to_dance do
13
+ type :boolean
14
+ question "Might we ask, do you like to dance to electronic music?"
15
+ transition to: :no_problem, if_rule: equals(:loves_to_dance, false)
16
+ transition to: :burning_man
17
+ end
18
+
19
+ say :no_problem do
20
+ text "Not everyone loves to dance. It's quite personal, we get it."
21
+ transition to: :burning_man
22
+ end
23
+
24
+ ask :burning_man do
25
+ type :enum
26
+ question "How many times have you been to Burning Man?"
27
+ options ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23", "24", "25", "26", "27", "28", "29", "30"]
28
+ transition to: :awesome, if_rule: greater_than(:burning_man, 20)
29
+ transition to: :good, if_rule: greater_than(:burning_man, 10)
30
+ transition to: :decent, if_rule: greater_than(:burning_man, 1)
31
+ transition to: :lame, if_rule: less_than(:burning_man, 1)
32
+ widget target: :tty, type: :select
33
+ widget target: :desktop, type: :radio_group, columns: 5
34
+ widget target: :mobile, type: :dropdown
35
+ end
36
+
37
+ say :awesome do
38
+ text "You are awesome! That must be a record!"
39
+ end
40
+
41
+ say :good do
42
+ text "Not bad at all! This is a very respectable number."
43
+ end
44
+
45
+ say :decent do
46
+ text "It's decent, but not amazing, pardon our judgment :)"
47
+ end
48
+
49
+ say :lame do
50
+ text "You've never been? Pardon the judgment, but you MUST go. It's beyond compare."
51
+ end
52
+ end
data/exe/inqt ADDED
@@ -0,0 +1 @@
1
+ inquirex-tty
data/justfile CHANGED
@@ -4,7 +4,26 @@ set shell := ["bash", "-c"]
4
4
 
5
5
  repo := 'git@github.com:inquirex/inquirex-tty.git'
6
6
  version := `grep VERSION lib/inquirex/tty/version.rb | awk '{print $3}' | tr -d '"' | tr -d '\n'`
7
- rbenv := 'eval "$(rbenv init bash)"; bundle exec '
7
+ # The `-` matters: `rbenv init bash` prints human instructions ("skipping
8
+ # ~/.bash_login: already configured"), which eval then tries to run and the
9
+ # recipe dies with "skipping: command not found". `rbenv init - bash` prints
10
+ # the shell code that is meant to be eval'd.
11
+ rbenv := 'eval "$(rbenv init - bash 2>/dev/null || true)"; bundle exec '
12
+
13
+ # 1Password secret reference for the RubyGems TOTP, and the full path to `op`
14
+ # because a recipe does not inherit an interactive shell's PATH.
15
+ # The account is NOT named here: it is a private 1Password address and this
16
+ # file is public. `op` reads it from OP_ACCOUNT, exported by the ecosystem
17
+ # root's .envrc, which lives outside every repo. Two accounts are
18
+ # registered and only one holds the open-source-repos vault, so without
19
+ # OP_ACCOUNT the read can resolve against the wrong one, return nothing,
20
+ # and publish silently falls back to prompting mid-release.
21
+ op := '/opt/homebrew/bin/op'
22
+ otp_ref := 'op://open-source-repos/ruby-gems/one-time password?attribute=otp'
23
+
24
+ gem_name := 'inquirex-tty'
25
+ gem_file := 'pkg/' + gem_name + '-' + version + '.gem'
26
+ gem_url := 'https://rubygems.org/gems/' + gem_name
8
27
 
9
28
  [no-exit-message]
10
29
  recipes:
@@ -29,15 +48,15 @@ format:
29
48
 
30
49
  # Run a flow interactively
31
50
  run flow_file:
32
- {{ rbenv }} exe/inquirex-tty run {{flow_file}}
51
+ {{ rbenv }} exe/inqt run {{flow_file}}
33
52
 
34
53
  # Validate a flow definition
35
54
  validate flow_file:
36
- {{ rbenv }} exe/inquirex-tty validate {{flow_file}}
55
+ {{ rbenv }} exe/inqt validate {{flow_file}}
37
56
 
38
57
  # Export a flow as a Mermaid diagram (stdout)
39
58
  graph flow_file:
40
- {{ rbenv }} exe/inquirex-tty graph {{flow_file}}
59
+ {{ rbenv }} exe/inqt graph {{flow_file}}
41
60
 
42
61
  # Validate all examples
43
62
  examples:
@@ -45,7 +64,7 @@ examples:
45
64
  set -e
46
65
  for f in examples/*.rb; do
47
66
  echo "=== $f ==="
48
- {{ rbenv }} exe/inquirex validate "$f"
67
+ {{ rbenv }} exe/inqt validate "$f"
49
68
  echo ""
50
69
  done
51
70
 
@@ -59,12 +78,45 @@ clean:
59
78
  /usr/bin/find . -name .DS_Store -delete -print || true
60
79
  rm -rf tmp/*
61
80
 
62
- # Create
63
- publish: build
64
- {{ rbenv }} rake release[remote]
65
-
66
- version:
67
- @echo "{{ version }}"
81
+ # `gem push` rather than `rake release`: release also guards the tree, tags and
82
+ # pushes git — which `just release` does deliberately and separately — and it
83
+ # gives no way to pass a 2FA code, so it always stopped to prompt.
84
+ #
85
+ # The code comes from 1Password unless one is passed in:
86
+ #
87
+ # just publish # read the code from 1Password
88
+ # just publish 123456 # use this code
89
+ #
90
+ # `just publish-all` in inquirex-tools passes one, because a TOTP is single-use:
91
+ # four gems reading the same 30-second window would have the second push
92
+ # rejected as a replay.
93
+ #
94
+ # Build the .gem and push it to RubyGems, non-interactively
95
+ publish otp="": build
96
+ #!/usr/bin/env bash
97
+ set -euo pipefail
98
+ eval "$(rbenv init - bash 2>/dev/null || true)"
99
+
100
+ mkdir -p pkg
101
+ gem build {{ gem_name }}.gemspec --output "{{ gem_file }}"
102
+
103
+ # `|| true` is load-bearing: under `set -e` a failed `op read` — not signed
104
+ # in to 1Password, item renamed, op not installed — would abort the recipe
105
+ # before the prompting fallback below could run.
106
+ otp="{{ otp }}"
107
+ [[ -n "${otp}" ]] || otp=$({{ op }} read "{{ otp_ref }}" 2>/dev/null || true)
108
+
109
+ if [[ -n "${otp}" ]]; then
110
+ gem push "{{ gem_file }}" --otp "${otp}"
111
+ else
112
+ echo "rubygems: no OTP available — gem push will prompt if 2FA is required."
113
+ gem push "{{ gem_file }}"
114
+ fi
115
+
116
+ # Only reachable when the push succeeded: `set -e` aborts the recipe on a
117
+ # non-zero `gem push`, so the page never opens for a release that failed.
118
+ echo "published {{ gem_name }} {{ version }} → {{ gem_url }}"
119
+ open "{{ gem_url }}" 2>/dev/null || xdg-open "{{ gem_url }}" 2>/dev/null || true
68
120
 
69
121
  # Tag v{{ version }}, publish the GH release, & refresh the Homebrew tap.
70
122
  release:
@@ -12,9 +12,11 @@ module Inquirex
12
12
  # inquirex export examples/08_tax_preparer.rb -f yml -o . # write 08_tax_preparer.yml to cwd
13
13
  # inquirex export examples/08_tax_preparer.rb -o out.json # write to out.json
14
14
  class Export < Dry::CLI::Command
15
+ # Multi-line help text shown by `inquirex export --help`.
15
16
  LONG_DESCRIPTION = "Export a flow definition as JSON or YAML.\n\n" \
16
17
  "Example:\n inquirex export examples/08_tax_preparer.rb -f yml -o ."
17
18
 
19
+ # One-line summary shown in the top-level command listing.
18
20
  SHORT_DESCRIPTION = "Export a flow definition as JSON or YAML ."
19
21
 
20
22
  if ARGV[0] == "export"
@@ -44,7 +46,7 @@ module Inquirex
44
46
  content = serialize(definition, format)
45
47
  write(content, flow_file, options[:output], extension_for(format))
46
48
  rescue Inquirex::TTY::Error => e
47
- warn "Error: #{e.message}"
49
+ $stderr.puts "Error: #{e.message}"
48
50
  exit 1
49
51
  end
50
52
 
@@ -73,7 +75,7 @@ module Inquirex
73
75
  end
74
76
 
75
77
  File.write(path, content)
76
- warn "Exported to #{path}"
78
+ $stderr.puts "Exported to #{path}"
77
79
  end
78
80
  end
79
81
  end
@@ -5,11 +5,13 @@ module Inquirex
5
5
  module Commands
6
6
  # Exports a flow definition as a Mermaid flowchart (stdout or file).
7
7
  class Graph < Dry::CLI::Command
8
+ # Multi-line help text shown by `inquirex graph --help`.
8
9
  LONG_DESCRIPTION = "Export a flow definition as a Mermaid diagram source, an image, or both.\n " \
9
10
  "Image generation requires mermaid-cli (npm install -g @mermaid-js/mermaid-cli)\n " \
10
11
  "which this gem will attempt to install for you if mmdc command is not available.\n\n" \
11
12
  "Example:\n inquirex graph qualify_dsl.rb --format both --output ~/Desktop --open"
12
13
 
14
+ # One-line summary shown in the top-level command listing.
13
15
  SHORT_DESCRIPTION = "Export a flow definition as a Mermaid diagram source, an image, or both."
14
16
 
15
17
  if ARGV[0] == "graph"
@@ -54,7 +56,7 @@ module Inquirex
54
56
  write_image(source, OutputPath.resolve_with_default(flow_file, output, ".png"), options[:open])
55
57
  end
56
58
  rescue Inquirex::TTY::Error => e
57
- warn "Error: #{e.message}"
59
+ $stderr.puts "Error: #{e.message}"
58
60
  exit 1
59
61
  end
60
62
 
@@ -66,7 +68,7 @@ module Inquirex
66
68
  return
67
69
  end
68
70
  File.write(output_path, source)
69
- warn "Diagram written to #{output_path}"
71
+ $stderr.puts "Diagram written to #{output_path}"
70
72
  end
71
73
 
72
74
  def write_image(source, output_path, open_file)
@@ -81,14 +83,14 @@ module Inquirex
81
83
  )
82
84
  end
83
85
 
84
- warn "Diagram written to #{output_path}"
86
+ $stderr.puts "Diagram written to #{output_path}"
85
87
  open_image_file(output_path) if open_file
86
88
  end
87
89
 
88
90
  def ensure_mermaid_cli_installed!
89
91
  return if command_available?("mmdc")
90
92
 
91
- warn "Installing @mermaid-js/mermaid-cli..."
93
+ $stderr.puts "Installing @mermaid-js/mermaid-cli..."
92
94
  installed = system("npm install -g @mermaid-js/mermaid-cli")
93
95
  return if installed && command_available?("mmdc")
94
96
 
@@ -22,7 +22,7 @@ module Inquirex
22
22
  File.write(options[:output], json)
23
23
  puts "\nResults saved to #{options[:output]}"
24
24
  else
25
- $stderr.puts json # rubocop:disable Style/StderrPuts
25
+ $stderr.puts json
26
26
  end
27
27
  rescue Inquirex::TTY::Error => e
28
28
  error(e.message)
@@ -43,6 +43,8 @@ module Inquirex
43
43
  renderer = Renderer.new
44
44
  adapter = build_llm_adapter
45
45
 
46
+ engine.after_completion { |eng| eng.completion_metadata = build_completion_metadata }
47
+
46
48
  show_banner(definition)
47
49
 
48
50
  until engine.finished?
@@ -55,7 +57,8 @@ module Inquirex
55
57
  renderer.render(step)
56
58
  engine.advance
57
59
  else
58
- engine.answer(renderer.render(step))
60
+ suggestion = engine.suggestion_for(engine.current_step_id)
61
+ engine.answer(renderer.render(step, suggestion:))
59
62
  end
60
63
  end
61
64
 
@@ -183,11 +186,56 @@ module Inquirex
183
186
  {
184
187
  flow_file: flow_file,
185
188
  path_taken: engine.history,
186
- answers: engine.answers,
189
+ answers: engine.answers_with_metadata,
187
190
  steps_completed: engine.history.length,
188
191
  completed_at: Time.now.iso8601
189
192
  }
190
193
  end
194
+
195
+ # Rich completion metadata for the answers. Only :engine and
196
+ # :engine_version are guaranteed — the environment probes are
197
+ # best-effort: a failing one (offline host, no login name, blocked
198
+ # egress) yields nil and its member is dropped.
199
+ #
200
+ # @return [Inquirex::CompletionMetadata]
201
+ def build_completion_metadata
202
+ optional = {
203
+ uname: best_effort { OpenStruct.new(Etc.uname) },
204
+ user: best_effort { Etc.getlogin },
205
+ local_ip: best_effort { local_ip },
206
+ public_ip: best_effort { public_ip },
207
+ terminal: ENV["LC_TERMINAL"] || ENV["TERM_PROGRAM"] || "Unknown"
208
+ }.compact
209
+ Inquirex::CompletionMetadata.new(
210
+ engine: "inquirex-tty",
211
+ engine_version: Inquirex::TTY::VERSION,
212
+ **optional
213
+ )
214
+ end
215
+
216
+ def best_effort
217
+ yield
218
+ rescue StandardError
219
+ nil
220
+ end
221
+
222
+ # The local address the OS would route out through — a UDP "connect"
223
+ # picks the outbound interface without sending a single packet.
224
+ def local_ip
225
+ UDPSocket.open do |socket|
226
+ socket.connect("8.8.8.8", 1)
227
+ socket.addr.last
228
+ end
229
+ end
230
+
231
+ # Short timeouts so a slow or airgapped network can never stall the
232
+ # CLI at completion time.
233
+ def public_ip
234
+ Net::HTTP.start("api.ipify.org", 443, use_ssl: true, open_timeout: 2, read_timeout: 2) do |http|
235
+ response = http.get("/")
236
+ response.is_a?(Net::HTTPSuccess) ? response.body.to_s.strip : nil
237
+ end
238
+ end
191
239
  end
192
240
  end
193
241
  end
@@ -23,10 +23,10 @@ module Inquirex
23
23
  exit 1
24
24
  end
25
25
  rescue Inquirex::TTY::Error => e
26
- warn "Error: #{e.message}"
26
+ $stderr.puts "Error: #{e.message}"
27
27
  exit 1
28
28
  rescue Inquirex::Errors::Error => e
29
- warn "Definition error: #{e.message}"
29
+ $stderr.puts "Definition error: #{e.message}"
30
30
  exit 1
31
31
  end
32
32
 
@@ -48,8 +48,8 @@ module Inquirex
48
48
  end
49
49
 
50
50
  def print_errors(errors)
51
- warn "Flow definition has #{errors.length} error(s):"
52
- errors.each { |e| warn " - #{e}" }
51
+ $stderr.puts "Flow definition has #{errors.length} error(s):"
52
+ errors.each { |e| $stderr.puts " - #{e}" }
53
53
  end
54
54
 
55
55
  def validate_definition(definition)
@@ -7,7 +7,9 @@ module Inquirex
7
7
  class Version < Dry::CLI::Command
8
8
  desc "Print version information"
9
9
 
10
- # @param **_ [Hash] ignored options
10
+ # Prints the version of this gem and of the core inquirex gem.
11
+ # Any CLI options are accepted and ignored.
12
+ #
11
13
  # @return [void]
12
14
  def call(**)
13
15
  puts "inquirex-tty #{Inquirex::TTY::VERSION}"
@@ -6,6 +6,12 @@ module Inquirex
6
6
  # binding. The file is expected to call +Inquirex.define+ and return an
7
7
  # +Inquirex::Definition+.
8
8
  class FlowLoader
9
+ # Loads and evaluates a flow definition file in one call.
10
+ #
11
+ # @example Load a flow definition from disk
12
+ # definition = Inquirex::TTY::FlowLoader.load("examples/08_tax_preparer.rb")
13
+ # definition.step_ids # => [:filing_status, :dependents, ...]
14
+ #
9
15
  # @param path [String] path to a .rb flow definition file
10
16
  # @return [Inquirex::Definition]
11
17
  # @raise [Inquirex::TTY::Error] if file is missing, not .rb, or has syntax errors
@@ -18,6 +18,11 @@ module Inquirex
18
18
 
19
19
  # Resolves an output path, returning nil when the caller should use stdout.
20
20
  #
21
+ # @example Resolve the --output option for a JSON export
22
+ # OutputPath.resolve("flows/intake.rb", nil, ".json") # => nil (stdout)
23
+ # OutputPath.resolve("flows/intake.rb", "out", ".json") # => "out.json"
24
+ # OutputPath.resolve("flows/intake.rb", "/tmp", ".json") # => "/tmp/intake.json"
25
+ #
21
26
  # @param flow_file [String] source .rb flow file (basename used for defaults)
22
27
  # @param output [String, nil] value of the --output option
23
28
  # @param extension [String] including leading dot, e.g. ".json"
@@ -63,15 +63,28 @@ module Inquirex
63
63
  end
64
64
 
65
65
  # Renders a node. Returns the collected answer, or +nil+ for display verbs.
66
+ #
67
+ # @example Collect an answer for the current step, pre-checking LLM suggestions
68
+ # renderer = Inquirex::TTY::Renderer.new
69
+ # step = engine.current_step
70
+ # answer = renderer.render(step, suggestion: engine.suggestion_for(engine.current_step_id))
71
+ # engine.answer(answer) unless step.display?
72
+ #
66
73
  # @param node [Inquirex::Node]
74
+ # @param suggestion [Array, nil] prefill suggestion for the step (option
75
+ # form values, e.g. from Engine#suggestion_for) — multi-select prompts
76
+ # render these choices pre-checked so the user confirms or extends
67
77
  # @return [Object, nil]
68
- def render(node)
78
+ def render(node, suggestion: nil)
79
+ @current_suggestion = suggestion
69
80
  if node.display?
70
81
  render_display_verb(node)
71
82
  nil
72
83
  else
73
84
  render_collecting(node)
74
85
  end
86
+ ensure
87
+ @current_suggestion = nil
75
88
  end
76
89
 
77
90
  private
@@ -195,9 +208,26 @@ module Inquirex
195
208
  prompt.select(node.question, select_options(node))
196
209
  end
197
210
 
198
- # Multiple-choice list (space to toggle, min 1 selection).
211
+ # Multiple-choice list (space to toggle, min 1 selection). Suggested
212
+ # choices (an LLM extraction the user should confirm) render pre-checked.
199
213
  def render_multi_select(node)
200
- prompt.multi_select(node.question, select_options(node), min: 1)
214
+ prompt.multi_select(node.question, select_options(node), min: 1, **multi_select_defaults(node))
215
+ end
216
+
217
+ # tty-prompt pre-checks multi_select choices via default: choice NAMES —
218
+ # the labels, not the return values. Suggestions arrive as option form
219
+ # values (that is the wire contract), so translate value → label here;
220
+ # a value with no label entry falls back to itself.
221
+ #
222
+ # @param node [Inquirex::Node]
223
+ # @return [Hash] {} or { default: Array<String> }
224
+ def multi_select_defaults(node)
225
+ suggested = Array(@current_suggestion)
226
+ return {} if suggested.empty?
227
+
228
+ labels = node.respond_to?(:option_labels) ? node.option_labels : nil
229
+ names = suggested.map { |value| labels&.fetch(value.to_s, nil) || value.to_s }
230
+ { default: names }
201
231
  end
202
232
 
203
233
  # Numbered choice menu.
@@ -12,6 +12,13 @@ module Inquirex
12
12
  @pastel ||= Pastel.new
13
13
  end
14
14
 
15
+ # Module hook that installs the UI helpers on the including class:
16
+ # +tty_box+, +tty_screen+, +pastel+, the TTY::Box wrappers (+frame+,
17
+ # +info+, +success+, +error+, +warning+), the +width+ delegator, and
18
+ # the +box+ / +next_step+ / +sep+ convenience methods.
19
+ #
20
+ # @param base [Class, Module] the class or module including UIHelper
21
+ # @return [void]
15
22
  def included(base)
16
23
  base.extend(Forwardable)
17
24
  base.define_method(:tty_box) { ::TTY::Box }
@@ -27,6 +34,12 @@ module Inquirex
27
34
 
28
35
  base.class_eval do
29
36
  # Draw a bordered box with optional title.
37
+ #
38
+ # @param text [String] text to display inside the box
39
+ # @param title [String, nil] optional title on the top border
40
+ # @param bg [Symbol] background color name
41
+ # @param fg [Symbol] foreground color name
42
+ # @return [void]
30
43
  def box(text, title: nil, bg: :green, fg: :white) # rubocop:disable Naming/MethodParameterName
31
44
  w = [width, 80].min
32
45
  args = {
@@ -41,12 +54,21 @@ module Inquirex
41
54
  end
42
55
 
43
56
  # Print step progress and separator.
57
+ #
58
+ # @param step_id [Symbol, String] id of the step about to run
59
+ # @param step_number [Integer] 1-based position in the flow
60
+ # @return [void]
44
61
  def next_step(step_id, step_number)
62
+ puts
45
63
  puts pastel.yellow("Step #{step_number}: #{step_id}")
46
64
  sep(:yellow, "━")
47
65
  end
48
66
 
49
67
  # Print a full-width separator in the given color.
68
+ #
69
+ # @param color [Symbol] Pastel color name
70
+ # @param char [String] character repeated across the line
71
+ # @return [void]
50
72
  def sep(color = :yellow, char = "━")
51
73
  puts pastel.send(color, char * 80)
52
74
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Inquirex
4
4
  module TTY
5
- VERSION = "0.5.0"
5
+ VERSION = "0.9.4"
6
6
  end
7
7
  end
data/lib/inquirex/tty.rb CHANGED
@@ -14,7 +14,15 @@ require "yaml"
14
14
  require "time"
15
15
  require "shellwords"
16
16
  require "tempfile"
17
+ require "etc"
18
+ require "socket"
19
+ require "net/http"
20
+ require "uri"
21
+ require "ostruct"
17
22
 
23
+ # Top-level namespace shared by every gem in the Inquirex family.
24
+ # The core gem defines the DSL, rules, and engine; this gem adds the
25
+ # terminal adapter under {Inquirex::TTY}.
18
26
  module Inquirex
19
27
  # Terminal adapter for Inquirex flows. Renders questions as interactive
20
28
  # TTY prompts via tty-prompt, mapping each data type to the appropriate
data/sig/inquirex/tty.rbs CHANGED
@@ -1,4 +1,5 @@
1
1
  module Inquirex
2
+ # RBS scaffold namespace for the inquirex-tty gem's type signatures.
2
3
  module Tty
3
4
  VERSION: String
4
5
  # See the writing guide of rbs: https://github.com/ruby/rbs#guides
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inquirex-tty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.9.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Gredeskoul
@@ -29,28 +29,42 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '0.5'
32
+ version: '0.6'
33
33
  type: :runtime
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0.5'
39
+ version: '0.6'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: inquirex-llm
42
42
  requirement: !ruby/object:Gem::Requirement
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.5'
46
+ version: '0.6'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0.5'
53
+ version: '0.6'
54
+ - !ruby/object:Gem::Dependency
55
+ name: ostruct
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.6'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.6'
54
68
  - !ruby/object:Gem::Dependency
55
69
  name: pastel
56
70
  requirement: !ruby/object:Gem::Requirement
@@ -127,11 +141,13 @@ description: 'Renders Inquirex flow definitions as interactive ANSI terminal wiz
127
141
  email:
128
142
  - kigster@gmail.com
129
143
  executables:
130
- - inquirex
144
+ - inqt
145
+ - inquirex-tty
131
146
  extensions: []
132
147
  extra_rdoc_files: []
133
148
  files:
134
149
  - ".envrc"
150
+ - ".github_changelog_generator"
135
151
  - ".relaxed_rubocop.yml"
136
152
  - ".secrets.baseline"
137
153
  - CHANGELOG.md
@@ -153,7 +169,9 @@ files:
153
169
  - examples/10_real_tax_preparer.rb
154
170
  - examples/11_llm_keywords.rb
155
171
  - examples/README.md
156
- - exe/inquirex
172
+ - examples/burning-man.rb
173
+ - exe/inqt
174
+ - exe/inquirex-tty
157
175
  - justfile
158
176
  - lefthook.yml
159
177
  - lib/inquirex/tty.rb
@@ -192,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
210
  - !ruby/object:Gem::Version
193
211
  version: '0'
194
212
  requirements: []
195
- rubygems_version: 4.0.16
213
+ rubygems_version: 4.0.17
196
214
  specification_version: 4
197
215
  summary: Terminal adapter for Inquirex flows — interactive TTY wizard via tty-prompt
198
216
  test_files: []
File without changes