riffer-rig 0.15.0 → 0.17.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 +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +25 -204
- data/lib/riffer/rig/acp/mapper.rb +14 -2
- data/lib/riffer/rig/acp/servers.rb +42 -0
- data/lib/riffer/rig/acp.rb +17 -56
- data/lib/riffer/rig/cli/flags.rb +5 -0
- data/lib/riffer/rig/cli/sessions.rb +0 -3
- data/lib/riffer/rig/cli.rb +0 -3
- data/lib/riffer/rig/commands/auth.rb +28 -20
- data/lib/riffer/rig/commands/model.rb +37 -34
- data/lib/riffer/rig/commands/reasoning.rb +10 -10
- data/lib/riffer/rig/commands/reload.rb +6 -6
- data/lib/riffer/rig/credentials.rb +46 -14
- data/lib/riffer/rig/directories.rb +7 -0
- data/lib/riffer/rig/extension.rb +1 -0
- data/lib/riffer/rig/headless.rb +3 -2
- data/lib/riffer/rig/loader/extension_source.rb +4 -4
- data/lib/riffer/rig/loader/reasoning.rb +25 -0
- data/lib/riffer/rig/loader/reloader.rb +91 -0
- data/lib/riffer/rig/loader.rb +52 -171
- data/lib/riffer/rig/mcp/auth.rb +11 -22
- data/lib/riffer/rig/mcp/declaration.rb +3 -3
- data/lib/riffer/rig/mcp.rb +0 -8
- data/lib/riffer/rig/prompts/agents_md.rb +1 -1
- data/lib/riffer/rig/provider_setup.rb +22 -3
- data/lib/riffer/rig/runtime/assembly.rb +128 -0
- data/lib/riffer/rig/{tools/bash → runtime}/job.rb +4 -5
- data/lib/riffer/rig/{tools/bash → runtime}/job_log.rb +1 -1
- data/lib/riffer/rig/runtime/job_notices.rb +89 -0
- data/lib/riffer/rig/{tools/bash → runtime}/jobs.rb +38 -29
- data/lib/riffer/rig/runtime/mcp_registrar.rb +4 -3
- data/lib/riffer/rig/runtime/snapshot.rb +18 -0
- data/lib/riffer/rig/runtime/stop_reason.rb +22 -0
- data/lib/riffer/rig/runtime/turn.rb +151 -0
- data/lib/riffer/rig/runtime.rb +108 -347
- data/lib/riffer/rig/sdk.rb +8 -10
- data/lib/riffer/rig/settings/file.rb +3 -3
- data/lib/riffer/rig/settings/model_options.rb +23 -0
- data/lib/riffer/rig/settings.rb +2 -1
- data/lib/riffer/rig/stores/entries.rb +4 -4
- data/lib/riffer/rig/stores/jsonl.rb +1 -1
- data/lib/riffer/rig/stores/message_entry.rb +1 -1
- data/lib/riffer/rig/stores/model_entry.rb +1 -1
- data/lib/riffer/rig/stores/reasoning_entry.rb +1 -1
- data/lib/riffer/rig/stores/recorder.rb +7 -11
- data/lib/riffer/rig/stores/skill_entry.rb +1 -1
- data/lib/riffer/rig/stores/timestamp.rb +6 -0
- data/lib/riffer/rig/terminal/banner.rb +36 -7
- data/lib/riffer/rig/terminal/count.rb +6 -0
- data/lib/riffer/rig/terminal/frame.rb +12 -4
- data/lib/riffer/rig/terminal/message_queue.rb +4 -4
- data/lib/riffer/rig/terminal/opening.rb +1 -0
- data/lib/riffer/rig/terminal/picker.rb +5 -2
- data/lib/riffer/rig/terminal/repl.rb +2 -2
- data/lib/riffer/rig/terminal/session.rb +0 -2
- data/lib/riffer/rig/terminal/status_bar.rb +20 -12
- data/lib/riffer/rig/terminal/theme.rb +1 -0
- data/lib/riffer/rig/terminal/ticker.rb +8 -3
- data/lib/riffer/rig/terminal/turn_report.rb +1 -1
- data/lib/riffer/rig/tools/bash/kill.rb +1 -1
- data/lib/riffer/rig/tools/bash/output.rb +3 -2
- data/lib/riffer/rig/tools/bash.rb +3 -4
- data/lib/riffer/rig/version.rb +1 -1
- data/sig/generated/riffer/rig/acp/mapper.rbs +4 -0
- data/sig/generated/riffer/rig/acp/servers.rbs +21 -0
- data/sig/generated/riffer/rig/acp.rbs +4 -19
- data/sig/generated/riffer/rig/cli/flags.rbs +2 -0
- data/sig/generated/riffer/rig/commands/auth.rbs +18 -5
- data/sig/generated/riffer/rig/commands/model.rbs +19 -13
- data/sig/generated/riffer/rig/commands/reasoning.rbs +4 -6
- data/sig/generated/riffer/rig/commands/reload.rbs +4 -4
- data/sig/generated/riffer/rig/credentials.rbs +18 -1
- data/sig/generated/riffer/rig/directories.rbs +5 -0
- data/sig/generated/riffer/rig/loader/reasoning.rbs +13 -0
- data/sig/generated/riffer/rig/loader/reloader.rbs +50 -0
- data/sig/generated/riffer/rig/loader.rbs +13 -88
- data/sig/generated/riffer/rig/mcp/auth.rbs +6 -9
- data/sig/generated/riffer/rig/mcp/declaration.rbs +0 -3
- data/sig/generated/riffer/rig/mcp.rbs +0 -4
- data/sig/generated/riffer/rig/provider_setup.rbs +10 -5
- data/sig/generated/riffer/rig/runtime/assembly.rbs +74 -0
- data/sig/generated/riffer/rig/{tools/bash → runtime}/job.rbs +8 -8
- data/sig/generated/riffer/rig/{tools/bash → runtime}/job_log.rbs +2 -2
- data/sig/generated/riffer/rig/runtime/job_notices.rbs +46 -0
- data/sig/generated/riffer/rig/runtime/jobs.rbs +86 -0
- data/sig/generated/riffer/rig/runtime/snapshot.rbs +12 -0
- data/sig/generated/riffer/rig/runtime/stop_reason.rbs +13 -0
- data/sig/generated/riffer/rig/runtime/turn.rbs +74 -0
- data/sig/generated/riffer/rig/runtime.rbs +45 -148
- data/sig/generated/riffer/rig/sdk.rbs +5 -5
- data/sig/generated/riffer/rig/settings/file.rbs +2 -2
- data/sig/generated/riffer/rig/settings/model_options.rbs +12 -0
- data/sig/generated/riffer/rig/stores/entries.rbs +2 -2
- data/sig/generated/riffer/rig/stores/recorder.rbs +2 -8
- data/sig/generated/riffer/rig/stores/timestamp.rbs +4 -0
- data/sig/generated/riffer/rig/terminal/banner.rbs +10 -3
- data/sig/generated/riffer/rig/terminal/count.rbs +4 -0
- data/sig/generated/riffer/rig/terminal/frame.rbs +7 -3
- data/sig/generated/riffer/rig/terminal/message_queue.rbs +5 -5
- data/sig/generated/riffer/rig/terminal/picker.rbs +4 -1
- data/sig/generated/riffer/rig/terminal/status_bar.rbs +11 -5
- data/sig/generated/riffer/rig/terminal/ticker.rbs +4 -1
- data/sig/generated/riffer/rig/tools/bash.rbs +0 -2
- data/sig/manual/riffer/rig/{terminal/tally.rbs → acp/servers.rbs} +2 -2
- data/sig/manual/riffer/rig/{terminal/spinner.rbs → loader/reasoning.rbs} +2 -2
- data/sig/manual/riffer/rig/mcp.rbs +0 -5
- data/sig/manual/riffer/rig/{loader/credentials.rbs → runtime/stop_reason.rbs} +2 -2
- metadata +24 -20
- data/lib/riffer/rig/loader/credentials.rb +0 -23
- data/lib/riffer/rig/terminal/message_queue/message.rb +0 -13
- data/lib/riffer/rig/terminal/spinner.rb +0 -13
- data/lib/riffer/rig/terminal/tally.rb +0 -12
- data/lib/riffer/rig/tools/bash/monotonic_clock.rb +0 -8
- data/sig/generated/riffer/rig/loader/credentials.rbs +0 -12
- data/sig/generated/riffer/rig/terminal/message_queue/message.rbs +0 -10
- data/sig/generated/riffer/rig/terminal/spinner.rbs +0 -9
- data/sig/generated/riffer/rig/terminal/tally.rbs +0 -7
- data/sig/generated/riffer/rig/tools/bash/jobs.rbs +0 -86
- data/sig/generated/riffer/rig/tools/bash/monotonic_clock.rbs +0 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9b1648704ba63c4826d30aec44988486557c4a2fccc5b3b527fef879fc407cef
|
|
4
|
+
data.tar.gz: 574c73fcb0f3512f486d6d4c355b69add66d54de82c87ed170c8409821c4b3f1
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0da1408916c461365dc8d61c07dca3b00ad5f2d22d77e42d036d379ce6231c5cff0ae3ade79a2d8f907439c8e70e33753ae3e5b91786905e5366bf414d0f3521
|
|
7
|
+
data.tar.gz: 994802e5749d8708313eda67386af949ff4112b9b2275b6687a2078ea2f4332b8147916608cdbf8adb5f90c632bea9b395d5929b9024907f4811e2ecca75eff1
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.17.0](https://github.com/bottrall/riffer-rig/compare/v0.16.0...v0.17.0) (2026-10-24)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* authenticate to Anthropic through the SDK's credential chain ([#606](https://github.com/bottrall/riffer-rig/issues/606)) ([617fdb7](https://github.com/bottrall/riffer-rig/commit/617fdb76f5a54290d2d001b906fce3d530e2cef4))
|
|
9
|
+
* give tools the background job registry as a documented context key ([#579](https://github.com/bottrall/riffer-rig/issues/579)) ([a8e98ac](https://github.com/bottrall/riffer-rig/commit/a8e98acef71b354a2c7601d46366ed0f95b6da37))
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* --save and /auth honour the injected home ([#596](https://github.com/bottrall/riffer-rig/issues/596)) ([032f26f](https://github.com/bottrall/riffer-rig/commit/032f26f317216f501a9dc71c3d1157ce12bc3893))
|
|
15
|
+
* /auth and /model refusals say how to supply a missing credential ([#597](https://github.com/bottrall/riffer-rig/issues/597)) ([ffe18fe](https://github.com/bottrall/riffer-rig/commit/ffe18fe653db97f4bbb737359f454637bc347778))
|
|
16
|
+
* reasoning in settings reaches the agent ([#577](https://github.com/bottrall/riffer-rig/issues/577)) ([9216429](https://github.com/bottrall/riffer-rig/commit/9216429421be51bb7511e4c8b6eec3bd177e8ba3))
|
|
17
|
+
* reload each Loader-built runtime with its own build's choices ([#598](https://github.com/bottrall/riffer-rig/issues/598)) ([d6b95a6](https://github.com/bottrall/riffer-rig/commit/d6b95a6f1205ecb8ae0221c88bb1012c2ce15e79))
|
|
18
|
+
* **terminal:** keep a capped input inside a short terminal ([#575](https://github.com/bottrall/riffer-rig/issues/575)) ([8b67c2b](https://github.com/bottrall/riffer-rig/commit/8b67c2b9d1ba404ffb12bb42f120e034aae8183b))
|
|
19
|
+
|
|
20
|
+
## [0.16.0](https://github.com/bottrall/riffer-rig/compare/v0.15.0...v0.16.0) (2026-10-18)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
### Features
|
|
24
|
+
|
|
25
|
+
* **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))
|
|
26
|
+
|
|
3
27
|
## [0.15.0](https://github.com/bottrall/riffer-rig/compare/v0.14.0...v0.15.0) (2026-10-18)
|
|
4
28
|
|
|
5
29
|
|
|
@@ -15,6 +39,10 @@
|
|
|
15
39
|
## [0.14.0](https://github.com/bottrall/riffer-rig/compare/v0.13.0...v0.14.0) (2026-10-18)
|
|
16
40
|
|
|
17
41
|
|
|
42
|
+
### Upgrading
|
|
43
|
+
|
|
44
|
+
* 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`.
|
|
45
|
+
|
|
18
46
|
### Features
|
|
19
47
|
|
|
20
48
|
* **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,66 @@
|
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
9
|
+
## Install
|
|
10
10
|
|
|
11
|
-
|
|
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
|
-
|
|
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
|
|
36
|
-
|
|
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)).
|
|
38
|
-
|
|
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)):
|
|
61
|
-
|
|
62
|
-
```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
|
|
66
|
-
```
|
|
67
|
-
|
|
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)).
|
|
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.
|
|
83
25
|
|
|
84
|
-
|
|
26
|
+
Type a prompt and press Enter. `/help` lists the commands, and [Getting started](docs/GETTING_STARTED.md) walks through a first session.
|
|
85
27
|
|
|
86
|
-
|
|
28
|
+
## Extend it
|
|
87
29
|
|
|
88
|
-
|
|
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
|
-
```
|
|
114
|
-
|
|
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.
|
|
116
|
-
|
|
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:
|
|
30
|
+
An extension is a Ruby block in `~/.riffer/rig.rb` (every project) or `.riffer/rig.rb` (one project):
|
|
143
31
|
|
|
144
32
|
```ruby
|
|
145
33
|
Riffer::Rig.extension('git') do |rig|
|
|
146
|
-
|
|
147
|
-
rig.
|
|
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`}" }
|
|
34
|
+
rig.command('log', description: 'Recent commits') { |ctx| ctx.say `git log --oneline -n 10` }
|
|
35
|
+
rig.prompt(:branch) { "Current branch: #{`git branch --show-current`}" }
|
|
156
36
|
end
|
|
157
37
|
```
|
|
158
38
|
|
|
159
|
-
|
|
39
|
+
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.
|
|
160
40
|
|
|
161
|
-
##
|
|
162
|
-
|
|
163
|
-
The runtime is separate from the terminal, so the same agent runs inside any Ruby process:
|
|
41
|
+
## Embed it
|
|
164
42
|
|
|
165
43
|
```ruby
|
|
166
44
|
runtime = Riffer::Rig::Loader.runtime(cwd: Dir.pwd, host: Riffer::Rig::Hosts::Null.new)
|
|
167
|
-
|
|
168
|
-
puts response.content
|
|
45
|
+
puts runtime.ask('why is this test failing?').content
|
|
169
46
|
```
|
|
170
47
|
|
|
171
|
-
|
|
48
|
+
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
49
|
|
|
173
50
|
## Documentation
|
|
174
51
|
|
|
175
|
-
The guides
|
|
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
|
|
52
|
+
The guides are at **<https://riffer.bottrall.dev>**, and their sources are in [`docs/`](docs/). Good places to start:
|
|
194
53
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
- [
|
|
198
|
-
- [
|
|
199
|
-
- [
|
|
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>.
|
|
207
|
-
|
|
208
|
-
## Development
|
|
209
|
-
|
|
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.
|
|
229
|
-
|
|
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.
|
|
54
|
+
- [Overview](docs/OVERVIEW.md) — what riffer-rig is and how it fits together
|
|
55
|
+
- [Configuration](docs/CONFIGURATION.md) — settings and where each value comes from
|
|
56
|
+
- [Command line](docs/CLI.md) — every flag
|
|
57
|
+
- [Headless mode](docs/HEADLESS.md) — one prompt from a script or CI job, with exit codes and an NDJSON stream
|
|
58
|
+
- [ACP](docs/ACP.md) — riffer-rig in your editor, with `riffer acp`
|
|
59
|
+
- [Extensions](docs/EXTENSIONS.md) — your own tools, commands and hooks
|
|
231
60
|
|
|
232
61
|
## Contributing
|
|
233
62
|
|
|
234
|
-
|
|
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).
|
|
63
|
+
[`AGENTS.md`](AGENTS.md) has the scripts and the project's conventions. Please follow the [Code of Conduct](CODE_OF_CONDUCT.md).
|
|
239
64
|
|
|
240
65
|
## License
|
|
241
66
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
## Maintainer
|
|
245
|
-
|
|
246
|
-
- Jake Bottrall - https://github.com/bottrall
|
|
67
|
+
MIT — see [`LICENSE.txt`](LICENSE.txt). Maintained by [Jake Bottrall](https://github.com/bottrall).
|
|
@@ -27,6 +27,18 @@ module Riffer::Rig::ACP::Mapper
|
|
|
27
27
|
blocks.filter_map { |block| prompt_text_of(block) }.join("\n\n")
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
+
# @rbs event: ::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event
|
|
31
|
+
# @rbs return: ::ACP::Types::SessionUpdate::t?
|
|
32
|
+
def update_for(event)
|
|
33
|
+
case event
|
|
34
|
+
when Riffer::StreamEvents::TextDelta then message_chunk(event.content)
|
|
35
|
+
when Riffer::Rig::Events::CommandOutput then message_chunk(event.text)
|
|
36
|
+
when Riffer::StreamEvents::ToolCallDone then tool_call(event)
|
|
37
|
+
when Riffer::Rig::Events::AfterToolCall then tool_call_done(event)
|
|
38
|
+
when Riffer::Rig::Events::JobExit then job_exit
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
30
42
|
# @rbs content: String
|
|
31
43
|
# @rbs return: ::ACP::Types::SessionUpdate::AgentMessageChunk
|
|
32
44
|
def message_chunk(content)
|
|
@@ -96,10 +108,10 @@ module Riffer::Rig::ACP::Mapper
|
|
|
96
108
|
def stop_reason_of(stop_reason)
|
|
97
109
|
case stop_reason
|
|
98
110
|
when :completed then ::ACP::Types::StopReason::END_TURN
|
|
99
|
-
when Riffer::Rig::Runtime::INTERRUPT_CANCELLED then ::ACP::Types::StopReason::CANCELLED
|
|
111
|
+
when Riffer::Rig::Runtime::StopReason::INTERRUPT_CANCELLED then ::ACP::Types::StopReason::CANCELLED
|
|
100
112
|
when :max_steps then ::ACP::Types::StopReason::MAX_TURN_REQUESTS
|
|
101
113
|
else
|
|
102
|
-
stopped = Riffer::Rig::Runtime::STOPPED_WITHOUT_COMPLETING.include?(stop_reason)
|
|
114
|
+
stopped = Riffer::Rig::Runtime::StopReason::STOPPED_WITHOUT_COMPLETING.include?(stop_reason)
|
|
103
115
|
stopped ? ::ACP::Types::StopReason::MAX_TOKENS : ::ACP::Types::StopReason::REFUSAL
|
|
104
116
|
end
|
|
105
117
|
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Riffer::Rig::ACP::Servers
|
|
4
|
+
extend self
|
|
5
|
+
|
|
6
|
+
# @rbs servers: Array[::ACP::Types::McpServer::t]
|
|
7
|
+
# @rbs return: Hash[String, Riffer::Rig::Mcp::Declaration]
|
|
8
|
+
def declarations(servers)
|
|
9
|
+
servers.filter_map { |server| declaration_of(server) }.to_h
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# @rbs servers: Array[::ACP::Types::McpServer::t]
|
|
13
|
+
# @rbs return: Array[String]
|
|
14
|
+
def warnings(servers)
|
|
15
|
+
servers.filter_map do |server|
|
|
16
|
+
next if server.is_a?(::ACP::Types::McpServerStdio)
|
|
17
|
+
|
|
18
|
+
"Skipped MCP server #{name_of(server)}: only stdio servers are supported"
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# @rbs server: ::ACP::Types::McpServer::t
|
|
25
|
+
# @rbs return: [String, Riffer::Rig::Mcp::Declaration]?
|
|
26
|
+
def declaration_of(server)
|
|
27
|
+
return unless server.is_a?(::ACP::Types::McpServerStdio)
|
|
28
|
+
|
|
29
|
+
[
|
|
30
|
+
server.name,
|
|
31
|
+
Riffer::Rig::Mcp::Declaration.new(
|
|
32
|
+
command: server.command, args: server.args, env: server.env.to_h { |variable| [variable.name, variable.value] }
|
|
33
|
+
)
|
|
34
|
+
]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @rbs server: ::ACP::Types::McpServer::t
|
|
38
|
+
# @rbs return: String
|
|
39
|
+
def name_of(server)
|
|
40
|
+
server.is_a?(Hash) ? server['name'].to_s : server.name
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/riffer/rig/acp.rb
CHANGED
|
@@ -36,10 +36,6 @@ class Riffer::Rig::ACP
|
|
|
36
36
|
new(input: input, output: output, env: env, home: home, cwd: cwd, build: build, adopt: adopt)
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
-
# Builds the Runtime a session runs on: the client's MCP servers flow
|
|
40
|
-
# through the rig.mcp seam as a synthetic extension, so a reload rebuild
|
|
41
|
-
# re-declares them and a same-name declaration from an extension replaces
|
|
42
|
-
# them with the usual notify.
|
|
43
39
|
# @rbs cwd: String
|
|
44
40
|
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
45
41
|
# @rbs servers: Hash[String, Riffer::Rig::Mcp::Declaration]
|
|
@@ -57,6 +53,8 @@ class Riffer::Rig::ACP
|
|
|
57
53
|
def self.session_extensions(servers)
|
|
58
54
|
return [] if servers.empty?
|
|
59
55
|
|
|
56
|
+
# A synthetic extension, so a reload rebuild re-declares the servers and an
|
|
57
|
+
# extension's same-name declaration replaces them with the usual notify.
|
|
60
58
|
extension = Riffer::Rig::Extension.new(SESSION_EXTENSION) do |rig|
|
|
61
59
|
servers.each { |name, server| rig.mcp(name, command: server.command, args: server.args, env: server.env) }
|
|
62
60
|
end
|
|
@@ -88,8 +86,6 @@ class Riffer::Rig::ACP
|
|
|
88
86
|
@lock = Mutex.new
|
|
89
87
|
end
|
|
90
88
|
|
|
91
|
-
# Serves the connection until the client closes stdin; then the reader
|
|
92
|
-
# thread ends and run returns.
|
|
93
89
|
# @rbs &block: ? (::ACP::AgentConnection) -> void
|
|
94
90
|
# @rbs return: Integer
|
|
95
91
|
def run
|
|
@@ -129,7 +125,7 @@ class Riffer::Rig::ACP
|
|
|
129
125
|
end
|
|
130
126
|
::ACP::Types::NewSessionResponse.new(session_id: runtime.id)
|
|
131
127
|
rescue StandardError => e
|
|
132
|
-
|
|
128
|
+
internal_error(e)
|
|
133
129
|
end
|
|
134
130
|
|
|
135
131
|
# @rbs request: ::ACP::Types::PromptRequest
|
|
@@ -143,19 +139,10 @@ class Riffer::Rig::ACP
|
|
|
143
139
|
stop_reason = :completed
|
|
144
140
|
dispatch(runtime, Riffer::Rig::ACP::Mapper.prompt_text(request.prompt)) do |event|
|
|
145
141
|
case event
|
|
146
|
-
when Riffer::
|
|
147
|
-
client.update(request.session_id, Riffer::Rig::ACP::Mapper.message_chunk(event.content))
|
|
148
|
-
when Riffer::Rig::Events::CommandOutput
|
|
149
|
-
client.update(request.session_id, Riffer::Rig::ACP::Mapper.message_chunk(event.text))
|
|
150
|
-
when Riffer::StreamEvents::ToolCallDone
|
|
151
|
-
client.update(request.session_id, Riffer::Rig::ACP::Mapper.tool_call(event))
|
|
152
|
-
when Riffer::Rig::Events::AfterToolCall
|
|
153
|
-
client.update(request.session_id, Riffer::Rig::ACP::Mapper.tool_call_done(event))
|
|
154
|
-
when Riffer::Rig::Events::JobExit
|
|
155
|
-
client.update(request.session_id, Riffer::Rig::ACP::Mapper.job_exit)
|
|
156
|
-
when Riffer::Rig::Events::TurnEnd
|
|
157
|
-
stop_reason = event.stop_reason
|
|
142
|
+
when Riffer::Rig::Events::TurnEnd then stop_reason = event.stop_reason
|
|
158
143
|
end
|
|
144
|
+
update = Riffer::Rig::ACP::Mapper.update_for(event)
|
|
145
|
+
client.update(request.session_id, update) if update
|
|
159
146
|
end
|
|
160
147
|
# A reload in the dispatch, by /reload or the automatic check, can change
|
|
161
148
|
# the commands; its rebuild also reorders them, which is no change.
|
|
@@ -168,9 +155,7 @@ class Riffer::Rig::ACP
|
|
|
168
155
|
message: 'a prompt is already running on this session; wait for it to finish or cancel it'
|
|
169
156
|
)
|
|
170
157
|
rescue StandardError => e
|
|
171
|
-
|
|
172
|
-
code: ::ACP::RequestError::INTERNAL_ERROR, message: Riffer::Rig::Runtime::ErrorMessage.of(e)
|
|
173
|
-
)
|
|
158
|
+
internal_error(e)
|
|
174
159
|
end
|
|
175
160
|
|
|
176
161
|
# @rbs request: ::ACP::Types::LoadSessionRequest
|
|
@@ -192,7 +177,7 @@ class Riffer::Rig::ACP
|
|
|
192
177
|
client.available_commands(request.session_id, available_commands(runtime))
|
|
193
178
|
::ACP::Types::LoadSessionResponse.new
|
|
194
179
|
rescue StandardError => e
|
|
195
|
-
|
|
180
|
+
internal_error(e)
|
|
196
181
|
end
|
|
197
182
|
|
|
198
183
|
# @rbs request: ::ACP::Types::ListSessionsRequest
|
|
@@ -215,7 +200,7 @@ class Riffer::Rig::ACP
|
|
|
215
200
|
end
|
|
216
201
|
::ACP::Types::ListSessionsResponse.new(sessions: sessions)
|
|
217
202
|
rescue StandardError => e
|
|
218
|
-
|
|
203
|
+
internal_error(e)
|
|
219
204
|
end
|
|
220
205
|
|
|
221
206
|
# Runs on the transport's reader thread, so it asks the turn to stop and
|
|
@@ -255,6 +240,12 @@ class Riffer::Rig::ACP
|
|
|
255
240
|
@lock.synchronize { @sessions[id] }
|
|
256
241
|
end
|
|
257
242
|
|
|
243
|
+
# @rbs error: StandardError
|
|
244
|
+
# @rbs return: ::ACP::RequestError
|
|
245
|
+
def internal_error(error)
|
|
246
|
+
::ACP::RequestError.new(code: ::ACP::RequestError::INTERNAL_ERROR, message: Riffer::Rig::Runtime::ErrorMessage.of(error))
|
|
247
|
+
end
|
|
248
|
+
|
|
258
249
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
259
250
|
# @rbs text: String
|
|
260
251
|
# @rbs &: (::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void
|
|
@@ -312,38 +303,8 @@ class Riffer::Rig::ACP
|
|
|
312
303
|
# @rbs host: Riffer::Rig::ACP::Host
|
|
313
304
|
# @rbs return: Hash[String, Riffer::Rig::Mcp::Declaration]
|
|
314
305
|
def declarations(servers, host)
|
|
315
|
-
servers.
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
# @rbs server: ::ACP::Types::McpServer::t
|
|
319
|
-
# @rbs host: Riffer::Rig::ACP::Host
|
|
320
|
-
# @rbs return: [String, Riffer::Rig::Mcp::Declaration]?
|
|
321
|
-
def declaration_of(server, host)
|
|
322
|
-
unless server.is_a?(::ACP::Types::McpServerStdio)
|
|
323
|
-
host.notify("Skipped MCP server #{server_name(server)}: only stdio servers are supported", level: :warning)
|
|
324
|
-
return nil
|
|
325
|
-
end
|
|
326
|
-
|
|
327
|
-
[
|
|
328
|
-
server.name,
|
|
329
|
-
Riffer::Rig::Mcp::Declaration.new(command: server.command, args: server.args, env: stdio_env(server))
|
|
330
|
-
]
|
|
331
|
-
end
|
|
332
|
-
|
|
333
|
-
# @rbs server: ::ACP::Types::McpServer::t
|
|
334
|
-
# @rbs return: String
|
|
335
|
-
def server_name(server)
|
|
336
|
-
case server
|
|
337
|
-
when ::ACP::Types::McpServer::Http, ::ACP::Types::McpServer::Sse then server.name
|
|
338
|
-
when Hash then server['name'].to_s
|
|
339
|
-
else 'unnamed'
|
|
340
|
-
end
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
# @rbs server: ::ACP::Types::McpServerStdio
|
|
344
|
-
# @rbs return: Hash[String, String]
|
|
345
|
-
def stdio_env(server)
|
|
346
|
-
server.env.to_h { |variable| [variable.name, variable.value] }
|
|
306
|
+
Riffer::Rig::ACP::Servers.warnings(servers).each { |warning| host.notify(warning, level: :warning) }
|
|
307
|
+
Riffer::Rig::ACP::Servers.declarations(servers)
|
|
347
308
|
end
|
|
348
309
|
|
|
349
310
|
# @rbs runtime: Riffer::Rig::Runtime
|
data/lib/riffer/rig/cli/flags.rb
CHANGED
|
@@ -9,6 +9,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
9
9
|
riffer acp serve Runtimes as an ACP agent over stdio
|
|
10
10
|
TEXT
|
|
11
11
|
|
|
12
|
+
HEADLESS_ONLY = %i[verbose json job-idle-timeout].freeze #: Array[Symbol]
|
|
13
|
+
|
|
12
14
|
# @dynamic model, reasoning, extensions, skills, agents_md, tools, max_steps, help, version
|
|
13
15
|
# @dynamic save, verbose, json, job_idle_timeout, headless, prompt, continue, resume
|
|
14
16
|
attr_reader :model #: String?
|
|
@@ -34,6 +36,9 @@ class Riffer::Rig::CLI::Flags
|
|
|
34
36
|
return "invalid argument: --max-steps #{flags.max_steps}" if flags.max_steps&.<(1)
|
|
35
37
|
return "invalid argument: --job-idle-timeout #{flags.job_idle_timeout}" if flags.job_idle_timeout&.negative?
|
|
36
38
|
|
|
39
|
+
stray = HEADLESS_ONLY.find { |key| values.key?(key) } unless flags.headless || flags.help || flags.version
|
|
40
|
+
return "--#{stray} is only available with -p" if stray
|
|
41
|
+
|
|
37
42
|
flags
|
|
38
43
|
rescue OptionParser::ParseError => e
|
|
39
44
|
e.message
|
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
class Riffer::Rig::CLI::Sessions
|
|
4
4
|
OFF = 'Sessions are off for this run (--no-save).' #: String
|
|
5
5
|
|
|
6
|
-
# The terminal's sessions collaborator: translates the parsed flags into
|
|
7
|
-
# Loader builds and store lookups, so the host never sees the flags.
|
|
8
|
-
#
|
|
9
6
|
# @rbs @flags: Riffer::Rig::CLI::Flags
|
|
10
7
|
# @rbs @env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid
|
|
11
8
|
# @rbs @cwd: String
|
data/lib/riffer/rig/cli.rb
CHANGED
|
@@ -31,9 +31,6 @@ module Riffer::Rig::CLI
|
|
|
31
31
|
return refuse(flags, error) if flags.is_a?(String)
|
|
32
32
|
return help(output) if flags.help
|
|
33
33
|
return version(output) if flags.version
|
|
34
|
-
return refuse('--verbose is only available with -p', error) if flags.verbose && !flags.headless
|
|
35
|
-
return refuse('--json is only available with -p', error) if flags.json && !flags.headless
|
|
36
|
-
return refuse('--job-idle-timeout is only available with -p', error) if flags.job_idle_timeout && !flags.headless
|
|
37
34
|
|
|
38
35
|
return headless(flags, input:, output:, error:, env:, cwd:, home:) if flags.headless
|
|
39
36
|
|