rubino-agent 0.3.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 +172 -5
- 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 +35 -3
- data/docs/configuration.md +20 -23
- data/docs/getting-started.md +5 -3
- data/docs/security.md +16 -5
- data/docs/skills.md +31 -0
- data/docs/troubleshooting.md +1 -1
- data/exe/rubino +16 -2
- data/install.sh +721 -59
- 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/attachments/classify.rb +35 -17
- 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 +282 -2
- 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 +183 -9
- 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 +156 -41
- 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/environment_inspector.rb +2 -2
- data/lib/rubino/context/file_discovery.rb +2 -2
- 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 +105 -22
- 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 +230 -0
- data/lib/rubino/skills/prompt_index.rb +2 -2
- data/lib/rubino/skills/registry.rb +52 -1
- data/lib/rubino/skills/skill.rb +64 -3
- data/lib/rubino/skills/skill_tool.rb +16 -5
- data/lib/rubino/tools/background_tasks.rb +157 -13
- 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 +29 -4
- 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 +42 -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
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).
|
data/docs/security.md
CHANGED
|
@@ -4,7 +4,7 @@ rubino runs real tools — shell, file writes, Ruby, git. The safety model is la
|
|
|
4
4
|
|
|
5
5
|
## Is it safe to enable shell?
|
|
6
6
|
|
|
7
|
-
Yes. `tools.shell` is **on by default** because the agent ships to run inside an isolated VM where running commands is the whole point. Every command is still gated: by default `security.
|
|
7
|
+
Yes. `tools.shell` is **on by default** because the agent ships to run inside an isolated VM where running commands is the whole point. Every command is still gated: by default `security.confirm_policy` is `dangerous_only`, so a command matching a dangerous pattern goes through an approval prompt (set it to `confirm_all` to prompt on every command), and a hardline floor blocks catastrophic commands regardless of any setting.
|
|
8
8
|
|
|
9
9
|
## The approval decision order
|
|
10
10
|
|
|
@@ -51,10 +51,12 @@ Actions: `allow`, `ask`, `deny`. A `deny` rule is a deny-class check and beats e
|
|
|
51
51
|
|
|
52
52
|
## Shell confirmation policy
|
|
53
53
|
|
|
54
|
-
`security.confirm_policy
|
|
54
|
+
`security.confirm_policy`:
|
|
55
55
|
|
|
56
|
-
- **`
|
|
57
|
-
- **`
|
|
56
|
+
- **`dangerous_only`** (default) — safe commands run unprompted; only commands matching a dangerous pattern prompt. The hardline floor and `permissions: deny` still run first, so this never weakens the floor.
|
|
57
|
+
- **`confirm_all`** — every shell command not otherwise allowed/denied prompts for approval.
|
|
58
|
+
|
|
59
|
+
(The old `security.require_confirmation_for_shell` key was **removed** — it is no longer honored. Use `security.confirm_policy`.)
|
|
58
60
|
|
|
59
61
|
## Command allowlist
|
|
60
62
|
|
|
@@ -65,11 +67,14 @@ security:
|
|
|
65
67
|
command_allowlist:
|
|
66
68
|
- "git status"
|
|
67
69
|
- "git diff"
|
|
68
|
-
- "bundle exec rspec"
|
|
69
70
|
```
|
|
70
71
|
|
|
71
72
|
An **empty** allowlist pre-approves nothing — pre-approval is opt-in.
|
|
72
73
|
|
|
74
|
+
A matched entry pre-approves only its **read-only intent**, never a smuggled write/exec form: an allowlisted head can't carry an output/exec flag (`git diff --output FILE`, `sort -o FILE`, `find -exec/-delete`), a git **global** flag (`git -c alias.x='!cmd' x`, `git -c core.sshCommand=…`, `git -C dir`, `--exec-path`), or a mutating/code-loading git subcommand (`git apply`, `git am`, `git push`, hooks). Heads whose argument is itself a program (`awk`, `sed`, `perl`, `python`, `ruby`, `node`, `tar`, `tee`, `xargs`, shells) are **never** auto-approved even if allowlisted — they still prompt. The **shipped default** is read-only git only; test/build runners (`bundle exec rspec`, `rake`, `npm test`) are deliberately not shipped auto-approved because they load and execute arbitrary project code by design — add one explicitly only if you accept that.
|
|
75
|
+
|
|
76
|
+
> An allowlist is a **convenience** layer, not a security boundary. Per industry practice (Claude Code/Codex, GTFOBins) a deny/allow list of command strings cannot be exhaustive; the OS sandbox is the real floor. This layer is narrowed to close the default-config and bare-`git` RCEs, not to be relied on as the only barrier.
|
|
77
|
+
|
|
73
78
|
## Auto-allowed read-only commands
|
|
74
79
|
|
|
75
80
|
A built-in allowlist layer (`Security::ReadonlyCommands`, **on by default**) lets provably read-only shell commands run without a prompt. It is evaluated at the same decision step as the command allowlist — *below* the hardline floor and `permissions: deny`, which always win, even for commands you add yourself.
|
|
@@ -99,6 +104,12 @@ approvals:
|
|
|
99
104
|
- "docker ps" # multi-word entry matches those leading tokens
|
|
100
105
|
```
|
|
101
106
|
|
|
107
|
+
## Headless / non-interactive approvals fail closed
|
|
108
|
+
|
|
109
|
+
A one-shot or scripted run (`rubino prompt`, `chat -q`, or any run with no TTY) has **no interactive session to approve from**, so it **fails closed**: a tool that would otherwise prompt — a write/edit, or a shell command **not** covered by your `permissions` / command allowlist / read-only auto-allow — is **blocked, not run**. A single-line `blocked: <tool> needs approval but no interactive session (use --yolo to allow, or allowlist it)` goes to stderr and the run exits **2**, so automation/CI fails loudly instead of silently skipping (or, worse, auto-executing) the action. Anything you already allowlisted, and every read-only command, still runs unprompted.
|
|
110
|
+
|
|
111
|
+
To opt back into full auto-execute, pass **`--yolo`**; **`--no-yolo`** forces fail-closed even if a yolo default was set. `--yolo` is honored **only** as a CLI flag — a project-local or persisted config can never grant it, so an untrusted checkout can't silently switch a scripted run into auto-execute. The hardline floor and explicit `permissions: deny` rules still apply under `--yolo`. (See [commands.md §Exit codes](commands.md#exit-codes-scripting-around-prompt--one-shot).)
|
|
112
|
+
|
|
102
113
|
## Deny/approve scope: once vs session
|
|
103
114
|
|
|
104
115
|
At the approval prompt you can decide for just this call or for the rest of the session. Session approvals are remembered by a **prefix/pattern class**, not the raw command:
|
data/docs/skills.md
CHANGED
|
@@ -31,6 +31,12 @@ skills:
|
|
|
31
31
|
Override the search paths via the `skills.paths` config key. On a name collision
|
|
32
32
|
the **directory** layout wins over the flat-file layout (it is the richer unit).
|
|
33
33
|
|
|
34
|
+
The registry additionally scans the **agent-neutral** skill dirs — project
|
|
35
|
+
`.agents/skills/` and `~/.agents/skills/` (the emerging cross-agent convention
|
|
36
|
+
used by `npx skills`, Gemini CLI, goose) — at the lowest precedence: a
|
|
37
|
+
rubino-path skill of the same name wins, and nothing changes when those dirs
|
|
38
|
+
are absent. The project-local one is trust-gated exactly like `.rubino/skills`.
|
|
39
|
+
|
|
34
40
|
### Two layouts
|
|
35
41
|
|
|
36
42
|
| Layout | Path | Skill name | Bundled files |
|
|
@@ -59,6 +65,31 @@ skills:
|
|
|
59
65
|
include_builtin: false # default true
|
|
60
66
|
```
|
|
61
67
|
|
|
68
|
+
### Installing skills from git (`rubino skills install`)
|
|
69
|
+
|
|
70
|
+
Any git repo shipping the `<name>/SKILL.md` layout is a skill source — there is
|
|
71
|
+
no marketplace and nothing else is vendored in the gem. Skills are
|
|
72
|
+
shallow-cloned and copied into `~/.rubino/skills`, where the registry discovers
|
|
73
|
+
them like any hand-written skill:
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
rubino skills install anthropics/skills --list # see what a source ships
|
|
77
|
+
rubino skills install anthropics/skills --skill pdf # pick by name (repeatable)
|
|
78
|
+
rubino skills install owner/repo --all # take everything
|
|
79
|
+
rubino skills install https://gitlab.com/o/r.git # any git URL works
|
|
80
|
+
rubino skills install --documents # anthropics/skills: pdf docx pptx xlsx
|
|
81
|
+
rubino skills update # re-fetch installed skills
|
|
82
|
+
rubino skills remove NAME # delete dir + provenance
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
With no `--skill`/`--all` and multiple skills in the source, the CLI prints the
|
|
86
|
+
catalogue and asks you to pick (off a TTY it just prints the hint). Provenance
|
|
87
|
+
is recorded per installed skill in `~/.rubino/skills/.sources.json`
|
|
88
|
+
(`name → {source, path, commit}`): `rubino skills list` shows it in the Source
|
|
89
|
+
column, and `update` re-fetches from the recorded source, reporting
|
|
90
|
+
*up to date* vs *updated* by comparing commits. `remove` only deletes skills
|
|
91
|
+
this mechanism installed — hand-written skills are never touched.
|
|
92
|
+
|
|
62
93
|
### Authoring a `SKILL.md`
|
|
63
94
|
|
|
64
95
|
A `SKILL.md` is YAML frontmatter followed by the instruction body:
|
data/docs/troubleshooting.md
CHANGED
|
@@ -48,7 +48,7 @@ The sqlite backend ranks by direct content relevance (FTS5/BM25) first; graph/re
|
|
|
48
48
|
|
|
49
49
|
## The agent keeps asking to approve every shell command
|
|
50
50
|
|
|
51
|
-
That's `security.
|
|
51
|
+
That's the `confirm_all` policy (`security.confirm_policy: confirm_all`). The default is `dangerous_only` (only dangerous-pattern commands prompt). Options: switch back with `rubino config set security.confirm_policy dangerous_only`, approve for the session at the prompt, add prefixes to `security.command_allowlist`, or use `/mode yolo` / `--yolo` to skip prompts (the hardline floor and `permissions: deny` still apply). See [security.md](security.md).
|
|
52
52
|
|
|
53
53
|
## A command was denied even with `--yolo`
|
|
54
54
|
|
data/exe/rubino
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
+
# On a bare POSIX/C-locale system Ruby's default_external is US-ASCII, so any
|
|
5
|
+
# File.read of a UTF-8 prompt/template (em-dashes etc.) raises
|
|
6
|
+
# Encoding::CompatibilityError before the agent even boots (#273; same class as
|
|
7
|
+
# #250/#251). Pin the process to UTF-8 up front so NO read path can regress,
|
|
8
|
+
# regardless of the user's locale. Promoting to UTF-8 is always safe; only
|
|
9
|
+
# touch it when the locale left us on a non-UTF-8 default.
|
|
10
|
+
Encoding.default_external = Encoding::UTF_8 unless Encoding.default_external == Encoding::UTF_8
|
|
11
|
+
Encoding.default_internal = Encoding::UTF_8 unless Encoding.default_internal == Encoding::UTF_8
|
|
12
|
+
|
|
4
13
|
require "rubino"
|
|
5
14
|
|
|
6
|
-
# Load .env before anything else so ENV vars are available immediately
|
|
7
|
-
|
|
15
|
+
# Load .env before anything else so ENV vars are available immediately.
|
|
16
|
+
# A malformed config.yml must NOT crash the process with a raw Ruby+Psych
|
|
17
|
+
# backtrace (CFG-1) — ConfigGuard turns it into a clean boot abort so every
|
|
18
|
+
# command (including `rubino doctor`) reports it gracefully.
|
|
19
|
+
# Pass ARGV so the config-issue warning is suppressed for the pure-meta
|
|
20
|
+
# commands (version/help) that never need a configured model.
|
|
21
|
+
Rubino::Boot::ConfigGuard.load!(argv: ARGV)
|
|
8
22
|
|
|
9
23
|
Rubino::CLI::Commands.start(ARGV)
|