inquirex-tty 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md ADDED
@@ -0,0 +1,530 @@
1
+ [![Ruby](https://github.com/inquirex/inquirex-tty/actions/workflows/main.yml/badge.svg)](https://github.com/inquirex/inquirex-tty/actions/workflows/main.yml)  ![Coverage](docs/badges/coverage_badge.svg)
2
+
3
+ # inquirex-tty
4
+
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
+
7
+ Ships as a CLI (`inquirex`) with commands to run flows interactively, validate definitions, and export Mermaid diagrams.
8
+
9
+ ## Status
10
+
11
+ - Version: `0.2.1`
12
+ - Ruby: `>= 4.0.0`
13
+ - Depends on: `inquirex`, `tty-prompt`, `tty-box`, `tty-font`, `pastel`, `dry-cli`
14
+
15
+ ## Installation
16
+
17
+ ```ruby
18
+ gem "inquirex-tty"
19
+ ```
20
+
21
+ The gem installs an `inquirex` executable.
22
+
23
+ ## Quick Start
24
+
25
+ Write a flow definition in Ruby:
26
+
27
+ ```ruby
28
+ # my_flow.rb
29
+ require "inquirex"
30
+
31
+ Inquirex.define id: "hello", version: "1.0.0" do
32
+ meta title: "Hello World"
33
+ start :name
34
+
35
+ ask :name do
36
+ type :string
37
+ question "What is your name?"
38
+ transition to: :age
39
+ end
40
+
41
+ ask :age do
42
+ type :integer
43
+ question "How old are you?"
44
+ transition to: :farewell
45
+ end
46
+
47
+ say :farewell do
48
+ text "Thanks for chatting!"
49
+ end
50
+ end
51
+ ```
52
+
53
+ Run it:
54
+
55
+ ```bash
56
+ inquirex run my_flow.rb
57
+ ```
58
+
59
+ 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.
60
+
61
+ ## CLI Commands
62
+
63
+ ### `inquirex run <flow_file>`
64
+
65
+ Execute a flow interactively. Each step is rendered with the appropriate tty-prompt widget based on the node's data type and widget hints.
66
+
67
+ ```bash
68
+ # run it and then dump answers as json to stdout
69
+ inquirex run examples/08_tax_preparer.rb
70
+
71
+ # run it and save the anawers to a json file
72
+ inquirex run examples/08_tax_preparer.rb \
73
+ --output answers.json
74
+ ```
75
+
76
+ Options:
77
+
78
+ | Flag | Description |
79
+ |------|-------------|
80
+ | `--output`, `-o` | Write JSON results to a file instead of stderr |
81
+
82
+ On completion, outputs a JSON summary:
83
+
84
+ ```json
85
+ {
86
+ "flow_file": "examples/08_tax_preparer.rb",
87
+ "path_taken": [
88
+ "intro", "filing_status", "dependents", "income_types",
89
+ "state_filing", "foreign_accounts", "deduction_types",
90
+ "client_name", "client_email", "thanks"
91
+ ],
92
+ "answers": {
93
+ "filing_status": "single",
94
+ "dependents": 2,
95
+ "income_types": ["W2", "1099"],
96
+ "state_filing": ["California"],
97
+ "foreign_accounts": "no",
98
+ "deduction_types": ["Medical"],
99
+ "client_name": "Konstantin Gredeskoul",
100
+ "client_email": "kigster@gmail.com"
101
+ },
102
+ "steps_completed": 10,
103
+ "completed_at": "2026-04-13T23:51:33-07:00"
104
+ }
105
+ ```
106
+
107
+ ### `inquirex validate <flow_file>`
108
+
109
+ Check that a flow definition is well-formed without running it. Validates:
110
+
111
+ - Start step exists in the step list
112
+ - All transition targets reference known steps
113
+ - All steps are reachable from the start step (detects orphans)
114
+
115
+ ```bash
116
+ inquirex validate examples/08_tax_preparer.rb
117
+ ```
118
+
119
+ ### `inquirex graph <flow_file>`
120
+
121
+ Export the flow as a [Mermaid](https://mermaid.js.org/) diagram source, an image, or both.
122
+
123
+ ```bash
124
+ inquirex graph examples/08_tax_preparer.rb # Mermaid source to stdout
125
+ inquirex graph examples/08_tax_preparer.rb --output flow.mmd # write source to a file
126
+ inquirex graph examples/08_tax_preparer.rb --format image -o flow.svg # SVG via mmdc
127
+ inquirex graph examples/08_tax_preparer.rb --format both --output ~/Desktop # source + image into a directory
128
+ inquirex graph examples/08_tax_preparer.rb --format image --open # SVG + open in viewer
129
+ ```
130
+
131
+ Options:
132
+
133
+ | Flag | Description |
134
+ |------|-------------|
135
+ | `--output`, `-o` | Output file or directory (default: stdout) |
136
+ | `--format`, `-f` | `source` (default), `image` (SVG via `mmdc`), or `both` |
137
+ | `--open`, `-p` | Open the generated image in the system viewer (default: false) |
138
+
139
+ 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`.
140
+
141
+ ### `inquirex export <flow_file>`
142
+
143
+ 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.
144
+
145
+ ```bash
146
+ inquirex export examples/08_tax_preparer.rb # pretty JSON to stdout
147
+ inquirex export examples/08_tax_preparer.rb -f yml # YAML to stdout
148
+ inquirex export examples/08_tax_preparer.rb -o . # write 08_tax_preparer.json to cwd
149
+ inquirex export examples/08_tax_preparer.rb -f yml -o ~/flows # write 08_tax_preparer.yml to ~/flows
150
+ inquirex export examples/08_tax_preparer.rb -o out.json # write to named file
151
+ inquirex export examples/08_tax_preparer.rb -f yml -o out # appends .yml → out.yml
152
+ ```
153
+
154
+ Options:
155
+
156
+ | Flag | Description |
157
+ |------|-------------|
158
+ | `--format`, `-f` | `json` (default), `yaml`, or `yml` |
159
+ | `--output`, `-o` | Output file or directory (default: stdout) |
160
+
161
+ Output path rules:
162
+
163
+ - No `--output` → print to stdout
164
+ - `--output <dir>` (existing directory) → write `<flow-basename>.<ext>` inside it
165
+ - `--output <file>` → use that filename; if the extension is missing or mismatched, the appropriate one (`.json`/`.yml`) is substituted
166
+
167
+ ### `inquirex version`
168
+
169
+ Print version information for the TTY adapter and its dependencies.
170
+
171
+ ```bash
172
+ inquirex version
173
+ ```
174
+
175
+ ## Example Session
176
+
177
+ Running the tax preparation intake example:
178
+
179
+ ```
180
+ $ inquirex run examples/08_tax_preparer.rb
181
+
182
+ _____ _ __ __ ____ ____ _____ ____ _ ____ _ _____ ___ ___ _ _ ___ _ _ _____ _ _ __ _____
183
+ |_ _| / \ \ \/ / | _ \ | _ \ | ____| | _ \ / \ | _ \ / \ |_ _| |_ _| / _ \ | \ | | |_ _| | \ | | |_ _| / \ | |/ / | ____|
184
+ | | / _ \ \ / | |_) | | |_) | | _| | |_) | / _ \ | |_) | / _ \ | | | | | | | | | \| | | | | \| | | | / _ \ | ' / | _|
185
+ | | / ___ \ / \ | __/ | _ < | |___ | __/ / ___ \ | _ < / ___ \ | | | | | |_| | | |\ | | | | |\ | | | / ___ \ | . \ | |___
186
+ |_| /_/ \_\ /_/\_\ |_| |_| \_\ |_____| |_| /_/ \_\ |_| \_\ /_/ \_\ |_| |___| \___/ |_| \_| |___| |_| \_| |_| /_/ \_\ |_|\_\ |_____|
187
+
188
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
189
+ Step 2: intro
190
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
191
+
192
+ Please describe your tax situation in a few sentences.
193
+ Do not under any circumstances provide personal information,
194
+ such as your address or social security number.
195
+
196
+ Example: I have two W-2s from my two jobs, a rental property, and a side business.
197
+ Press any key to continue...
198
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
199
+ Step 3: filing_status
200
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
201
+ What is your filing status for 2025? single
202
+ Step 4: dependents
203
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
204
+ How many dependents do you have? 2
205
+ Step 5: income_types
206
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
207
+ Select all income types that apply to you in 2025. W2, 1099
208
+ Step 6: state_filing
209
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
210
+ Which states do you need to file in? California
211
+ Step 7: foreign_accounts
212
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
213
+ Do you have any foreign financial accounts? no
214
+ Step 8: deduction_types
215
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
216
+ Which additional deductions apply to you? Medical
217
+ Step 9: client_name
218
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
219
+ Your name, please: Konstantin Gredeskoul
220
+ Step 10: client_email
221
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
222
+ Your email address: kigster@gmail.com
223
+ Step 11: thanks
224
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
225
+
226
+ Thank you! We will review your information and send you a
227
+ tax preparation estimate within 1-2 business days.
228
+ Press any key to continue...
229
+ ```
230
+
231
+ ## Widget Mapping
232
+
233
+ The renderer selects a tty-prompt method for each node based on the `:tty` widget hint from `WidgetRegistry`:
234
+
235
+ | Widget Hint | tty-prompt Method | Used For |
236
+ |-------------|-------------------|----------|
237
+ | `text_input` | `prompt.ask` | `:string`, `:date`, `:email`, `:phone` |
238
+ | `multiline` | `prompt.multiline` | `:text` |
239
+ | `number_input` | `prompt.ask` (with `convert:`) | `:integer`, `:decimal`, `:currency` |
240
+ | `yes_no` | `prompt.yes?` | `:boolean` / `confirm` |
241
+ | `select` | `prompt.select` | `:enum` |
242
+ | `multi_select` | `prompt.multi_select` | `:multi_enum` |
243
+ | `enum_select` | `prompt.enum_select` | Numbered menu variant |
244
+ | `mask` | `prompt.mask` | Password/hidden input |
245
+ | `slider` | `prompt.slider` | Numeric range |
246
+
247
+ You can override the default by setting an explicit `:tty` widget hint in the DSL:
248
+
249
+ ```ruby
250
+ ask :priority do
251
+ type :enum
252
+ question "How urgent?"
253
+ options low: "Low", medium: "Medium", high: "High"
254
+ widget target: :tty, type: :enum_select # numbered menu instead of arrow-key select
255
+ transition to: :next_step
256
+ end
257
+ ```
258
+
259
+ ## Display Verbs
260
+
261
+ | Verb | Rendering |
262
+ |------|-----------|
263
+ | `header` | Large ASCII-art text via TTY::Font (falls back to TTY::Box) |
264
+ | `say` | Plain text with "Press any key to continue..." |
265
+ | `btw` | Info-styled box (blue border) |
266
+ | `warning` | Warning-styled box (yellow/red) |
267
+
268
+ ## LLM Integration
269
+
270
+ When a flow contains [inquirex-llm](../inquirex-llm) verbs (`clarify`,
271
+ `describe`, `summarize`, `detour`), the `inquirex run` command automatically:
272
+
273
+ 1. Loads `.env` files, walking up from the current directory and the flow
274
+ file's directory. Shell-set values take precedence; empty-string keys are
275
+ treated as unset.
276
+ 1. Picks an adapter based on available credentials (see "Adapter selection"
277
+ below).
278
+ 1. Shows a `🧠 Thinking — asking <provider> to extract structured data…`
279
+ banner while the LLM call is in flight.
280
+ 1. For `clarify` steps, splats the extracted fields into the engine's
281
+ top-level answers via `Engine#prefill!`, so any downstream step with
282
+ `skip_if not_empty(:field)` is auto-skipped.
283
+ 1. Prints a `✅` / `❓` extraction table showing which fields the LLM filled
284
+ in vs. which ones will still be asked.
285
+
286
+ ### Adapter selection (first match wins)
287
+
288
+ | Condition | Adapter used |
289
+ |---------------------------------------------|--------------------------------------|
290
+ | `INQUIREX_LLM_ADAPTER=null` | `Inquirex::LLM::NullAdapter` |
291
+ | `INQUIREX_LLM_ADAPTER=anthropic` | `Inquirex::LLM::AnthropicAdapter` |
292
+ | `INQUIREX_LLM_ADAPTER=openai` | `Inquirex::LLM::OpenAIAdapter` |
293
+ | `ANTHROPIC_API_KEY` is set | `Inquirex::LLM::AnthropicAdapter` |
294
+ | `OPENAI_API_KEY` is set | `Inquirex::LLM::OpenAIAdapter` |
295
+ | nothing set | `Inquirex::LLM::NullAdapter` (demo) |
296
+
297
+ ### End-to-end example
298
+
299
+ `examples/09_tax_preparer_llm.rb` is a complete LLM-assisted tax intake. The
300
+ user types one free-text description of their tax situation; the LLM extracts
301
+ `filing_status`, `dependents`, `income_types`, `state_filing`; the wizard only
302
+ asks for whatever the LLM couldn't determine, then runs a final `summarize`
303
+ for a complexity / fee-estimate write-up.
304
+
305
+ ```ruby
306
+ # examples/09_tax_preparer_llm.rb (excerpt)
307
+ require "inquirex"
308
+ require "inquirex/llm"
309
+
310
+ Inquirex.define id: "tax-preparer-llm-2025", version: "1.0.0" do
311
+ meta title: "Tax Prep Intake (LLM-assisted)"
312
+ start :describe
313
+
314
+ ask :describe do
315
+ type :text
316
+ question "Describe your 2025 tax situation in your own words…"
317
+ widget target: :tty, type: :multiline
318
+ transition to: :extracted
319
+ end
320
+
321
+ clarify :extracted do
322
+ from :describe
323
+ prompt <<~PROMPT
324
+ Extract tax intake fields. Use these EXACT value conventions:
325
+ filing_status: "single" | "married_filing_jointly" | …
326
+ income_types: array of "W2" | "1099" | "Business" | "Investment" | "Rental" | "Retirement"
327
+ Use "" / 0 / [] for anything the client did not mention.
328
+ PROMPT
329
+ schema filing_status: :string,
330
+ dependents: :integer,
331
+ income_types: :multi_enum,
332
+ state_filing: :string
333
+ model :claude_sonnet
334
+ temperature 0.0
335
+ transition to: :filing_status
336
+ end
337
+
338
+ ask :filing_status do
339
+ type :enum
340
+ question "What is your filing status?"
341
+ options(%w[single married_filing_jointly married_filing_separately head_of_household widowed])
342
+ skip_if not_empty(:filing_status)
343
+ transition to: :dependents
344
+ end
345
+
346
+ # …same pattern for :dependents, :income_types, :state_filing…
347
+
348
+ ask :client_contact do
349
+ type :string
350
+ question "Your name and email?"
351
+ transition to: :summary
352
+ end
353
+
354
+ summarize :summary do
355
+ from_all
356
+ prompt "Return JSON: { complexity, fee_estimate_low, fee_estimate_high, red_flags, notes }"
357
+ transition to: :done
358
+ end
359
+
360
+ say :done do
361
+ text "Thank you! A tax professional will review your intake and reach out."
362
+ end
363
+ end
364
+ ```
365
+
366
+ Run it:
367
+
368
+ ```bash
369
+ # Put your key in any .env up the tree — OPENAI_API_KEY or ANTHROPIC_API_KEY
370
+ echo 'OPENAI_API_KEY=sk-…' >> ../.env
371
+
372
+ inquirex run examples/09_tax_preparer_llm.rb
373
+ ```
374
+
375
+ A typical session (input shortened):
376
+
377
+ ```
378
+ > I'm MFJ with two kids. I'm W-2 at Google, my wife runs a consulting LLC,
379
+ we have a rental in Oakland and some Coinbase crypto. We live in California.
380
+
381
+ 🧠 Thinking — asking GPT to extract structured data…
382
+ 📋 LLM extracted:
383
+ ✅ filing_status: "married_filing_jointly"
384
+ ✅ dependents: 2
385
+ ✅ income_types: ["W2", "Business", "Rental", "Investment"]
386
+ ✅ state_filing: "California"
387
+ # :filing_status, :dependents, :income_types, :state_filing all auto-skipped.
388
+ # User only gets asked for :client_contact before the summary.
389
+ ```
390
+
391
+ ### Troubleshooting
392
+
393
+ - **"the null adapter"** shown in the thinking banner → no API key was found.
394
+ Check `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` in your shell or in a `.env`
395
+ up the directory tree.
396
+ - **`NoMethodError: undefined method 'prefill!'`** → you're running the
397
+ `inquirex` binary against an older rubygems-installed copy of the core gem.
398
+ `exe/inquirex` now bootstraps Bundler against the repo's Gemfile
399
+ automatically, but a global `rake install`'d copy will still be
400
+ stale — run `rake install` in `../inquirex` to update, or run from a
401
+ checkout so the shim's Bundler bootstrap takes over.
402
+
403
+ ## Examples
404
+
405
+ The gem ships with 10 examples of increasing complexity:
406
+
407
+ | Example | Description | Steps | Features |
408
+ |---------|-------------|-------|----------|
409
+ | `01_hello_world.rb` | Minimal flow | 3 | String and integer input |
410
+ | `02_yes_or_no.rb` | Boolean branching | 3 | `confirm`, `equals` rule |
411
+ | `03_food_preferences.rb` | Multi-select branching | 6 | `multi_enum`, `contains` rule |
412
+ | `04_event_registration.rb` | Two-level branching | 9 | Nested conditionals |
413
+ | `05_job_application.rb` | Composed rules | 13 | `all()`, `any()`, `greater_than` |
414
+ | `06_health_assessment.rb` | Three-level branching | 18 | Complex composed rules |
415
+ | `07_loan_application.rb` | Real-world loan intake | 20+ | Currency, 3-level branching |
416
+ | `08_tax_preparer.rb` | Full tax preparation wizard | 18+ | All data types, deep branching |
417
+ | `09_tax_preparer_llm.rb` | **LLM-assisted tax intake** | 9 | `clarify` + `summarize`, `skip_if not_empty`, auto-prefill |
418
+ | `10_real_tax_preparer.rb` | Realistic tax preparer flow | 20+ | Full intake variant |
419
+
420
+ Run any example:
421
+
422
+ ```bash
423
+ inquirex run examples/01_hello_world.rb
424
+ inquirex run examples/08_tax_preparer.rb
425
+ inquirex run examples/09_tax_preparer_llm.rb
426
+ ```
427
+
428
+ Validate all examples:
429
+
430
+ ```bash
431
+ for f in examples/*.rb; do inquirex validate "$f"; done
432
+ ```
433
+
434
+ ## Architecture
435
+
436
+ ```
437
+ inquirex-tty/
438
+ ├── exe/inquirex # CLI entry point (dry-cli)
439
+ └── lib/inquirex/tty/
440
+ ├── commands/
441
+ │ ├── run.rb # Interactive flow execution
442
+ │ ├── validate.rb # Definition validation
443
+ │ ├── graph.rb # Mermaid diagram export
444
+ │ ├── export.rb # JSON / YAML serialization
445
+ │ └── version.rb # Print version info
446
+ ├── renderer.rb # Node → tty-prompt widget dispatcher
447
+ ├── flow_loader.rb # Load .rb flow definitions
448
+ ├── output_path.rb # Shared -o/--output path resolution
449
+ ├── ui_helper.rb # TTY::Box/Pastel/Screen helpers
450
+ └── commands.rb # dry-cli command registry
451
+ ```
452
+
453
+ ### Renderer
454
+
455
+ `Inquirex::TTY::Renderer` is the core class that maps nodes to tty-prompt calls. It reads the `:tty` widget hint from each node (via `WidgetRegistry` defaults or explicit DSL hints) and dispatches to the matching `render_*` method.
456
+
457
+ The `TTY::Prompt` instance is injectable for testing:
458
+
459
+ ```ruby
460
+ prompt = TTY::Prompt::Test.new
461
+ renderer = Inquirex::TTY::Renderer.new(prompt:)
462
+ ```
463
+
464
+ ### FlowLoader
465
+
466
+ Loads a `.rb` file and evaluates it to produce an `Inquirex::Definition`. The file must contain an `Inquirex.define` call that returns the definition.
467
+
468
+ ### UIHelper
469
+
470
+ A mixin module providing styled output helpers (`box`, `info`, `success`, `error`, `warning`, `sep`, `next_step`) built on TTY::Box, Pastel, and TTY::Screen. Included automatically in all CLI commands and the Renderer.
471
+
472
+ ## Development
473
+
474
+ ```bash
475
+ bin/setup # install dependencies
476
+ bundle exec rspec # run tests
477
+ bundle exec rspec --format doc # verbose test output
478
+ bundle exec rubocop # lint
479
+ ```
480
+
481
+ ### Useful `just` tasks
482
+
483
+ ```bash
484
+ just test # run full test suite with coverage
485
+ just lint # rubocop
486
+ just format # rubocop --auto-correct
487
+ just run examples/01_hello_world.rb # run a flow
488
+ just validate examples/01_hello_world.rb
489
+ just graph examples/01_hello_world.rb
490
+ just examples # validate all examples
491
+ just ci # tests + lint
492
+ ```
493
+
494
+ ### Writing a Custom Flow
495
+
496
+ Any `.rb` file that calls `Inquirex.define` and returns a `Definition` works:
497
+
498
+ ```ruby
499
+ require "inquirex"
500
+
501
+ Inquirex.define id: "my-flow", version: "1.0.0" do
502
+ meta title: "My Flow"
503
+ start :first_question
504
+
505
+ ask :first_question do
506
+ type :enum
507
+ question "Pick one:"
508
+ options a: "Option A", b: "Option B", c: "Option C"
509
+ widget target: :tty, type: :select
510
+ transition to: :detail, if_rule: equals(:first_question, "a")
511
+ transition to: :done
512
+ end
513
+
514
+ ask :detail do
515
+ type :text
516
+ question "Tell me more about A:"
517
+ transition to: :done
518
+ end
519
+
520
+ say :done do
521
+ text "All done!"
522
+ end
523
+ end
524
+ ```
525
+
526
+ ## License
527
+
528
+ MIT. See [LICENSE.txt](LICENSE.txt).
529
+
530
+ Copyright 2026 Konstantin Gredeskoul & Inquirex.
data/Rakefile ADDED
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+ require "timeout"
6
+ require "yard"
7
+
8
+ def shell(*args)
9
+ puts "running: #{args.join(" ")}"
10
+ system(args.join(" "))
11
+ end
12
+
13
+ task :clean do
14
+ shell("rm -rf pkg/ tmp/ coverage/ doc/ ")
15
+ end
16
+
17
+ task gem: [:build] do
18
+ shell("gem install pkg/*")
19
+ end
20
+
21
+ task permissions: [:clean] do
22
+ shell("chmod -v o+r,g+r * */* */*/* */*/*/* */*/*/*/* */*/*/*/*/*")
23
+ shell("find . -type d -exec chmod o+x,g+x {} \\;")
24
+ end
25
+
26
+ task build: :permissions
27
+
28
+ YARD::Rake::YardocTask.new(:doc) do |t|
29
+ t.files = %w[lib/**/*.rb exe/*.rb - README.md LICENSE.txt CHANGELOG.md]
30
+ t.options.unshift("--title", '"FlowEngine — DSL + AST for buildiong complex flows in Ruby."')
31
+ t.after = -> { exec("open doc/index.html") } if RUBY_PLATFORM =~ /darwin/
32
+ end
33
+
34
+ RSpec::Core::RakeTask.new(:spec)
35
+
36
+ task default: :spec
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="99" height="20">
3
+ <linearGradient id="b" x2="0" y2="100%">
4
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
5
+ <stop offset="1" stop-opacity=".1"/>
6
+ </linearGradient>
7
+ <mask id="a">
8
+ <rect width="99" height="20" rx="3" fill="#fff"/>
9
+ </mask>
10
+ <g mask="url(#a)">
11
+ <path fill="#555" d="M0 0h63v20H0z"/>
12
+ <path fill="#4c1" d="M63 0h36v20H63z"/>
13
+ <path fill="url(#b)" d="M0 0h99v20H0z"/>
14
+ </g>
15
+ <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
16
+ <text x="31.5" y="15" fill="#010101" fill-opacity=".3">coverage</text>
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>
20
+ </g>
21
+ </svg>
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example 1: Hello World
4
+ #
5
+ # The simplest possible flow — three linear steps, no branching.
6
+ # Demonstrates: string input, integer input, say display step.
7
+ #
8
+ # Run: bundle exec exe/inquirex-tty run examples/01_hello_world.rb
9
+
10
+ Inquirex.define id: "hello-world", version: "1.0.0" do
11
+ meta title: "Hello World", subtitle: "A simple introduction"
12
+
13
+ start :name
14
+
15
+ ask :name do
16
+ type :string
17
+ question "What is your name?"
18
+ widget target: :tty, type: :text_input
19
+ transition to: :age
20
+ end
21
+
22
+ ask :age do
23
+ type :integer
24
+ question "How old are you?"
25
+ widget target: :tty, type: :number_input
26
+ transition to: :farewell
27
+ end
28
+
29
+ say :farewell do
30
+ text "Thanks for stopping by! Have a great day."
31
+ end
32
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Example 2: Yes or No
4
+ #
5
+ # A single boolean branch. Answering "yes" takes a detour;
6
+ # answering "no" goes straight to the end.
7
+ # Demonstrates: confirm verb, equals rule, simple branching.
8
+ #
9
+ # Run: bundle exec exe/inquirex-tty run examples/02_yes_or_no.rb
10
+
11
+ Inquirex.define id: "yes-or-no", version: "1.0.0" do
12
+ meta title: "Yes or No", subtitle: "A simple boolean branch"
13
+
14
+ start :has_pet
15
+
16
+ confirm :has_pet do
17
+ question "Do you have a pet?"
18
+ widget target: :tty, type: :yes_no
19
+ transition to: :pet_name, if_rule: equals(:has_pet, true)
20
+ transition to: :done
21
+ end
22
+
23
+ ask :pet_name do
24
+ type :string
25
+ question "What is your pet's name?"
26
+ widget target: :tty, type: :text_input
27
+ transition to: :done
28
+ end
29
+
30
+ say :done do
31
+ text "All done! Thanks for answering."
32
+ end
33
+ end
Binary file