rubino-agent 0.4.0 → 0.5.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/.rubocop_todo.yml +11 -2
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +137 -1
- data/CONTRIBUTING.md +10 -1
- data/README.md +14 -5
- data/Rakefile +31 -0
- data/docs/agents.md +42 -23
- data/docs/architecture.md +2 -2
- data/docs/commands.md +28 -1
- data/docs/configuration.md +20 -23
- data/docs/getting-started.md +5 -3
- data/docs/security.md +16 -5
- data/docs/troubleshooting.md +1 -1
- data/exe/rubino +16 -2
- data/install.sh +715 -54
- data/lib/rubino/active_agent.rb +73 -0
- data/lib/rubino/agent/action_claim_guard.rb +881 -0
- data/lib/rubino/agent/agent_registry.rb +5 -2
- data/lib/rubino/agent/definition.rb +1 -9
- data/lib/rubino/agent/fallback_chain.rb +0 -6
- data/lib/rubino/agent/iteration_budget.rb +109 -3
- data/lib/rubino/agent/loop.rb +476 -20
- data/lib/rubino/agent/model_call_runner.rb +81 -3
- data/lib/rubino/agent/prompts/build.txt +22 -5
- data/lib/rubino/agent/response_validator.rb +8 -0
- data/lib/rubino/agent/runner.rb +133 -8
- data/lib/rubino/agent/tool_executor.rb +166 -14
- data/lib/rubino/agent/truncation_continuation.rb +4 -1
- data/lib/rubino/api/server.rb +19 -0
- data/lib/rubino/boot/config_guard.rb +71 -0
- data/lib/rubino/cli/chat/completion_builder.rb +42 -6
- data/lib/rubino/cli/chat/idle_card_host.rb +7 -1
- data/lib/rubino/cli/chat/session_resolver.rb +87 -21
- data/lib/rubino/cli/chat_command.rb +1189 -50
- data/lib/rubino/cli/commands.rb +281 -1
- data/lib/rubino/cli/config_command.rb +68 -8
- data/lib/rubino/cli/doctor_command.rb +204 -12
- data/lib/rubino/cli/jobs_command.rb +12 -0
- data/lib/rubino/cli/memory_command.rb +53 -20
- data/lib/rubino/cli/onboarding_wizard.rb +79 -6
- data/lib/rubino/cli/session_command.rb +172 -18
- data/lib/rubino/cli/setup_command.rb +131 -8
- data/lib/rubino/cli/skills_command.rb +67 -20
- data/lib/rubino/cli/trust_gate.rb +16 -7
- data/lib/rubino/commands/built_ins.rb +2 -0
- data/lib/rubino/commands/command.rb +12 -2
- data/lib/rubino/commands/executor.rb +149 -12
- data/lib/rubino/commands/handlers/agent_switch.rb +100 -0
- data/lib/rubino/commands/handlers/agents.rb +133 -38
- data/lib/rubino/commands/handlers/config.rb +4 -1
- data/lib/rubino/commands/handlers/help.rb +113 -14
- data/lib/rubino/commands/handlers/memory.rb +15 -5
- data/lib/rubino/commands/handlers/sessions.rb +26 -3
- data/lib/rubino/commands/handlers/status.rb +9 -4
- data/lib/rubino/commands/loader.rb +12 -0
- data/lib/rubino/config/configuration.rb +86 -24
- data/lib/rubino/config/defaults.rb +140 -33
- data/lib/rubino/config/loader.rb +62 -12
- data/lib/rubino/config/validator.rb +341 -0
- data/lib/rubino/config/writer.rb +123 -31
- data/lib/rubino/context/compressor.rb +184 -22
- data/lib/rubino/context/message_boundary.rb +27 -1
- data/lib/rubino/context/project_languages.rb +90 -0
- data/lib/rubino/context/prompt_assembler.rb +104 -21
- data/lib/rubino/context/summary_builder.rb +45 -4
- data/lib/rubino/context/token_budget.rb +36 -11
- data/lib/rubino/context/token_estimate.rb +45 -0
- data/lib/rubino/context/tool_result_pruner.rb +81 -0
- data/lib/rubino/database/connection.rb +154 -3
- data/lib/rubino/database/migrations/001_create_initial_schema.rb +314 -40
- data/lib/rubino/database/migrator.rb +98 -5
- data/lib/rubino/documents/cap_exceeded.rb +13 -0
- data/lib/rubino/documents/converters/csv.rb +4 -3
- data/lib/rubino/documents/converters/docx.rb +29 -5
- data/lib/rubino/documents/converters/html.rb +5 -1
- data/lib/rubino/documents/converters/json.rb +2 -1
- data/lib/rubino/documents/converters/pdf.rb +11 -2
- data/lib/rubino/documents/converters/plain.rb +2 -1
- data/lib/rubino/documents/converters/pptx.rb +11 -2
- data/lib/rubino/documents/converters/xlsx.rb +35 -4
- data/lib/rubino/documents/converters/xml.rb +2 -1
- data/lib/rubino/documents/limits.rb +210 -0
- data/lib/rubino/documents.rb +10 -3
- data/lib/rubino/errors.rb +36 -5
- data/lib/rubino/interaction/cancel_token.rb +19 -3
- data/lib/rubino/interaction/events.rb +13 -0
- data/lib/rubino/interaction/lifecycle.rb +99 -13
- data/lib/rubino/interaction/polishing.rb +176 -0
- data/lib/rubino/jobs/cron_job_repository.rb +5 -8
- data/lib/rubino/jobs/handlers/cleanup_sessions_job.rb +11 -0
- data/lib/rubino/jobs/handlers/distill_skill_job.rb +65 -9
- data/lib/rubino/jobs/queue.rb +63 -8
- data/lib/rubino/jobs/runner.rb +24 -6
- data/lib/rubino/jobs/worker.rb +0 -4
- data/lib/rubino/llm/adapter_response.rb +47 -4
- data/lib/rubino/llm/credential_check.rb +15 -16
- data/lib/rubino/llm/error_classifier.rb +89 -1
- data/lib/rubino/llm/inline_think_filter.rb +69 -12
- data/lib/rubino/llm/request.rb +30 -3
- data/lib/rubino/llm/ruby_llm_adapter.rb +394 -46
- data/lib/rubino/llm/tool_bridge.rb +113 -9
- data/lib/rubino/mcp/manager.rb +18 -1
- data/lib/rubino/mcp/mcp_tool_wrapper.rb +14 -3
- data/lib/rubino/memory/aux_retry.rb +107 -0
- data/lib/rubino/memory/backends/sqlite.rb +73 -44
- data/lib/rubino/memory/backends.rb +23 -7
- data/lib/rubino/memory/salience_gate.rb +103 -0
- data/lib/rubino/memory/sqlite_extraction.rb +70 -0
- data/lib/rubino/memory/sqlite_extraction_prompt.rb +11 -0
- data/lib/rubino/memory/store.rb +33 -5
- data/lib/rubino/memory/threat_scanner.rb +52 -0
- data/lib/rubino/output/cost.rb +52 -0
- data/lib/rubino/output/headless_block_latch.rb +53 -0
- data/lib/rubino/output/result_serializer.rb +222 -0
- data/lib/rubino/output/turn_recorder.rb +77 -0
- data/lib/rubino/security/approval_policy.rb +227 -32
- data/lib/rubino/security/command_allowlist.rb +79 -4
- data/lib/rubino/security/doom_loop_detector.rb +21 -2
- data/lib/rubino/security/hardline_guard.rb +189 -16
- data/lib/rubino/security/pattern_matcher.rb +28 -5
- data/lib/rubino/security/prefix_deriver.rb +25 -6
- data/lib/rubino/security/readonly_commands.rb +145 -5
- data/lib/rubino/security/secret_path.rb +134 -0
- data/lib/rubino/security/url_safety.rb +255 -0
- data/lib/rubino/session/repository.rb +212 -11
- data/lib/rubino/session/store.rb +139 -14
- data/lib/rubino/skills/installer.rb +116 -32
- data/lib/rubino/skills/prompt_index.rb +2 -2
- data/lib/rubino/skills/registry.rb +42 -1
- data/lib/rubino/skills/skill.rb +63 -2
- data/lib/rubino/skills/skill_tool.rb +16 -5
- data/lib/rubino/tools/background_tasks.rb +122 -9
- data/lib/rubino/tools/base.rb +204 -3
- data/lib/rubino/tools/edit_tool.rb +73 -18
- data/lib/rubino/tools/glob_tool.rb +48 -9
- data/lib/rubino/tools/grep_tool.rb +103 -9
- data/lib/rubino/tools/multi_edit_tool.rb +64 -9
- data/lib/rubino/tools/patch_tool.rb +5 -0
- data/lib/rubino/tools/read_attachment_tool.rb +3 -1
- data/lib/rubino/tools/read_tool.rb +33 -15
- data/lib/rubino/tools/read_tracker.rb +153 -35
- data/lib/rubino/tools/registry.rb +113 -12
- data/lib/rubino/tools/result.rb +9 -1
- data/lib/rubino/tools/ruby_tool.rb +0 -0
- data/lib/rubino/tools/shell_registry.rb +70 -0
- data/lib/rubino/tools/shell_tool.rb +40 -1
- data/lib/rubino/tools/summarize_file_tool.rb +6 -0
- data/lib/rubino/tools/task_stop_tool.rb +10 -16
- data/lib/rubino/tools/task_tool.rb +36 -8
- data/lib/rubino/tools/vision_tool.rb +5 -0
- data/lib/rubino/tools/webfetch_tool.rb +39 -7
- data/lib/rubino/tools/websearch_tool.rb +92 -30
- data/lib/rubino/tools/write_tool.rb +23 -4
- data/lib/rubino/ui/api.rb +10 -1
- data/lib/rubino/ui/base.rb +11 -0
- data/lib/rubino/ui/bottom_composer.rb +382 -74
- data/lib/rubino/ui/cli.rb +515 -83
- data/lib/rubino/ui/completion_menu.rb +11 -7
- data/lib/rubino/ui/headless_trace.rb +63 -0
- data/lib/rubino/ui/live_region.rb +70 -7
- data/lib/rubino/ui/markdown_renderer.rb +142 -7
- data/lib/rubino/ui/notifier.rb +0 -2
- data/lib/rubino/ui/null.rb +52 -5
- data/lib/rubino/ui/paste_store.rb +16 -2
- data/lib/rubino/ui/queued_indicators.rb +6 -1
- data/lib/rubino/ui/status_bar.rb +61 -7
- data/lib/rubino/ui/streaming_markdown.rb +59 -6
- data/lib/rubino/ui/subagent_view.rb +15 -1
- data/lib/rubino/ui/tool_label.rb +52 -0
- data/lib/rubino/update_check.rb +39 -4
- data/lib/rubino/util/atomic_file.rb +117 -0
- data/lib/rubino/util/ignore_rules.rb +120 -0
- data/lib/rubino/util/output.rb +229 -12
- data/lib/rubino/util/secrets_mask.rb +70 -7
- data/lib/rubino/util/spill_store.rb +153 -0
- data/lib/rubino/version.rb +1 -1
- data/lib/rubino/workspace.rb +9 -1
- data/lib/rubino.rb +191 -7
- data/rubino-agent.gemspec +1 -0
- data/skills/ruby-expert/SKILL.md +1 -0
- metadata +41 -12
- data/lib/rubino/agent/router.rb +0 -65
- data/lib/rubino/database/migrations/002_create_runs.rb +0 -45
- data/lib/rubino/database/migrations/003_create_skill_states.rb +0 -15
- data/lib/rubino/database/migrations/004_create_cron_jobs.rb +0 -36
- data/lib/rubino/database/migrations/005_create_oauth_connections.rb +0 -27
- data/lib/rubino/database/migrations/006_create_webhook_deliveries.rb +0 -34
- data/lib/rubino/database/migrations/007_create_messages_fts.rb +0 -59
- data/lib/rubino/database/migrations/008_create_memory_facts.rb +0 -75
- data/lib/rubino/database/migrations/009_create_memory_graph.rb +0 -55
- data/lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a3a48a6f7deb104446c624354a271ff1a1671c9b3ea846cee1144b3f55538db
|
|
4
|
+
data.tar.gz: cbc407cd78db827d75f150f61045b2cce759b266816e32fdba874c30eb3647c4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3342a4c8b1856691788ac9625b81eb4058870a0eb5a75946e99e6570ce40a05a7b44475a798863ac0fc92c9fcb505cf6caf27fcedc10a8e1bd9653ba18edf440
|
|
7
|
+
data.tar.gz: e54077b30f385942dd3591fb7e6c4e7afc0f301e5fea9d3111732c91ce56e63a4c7d192704ebd74ea97ee4a04437784d73a8d398bf02ce6143cdd69b2227a841
|
data/.rubocop_todo.yml
CHANGED
|
@@ -256,6 +256,8 @@ Metrics/BlockLength:
|
|
|
256
256
|
Metrics/ClassLength:
|
|
257
257
|
Exclude:
|
|
258
258
|
- 'lib/rubino/cli/chat_command.rb'
|
|
259
|
+
- 'lib/rubino/commands/executor.rb'
|
|
260
|
+
- 'lib/rubino/llm/ruby_llm_adapter.rb'
|
|
259
261
|
- 'lib/rubino/ui/bottom_composer.rb'
|
|
260
262
|
- 'lib/rubino/ui/cli.rb'
|
|
261
263
|
|
|
@@ -406,7 +408,6 @@ Naming/PredicateMethod:
|
|
|
406
408
|
Exclude:
|
|
407
409
|
- 'lib/rubino/agent/model_call_runner.rb'
|
|
408
410
|
- 'lib/rubino/agent/response_validator.rb'
|
|
409
|
-
- 'lib/rubino/agent/router.rb'
|
|
410
411
|
- 'lib/rubino/cli/onboarding_wizard.rb'
|
|
411
412
|
- 'lib/rubino/cli/trust_gate.rb'
|
|
412
413
|
- 'lib/rubino/memory/backends/sqlite.rb'
|
|
@@ -451,6 +452,7 @@ RSpec/AnyInstance:
|
|
|
451
452
|
- 'spec/rubino/commands/commands_spec.rb'
|
|
452
453
|
- 'spec/rubino/context/compressor_spec.rb'
|
|
453
454
|
- 'spec/rubino/context/prompt_assembler_active_skill_spec.rb'
|
|
455
|
+
- 'spec/rubino/context/prompt_assembler_cache_breakpoints_spec.rb'
|
|
454
456
|
- 'spec/rubino/context/prompt_assembler_ignore_rules_spec.rb'
|
|
455
457
|
- 'spec/rubino/context/prompt_assembler_layering_spec.rb'
|
|
456
458
|
- 'spec/rubino/context/prompt_assembler_memory_snapshot_spec.rb'
|
|
@@ -462,8 +464,10 @@ RSpec/BeforeAfterAll:
|
|
|
462
464
|
- '**/spec/spec_helper.rb'
|
|
463
465
|
- '**/spec/rails_helper.rb'
|
|
464
466
|
- '**/spec/support/**/*.rb'
|
|
467
|
+
- 'spec/rubino/llm/tool_bridge_cache_breakpoint_spec.rb'
|
|
465
468
|
- 'spec/rubino/security/approval_policy_mode_spec.rb'
|
|
466
469
|
- 'spec/rubino/tools/registry_mode_spec.rb'
|
|
470
|
+
- 'spec/rubino/tools/registry_situational_gating_spec.rb'
|
|
467
471
|
- 'spec/rubino/tools/registry_spec.rb'
|
|
468
472
|
|
|
469
473
|
# Offense count: 31
|
|
@@ -550,7 +554,11 @@ RSpec/DescribeMethod:
|
|
|
550
554
|
- 'spec/rubino/api/middleware/json_body_size_spec.rb'
|
|
551
555
|
- 'spec/rubino/cli/chat_command_continue_spec.rb'
|
|
552
556
|
- 'spec/rubino/cli/commands_version_spec.rb'
|
|
557
|
+
- 'spec/rubino/llm/ruby_llm_adapter_cache_spec.rb'
|
|
558
|
+
- 'spec/rubino/llm/tool_bridge_cache_breakpoint_spec.rb'
|
|
559
|
+
- 'spec/rubino/tools/registry_situational_gating_spec.rb'
|
|
553
560
|
- 'spec/rubino/context/prompt_assembler_active_skill_spec.rb'
|
|
561
|
+
- 'spec/rubino/context/prompt_assembler_cache_breakpoints_spec.rb'
|
|
554
562
|
- 'spec/rubino/context/prompt_assembler_ignore_rules_spec.rb'
|
|
555
563
|
- 'spec/rubino/context/prompt_assembler_layering_spec.rb'
|
|
556
564
|
- 'spec/rubino/context/prompt_assembler_memory_snapshot_spec.rb'
|
|
@@ -654,12 +662,13 @@ RSpec/LeakyLocalVariable:
|
|
|
654
662
|
Exclude:
|
|
655
663
|
- 'spec/rubino/no_direct_output_spec.rb'
|
|
656
664
|
|
|
657
|
-
# Offense count:
|
|
665
|
+
# Offense count: 8
|
|
658
666
|
RSpec/MultipleDescribes:
|
|
659
667
|
Exclude:
|
|
660
668
|
- 'spec/rubino/commands/commands_spec.rb'
|
|
661
669
|
- 'spec/rubino/context/prompt_assembler_layering_spec.rb'
|
|
662
670
|
- 'spec/rubino/memory/backends_spec.rb'
|
|
671
|
+
- 'spec/rubino/tools/encoding_robustness_spec.rb'
|
|
663
672
|
- 'spec/rubino/tools/error_code_spec.rb'
|
|
664
673
|
- 'spec/rubino/tools/tool_fixes_spec.rb'
|
|
665
674
|
- 'spec/rubino/trust_spec.rb'
|
data/AGENTS.md
CHANGED
|
@@ -77,7 +77,7 @@ lib/rubino/
|
|
|
77
77
|
## Surfaces this project exposes
|
|
78
78
|
|
|
79
79
|
- **HTTP API** (`/v1/*`) — the canonical interface. See `docs/api/v1.md`.
|
|
80
|
-
- **CLI** — `rubino {setup,chat,prompt,server,config,memory,sessions,jobs,tools,doctor,version}`.
|
|
80
|
+
- **CLI** — `rubino {setup,chat,prompt,server,config,memory,sessions,jobs,skills,tools,tls_cert,doctor,version,update}`.
|
|
81
81
|
- **Library** — `require "rubino"; Rubino.run(...)`.
|
|
82
82
|
|
|
83
83
|
The interactive CLI ships as part of `rubino chat`. Multi-agent routing, MCP, and plugin hooks are designed in but not fully wired yet.
|
data/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,142 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [0.5.0] - 2026-06-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- **One-shot tool-activity trace.** The non-interactive text path (`rubino
|
|
8
|
+
prompt` / `-q` / piped `chat`) now prints a concise per-tool activity trace
|
|
9
|
+
by default — one line per tool completion (`· edit foo.rb`, `· bash npm
|
|
10
|
+
test`) — routed to STDERR so the final answer on STDOUT stays clean
|
|
11
|
+
(`x=$(rubino prompt …)` captures only the answer). `--quiet`/`-Q` silences
|
|
12
|
+
the trace (machine-silent path); `--verbose`/`-v` widens each line's args.
|
|
13
|
+
`--output-format json`/`stream-json` (structured events on stdout) and the
|
|
14
|
+
interactive TUI tool-cards are unchanged. Mirrors the Codex/gemini-cli/Hermes
|
|
15
|
+
stderr-trace norm (Hermes `-q` default / `-Q` quiet).
|
|
16
|
+
|
|
17
|
+
- **Prompt-cache breakpoints (`cache_control`).** The conversation now inserts
|
|
18
|
+
cache breakpoints so the stable prefix (system + tool schemas + prior turns)
|
|
19
|
+
is reused across round-trips, cutting input-token cost/latency.
|
|
20
|
+
- **Situational tool-schema gating.** Tool definitions sent to the model are
|
|
21
|
+
scoped to the situation instead of always shipping the full set, reducing
|
|
22
|
+
prompt size and accidental tool selection.
|
|
23
|
+
- **Primary-agent switching.** Switch the active primary agent inline with
|
|
24
|
+
`/<name>`, the `/agent` command, or `Tab`; `@` remains reserved for file
|
|
25
|
+
references.
|
|
26
|
+
- **Detached post-turn polishing.** A post-turn polishing pass runs detached and
|
|
27
|
+
is cancellable with `Esc`, so it never blocks the next prompt.
|
|
28
|
+
- **Stdin pipe for one-shot.** Piped stdin is consumed as the prompt for
|
|
29
|
+
one-shot runs (`echo … | rubino prompt`), enabling unix-style composition.
|
|
30
|
+
- **Per-round-trip loop accounting.** Round-trips are counted, usage is summed
|
|
31
|
+
across them, and `tool_calls` are persisted on the streaming path.
|
|
32
|
+
- **Machine-readable headless output (`--output-format json | stream-json`, #312).**
|
|
33
|
+
`rubino prompt` / `chat -q` can now emit Claude-Code-aligned JSON for
|
|
34
|
+
CI/automation instead of prose. `--output-format json` (or the `--json` alias)
|
|
35
|
+
prints a single `{type:"result", subtype, is_error, result, session_id,
|
|
36
|
+
exit_reason, num_turns, duration_ms, usage:{input/output/cache_* tokens},
|
|
37
|
+
total_cost_usd, model}` object on stdout at completion; `--output-format
|
|
38
|
+
stream-json` emits JSONL (a `system`/`init` line, then Messages-API-shaped
|
|
39
|
+
`assistant`/`user` step objects, then the same final `result`). In both modes
|
|
40
|
+
ALL JSON goes to stdout and ALL logs/diagnostics/errors to stderr, and markdown
|
|
41
|
+
rendering is suppressed. The fail-closed / exit-code contract is preserved: a
|
|
42
|
+
blocked tool still emits the result with `is_error:true` and a non-zero exit.
|
|
43
|
+
The schema lives in a single shared serializer (`Rubino::Output::ResultSerializer`)
|
|
44
|
+
so it never drifts. `text` (default) is unchanged.
|
|
45
|
+
- **Higher tool-loop budget with an interactive extension prompt (#399).** The
|
|
46
|
+
`max_tool_iterations` default is raised from 8 to 25 so longer agent runs no
|
|
47
|
+
longer hit the cap mid-task. When the cap is reached interactively, the run
|
|
48
|
+
pauses with a budget-extension prompt — **Continue +N** (grant another batch),
|
|
49
|
+
**Summarize** (wrap up with what's done), or **Abort** — instead of failing
|
|
50
|
+
silently; headless runs keep the force-summarize behavior.
|
|
51
|
+
- **TUI: Ctrl-L clear-screen and a resize-while-typing fix (#395 / #401).**
|
|
52
|
+
`Ctrl-L` now clears the screen from the composer. Fixed a bug where resizing
|
|
53
|
+
the terminal while typing reflowed and duplicated the in-progress input into
|
|
54
|
+
the scrollback.
|
|
55
|
+
|
|
56
|
+
### Security
|
|
57
|
+
|
|
58
|
+
- **Hardened/narrowed the command-allowlist convenience layer (SEC-R2-1/2/3).**
|
|
59
|
+
Closes three default-config / bare-`git` paths that could run arbitrary code
|
|
60
|
+
or write arbitrary files past the headless gate **without `--yolo`**:
|
|
61
|
+
- removed code-loading test/build runners (`bundle exec rspec`, …) from the
|
|
62
|
+
**shipped default** `command_allowlist` — they load and execute arbitrary
|
|
63
|
+
project code by design (`rspec -r FILE`), so they are not safely
|
|
64
|
+
auto-approvable (SEC-R2-3);
|
|
65
|
+
- an allowlisted **git** head is now vetted for GLOBAL flags before the
|
|
66
|
+
subcommand (`git -c alias.x='!cmd' x`, `-c core.sshCommand=…`, `-C dir`,
|
|
67
|
+
`--exec-path`) and for code-loading/mutating subcommands (`apply`, `am`,
|
|
68
|
+
`push`, hooks, …); the "approve git always" path now persists only a
|
|
69
|
+
narrowed `git <read-only verb>`, never bare `git` (SEC-R2-1);
|
|
70
|
+
- any allowlisted head whose argument is itself a program
|
|
71
|
+
(`awk`/`sed`/`perl`/`python`/`ruby`/`node`/`tar`/`tee`/`xargs`/shells) is
|
|
72
|
+
default-denied auto-approval, and write flags on read heads (`sort -o`, …)
|
|
73
|
+
are rejected (SEC-R2-2).
|
|
74
|
+
|
|
75
|
+
An allowlist is a convenience layer, **not** a security boundary (per industry
|
|
76
|
+
practice the OS sandbox is the real floor, tracked separately); this narrows
|
|
77
|
+
it to close the above default-config and bare-`git` RCEs.
|
|
78
|
+
|
|
79
|
+
### Hardening
|
|
80
|
+
|
|
81
|
+
Four adversarial QA rounds fixed ~45 issues across the agent. Highlights:
|
|
82
|
+
|
|
83
|
+
- **Security.** Hardline-floor canonicalization; OOXML zip-bomb total-archive
|
|
84
|
+
cap; CWE-150 argument sanitization; threat-scanner; tightened
|
|
85
|
+
command-allowlist (see above).
|
|
86
|
+
- **Correctness.** UTF-8-safe edits; atomic compaction with auto-switch-to-child;
|
|
87
|
+
resume keeps the full tool history; cwd-scoped sessions; corrupt-DB recovery
|
|
88
|
+
(incl. `NotADatabaseException`); job-queue compare-and-swap; headless job drain
|
|
89
|
+
so memory works in automation.
|
|
90
|
+
- **Interrupt.** True cancel — stream cancellation with the partial persisted;
|
|
91
|
+
clean one-shot `SIGINT`/`SIGTERM` labels.
|
|
92
|
+
- **Performance.** Bounded huge-output memory; spill/paste eviction; streaming
|
|
93
|
+
grep with consistent ignore rules.
|
|
94
|
+
- **UX.** Config validation; `doctor` checks; resilient timeouts and error
|
|
95
|
+
classification.
|
|
96
|
+
|
|
97
|
+
Every fix was container-verified (non-root QA image, real MiniMax for live
|
|
98
|
+
behavior, true 0 failures); a full pre-release functionality sweep confirmed all
|
|
99
|
+
subsystems release-ready.
|
|
100
|
+
|
|
101
|
+
## [0.4.1] - 2026-06-13
|
|
102
|
+
|
|
103
|
+
### Security
|
|
104
|
+
|
|
105
|
+
- **Headless approvals now fail closed (#260).** A one-shot / scripted run
|
|
106
|
+
(`rubino prompt`, `chat -q`, no TTY) no longer auto-runs a tool that would
|
|
107
|
+
otherwise prompt: a write/edit, or a shell command not covered by your
|
|
108
|
+
`permissions` / command allowlist / read-only auto-allow, is **blocked, not
|
|
109
|
+
run**. A `blocked: <tool> needs approval …` line goes to stderr and the run
|
|
110
|
+
exits **2**, so CI/automation fails loudly instead of silently skipping (or
|
|
111
|
+
auto-executing). Full auto-exec now requires an explicit **`--yolo`** —
|
|
112
|
+
honored ONLY as a CLI flag, never grantable by a project-local/persisted
|
|
113
|
+
config — and **`--no-yolo`** forces fail-closed even over a yolo boot default.
|
|
114
|
+
|
|
115
|
+
### Fixed — installer
|
|
116
|
+
|
|
117
|
+
- **`mise` method (#256)** alongside Homebrew and `rv`, with `global`/`local`
|
|
118
|
+
scope (`RUBINO_INSTALL_SCOPE`); `RUBINO_INSTALL_METHOD` now accepts `mise`.
|
|
119
|
+
- **Activation/PATH is persisted to your shell rc (#268)** (`.zshrc` /
|
|
120
|
+
`.bashrc` / `.profile`) and a **post-install fresh-shell gate** fails loudly
|
|
121
|
+
if `rubino` isn't on PATH in a new shell. `RUBINO_NO_MODIFY_RC=1` opts out.
|
|
122
|
+
- **`mise` installs pin the latest published gem version (#258/#268)** instead
|
|
123
|
+
of drifting to a pre-release / age-gated build.
|
|
124
|
+
- **Method-aware prereq preflight (#272)** (xz/git/toolchain) with real gem
|
|
125
|
+
error surfacing, and a **Debian-12 / glibc-too-old steer from rv → mise
|
|
126
|
+
(#241/#242/#272)** so users don't land on a broken musl Ruby.
|
|
127
|
+
|
|
128
|
+
### Fixed
|
|
129
|
+
|
|
130
|
+
- **Config corruption + `doctor` crash on a scalar written over a section (#259).**
|
|
131
|
+
- **Streaming persistence (#266):** pre-tool narration is persisted and the
|
|
132
|
+
`tool_calls` audit is populated.
|
|
133
|
+
- **TUI render (#269):** table columns sized to content, nested/markdown fences
|
|
134
|
+
consumed, interrupt "ghost" line cleared.
|
|
135
|
+
- **Memory extraction bounded by a per-session cursor (#249)** — no more
|
|
136
|
+
re-scanning the whole transcript every turn.
|
|
137
|
+
- **Boots under a bare C/POSIX locale (#273)** without
|
|
138
|
+
`Encoding::CompatibilityError`.
|
|
139
|
+
- **Session summary folded into the single system message (#253/#254).**
|
|
4
140
|
|
|
5
141
|
## [0.4.0] - 2026-06-13
|
|
6
142
|
|
data/CONTRIBUTING.md
CHANGED
|
@@ -21,11 +21,20 @@ Run the CLI from the checkout with `bundle exec rubino <command>`.
|
|
|
21
21
|
## Tests
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
bundle exec rspec # full suite
|
|
24
|
+
bundle exec rspec # full suite (sequential; generates the coverage report)
|
|
25
25
|
bundle exec rspec path/to/file_spec.rb
|
|
26
26
|
bundle exec rake # default task == spec
|
|
27
|
+
bundle exec rake parallel:spec # full suite across all CPU cores (no coverage report)
|
|
28
|
+
bundle exec rake parallel:spec[4] # ...forced to 4 workers
|
|
27
29
|
```
|
|
28
30
|
|
|
31
|
+
`parallel:spec` shards the suite across one process per core via the
|
|
32
|
+
`parallel_tests` gem; each worker is isolated by `TEST_ENV_NUMBER`
|
|
33
|
+
(per-worker `RUBINO_HOME`, document fixtures, and example-status file).
|
|
34
|
+
SimpleCov is skipped under parallel runs (the workers would race the
|
|
35
|
+
coverage resultset) — use the sequential `bundle exec rspec` when you need
|
|
36
|
+
the coverage report.
|
|
37
|
+
|
|
29
38
|
The HTTP boundary is locked by an end-to-end contract suite under `spec/rubino/api/contract/`. When the docs and the contract suite disagree, **the contract suite is canonical** — update the docs to match.
|
|
30
39
|
|
|
31
40
|
## Lint
|
data/README.md
CHANGED
|
@@ -19,7 +19,15 @@ One line, Linux and macOS (x86_64 / arm64). Installs a compatible Ruby, then the
|
|
|
19
19
|
curl -fsSL https://raw.githubusercontent.com/Jhonnyr97/rubino-agent/main/install.sh | bash
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
The installer supports **three** methods for getting a compatible Ruby + the gem:
|
|
23
|
+
|
|
24
|
+
- **`rv`** ([`rv`](https://github.com/spinel-coop/rv)) — fetches a precompiled Ruby into user space.
|
|
25
|
+
- **Homebrew** (`brew install ruby`) — offered on **macOS** when [Homebrew](https://brew.sh) is present.
|
|
26
|
+
- **`mise`** ([mise](https://mise.jdx.dev)) — a polyglot tool manager; installs `rubino` via its `gem:` backend and pins the latest published gem version.
|
|
27
|
+
|
|
28
|
+
On **macOS** (interactive) you're asked to pick Homebrew / `rv` / `mise`; on **Linux** (interactive) you pick `rv` / `mise` (Homebrew is offered only if `brew` is already on PATH). Skip the prompt with `RUBINO_INSTALL_METHOD=brew`, `=rv`, or `=mise`. For the **mise** method, `RUBINO_INSTALL_SCOPE=global` (default, user-wide `~/.config/mise/config.toml`) or `=local` (this directory only, `./mise.toml`) chooses the scope.
|
|
29
|
+
|
|
30
|
+
On **Debian 12 / old-glibc** systems `rv` would install a musl Ruby this glibc box can't execute; the installer detects that and **steers you from `rv` to `mise`** (precompiled, glibc-correct) so you don't land on a broken `rubino`.
|
|
23
31
|
|
|
24
32
|
> **Review before you pipe.** Piping a script into your shell runs whatever it contains. Read it first:
|
|
25
33
|
> ```bash
|
|
@@ -27,7 +35,7 @@ On **Linux** the installer fetches a precompiled Ruby via [`rv`](https://github.
|
|
|
27
35
|
> less install.sh && bash install.sh
|
|
28
36
|
> ```
|
|
29
37
|
|
|
30
|
-
The installer is idempotent — safe to re-run
|
|
38
|
+
The installer is idempotent — safe to re-run. It **persists the activation / `PATH` line to your shell rc** (`.zshrc` / `.bashrc` / `.profile`) and then runs a **fresh-shell verification gate** — it opens a clean login shell and fails loudly if `rubino` isn't on `PATH` there, instead of merely printing a hint you might miss. Opt out of any rc modification with `RUBINO_NO_MODIFY_RC=1` (the installer then prints the line for you to add yourself).
|
|
31
39
|
|
|
32
40
|
**Manual install** (if you'd rather not pipe, or already manage Ruby yourself):
|
|
33
41
|
|
|
@@ -99,7 +107,7 @@ model:
|
|
|
99
107
|
|
|
100
108
|
agent:
|
|
101
109
|
max_turns: 90
|
|
102
|
-
max_tool_iterations:
|
|
110
|
+
max_tool_iterations: 25
|
|
103
111
|
|
|
104
112
|
memory:
|
|
105
113
|
enabled: true
|
|
@@ -142,7 +150,7 @@ Full reference (every key, env vars, precedence): **[docs/configuration.md](docs
|
|
|
142
150
|
|
|
143
151
|
## Built-in tools
|
|
144
152
|
|
|
145
|
-
The agent ships **
|
|
153
|
+
The agent ships **27 built-in tools** (the set `rubino tools` lists): `read`, `read_attachment`, `summarize_file`, `write`, `edit`, `multi_edit`, `apply_patch`, `grep`, `glob`, `git`, `github`, `shell`, `shell_output`, `shell_tail`, `shell_input`, `shell_kill`, `ruby`, `run_tests`, `web`, `question`, `todowrite`, `memory`, `session_search`, `attach_file`, `vision`, `skill`, `task`. A single `web` tool gates both fetching a URL and searching (config key `tools.web`, off by default). Each tool is gated by a `tools.<key>` config flag (opt-out) and the approval model. See **[docs/tools.md](docs/tools.md)**.
|
|
146
154
|
|
|
147
155
|
## Skills
|
|
148
156
|
|
|
@@ -189,7 +197,8 @@ These are designed-in but not fully wired yet — don't depend on them in produc
|
|
|
189
197
|
|
|
190
198
|
```bash
|
|
191
199
|
bundle install
|
|
192
|
-
bundle exec rspec # run tests
|
|
200
|
+
bundle exec rspec # run tests (sequential, with coverage)
|
|
201
|
+
bundle exec rake parallel:spec # run tests across all CPU cores
|
|
193
202
|
bundle exec rubino doctor # verify setup
|
|
194
203
|
```
|
|
195
204
|
|
data/Rakefile
CHANGED
|
@@ -6,3 +6,34 @@ require "rspec/core/rake_task"
|
|
|
6
6
|
RSpec::Core::RakeTask.new(:spec)
|
|
7
7
|
|
|
8
8
|
task default: :spec
|
|
9
|
+
|
|
10
|
+
# Parallel test execution across CPU cores via the `parallel_tests` gem.
|
|
11
|
+
#
|
|
12
|
+
# rake parallel:spec # auto: one worker per core
|
|
13
|
+
# rake parallel:spec[4] # force 4 workers
|
|
14
|
+
#
|
|
15
|
+
# Each worker is its own process with a distinct TEST_ENV_NUMBER, so the
|
|
16
|
+
# per-process isolation already baked into spec/spec_helper.rb (RUBINO_HOME,
|
|
17
|
+
# document fixtures, example-status file) keeps workers from colliding.
|
|
18
|
+
# SimpleCov is skipped in parallel (workers would race the resultset); run the
|
|
19
|
+
# plain sequential `rake spec` / `bundle exec rspec` for a coverage report.
|
|
20
|
+
#
|
|
21
|
+
# Balancing: we use parallel_tests' default **filesize** grouping rather than
|
|
22
|
+
# runtime grouping. Runtime grouping (`--group-by runtime`) is strict — it
|
|
23
|
+
# aborts with RuntimeLogTooSmallError whenever the recorded log is missing an
|
|
24
|
+
# entry for any current spec file (i.e. the first run after ANY new spec is
|
|
25
|
+
# added), which makes the entrypoint brittle. The wall-clock floor here is a
|
|
26
|
+
# single ~70s example (agent_e2e error-retry) that cannot be split across
|
|
27
|
+
# workers regardless of grouping, so filesize grouping already lands the
|
|
28
|
+
# longest worker on essentially that floor while staying deterministic and
|
|
29
|
+
# never breaking on a freshly-added spec.
|
|
30
|
+
namespace :parallel do
|
|
31
|
+
desc "Run the RSpec suite in parallel across CPU cores (rake parallel:spec[N])"
|
|
32
|
+
task :spec, [:count] do |_t, args|
|
|
33
|
+
count = args[:count]
|
|
34
|
+
cmd = %w[bundle exec parallel_rspec]
|
|
35
|
+
cmd += ["-n", count.to_s] if count && !count.empty?
|
|
36
|
+
cmd += ["--", "spec"]
|
|
37
|
+
sh(*cmd)
|
|
38
|
+
end
|
|
39
|
+
end
|
data/docs/agents.md
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
# Agents & Subagents
|
|
2
2
|
|
|
3
|
-
rubino has two distinct multi-agent surfaces
|
|
3
|
+
rubino has two distinct multi-agent surfaces, and **both ship today**:
|
|
4
4
|
|
|
5
5
|
1. **Background subagents** (✅ shipping) — the agent delegates bounded sub-tasks
|
|
6
6
|
to isolated subagent runs via its `task` tool, and you supervise them with
|
|
7
|
-
`/agents` and `/reply`.
|
|
8
|
-
2. **Primary-agent switching** (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
`/agents` and `/reply`.
|
|
8
|
+
2. **Primary-agent switching** (✅ shipping) — pick the primary agent that
|
|
9
|
+
handles your turns: `/agent <name>` (or a bare `/<name>` for a primary)
|
|
10
|
+
pins it for the session, **Tab** cycles through the primaries, and a one-shot
|
|
11
|
+
`/<name> <message>` routes a single message to any agent. The selected agent's
|
|
12
|
+
Definition (its system prompt and tool scope) is threaded into the runner each
|
|
13
|
+
turn, so the choice actually changes the model's persona/tools. See
|
|
14
|
+
[Primary-agent switching](#primary-agent-switching) below.
|
|
15
|
+
|
|
16
|
+
> **Channels are cleanly separated:** `@` is the **workspace file** picker
|
|
17
|
+
> (`@path/to/file`), and `/` is the **agent/command** channel. There are no
|
|
18
|
+
> `@mention` agent routes — a filename like `@explore.rb` is always a file, never
|
|
19
|
+
> an agent. Use `/explore`, `/plan`, etc. to reach an agent.
|
|
13
20
|
|
|
14
21
|
---
|
|
15
22
|
|
|
@@ -132,14 +139,15 @@ apply (hardline floor still enforced — see [security.md](security.md)).
|
|
|
132
139
|
## Built-in agent definitions
|
|
133
140
|
|
|
134
141
|
These definitions exist in `Agent::AgentRegistry` today. The two *subagents*
|
|
135
|
-
are live as `task` targets; the two *primary* agents are
|
|
136
|
-
|
|
137
|
-
switching; the *utility* agents are
|
|
142
|
+
are live as `task` targets; the two *primary* agents are switchable per session
|
|
143
|
+
(`/agent <name>`, a bare `/<name>`, or Tab — see
|
|
144
|
+
[Primary-agent switching](#primary-agent-switching)); the *utility* agents are
|
|
145
|
+
internal.
|
|
138
146
|
|
|
139
147
|
| Agent | Type | Access | Description |
|
|
140
148
|
|-------|------|--------|-------------|
|
|
141
|
-
| **build** | primary | Full tools | Default development agent
|
|
142
|
-
| **plan** | primary | Read-only | Analysis/planning
|
|
149
|
+
| **build** | primary | Full tools | Default development agent (the registry default). |
|
|
150
|
+
| **plan** | primary | Read-only | Analysis/planning agent. Switch to it with `/agent plan`; `/mode plan` is the orthogonal read-only run **mode**. |
|
|
143
151
|
| **explore** | subagent | Read-only | Fast codebase search and navigation (`task` target). |
|
|
144
152
|
| **general** | subagent | Full tools | Complex multi-step tasks (`task` target). |
|
|
145
153
|
| **compaction** | utility | None | Internal: compresses context. Hidden. |
|
|
@@ -173,18 +181,29 @@ pattern-based permission overrides (merged over the global rules by
|
|
|
173
181
|
|
|
174
182
|
---
|
|
175
183
|
|
|
176
|
-
##
|
|
184
|
+
## Primary-agent switching
|
|
177
185
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
The intended design: press **Tab** to cycle through primary agents, or route a
|
|
185
|
-
single message with an `@mention`:
|
|
186
|
+
You choose which primary agent handles your turns. The pinned agent is a
|
|
187
|
+
process-level slot (`Rubino::ActiveAgent`, sibling to `Rubino::Modes`): a fresh
|
|
188
|
+
`rubino chat` boots on the registry default (`build`), and an explicit switch
|
|
189
|
+
takes effect for the rest of that process (no premature persistence). Switching
|
|
190
|
+
is entirely on the **slash** channel and **Tab** — there is no `@mention` agent
|
|
191
|
+
routing (`@` is the workspace file picker).
|
|
186
192
|
|
|
187
193
|
```
|
|
188
|
-
you >
|
|
189
|
-
you >
|
|
194
|
+
you > /agent plan # pin a primary agent for the session
|
|
195
|
+
you > /plan # bare /<name> — same, for a primary agent
|
|
196
|
+
you > <Tab> # cycle through the primary agents, wrapping around
|
|
197
|
+
you > /explore Where is the database connection configured? # one-shot route a single message
|
|
190
198
|
```
|
|
199
|
+
|
|
200
|
+
- **`/agent <name>`** (or a bare **`/<name>`** when `<name>` is a primary) pins
|
|
201
|
+
the agent for the session. Only **primary** agents are switchable; subagents
|
|
202
|
+
(`explore`/`general`) are never pinned.
|
|
203
|
+
- **Tab** cycles through the primary agents.
|
|
204
|
+
- **`/<name> <message>`** routes a single message to any agent (primary or
|
|
205
|
+
subagent) without changing the sticky selection.
|
|
206
|
+
|
|
207
|
+
The selected agent's Definition — its system prompt and tool scope — is threaded
|
|
208
|
+
into the runner on every turn, so switching actually changes the model's
|
|
209
|
+
persona and the tools it can call, not just a cosmetic label.
|
data/docs/architecture.md
CHANGED
|
@@ -148,8 +148,8 @@ User Input
|
|
|
148
148
|
├─→ Commands::Executor (if /command)
|
|
149
149
|
│ └─→ Render template → feed to agent
|
|
150
150
|
│
|
|
151
|
-
├─→
|
|
152
|
-
│ └─→ Select agent definition
|
|
151
|
+
├─→ ActiveAgent (if /agent, /<name>, or Tab)
|
|
152
|
+
│ └─→ Select primary agent definition
|
|
153
153
|
│
|
|
154
154
|
└─→ Interaction::Lifecycle
|
|
155
155
|
│
|
data/docs/commands.md
CHANGED
|
@@ -60,7 +60,9 @@ is set.
|
|
|
60
60
|
| `--new` | | Start a fresh session (bare `chat` resumes the last one by default) |
|
|
61
61
|
| `--model` | `-m` | Override the model (e.g. `claude-sonnet-4-5`) |
|
|
62
62
|
| `--provider` | | Override the provider (e.g. `anthropic`, `bedrock`) |
|
|
63
|
-
| `--yolo` | | Skip approval prompts (equivalent to `/mode yolo`) |
|
|
63
|
+
| `--yolo` | | Skip approval prompts (equivalent to `/mode yolo`). Honored **only** as a CLI flag — cannot be set from config |
|
|
64
|
+
| `--no-yolo` | | Force fail-closed approvals even over a yolo default (the security half of [#260](#exit-codes-scripting-around-prompt--one-shot)) |
|
|
65
|
+
| `--add-dir` | | Add an extra allowed workspace directory write/edit can reach (repeatable) |
|
|
64
66
|
| `--max-turns` | | Max tool iterations per turn |
|
|
65
67
|
| `--ignore-rules` | | Skip `AGENTS.md` and context files |
|
|
66
68
|
|
|
@@ -93,6 +95,18 @@ Pasting **text** into the chat input goes through the file-backed paste pipeline
|
|
|
93
95
|
policy along the way (a write outside the workspace boundary, a denied
|
|
94
96
|
approval, a hardline-blocked command). A refusal the agent handled and
|
|
95
97
|
explained is expected behavior, not an error.
|
|
98
|
+
- **Headless approvals fail closed (security).** A one-shot / scripted run has
|
|
99
|
+
no interactive session, so a tool that would otherwise prompt for approval —
|
|
100
|
+
a write/edit, or a shell command **not** covered by your `permissions` /
|
|
101
|
+
command allowlist / read-only auto-allow — is **blocked, not run**. A
|
|
102
|
+
single-line `blocked: <tool> needs approval but no interactive session (use
|
|
103
|
+
--yolo to allow, or allowlist it)` goes to stderr and the run exits
|
|
104
|
+
**non-zero (2)**, so automation/CI fails loudly instead of silently skipping
|
|
105
|
+
(or, worse, auto-executing) the action. Anything you already allowlisted, and
|
|
106
|
+
every read-only command, still runs unprompted. Pass `--yolo` to opt back
|
|
107
|
+
into full auto-execute; `--no-yolo` forces fail-closed even if a yolo default
|
|
108
|
+
was set. `--yolo` is honored **only** as a CLI flag — a project-local config
|
|
109
|
+
can never grant it.
|
|
96
110
|
- It exits **non-zero** when the run itself fails: no usable credentials, the
|
|
97
111
|
`--resume`/`--session` target doesn't exist or is ambiguous, or the provider
|
|
98
112
|
call errors out. The reason is printed to stderr; the answer (when any) stays
|
|
@@ -160,9 +174,11 @@ Type these inside `rubino chat`. Generated from `BuiltIns::DESCRIPTIONS` (drift-
|
|
|
160
174
|
| `/compact` | Compact the context now: older turns become a summary |
|
|
161
175
|
| `/export` | Write the session transcript as markdown (/export [path]) |
|
|
162
176
|
| `/memory` | Inspect/search/forget what the agent remembers (show ID, backend, --all) |
|
|
177
|
+
| `/agent` | Switch the primary agent (/agent <name>; a bare /<name> or Tab cycles) |
|
|
163
178
|
| `/agents` | List background subagents; steer/probe a running one, or view output |
|
|
164
179
|
| `/tasks` | Alias for /agents |
|
|
165
180
|
| `/reply` | Answer a subagent that is blocked waiting on you (ask_parent) |
|
|
181
|
+
| `/stop` | Stop a running subagent (/stop <id>; alias for /agents <id> --stop) |
|
|
166
182
|
| `/jobs` | List the background job queue (status counts); /jobs <id> for detail |
|
|
167
183
|
| `/skills` | List skills; activate one ('none' clears), or enable/disable NAME |
|
|
168
184
|
| `/mcp` | List MCP servers and their tools; restart or disable one |
|
|
@@ -371,6 +387,17 @@ Custom commands live as Markdown templates in `.rubino/commands/` (project) or `
|
|
|
371
387
|
|
|
372
388
|
`/commands` lists the available custom commands and explains how to author them. See the [README](../README.md) for the template format (`$ARGUMENTS`, YAML frontmatter).
|
|
373
389
|
|
|
390
|
+
### Primary agents: `/agent`, `/<name>`, and Tab
|
|
391
|
+
|
|
392
|
+
Each turn runs under an **agent** — a persona with its own system prompt and tool scope. The built-ins are `build` (full access, the default) and `plan` (read-only analysis); `explore` and `general` are subagents you invoke one-shot. Switching the primary agent changes who answers the *next* turn:
|
|
393
|
+
|
|
394
|
+
- `/agent` lists the switchable primaries (the current one marked `▸`) and the one-shot subagents.
|
|
395
|
+
- `/agent <name>` — or a bare `/<name>` for a primary — **pins** that agent for the rest of the session (sticky). The active agent shows as an `agent <name>` chip in the status bar (omitted when it's the default `build`).
|
|
396
|
+
- **Tab** on an empty prompt cycles the primary agents (the agent counterpart of Shift+Tab's mode cycle), updating the chip live.
|
|
397
|
+
- `/<name> <message>` routes a **single** turn to that agent — any visible agent, primary or subagent (e.g. `/explore where is the parser`) — without disturbing your sticky pick.
|
|
398
|
+
|
|
399
|
+
Distinct from `/agents` (plural), which drills into the background `task` subagents. `@` is the file picker, so a filename like `@explore.rb` is never shadowed by an agent named `explore`; agent switching lives entirely on the slash channel and Tab.
|
|
400
|
+
|
|
374
401
|
### Modes
|
|
375
402
|
|
|
376
403
|
`/mode` (or the `--yolo` flag) switches between the modes below. **Shift+Tab** cycles them from the prompt (default → plan → yolo), updates the mode token that LEADS the status bar under the input (dim `default`, yellow `plan`, red `yolo`), and shows a transient `mode <old> → <new>` footer. Entering `yolo` from the cycle takes a second deliberate Shift+Tab to confirm (the toast says so, and warns when running background subagents would lose their approval gates); an explicit `/mode yolo` switches directly.
|
data/docs/configuration.md
CHANGED
|
@@ -102,8 +102,10 @@ auxiliary:
|
|
|
102
102
|
```yaml
|
|
103
103
|
agent:
|
|
104
104
|
max_turns: 90 # Max turns per session
|
|
105
|
-
max_tool_iterations:
|
|
106
|
-
|
|
105
|
+
max_tool_iterations: 25 # Max per-turn model<->tool round-trips (cap)
|
|
106
|
+
budget_extension_prompt: true # At the cap, prompt continue/summarize/abort (interactive only)
|
|
107
|
+
budget_extension_step: null # "+N" per extension (null = max_tool_iterations)
|
|
108
|
+
max_turn_seconds: 120 # Timeout per turn (outer rail; extensions never raise it)
|
|
107
109
|
api_max_retries: 5 # LLM API retry count (exp backoff)
|
|
108
110
|
api_retry_backoff_cap_seconds: 16 # Max per-retry backoff draw
|
|
109
111
|
api_retry_backoff_overload_cap_seconds: 60 # Higher cap used only for overload (529/503)
|
|
@@ -287,7 +289,7 @@ tools:
|
|
|
287
289
|
workspace_strict: true # Sandbox write/edit/delete to workspace_root; false = any reachable path
|
|
288
290
|
git: true
|
|
289
291
|
shell: true # ON by default (the agent ships to run inside an isolated VM);
|
|
290
|
-
#
|
|
292
|
+
# dangerous commands are still gated by security.confirm_policy
|
|
291
293
|
ruby: true
|
|
292
294
|
web: false # Gates BOTH the webfetch and websearch tools
|
|
293
295
|
memory: true
|
|
@@ -372,12 +374,13 @@ attachments:
|
|
|
372
374
|
|
|
373
375
|
```yaml
|
|
374
376
|
security:
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
command_allowlist: #
|
|
377
|
+
confirm_policy: "dangerous_only" # dangerous_only (default) | confirm_all
|
|
378
|
+
# (the old require_confirmation_for_shell key was removed)
|
|
379
|
+
command_allowlist: # pre-approved commands (read-only intent only; empty = approve nothing)
|
|
378
380
|
- "git status"
|
|
379
381
|
- "git diff"
|
|
380
|
-
|
|
382
|
+
# Test/build runners (bundle exec rspec, rake, npm test) are NOT shipped here:
|
|
383
|
+
# they load and run arbitrary project code, so add one only if you accept that.
|
|
381
384
|
website_blocklist:
|
|
382
385
|
enabled: false
|
|
383
386
|
domains: []
|
|
@@ -461,20 +464,6 @@ formatters:
|
|
|
461
464
|
"*.py": "black"
|
|
462
465
|
```
|
|
463
466
|
|
|
464
|
-
### agents
|
|
465
|
-
|
|
466
|
-
Custom agent definitions:
|
|
467
|
-
|
|
468
|
-
```yaml
|
|
469
|
-
agents:
|
|
470
|
-
security:
|
|
471
|
-
type: subagent
|
|
472
|
-
model: "anthropic/claude-sonnet-4-20250514"
|
|
473
|
-
description: "Security-focused code review"
|
|
474
|
-
tools: [read, grep, glob]
|
|
475
|
-
mcp_servers: []
|
|
476
|
-
```
|
|
477
|
-
|
|
478
467
|
### prompts
|
|
479
468
|
|
|
480
469
|
System-prompt layering. The defaults ship the built-in role prompts.
|
|
@@ -513,9 +502,17 @@ formatters:
|
|
|
513
502
|
"*.py": "black"
|
|
514
503
|
```
|
|
515
504
|
|
|
516
|
-
### agents (planned)
|
|
505
|
+
### agents (planned — not yet read)
|
|
506
|
+
|
|
507
|
+
> **Status: planned, has no effect today.** The `agents:` key is reserved but is
|
|
508
|
+
> **not read** by the registry, so declaring custom agents in `config.yml` does
|
|
509
|
+
> nothing yet. Primary-agent *switching* among the built-in agents already ships
|
|
510
|
+
> (`/agent`, `/<name>`, Tab — see [agents.md](agents.md#primary-agent-switching));
|
|
511
|
+
> what is not wired is authoring NEW agents from config. To register a custom
|
|
512
|
+
> agent today, use `AgentRegistry#register` programmatically (see
|
|
513
|
+
> [agents.md](agents.md#custom-agents-via-code)).
|
|
517
514
|
|
|
518
|
-
|
|
515
|
+
The intended shape, once config-authored agents land:
|
|
519
516
|
|
|
520
517
|
```yaml
|
|
521
518
|
agents:
|
data/docs/getting-started.md
CHANGED
|
@@ -4,7 +4,7 @@ From nothing to a working first answer in about five minutes. This is the happy
|
|
|
4
4
|
|
|
5
5
|
## 1. Install
|
|
6
6
|
|
|
7
|
-
The fastest path on Linux (x86_64 / arm64) is the one-line installer. It installs a compatible Ruby
|
|
7
|
+
The fastest path on Linux and macOS (x86_64 / arm64) is the one-line installer. It installs a compatible Ruby and then the gem — all in user space, no sudo:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
curl -fsSL https://raw.githubusercontent.com/Jhonnyr97/rubino-agent/main/install.sh | bash
|
|
@@ -17,7 +17,9 @@ curl -fsSL https://raw.githubusercontent.com/Jhonnyr97/rubino-agent/main/install
|
|
|
17
17
|
less install.sh && bash install.sh
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
On Linux it offers a choice of Ruby provider — [`rv`](https://github.com/spinel-coop/rv) or [`mise`](https://mise.jdx.dev) (and Homebrew if `brew` is already present on macOS); pick non-interactively with `RUBINO_INSTALL_METHOD=rv|mise|brew`. On a **Debian 12 / old-glibc** box, prefer **mise**: `rv`'s musl build there yields a Ruby this glibc system can't run, so the installer steers `rv → mise` automatically. (For the mise method, `RUBINO_INSTALL_SCOPE=global|local` chooses user-wide vs this-directory-only. See the [README install matrix](../README.md#install).)
|
|
21
|
+
|
|
22
|
+
The installer is idempotent (safe to re-run). When it finishes it **persists the activation / `PATH` line to your shell rc** (`.zshrc` / `.bashrc` / `.profile`) and then **verifies in a fresh shell** that `rubino` is on `PATH`, failing loudly if it isn't — so a new terminal just works. Opt out of rc edits with `RUBINO_NO_MODIFY_RC=1` (it then prints the line for you to add).
|
|
21
23
|
|
|
22
24
|
**Already manage Ruby yourself?** Requirements are Ruby >= 3.1 and SQLite3; then:
|
|
23
25
|
|
|
@@ -85,7 +87,7 @@ The first thing you see is a banner with the workspace, git branch, and model. T
|
|
|
85
87
|
|
|
86
88
|
```
|
|
87
89
|
▍❯ what does this project do?
|
|
88
|
-
default ·
|
|
90
|
+
default · openai/gpt-4.1 · ctx ~0/128k
|
|
89
91
|
```
|
|
90
92
|
|
|
91
93
|
> If you skipped the wizard during `setup`, a bare `rubino chat` re-runs it before the first turn (when on a TTY). If you're piping input or using `-q`, there's no prompt to run — instead you get a clear, actionable error telling you how to set a key (see below).
|