riffer-rig 0.15.0 → 0.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1743d133b2e91f50f10a376bbb8cd6f2d21f4d6c2f90600d5415d4ec9047b26
4
- data.tar.gz: c8131bfda29574b5c86acb1b1ebebfe0ec1dc5cc82ff7c8c8cca5410bb7b34d0
3
+ metadata.gz: 8651df8d201d07a31d22ee2df71fde5942a6219dc405a6e51ab4661f8b910c9d
4
+ data.tar.gz: 472fe4de066e0a621f61f4ff3da31233b325731709f974ad396973269de36b86
5
5
  SHA512:
6
- metadata.gz: 46930dae154e4a07916c0f1391e3f44419a7e385f5d31f02509a9740276ca6bfc993efb4f0e95695ad439045f69d9c3a74e344a43af7f83a75afdb6bd94f1e58
7
- data.tar.gz: 18ce4fe1003d1767a234936fae81fb921c49461cefb7f6516bf051047734b68580bfc124803d353296a71daa9919f5f77805c83bc3c2599c9e17e8b2650b4b1d
6
+ metadata.gz: 448c573762bcf24c313c5f214ebb8c17988339f74b8a511c1eb9948ef3303a46bec300f7f092a4ba4507a1b03f1dbfa2db43eda464ec78b1254df0455b86886f
7
+ data.tar.gz: 6c68f67169c7f20ea0ad31f3b2937d4b487b651033f93426d79d6a97ca248a8086bc247cff97688ad7406ea6f30fe9e973c166788f802bbcc99f9a8dad47ab97
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.16.0](https://github.com/bottrall/riffer-rig/compare/v0.15.0...v0.16.0) (2026-10-18)
4
+
5
+
6
+ ### Features
7
+
8
+ * **terminal:** show the reasoning level in the banner and status bar ([#565](https://github.com/bottrall/riffer-rig/issues/565)) ([ead3588](https://github.com/bottrall/riffer-rig/commit/ead358834580a4fcab18432cbe5414e3e21b7feb))
9
+
3
10
  ## [0.15.0](https://github.com/bottrall/riffer-rig/compare/v0.14.0...v0.15.0) (2026-10-18)
4
11
 
5
12
 
@@ -15,6 +22,10 @@
15
22
  ## [0.14.0](https://github.com/bottrall/riffer-rig/compare/v0.13.0...v0.14.0) (2026-10-18)
16
23
 
17
24
 
25
+ ### Upgrading
26
+
27
+ * the `models` pricing table in `settings.json` is no longer read. Move each entry into a [model catalog](https://riffer.ai/guides/catalog/) file's `pricing` — the same four rates, USD per million tokens — and list the file in `catalog_files`.
28
+
18
29
  ### Features
19
30
 
20
31
  * **headless:** print the session id on stderr ([#556](https://github.com/bottrall/riffer-rig/issues/556)) ([2816938](https://github.com/bottrall/riffer-rig/commit/2816938f66d32ccf557d83a33e40300e68a68032))
data/README.md CHANGED
@@ -2,245 +2,81 @@
2
2
 
3
3
  **riffer, in your terminal and in your process.**
4
4
 
5
- `riffer-rig` is a general-purpose agent for Ruby developers who want to own their harness. It runs as a terminal agent and, because the runtime is separate from the terminal, it also runs inside any Ruby process: a Rails app, a script, a job. You extend it in Ruby: tools, commands, providers and hooks are ordinary Ruby classes on [riffer](https://github.com/janeapp/riffer)'s primitives, and the same objects work whether the host is the terminal or your app. It ships with a coding toolkit (read, write, edit, bash) as the default bundle, because a shell and file access are the most efficient way to get almost any task done — but the prompt gives it no coding identity. [Overview](docs/OVERVIEW.md) has the full thesis.
5
+ `riffer-rig` is a general-purpose agent for Ruby developers who want to own their harness. It runs in your terminal, and because the runtime is separate from the terminal it also runs inside any Ruby process: a Rails app, a script, a job. You extend it in Ruby — tools, commands, providers and hooks are ordinary classes on [riffer](https://riffer.ai)'s primitives, and the same objects work in the terminal and in your app.
6
6
 
7
- ## Requirements
7
+ It ships with a coding toolkit (read, write, edit, bash) because a shell and file access get almost any task done, but nothing in its prompt makes it a coding agent. Tools run without asking for permission; see [what runs without asking](docs/TOOLS.md#what-runs-without-asking).
8
8
 
9
- - Ruby >= 4.0
9
+ ## Install
10
10
 
11
- ## Installation
12
-
13
- Install the gem:
11
+ Requires Ruby 4.0 or later.
14
12
 
15
13
  ```bash
16
14
  gem install riffer-rig
17
15
  ```
18
16
 
19
- This provides a `riffer` executable. No provider SDK is installed with it: `gemini/…` models work out of the box, and every other provider's SDK gem is either installed on its first use — `riffer-rig` asks, then installs it in-process — or added to a Gemfile ahead of time:
20
-
21
- ```ruby
22
- gem 'anthropic', '~> 1.69'
23
- ```
24
-
25
- [Providers](docs/PROVIDERS.md#sdk-gems) lists each provider's gem and how the install offer works.
26
-
27
- ## Usage
28
-
29
- Run the agent from any project directory:
17
+ ## Quickstart
30
18
 
31
19
  ```bash
20
+ cd my-project
32
21
  riffer
33
22
  ```
34
23
 
35
- The first run asks which model to use, as `provider/name` (for example `anthropic/claude-sonnet-4-6`), and saves the answer as `model` in `~/.riffer/settings.json`; it then asks for any missing credential ([Authentication](#authentication)). After that, `riffer` opens straight into the prompt. [Getting started](docs/GETTING_STARTED.md) walks through a first session.
24
+ The first run asks for a model as `provider/name` — for example `anthropic/claude-sonnet-4-6` — and then for that provider's API key, and saves both under `~/.riffer/`. `gemini/…` models work straight away; other providers' SDK gems are [offered for install](docs/PROVIDERS.md#sdk-gems) on first use.
36
25
 
37
- The model is the first of these that is set: `--model`, `RIFFER_MODEL`, `model` in `<cwd>/.riffer/settings.json`, then in `~/.riffer/settings.json` ([Configuration](docs/CONFIGURATION.md#model)). The reasoning effort resolves the same way: `--reasoning`, `RIFFER_REASONING`, then `reasoning` in the settings ([Configuration](docs/CONFIGURATION.md#reasoning)).
26
+ Type a prompt and press Enter. `/help` lists the commands, and [Getting started](docs/GETTING_STARTED.md) walks through a first session.
38
27
 
39
- ### Flags
40
-
41
- | Flag | Effect |
42
- | ----------------------- | -------------------------------------------------------------------------- |
43
- | `--model provider/name` | the model for this session, over `RIFFER_MODEL` and the settings |
44
- | `--reasoning LEVEL` | the reasoning effort for this session, over `RIFFER_REASONING` and the settings |
45
- | `--no-extensions` | skips the [`rig.rb` files](docs/EXTENSIONS.md#the-rigrb-files) and gem autoload; the bundle still loads |
46
- | `--no-skills` | leaves out [Agent Skills](docs/SKILLS.md) |
47
- | `--no-agents-md` | leaves out the [AGENTS.md](docs/INSTRUCTIONS.md#agentsmd) instructions |
48
- | `--tools read,bash` | only these tools, by identifier ([Tools](docs/TOOLS.md)) |
49
- | `--max-steps N` | stops a turn after `N` model calls |
50
- | `--no-save` | does not save this session to the [store](docs/SESSIONS.md) |
51
- | `-c`, `--continue` | continues the most recent session in this directory ([Sessions](docs/SESSIONS.md#resuming)) |
52
- | `-r [ID]`, `--resume [ID]` | resumes the session with id `ID`; bare `-r` opens the [picker](docs/SESSIONS.md#the-picker) ([Sessions](docs/SESSIONS.md#resuming)) |
53
- | `-h`, `--help` | prints the usage |
54
- | `-v`, `--version` | prints the version |
55
-
56
- An editor registers the agent as `riffer acp`, the same Runtimes over the [Agent Client Protocol](https://agentclientprotocol.com) on stdio ([ACP](docs/ACP.md)).
57
-
58
- ### Headless
59
-
60
- Run one prompt with no terminal ([Headless](docs/HEADLESS.md)):
28
+ To run one prompt from a script or CI job:
61
29
 
62
30
  ```bash
63
- riffer -p "what does this repo do"
64
- git diff | riffer -p "review this diff" # piped stdin appended as context
65
- echo "explain this diff" | riffer -p # piped stdin alone is the prompt
31
+ git diff | riffer -p "review this diff"
66
32
  ```
67
33
 
68
- Everything loads exactly as the REPL does, the shared flags work the same — `-c` and `-r` included: `riffer -p -c "now run the tests"` continues the most recent session in this directory, and a missing session exits 2 ([Headless](docs/HEADLESS.md)). The session is saved like any other unless `--no-save`. Only the assistant's text is printed, streamed to stdout; errors and warnings go to stderr, and `--verbose` adds a one-line trace of each tool call there. With `--json`, stdout is instead one JSON object per line for every Runtime event — the NDJSON stream ([Headless](docs/HEADLESS.md#the-ndjson-stream---json)). A run whose model leaves background jobs running waits for them, giving the model a further turn as each one exits, and kills any left after `--job-idle-timeout` seconds (600 by default) without an exit ([Headless](docs/HEADLESS.md#background-jobs)). Headless never prompts: a missing model, credential, or SDK prints the reason on stderr and exits 2.
69
-
70
- The exit code says how the turn ended: `0` finished, `1` runtime error, `2` usage or configuration, `3` the turn ended without completing (`--max-steps`, a full context window, a provider content filter) or its background jobs passed the idle timeout, `130` SIGINT (the turn is cancelled first).
71
-
72
- ### In the session
73
-
74
- Type a prompt and press Enter. While the turn runs, the status line under the input shows its time, steps, tool calls, tokens and cost, and Ctrl-T peeks at what the model is writing; the reply is printed when the turn ends, followed by a stat line (`N steps · mm:ss · ↑in · ↓out · session N tok`, plus the turn's cost and the session's when the model is [priced](docs/CONFIGURATION.md#catalog_files)). The input line stays open throughout: a message sent during a turn is queued and runs when the turn ends. [Terminal](docs/TERMINAL.md) has every key.
75
-
76
- - Ctrl-C during a turn cancels it. Ctrl-C with no turn running asks for a second one, which exits.
77
- - `/exit` or `/quit` ends the session, as does Ctrl-D on an empty line.
78
- - `/help` lists the session's slash commands. `/resume [--all]`, `/new`, `/cost`, `/trace`, `/skill:<name>` and the others are described in [Terminal](docs/TERMINAL.md#commands); `/model` is covered [below](#switching-the-model), and `/reasoning` works the same way for the reasoning effort.
79
-
80
- ### Switching the model
81
-
82
- `/model provider/name` switches the model for the current session only, keeping the conversation so far, and confirms it in one line: `/model openai/gpt-5` prints `Model switched to openai/gpt-5`. The provider prefix is required: a bare name such as `/model sonnet` is rejected with the list of providers. `/model` alone shows the model in use. Switching runs the same setup for the new provider as startup does: its SDK gem is offered for install (or refused with the exact Gemfile line), and missing credentials are resolved — you are prompted for them when the host can ask, and the values apply to the running session immediately. The switch is refused with one error when the host cannot supply what is missing, and the model is left unchanged. `/model --save` writes the model in effect to the home `settings.json`, so new sessions start with it; `/model provider/name --save` switches and saves in one step, printing `Model switched to openai/gpt-5 and saved to ~/.riffer/settings.json` (a bare `/model --save` prints `Saved openai/gpt-5 to ~/.riffer/settings.json`), and a refused switch saves nothing ([Configuration](docs/CONFIGURATION.md#model)).
83
-
84
- ### Switching the reasoning level
85
-
86
- `/reasoning level` switches the reasoning effort for the current session only, keeping the conversation so far: `/reasoning high` prints `Reasoning switched to high`, `/reasoning off` turns reasoning off, and `/reasoning` alone shows the level in effect. A level riffer does not accept is rejected naming the levels it accepts. `/reasoning --save` writes the level in effect to the home `settings.json` (`/reasoning high --save` switches and saves in one step), and a home file that no longer parses is reported instead of written. The switch survives a resume, and a `--reasoning` chosen for the run wins over it for that run only ([Configuration](docs/CONFIGURATION.md#reasoning)).
87
-
88
- ## Authentication
89
-
90
- `riffer-rig` talks to the provider named by the model's prefix — `anthropic/claude-sonnet-4-6` means Anthropic. Provide that provider's credentials in either of two ways:
91
-
92
- - Set the provider's environment variables, or
93
- - Run `riffer` and paste each missing value when prompted on first launch. Secrets are saved to `~/.riffer/auth.json` (file permissions `600`); plain values such as an endpoint or region are saved to the `providers` block in `~/.riffer/settings.json`.
94
-
95
- Each value resolves on its own: environment variable first, then the stored value, then a provider-specific fallback (the AWS shared config for the Bedrock region), then the prompt. Optional values are never prompted for.
96
-
97
- | Provider | Environment variables | Guide |
98
- | ---------------- | ------------------------------------------------------------------------- | -------------------------------------------------- |
99
- | `anthropic` | `ANTHROPIC_API_KEY` | [Anthropic](docs/PROVIDERS.md#anthropic) |
100
- | `openai` | `OPENAI_API_KEY`; optional `OPENAI_BASE_URL` | [OpenAI](docs/PROVIDERS.md#openai) |
101
- | `gemini` | `GEMINI_API_KEY` | [Gemini](docs/PROVIDERS.md#gemini) |
102
- | `openrouter` | `OPENROUTER_API_KEY` | [OpenRouter](docs/PROVIDERS.md#openrouter) |
103
- | `azure_openai` | `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_API_KEY` | [Azure OpenAI](docs/PROVIDERS.md#azure-openai) |
104
- | `amazon_bedrock` | `AWS_REGION` or `AWS_DEFAULT_REGION`; optional `AWS_BEARER_TOKEN_BEDROCK` | [Amazon Bedrock](docs/PROVIDERS.md#amazon-bedrock) |
105
-
106
- `auth.json` holds one typed entry per provider, and a stored secret may be a literal, a `$ENV_VAR` reference or a `!shell command` whose stdout is the secret:
107
-
108
- ```json
109
- {
110
- "anthropic": { "type": "api_key", "api_key": "sk-ant-…" },
111
- "openai": { "type": "api_key", "api_key": "!security find-generic-password -s openai -w" }
112
- }
113
- ```
34
+ [Headless](docs/HEADLESS.md) covers exit codes and the NDJSON stream; editors connect over [ACP](docs/ACP.md) with `riffer acp`.
114
35
 
115
- The flat `{"anthropic": "sk-…"}` shape earlier versions wrote is no longer read; paste the key again when prompted. [Providers](docs/PROVIDERS.md) has the full format.
36
+ ## Extend it
116
37
 
117
- Inside a session, `/auth` lists every provider with where its secret comes from — `env`, `stored`, `chain` (the SDK's own credential chain, for Bedrock) or `missing`. `/auth <provider>` re-runs that provider's setup, which is how you rotate a key, and applies the new values to the current session, so the next request uses them. `/auth remove <provider>` deletes the provider's `auth.json` entry and its `providers` block in settings.
118
-
119
- ## Configuration
120
-
121
- Settings live in two scopes, both optional: `~/.riffer/settings.json` for every project, and `<cwd>/.riffer/settings.json` for one project, merged key by key with the project winning ([Configuration](docs/CONFIGURATION.md)). Every core key is optional:
122
-
123
- | Key | What it sets |
124
- | -------------- | -------------------------------------------------------------------------------- |
125
- | `model` | the model new sessions start with, as `provider/name` |
126
- | `reasoning` | the reasoning effort, which riffer translates to the provider's own parameter |
127
- | `catalog_files`| the model catalog JSON files holding pricing and reasoning maps |
128
- | `providers` | non-secret provider fields such as an endpoint or region (home file only) |
129
- | `extensions` | `disabled` — bundled extensions to leave out; `autoload` — gem extension autoload |
130
- | `reload` | `"auto"` (default) or `"manual"` — the hot-reload trigger |
131
- | `sessions` | `save: false` stops saving sessions to the store |
132
- | `tools` | the provider-native tool switches (`native`), off by default |
133
-
134
- - `RIFFER_MODEL` — the model for this run as `provider/name`, winning over the `model` setting. A bare name such as `sonnet` is rejected with the list of providers.
135
- - `RIFFER_REASONING` — the reasoning effort for this run, winning over the `reasoning` setting; a level riffer does not accept is rejected naming the levels it accepts.
136
- - `AGENTS.md` — `~/.riffer/AGENTS.md` and an `AGENTS.md` in the current working directory or any directory above it are re-read every turn as instructions that take precedence over the default norms ([Instructions](docs/INSTRUCTIONS.md#agentsmd)).
137
- - Skills — Agent Skills in `.agents/skills/` from the working directory up to the repository root, and in `~/.agents/skills/`, are offered to the model, and `/skill:<name> [text]` runs one ([Skills](docs/SKILLS.md)).
138
- - MCP servers — declared under the `mcp` key in either settings file and registered with the Runtime ([MCP](docs/MCP.md)).
139
-
140
- ## Extending
141
-
142
- An extension is a named registrar block in a `rig.rb` file — `~/.riffer/rig.rb` runs in every project, `<project>/.riffer/rig.rb` in one, and the first run of a project file asks you to trust it:
38
+ An extension is a Ruby block in `~/.riffer/rig.rb` (every project) or `.riffer/rig.rb` (one project):
143
39
 
144
40
  ```ruby
145
41
  Riffer::Rig.extension('git') do |rig|
146
- # A tool the model can call
147
- rig.tool GitLog
148
-
149
- # A /log command in the session
150
- rig.command('log', description: 'Recent commits') do |ctx|
151
- ctx.say `git log --oneline -n #{ctx.args}`
152
- end
153
-
154
- # A section appended to the system message, re-read every turn
155
- rig.prompt(:branch) { |ctx| "Branch: #{`git branch --show-current`}" }
42
+ rig.command('log', description: 'Recent commits') { |ctx| ctx.say `git log --oneline -n 10` }
43
+ rig.prompt(:branch) { "Current branch: #{`git branch --show-current`}" }
156
44
  end
157
45
  ```
158
46
 
159
- Tools, commands, prompt sections, event handlers, skills, MCP servers and providers register the same way, and the same objects work whether the host is the terminal or your app. [Extensions](docs/EXTENSIONS.md) has all eight seams, error isolation, and packaging an extension as a gem.
160
-
161
- ## Embedding
47
+ That adds a `/log` command and puts the current branch in the system prompt on every turn. Extensions can also add tools, hooks, skills, MCP servers, settings and providers — [Extensions](docs/EXTENSIONS.md) starts with a complete tool.
162
48
 
163
- The runtime is separate from the terminal, so the same agent runs inside any Ruby process:
49
+ ## Embed it
164
50
 
165
51
  ```ruby
166
52
  runtime = Riffer::Rig::Loader.runtime(cwd: Dir.pwd, host: Riffer::Rig::Hosts::Null.new)
167
- response = runtime.ask('why is this test failing?')
168
- puts response.content
53
+ puts runtime.ask('why is this test failing?').content
169
54
  ```
170
55
 
171
- `Loader.runtime` applies the same filesystem conventions as the terminal — settings, credentials, `rig.rb`, skills. [Embedding](docs/EMBEDDING.md) has the streaming `prompt`, snapshots, and rebuilding after a code reload.
56
+ This builds the same agent the terminal does, from the same settings, credentials and extensions. [Embedding](docs/EMBEDDING.md) covers streaming, cancelling, snapshots and reloading.
172
57
 
173
58
  ## Documentation
174
59
 
175
- The guides, in reading order:
176
-
177
- Start here:
178
-
179
- - [Overview](https://riffer.bottrall.dev/guides/overview/) — What riffer-rig is, the four tiers, the runtime and host layers
180
- - [Getting started](https://riffer.bottrall.dev/guides/getting-started/) — Install, pick a model, first session
181
-
182
- Using the terminal:
183
-
184
- - [Terminal](https://riffer.bottrall.dev/guides/terminal/) — The input line and its keys, queueing, the status line, peek, resume replay
185
- - [Configuration](https://riffer.bottrall.dev/guides/configuration/) — Settings scopes and every core key
186
- - [Instructions](https://riffer.bottrall.dev/guides/instructions/) — How the system message is built; AGENTS.md
187
- - [Tools](https://riffer.bottrall.dev/guides/tools/) — The bundled toolkit and provider-native tools
188
- - [Skills](https://riffer.bottrall.dev/guides/skills/) — Agent Skills directories and activation
189
- - [MCP](https://riffer.bottrall.dev/guides/mcp/) — Declaring MCP servers
190
- - [Sessions](https://riffer.bottrall.dev/guides/sessions/) — Saving, resuming, the picker, the JSONL store
191
- - [Reloading](https://riffer.bottrall.dev/guides/reloading/) — The /reload command, what reloads and what does not
192
- - [Headless](https://riffer.bottrall.dev/guides/headless/) — riffer -p, NDJSON, exit codes
193
- - [ACP](https://riffer.bottrall.dev/guides/acp/) — riffer acp, the ACP agent over stdio
60
+ The guides are at **<https://riffer.bottrall.dev>**, and their sources are in [`docs/`](docs/). Good places to start:
194
61
 
195
- Extending and embedding:
196
-
197
- - [Extensions](https://riffer.bottrall.dev/guides/extensions/) — rig.rb, the registrar, the eight seams, commands, handlers, gems
198
- - [Embedding](https://riffer.bottrall.dev/guides/embedding/) — Runtime and Loader from Ruby; snapshots; rebuild
199
- - [Hosts](https://riffer.bottrall.dev/guides/hosts/) — The Host interface and writing a host
200
-
201
- Providers:
202
-
203
- - [Providers](https://riffer.bottrall.dev/guides/providers/overview/) — Model strings, precedence, credentials, /auth
204
- - [Custom providers](https://riffer.bottrall.dev/guides/providers/custom/) — Registering a provider from an extension; the setup, credentials, listings
205
-
206
- The guide sources are in `docs/`. To preview the site locally, run `bin/docs serve` and open <http://localhost:8000>.
62
+ - [Overview](docs/OVERVIEW.md) — what riffer-rig is and how it fits together
63
+ - [Configuration](docs/CONFIGURATION.md) — settings and where each value comes from
64
+ - [Command line](docs/CLI.md) — every flag
65
+ - [Extensions](docs/EXTENSIONS.md) — your own tools, commands and hooks
207
66
 
208
67
  ## Development
209
68
 
210
- Every project chore is a script in `bin/`. The Rakefile behind them is an implementation detail; you never need to call rake directly.
211
-
212
- | Script | What it does |
213
- | --------------- | ---------------------------------------------------------------------------------------------- |
214
- | `bin/setup` | Install dependencies on a fresh checkout (gems + rbs collection) |
215
- | `bin/test` | Run the test suite. Pass files and/or Minitest flags: `bin/test test/foo_test.rb -n /pattern/` |
216
- | `bin/lint` | Run RuboCop. Arguments are forwarded, e.g. `bin/lint -a` |
217
- | `bin/typecheck` | Check the rbs collection lockfile and `sig/generated` are current, then type-check with Steep |
218
- | `bin/rbs` | Regenerate `sig/generated` from the inline annotations in `lib/` |
219
- | `bin/rbs-watch` | Regenerate `sig/generated` whenever `lib/` changes |
220
- | `bin/ci` | Run everything CI runs, serially. Use before pushing |
221
- | `bin/build` | Build the gem into `pkg/`; the publish workflow runs this before `gem push` |
222
- | `bin/docs` | Build the docs site and API reference into `_site/`; `bin/docs serve` serves it at http://localhost:8000 |
223
-
224
- ## Releasing
225
-
226
- PR titles are [conventional commits](https://www.conventionalcommits.org/) and are linted in CI: `feat:` bumps the minor version, `fix:` bumps the patch, and `feat!:` marks a breaking change (also a minor bump while we are on 0.x). `chore:`, `docs:`, `ci:`, `refactor:` and `test:` never release. Squash-merging makes the title the commit on `main`.
227
-
228
- A PR whose code nothing in the live path reaches yet is titled `refactor:`, including fixes to such code; the PR that wires it in carries the `feat:` or `fix:`, titled for what the user gets. The changelog and GitHub Release are generated from titles, so a `feat:` on unreachable code advertises a feature nobody can use.
69
+ ```bash
70
+ bin/setup # install gems and RBS signatures
71
+ bin/ci # lint, test, type-check and build the docs, as CI does
72
+ ```
229
73
 
230
- [release-please](https://github.com/googleapis/release-please) keeps a release PR open that bumps `lib/riffer/rig/version.rb` and writes `CHANGELOG.md`. Merging that PR tags `vX.Y.Z`, creates the GitHub Release and publishes the gem to RubyGems.org through Trusted Publishing; nothing is pushed by hand.
74
+ `AGENTS.md` lists every script and the project's conventions. PR titles follow [Conventional Commits](https://www.conventionalcommits.org/); [release-please](https://github.com/googleapis/release-please) turns them into releases.
231
75
 
232
76
  ## Contributing
233
77
 
234
- 1. Fork the repository and create your branch: `git checkout -b feature/foo`
235
- 2. Run the tests and linters locally.
236
- 3. Submit a pull request with a clear description of the change.
237
-
238
- Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
78
+ Fork the repository, make your change on a branch, run `bin/ci`, and open a pull request with a clear description. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
239
79
 
240
80
  ## License
241
81
 
242
- Licensed under the MIT License. See [`LICENSE.txt`](LICENSE.txt) for details.
243
-
244
- ## Maintainer
245
-
246
- - Jake Bottrall - https://github.com/bottrall
82
+ MIT — see [`LICENSE.txt`](LICENSE.txt). Maintained by [Jake Bottrall](https://github.com/bottrall).
@@ -21,7 +21,7 @@ module Riffer::Rig::Terminal::Banner
21
21
  Riffer::Rig::Terminal::Palette::CYAN
22
22
  ].freeze #: Array[Array[Integer]]
23
23
 
24
- INFO_LABEL_WIDTH = 8 #: Integer
24
+ INFO_LABEL_WIDTH = 10 #: Integer
25
25
 
26
26
  INDENT = ' ' #: String
27
27
 
@@ -33,26 +33,46 @@ module Riffer::Rig::Terminal::Banner
33
33
 
34
34
  # @rbs theme: Riffer::Rig::Terminal::Theme
35
35
  # @rbs model: String
36
+ # @rbs reasoning: String?
36
37
  # @rbs cwd: String
37
38
  # @rbs skills: String
38
39
  # @rbs version: String
39
40
  # @rbs tally: Riffer::Providers::TokenUsage?
40
41
  # @rbs width: Integer?
41
42
  # @rbs return: String
42
- def call(theme, model:, cwd:, skills:, version:, tally: nil, width: nil)
43
- lines(theme, model: model, cwd: cwd, skills: skills, version: version, tally: tally, width: width).join("\n")
43
+ def call(theme, model:, reasoning:, cwd:, skills:, version:, tally: nil, width: nil)
44
+ lines(
45
+ theme,
46
+ model: model,
47
+ reasoning: reasoning,
48
+ cwd: cwd,
49
+ skills: skills,
50
+ version: version,
51
+ tally: tally,
52
+ width: width
53
+ ).join("\n")
44
54
  end
45
55
 
46
56
  # @rbs theme: Riffer::Rig::Terminal::Theme
47
57
  # @rbs model: String
58
+ # @rbs reasoning: String?
48
59
  # @rbs cwd: String
49
60
  # @rbs skills: String
50
61
  # @rbs version: String
51
62
  # @rbs tally: Riffer::Providers::TokenUsage?
52
63
  # @rbs width: Integer?
53
64
  # @rbs return: Array[String]
54
- def lines(theme, model:, cwd:, skills:, version:, tally:, width:)
55
- rows = info(theme, model: model, cwd: cwd, skills: skills, version: version, tally: tally, width: width)
65
+ def lines(theme, model:, reasoning:, cwd:, skills:, version:, tally:, width:)
66
+ rows = info(
67
+ theme,
68
+ model: model,
69
+ reasoning: reasoning,
70
+ cwd: cwd,
71
+ skills: skills,
72
+ version: version,
73
+ tally: tally,
74
+ width: width
75
+ )
56
76
  [''] + art(theme, width) + [''] + rows + ['']
57
77
  end
58
78
 
@@ -68,17 +88,19 @@ module Riffer::Rig::Terminal::Banner
68
88
 
69
89
  # @rbs theme: Riffer::Rig::Terminal::Theme
70
90
  # @rbs model: String
91
+ # @rbs reasoning: String?
71
92
  # @rbs cwd: String
72
93
  # @rbs skills: String
73
94
  # @rbs version: String
74
95
  # @rbs tally: Riffer::Providers::TokenUsage?
75
96
  # @rbs width: Integer?
76
97
  # @rbs return: Array[String]
77
- def info(theme, model:, cwd:, skills:, version:, tally:, width:)
98
+ def info(theme, model:, reasoning:, cwd:, skills:, version:, tally:, width:)
78
99
  room = width && (width - VALUE_COLUMN)
79
100
  session = tally && Riffer::Rig::Terminal::Tally.call(tally)
80
101
  values = {
81
102
  model: clip(model, room),
103
+ reasoning: reasoning || Riffer::Rig::Terminal::Reasoning::DEFAULT,
82
104
  cwd: clip(cwd, room),
83
105
  skills: skills,
84
106
  version: version,
@@ -53,6 +53,7 @@ class Riffer::Rig::Terminal::Frame
53
53
  # @rbs @loading: bool
54
54
  # @rbs @loading_line: String
55
55
  # @rbs @tally: (^() -> Riffer::Providers::TokenUsage?)?
56
+ # @rbs @reasoning: (^() -> String?)?
56
57
  # @rbs @now: ^() -> Time
57
58
  # @rbs @mutex: Mutex
58
59
  # @rbs @previous_resize: Signal::trap_command
@@ -94,6 +95,7 @@ class Riffer::Rig::Terminal::Frame
94
95
  @loading = loading
95
96
  @loading_line = LOADING
96
97
  @tally = nil
98
+ @reasoning = nil
97
99
  @now = now
98
100
  @mutex = Mutex.new
99
101
  @previous_resize = nil
@@ -200,10 +202,12 @@ class Riffer::Rig::Terminal::Frame
200
202
  end
201
203
 
202
204
  # @rbs tally: ^() -> Riffer::Providers::TokenUsage?
205
+ # @rbs reasoning: ^() -> String?
203
206
  # @rbs return: void
204
- def session(tally:)
207
+ def session(tally:, reasoning:)
205
208
  @mutex.synchronize do
206
209
  @tally = tally
210
+ @reasoning = reasoning
207
211
  redraw
208
212
  end
209
213
  end
@@ -225,6 +229,11 @@ class Riffer::Rig::Terminal::Frame
225
229
  @mutex.synchronize { @jobs }
226
230
  end
227
231
 
232
+ # @rbs return: String?
233
+ def reasoning
234
+ @mutex.synchronize { @reasoning&.call }
235
+ end
236
+
228
237
  # @rbs content: String
229
238
  # @rbs return: void
230
239
  def peek(content)
@@ -445,6 +454,7 @@ class Riffer::Rig::Terminal::Frame
445
454
 
446
455
  usage = tally.call
447
456
  Riffer::Rig::Terminal::StatusBar.idle(
457
+ reasoning: @reasoning&.call,
448
458
  clock: @now.call.strftime('%H:%M'),
449
459
  up_tokens: usage&.input_tokens || 0,
450
460
  down_tokens: usage&.output_tokens || 0,
@@ -38,6 +38,7 @@ class Riffer::Rig::Terminal::Opening
38
38
  Riffer::Rig::Terminal::Banner.lines(
39
39
  @theme,
40
40
  model: runtime.model,
41
+ reasoning: runtime.reasoning,
41
42
  cwd: runtime.cwd,
42
43
  skills: skills.zero? ? 'none' : skills.to_s,
43
44
  version: @version,
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Riffer::Rig::Terminal::Reasoning
4
+ DEFAULT = 'default' #: String
5
+ end
@@ -71,7 +71,7 @@ class Riffer::Rig::Terminal::Repl
71
71
  # @rbs return: void
72
72
  def watch(runtime)
73
73
  @mutex.synchronize { @runtime = runtime }
74
- @frame.session(tally: -> { runtime.tally })
74
+ @frame.session(tally: -> { runtime.tally }, reasoning: -> { runtime.reasoning })
75
75
  runtime.on_job_exit { wake }
76
76
  end
77
77
 
@@ -11,6 +11,8 @@ module Riffer::Rig::Terminal::StatusBar
11
11
 
12
12
  STATE_WIDTH = 7 #: Integer
13
13
 
14
+ REASONING_WIDTH = 7 #: Integer
15
+
14
16
  CLOCK_WIDTH = 5 #: Integer
15
17
 
16
18
  TOKEN_WIDTH = 6 #: Integer
@@ -25,19 +27,22 @@ module Riffer::Rig::Terminal::StatusBar
25
27
 
26
28
  SEPARATOR = ' ' #: String
27
29
 
28
- SLOTS = %i[glyph state clock up down cost jobs].freeze #: Array[Symbol]
30
+ SLOTS = %i[glyph state reasoning clock up down cost jobs].freeze #: Array[Symbol]
29
31
 
30
32
  # A narrow terminal sheds whole slots from the left in this order — the
31
- # clock first, then the cost, then the tokens, then the state — so the jobs
32
- # count at the right edge survives longest.
33
+ # reasoning level first, since it is static and recoverable via /reasoning,
34
+ # then the clock, then the cost, then the tokens, then the state — so the
35
+ # dynamic slots and the jobs count at the right edge survive longest.
33
36
  KEEP_SETS = [
34
37
  SLOTS,
35
- SLOTS - %i[clock],
36
- SLOTS - %i[clock cost],
37
- SLOTS - %i[clock cost up down],
38
+ SLOTS - %i[reasoning],
39
+ SLOTS - %i[reasoning clock],
40
+ SLOTS - %i[reasoning clock cost],
41
+ SLOTS - %i[reasoning clock cost up down],
38
42
  %i[glyph jobs]
39
43
  ].freeze #: Array[Array[Symbol]]
40
44
 
45
+ # @rbs reasoning: String?
41
46
  # @rbs clock: String
42
47
  # @rbs up_tokens: Integer
43
48
  # @rbs down_tokens: Integer
@@ -46,13 +51,14 @@ module Riffer::Rig::Terminal::StatusBar
46
51
  # @rbs theme: Riffer::Rig::Terminal::Theme
47
52
  # @rbs room: Integer
48
53
  # @rbs return: String
49
- def idle(clock:, up_tokens:, down_tokens:, cost:, jobs:, theme:, room:)
50
- slots = segments(IDLE_GLYPH, IDLE_STATE, clock, up_tokens, down_tokens, cost, jobs)
54
+ def idle(reasoning:, clock:, up_tokens:, down_tokens:, cost:, jobs:, theme:, room:)
55
+ slots = segments(IDLE_GLYPH, IDLE_STATE, reasoning, clock, up_tokens, down_tokens, cost, jobs)
51
56
  theme.dim(kept(slots, room).values.join(SEPARATOR))
52
57
  end
53
58
 
54
59
  # @rbs glyph: String
55
60
  # @rbs state: String
61
+ # @rbs reasoning: String?
56
62
  # @rbs clock: String
57
63
  # @rbs up_tokens: Integer
58
64
  # @rbs down_tokens: Integer
@@ -61,8 +67,8 @@ module Riffer::Rig::Terminal::StatusBar
61
67
  # @rbs theme: Riffer::Rig::Terminal::Theme
62
68
  # @rbs room: Integer
63
69
  # @rbs return: String
64
- def working(glyph:, state:, clock:, up_tokens:, down_tokens:, cost:, jobs:, theme:, room:)
65
- slots = segments(glyph, state, clock, up_tokens, down_tokens, cost, jobs)
70
+ def working(glyph:, state:, reasoning:, clock:, up_tokens:, down_tokens:, cost:, jobs:, theme:, room:)
71
+ slots = segments(glyph, state, reasoning, clock, up_tokens, down_tokens, cost, jobs)
66
72
  kept(slots, room).map { |key, text| color(text, key, theme) }.join(SEPARATOR)
67
73
  end
68
74
 
@@ -70,16 +76,18 @@ module Riffer::Rig::Terminal::StatusBar
70
76
 
71
77
  # @rbs glyph: String
72
78
  # @rbs state: String
79
+ # @rbs reasoning: String?
73
80
  # @rbs clock: String
74
81
  # @rbs up_tokens: Integer
75
82
  # @rbs down_tokens: Integer
76
83
  # @rbs cost: Float
77
84
  # @rbs jobs: Integer
78
85
  # @rbs return: Hash[Symbol, String]
79
- def segments(glyph, state, clock, up_tokens, down_tokens, cost, jobs)
86
+ def segments(glyph, state, reasoning, clock, up_tokens, down_tokens, cost, jobs)
80
87
  {
81
88
  glyph: field(glyph, GLYPH_WIDTH),
82
89
  state: field(state, STATE_WIDTH),
90
+ reasoning: field(reasoning || Riffer::Rig::Terminal::Reasoning::DEFAULT, REASONING_WIDTH),
83
91
  clock: field(clock, CLOCK_WIDTH),
84
92
  up: field("↑#{Riffer::Rig::Terminal::Count.call(up_tokens)}", TOKEN_WIDTH),
85
93
  down: field("↓#{Riffer::Rig::Terminal::Count.call(down_tokens)} tok", DOWN_WIDTH),
@@ -59,8 +59,9 @@ class Riffer::Rig::Terminal::Ticker
59
59
  def tick
60
60
  room = @footer.status_room
61
61
  jobs = @footer.job_count
62
+ reasoning = @footer.reasoning
62
63
  status = @mutex.synchronize do
63
- line(room, jobs).tap { @tick += 1 }
64
+ line(room, jobs, reasoning).tap { @tick += 1 }
64
65
  end
65
66
  @footer.status(status)
66
67
  end
@@ -76,14 +77,16 @@ class Riffer::Rig::Terminal::Ticker
76
77
 
77
78
  # @rbs room: Integer
78
79
  # @rbs jobs: Integer
80
+ # @rbs reasoning: String?
79
81
  # @rbs return: String
80
- def line(room, jobs)
82
+ def line(room, jobs, reasoning)
81
83
  stats = @buffer.stats
82
84
  usage = stats.usage
83
85
  estimate = @unmetered / CHARS_PER_TOKEN
84
86
  Riffer::Rig::Terminal::StatusBar.working(
85
87
  glyph: Riffer::Rig::Terminal::Spinner.call(@tick),
86
88
  state: stats.steps.zero? ? '' : "step #{stats.steps}",
89
+ reasoning: reasoning,
87
90
  clock: clock(stats.wall_time),
88
91
  up_tokens: usage&.input_tokens || 0,
89
92
  down_tokens: (usage&.output_tokens || 0) + estimate,
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Riffer
4
4
  module Rig
5
- VERSION = '0.15.0'
5
+ VERSION = '0.16.0'
6
6
  end
7
7
  end
@@ -17,23 +17,25 @@ module Riffer::Rig::Terminal::Banner
17
17
 
18
18
  # @rbs theme: Riffer::Rig::Terminal::Theme
19
19
  # @rbs model: String
20
+ # @rbs reasoning: String?
20
21
  # @rbs cwd: String
21
22
  # @rbs skills: String
22
23
  # @rbs version: String
23
24
  # @rbs tally: Riffer::Providers::TokenUsage?
24
25
  # @rbs width: Integer?
25
26
  # @rbs return: String
26
- def call: (Riffer::Rig::Terminal::Theme theme, model: String, cwd: String, skills: String, version: String, ?tally: Riffer::Providers::TokenUsage?, ?width: Integer?) -> String
27
+ def call: (Riffer::Rig::Terminal::Theme theme, model: String, reasoning: String?, cwd: String, skills: String, version: String, ?tally: Riffer::Providers::TokenUsage?, ?width: Integer?) -> String
27
28
 
28
29
  # @rbs theme: Riffer::Rig::Terminal::Theme
29
30
  # @rbs model: String
31
+ # @rbs reasoning: String?
30
32
  # @rbs cwd: String
31
33
  # @rbs skills: String
32
34
  # @rbs version: String
33
35
  # @rbs tally: Riffer::Providers::TokenUsage?
34
36
  # @rbs width: Integer?
35
37
  # @rbs return: Array[String]
36
- def lines: (Riffer::Rig::Terminal::Theme theme, model: String, cwd: String, skills: String, version: String, tally: Riffer::Providers::TokenUsage?, width: Integer?) -> Array[String]
38
+ def lines: (Riffer::Rig::Terminal::Theme theme, model: String, reasoning: String?, cwd: String, skills: String, version: String, tally: Riffer::Providers::TokenUsage?, width: Integer?) -> Array[String]
37
39
 
38
40
  # @rbs theme: Riffer::Rig::Terminal::Theme
39
41
  # @rbs width: Integer?
@@ -42,13 +44,14 @@ module Riffer::Rig::Terminal::Banner
42
44
 
43
45
  # @rbs theme: Riffer::Rig::Terminal::Theme
44
46
  # @rbs model: String
47
+ # @rbs reasoning: String?
45
48
  # @rbs cwd: String
46
49
  # @rbs skills: String
47
50
  # @rbs version: String
48
51
  # @rbs tally: Riffer::Providers::TokenUsage?
49
52
  # @rbs width: Integer?
50
53
  # @rbs return: Array[String]
51
- def info: (Riffer::Rig::Terminal::Theme theme, model: String, cwd: String, skills: String, version: String, tally: Riffer::Providers::TokenUsage?, width: Integer?) -> Array[String]
54
+ def info: (Riffer::Rig::Terminal::Theme theme, model: String, reasoning: String?, cwd: String, skills: String, version: String, tally: Riffer::Providers::TokenUsage?, width: Integer?) -> Array[String]
52
55
 
53
56
  private
54
57
 
@@ -67,6 +67,8 @@ class Riffer::Rig::Terminal::Frame
67
67
 
68
68
  @tally: (^() -> Riffer::Providers::TokenUsage?)?
69
69
 
70
+ @reasoning: (^() -> String?)?
71
+
70
72
  @now: ^() -> Time
71
73
 
72
74
  @mutex: Mutex
@@ -127,8 +129,9 @@ class Riffer::Rig::Terminal::Frame
127
129
  def jobs: (Integer count) -> void
128
130
 
129
131
  # @rbs tally: ^() -> Riffer::Providers::TokenUsage?
132
+ # @rbs reasoning: ^() -> String?
130
133
  # @rbs return: void
131
- def session: (tally: ^() -> Riffer::Providers::TokenUsage?) -> void
134
+ def session: (tally: ^() -> Riffer::Providers::TokenUsage?, reasoning: ^() -> String?) -> void
132
135
 
133
136
  # @rbs return: void
134
137
  def idle_tick: () -> void
@@ -139,6 +142,9 @@ class Riffer::Rig::Terminal::Frame
139
142
  # @rbs return: Integer
140
143
  def job_count: () -> Integer
141
144
 
145
+ # @rbs return: String?
146
+ def reasoning: () -> String?
147
+
142
148
  # @rbs content: String
143
149
  # @rbs return: void
144
150
  def peek: (String content) -> void
@@ -0,0 +1,5 @@
1
+ # Generated from lib/riffer/rig/terminal/reasoning.rb with RBS::Inline
2
+
3
+ module Riffer::Rig::Terminal::Reasoning
4
+ DEFAULT: String
5
+ end
@@ -9,6 +9,8 @@ module Riffer::Rig::Terminal::StatusBar
9
9
 
10
10
  STATE_WIDTH: Integer
11
11
 
12
+ REASONING_WIDTH: Integer
13
+
12
14
  CLOCK_WIDTH: Integer
13
15
 
14
16
  TOKEN_WIDTH: Integer
@@ -26,10 +28,12 @@ module Riffer::Rig::Terminal::StatusBar
26
28
  SLOTS: Array[Symbol]
27
29
 
28
30
  # A narrow terminal sheds whole slots from the left in this order — the
29
- # clock first, then the cost, then the tokens, then the state — so the jobs
30
- # count at the right edge survives longest.
31
+ # reasoning level first, since it is static and recoverable via /reasoning,
32
+ # then the clock, then the cost, then the tokens, then the state — so the
33
+ # dynamic slots and the jobs count at the right edge survive longest.
31
34
  KEEP_SETS: Array[Array[Symbol]]
32
35
 
36
+ # @rbs reasoning: String?
33
37
  # @rbs clock: String
34
38
  # @rbs up_tokens: Integer
35
39
  # @rbs down_tokens: Integer
@@ -38,10 +42,11 @@ module Riffer::Rig::Terminal::StatusBar
38
42
  # @rbs theme: Riffer::Rig::Terminal::Theme
39
43
  # @rbs room: Integer
40
44
  # @rbs return: String
41
- def idle: (clock: String, up_tokens: Integer, down_tokens: Integer, cost: Float, jobs: Integer, theme: Riffer::Rig::Terminal::Theme, room: Integer) -> String
45
+ def idle: (reasoning: String?, clock: String, up_tokens: Integer, down_tokens: Integer, cost: Float, jobs: Integer, theme: Riffer::Rig::Terminal::Theme, room: Integer) -> String
42
46
 
43
47
  # @rbs glyph: String
44
48
  # @rbs state: String
49
+ # @rbs reasoning: String?
45
50
  # @rbs clock: String
46
51
  # @rbs up_tokens: Integer
47
52
  # @rbs down_tokens: Integer
@@ -50,19 +55,20 @@ module Riffer::Rig::Terminal::StatusBar
50
55
  # @rbs theme: Riffer::Rig::Terminal::Theme
51
56
  # @rbs room: Integer
52
57
  # @rbs return: String
53
- def working: (glyph: String, state: String, clock: String, up_tokens: Integer, down_tokens: Integer, cost: Float, jobs: Integer, theme: Riffer::Rig::Terminal::Theme, room: Integer) -> String
58
+ def working: (glyph: String, state: String, reasoning: String?, clock: String, up_tokens: Integer, down_tokens: Integer, cost: Float, jobs: Integer, theme: Riffer::Rig::Terminal::Theme, room: Integer) -> String
54
59
 
55
60
  private
56
61
 
57
62
  # @rbs glyph: String
58
63
  # @rbs state: String
64
+ # @rbs reasoning: String?
59
65
  # @rbs clock: String
60
66
  # @rbs up_tokens: Integer
61
67
  # @rbs down_tokens: Integer
62
68
  # @rbs cost: Float
63
69
  # @rbs jobs: Integer
64
70
  # @rbs return: Hash[Symbol, String]
65
- def segments: (String glyph, String state, String clock, Integer up_tokens, Integer down_tokens, Float cost, Integer jobs) -> Hash[Symbol, String]
71
+ def segments: (String glyph, String state, String? reasoning, String clock, Integer up_tokens, Integer down_tokens, Float cost, Integer jobs) -> Hash[Symbol, String]
66
72
 
67
73
  # @rbs slots: Hash[Symbol, String]
68
74
  # @rbs room: Integer
@@ -47,8 +47,9 @@ class Riffer::Rig::Terminal::Ticker
47
47
 
48
48
  # @rbs room: Integer
49
49
  # @rbs jobs: Integer
50
+ # @rbs reasoning: String?
50
51
  # @rbs return: String
51
- def line: (Integer room, Integer jobs) -> String
52
+ def line: (Integer room, Integer jobs, String? reasoning) -> String
52
53
 
53
54
  # @rbs wall_time: Float
54
55
  # @rbs return: String
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: riffer-rig
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Bottrall
@@ -259,6 +259,7 @@ files:
259
259
  - lib/riffer/rig/terminal/picker.rb
260
260
  - lib/riffer/rig/terminal/question.rb
261
261
  - lib/riffer/rig/terminal/quiet_display.rb
262
+ - lib/riffer/rig/terminal/reasoning.rb
262
263
  - lib/riffer/rig/terminal/renderer.rb
263
264
  - lib/riffer/rig/terminal/repl.rb
264
265
  - lib/riffer/rig/terminal/replay.rb
@@ -429,6 +430,7 @@ files:
429
430
  - sig/generated/riffer/rig/terminal/picker.rbs
430
431
  - sig/generated/riffer/rig/terminal/question.rbs
431
432
  - sig/generated/riffer/rig/terminal/quiet_display.rbs
433
+ - sig/generated/riffer/rig/terminal/reasoning.rbs
432
434
  - sig/generated/riffer/rig/terminal/renderer.rbs
433
435
  - sig/generated/riffer/rig/terminal/repl.rbs
434
436
  - sig/generated/riffer/rig/terminal/replay.rbs