1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
# roba-config.sample.toml
# =========================
# A documented example roba.toml. Copy it to `roba.toml` (project root) or
# `~/.config/roba.toml` (user-wide) and uncomment/edit. `roba profile init`
# drops this file for you.
#
# DISCOVERY roba reads ~/.config/roba.toml, then every roba.toml from the
# git root down to the cwd. Closer-to-cwd files win per key; list
# keys (prepend/append/attach/allow_tool/deny_tool/add_dir/
# mcp_config) concatenate.
# PRECEDENCE highest wins: CLI flag > ROBA_<KEY> env var > active
# [profile.NAME] > top-level keys below > roba's built-in defaults.
# MIRRORS every key here is also a CLI flag (--key) and an env var
# (ROBA_KEY). See `roba --help` for the flag/env reference.
# ===========================================================================
# Top-level defaults -- apply to every roba call in this file's scope.
# All commented out; uncomment what you want as a project/user-wide default.
# ===========================================================================
# --- Prompt composition ---
# prepend = ["CLAUDE.md"] # files prepended to every prompt
# append = ["CONVENTIONS.md"] # files appended
# attach = ["src/**/*.rs"] # globbed files embedded with `File: PATH`
# editor_history = 1 # pre-fill -e with the last N responses (0 disables)
# git_diff = true # embed the working-tree diff
# git_log = 5 # embed the last N commits
# git_status = true # embed `git status --short`
# --- Permissions (safe by default: Read, Glob, Grep only) ---
# readonly = true # explicit default (no-op marker)
# writable = true # add Edit + Write
# full_auto = true # bypass ALL tool checks (sandbox only)
# allow_tool = ["Bash(git:*)"] # add specific tool patterns (repeatable)
# deny_tool = ["WebFetch"] # block patterns (deny wins over allow)
# add_dir = ["../shared"] # extra tool-access dirs (claude --add-dir;
# # forwarded verbatim, claude resolves them)
# permission_mode = "plan" # claude's own mode: plan | acceptEdits
# # | dontAsk | auto | bypassPermissions | default
# --- Model / effort / system prompt ---
# model = "haiku" # alias (sonnet/opus/haiku) or full id
# fallback_model = "haiku" # model to retry on if the primary is overloaded
# effort = "high" # low | medium | high | xhigh | max
# system_prompt = "..." # replace the system prompt for the call
# append_system_prompt = "..." # add to the default system prompt
# agent_notice = "..." # replace the built-in single-turn advisory
# # text (roba injects it by default; disable
# # with --no-agent-notice or no_agent_notice).
# # "" injects nothing
# no_agent_notice = true # suppress the built-in single-turn advisory
# --- Sessions ---
# continue = true # auto-continue the most recent session here
# # (or continue = "session-id" for a specific one)
# worktree = true # run in a fresh git worktree
# agent = "reviewer" # pin a claude-code subagent for the call
# no_session_persistence = true # run without writing a resumable session record
# --- Output / rendering ---
# json = true # structured --json envelope on stdout
# json_schema = "schema.json" # path to a JSON Schema; constrains structured
# # output (claude --json-schema). roba reads the
# # file and inlines it; output surfaces under
# # .result.* in the --json envelope
# quiet = true # suppress metadata (footer/spinner/markers)
# plain = true # no markdown render / color / spinner
# stream = true # live TTY progress (TTY only)
# echo = true # print the resolved prompt before the answer
# show_thinking = true # render extended-thinking blocks (needs stream)
# no_dollars = true # footer shows tokens only, no dollar figure
# trace = "trace.jsonl" # write the spawned session's events as JSONL
# rates_file = "rates.toml" # override the bundled per-model rate table
# --- Failure / agent-tier ---
# no_retry = true # surface transient failures immediately
# bare = true # minimal-overhead mode (ANTHROPIC_API_KEY auth)
# no_agent_check = true # skip the --agent frontmatter permission check
# --- Limits (unattended guardrails; cap hit -> run errors, exit 1) ---
# max_turns = 20 # cap the agentic turn count
# max_budget_usd = 5.0 # cap total spend in USD
# --- MCP servers (thin pass-through to claude --mcp-config; NOT a daemon) ---
# mcp_config = ["mcp.json"] # load MCP servers from JSON files (forwarded
# # verbatim; claude reads them, roba does not)
# strict_mcp_config = true # use ONLY the mcp_config servers, ignoring
# # all other configured MCP servers
# --- Template variables --- substituted as {{KEY}} in prompts/prepends
# [vars]
# TEAM = "platform"
# STYLE = "imperative, concise"
# ===========================================================================
# Profiles -- named bundles of the keys above, activated with --profile NAME.
# A profile named `default` auto-applies when no --profile is given.
# Inspect with `roba profile {list,show,active,path}`.
# ===========================================================================
# Read-only review against the working-tree diff.
# roba --profile review "is this safe to merge?"
[]
= true
= true
# Read-only walkthrough; pair with --attach to focus on specific files.
# roba --profile explain --attach 'src/foo.rs' "what does this do?"
[]
= true
# Commit message from staged work, with a profile-scoped template var.
# roba --profile commit-msg "write a commit message"
[]
= true
= true
[]
= "imperative, concise, no marketing"
# Diagnose a failed build from piped output.
# cargo build 2>&1 | roba --profile fix-build "what's broken?"
[]
= true
= true
# ===========================================================================
# Aliases -- new verbs. `roba NAME [args]` expands a prompt template + flags
# and dispatches like a normal call. Inspect with `roba alias {list,show,path}`.
#
# Substitution inside `template`:
# ${1} ${2} ... positional args after NAME (1-based)
# ${@} all positional args, space-joined
# ${name} named arg, resolved via the `args` schema below
# $$ a literal '$' (so dollar amounts / shell vars survive)
# $(command) shell substitution -- runs in YOUR shell (sh -c), stdout
# interpolated. This is NOT sandboxed; don't put a command
# you wouldn't run yourself. (Orthogonal to claude's perms.)
# Args interpolated into a $(...) region are shell-quoted:
# they are DATA, not shell code, so `roba review '1; rm -rf ~'`
# runs `gh pr diff '1; rm -rf ~'` (one inert arg). Put shell
# syntax in the template literal, not in an arg.
#
# `flags` merge BEFORE your CLI flags, so CLI wins (put extra flags after the
# positional args: `roba review 42 --full-auto`). A template-less alias is a
# flag-shortcut: your args become the prompt verbatim. Built-in names
# (cost/history/last/profile/alias) cannot be shadowed.
# ===========================================================================
# Pull a PR into a review prompt. -> roba review 42
[]
= "Review a PR by number"
= "reviewer"
= ["pr"] # positional 1 -> ${pr}
= ["--readonly"]
= """
Review PR #${pr} in this repo.
$(gh pr view ${pr} --json title -q '.title')
Diff:
$(gh pr diff ${pr})
"""
# Conventional-commit message from the staged diff. -> roba commit-msg
[]
= "Conventional-commit message from the staged diff"
= ["--quiet"]
= "Write a conventional-commit message for:\n\n$(git diff --staged)"
# Flag-shortcut (no template): args become the prompt verbatim.
# roba r "look at the auth module" -> --readonly --agent reviewer "look at the auth module"
[]
= "Quick read-only review preset"
= "reviewer"
= ["--readonly"]
# Self-hosted "draft an alias" verb (pure config form). -> roba draft "..."
# The built-in `roba alias draft "..."` is the VALIDATED form -- it parses the
# generation against the real schema before printing (and `roba profile draft
# "..."` is its profile twin). This config alias is the unvalidated shortcut:
# it just asks claude, pipes nothing back through the deserializer. Reach for
# the built-in when you want the safety rail.
[]
= "Draft a roba alias from a description (unvalidated; see `roba alias draft`)"
= ["--quiet"]
= "Write one roba `[alias.NAME]` TOML block for: ${@}. Fields: description, agent, template, flags, args. Output only the TOML block, no fences, no prose."
# ===========================================================================
# Named sessions -- bind a stable handle to a claude session id, then resume
# it with `roba --session NAME` (or ROBA_SESSION=NAME). roba only READS this
# map; it never writes it. To bind a name, run a session, grab its id from the
# JSON envelope (`roba --json ... | jq -r '.result.session_id'`), and paste it
# here. UUIDs are machine-local, so keep `[session]` in an untracked/local
# roba.toml (e.g. one git-ignored alongside the repo, or ~/.config/roba.toml).
# ===========================================================================
# [session]
# meta = "0199aabb-ccdd-eeff-0011-223344556677" # roba --session meta "..."
# this-repo = "0199eeff-0011-2233-4455-66778899aabb"