Imported from dzhioev/bro (
bro/prompts/AGENTS.md). Install upstream withnpx skills add dzhioev/bro --skill prompts. Copyright stays with the author.
Prompt store
Centralised prompt store.
Five loading conventions:
auto-inject into every bro + ride solo|along session (shared/);
serve as a reference doc
— injected into ride solo|along sessions or mounted as a FileSource tool (*.md);
inject the session fragments at launch (hold.md composing holds/, plus member.md, summoner.md, and summoned.md);
splice into an opting-in text via {{include}} (fragments/);
load explicitly by name (top-level *.prompt / *.prompt.template).
Files
*.prompt— plain text, used as-is. Loaded withbro.prompts.get_prompt('name.prompt')*.prompt.template— Pythonstr.formattemplate. Requires kwargs at load time;get_promptenforces "template ↔ kwargs" symmetry — passing kwargs to a non-template, or omitting kwargs for a template, raises
Prompt content may carry bro.base.template directives (#harness/#wire/#creds, plus session-fragment #talk; grammar and semantics: bro/reference/template.md):
every rendering surface renders its text once with its own facts via bro.mcp.render_text
— BaseBro.__init__ for the two bro flavors, ride/ride/claude/system_prompt.py:session_append_prompt for managed Claude sessions
— so a directive works in shared/ and bro class prompts alike.
FileSource-served docs are the exception:
one rendering is read by every harness, so their bodies must be surface-neutral
— FileSource.read supplies no facts and a surface directive raises.
Harness-specific conditioning is expressed with these directives, never as prose that addresses both surfaces and leaves the reader to pick:
fork the text with {{iff #harness = bro}}…{{eliff #harness = claude}}…{{end}}
— the chain raises when no branch matches, so the fork is self-guarding
— and each surface reads only its own instruction.
A condition on a tool the harness offers is no harness fork and stays prose:
the model reads its own tool list, and one harness offers different tools by mode.
PromptLoader is the contained directory-backed loader;
__init__.py binds the framework's module-level get_prompt / get_prompt_path surface to bro/prompts/.
Consumer packages bind their own loader for package-local prompts.
Do not open() prompts ad-hoc from elsewhere.
Names are contained to the loader's directory:
a name that resolves outside it (.. traversal, absolute path) raises.
get_prompt_path(name) returns the Path if a caller needs to hand the file off to something that wants a path rather than the body (e.g. bro.datasources.file.FileSource).
{{include <name>}} directives resolve through it too
— bro.mcp.render_text wires get_prompt as the template engine's include resolver, so a spliced prompt loads exactly like a directly-requested one.
Auto-injected shared/ directory
bro/prompts/shared/*.md is appended to the system prompt of every Bro (see bro/bro.py:_load_shared_prompts) AND injected into every ride solo|along Claude Code session via ride/ride/claude/system_prompt.py:_load_base_prompts.
Conventions that must hold across both surfaces and at every hold (e.g. word choices) belong here.
Files are sorted alphabetically at load time, so prefix with 00-, 10-, etc. if order matters.
*.md reference docs
A markdown reference doc in bro/prompts/ (loader names are /-relative) reaches sessions one of two ways:
- injected — listed in
ride/ride/claude/system_prompt.py:_BASE_PROMPT_FILES, appended to everyride solo|alongsession's--append-system-prompt. For content every managed Claude session must carry unconditionally. - tool-served — declared as a
FileSourceinbro/datasources/references.py, then listed in a bro'sdata_sourceseither on its own (areadtool of its own namespace) or asman('<topic>'), joining that bro's manual: on every harness the bro serves, and the framework lists whichever it mounts in the bro system prompt's## Data sourcesblock. For reference docs the agent consults on demand; the body must be surface-neutral (no#harnessforks —FileSource.readrenders with no facts and raises on one).
Current reference docs:
-
environment.md— session-banner playbook: every surface calls thebro::bannerservice tool and reads this doc through theenvironmentpage (ride banner --llmstays as the human CLI). Tool-served only — not injected -
tool_names.md— the tool-name resolution rule, templated on the#wirescheme; one file serves every surface. Claude sessions get themcprendering (ns::tool→mcp__ns__tool): injected here for non-raw sessions, composed intoBaseBro.claude_system_promptforride solo|along --rawones. Bro-native LLM runs compose thebarerendering (ns::tool→ns__tool) intoBaseBro.system_prompt. Deliberately noFileSource
Session fragments
bro.prompts.session_fragment(hold, …facts) renders the text a launch surface appends after the composed prompt, and every injection site calls it
(ride/ride/claude/system_prompt.py:session_append_prompt, ride/ride/claude/claude_argv.py for --raw, bro/bro.py:BaseBro.system_prompt_for).
Those callers pass bro.summon.talk() as the #talk fact for the summoned contract;
unset stays unpublished and empty means the run's quest is mute.
It is the joined-party warning when the run is a member, the summoner’s watch when the run may summon, the summoned-delivery contract when the run is one another session is waiting on, then the hold fragment
— last, where instruction recency is strongest.
Party-member contract
member.md states that a joined session shares its summoner’s tree and works beside it.
It renders when bro.summon.party_member() reads RIDE_PARTY_MEMBER from the launch environment.
Summoner contract
summoner.md (top level) has a session that may summon keep the quest watch armed, so every summon's lifecycle and chat remains observable.
It renders only for a run whose effective allow-list (bro.summon.effective_may_summon()) is non-empty, and its body forks by surface:
the Claude harness holds the watch on a persistent Monitor, bro-native starts quest watch as a watch-mode job and chills on its inbox, and raw MCP sessions poll the retained quests because that wire has no notification wake.
The same text states the notification trust rule and tells the summoner how to exchange questions, continue a retained quest, cancel a child, and how a one-shot run ends on its surface:
native ends when a turn ends with nothing running and nothing in flight and gives one notice otherwise,
managed Claude holds while the watch is armed and stops it once every summon has ended,
and raw ends with its turn after one notice for summons still in flight.
Summoned contract
summoned.md (top level) states what a summoned run owes its summoner and when to deliver it.
It renders only for a run bro.summon.summoned() reports as summoned, and hold-neutrally
— the duty comes with being summoned, so an attended or guided child carries the same text a spawned unattended one does.
Its #talk branches admit only the quest's live moves:
a speaking summoner reaches managed Claude through Monitor, bro-native through its watch job, and raw MCP through retained-quest polls.
worker.say enables progress, worker.question uses the surface's non-blocking watch or bounded consult, and a child without that right raises instead of asking.
Hold text
A session's hold — its user-involvement level
— is one of unattended | detached | attended | guided, ordered from no human channel to human-driven.
Every session gets exactly one level's text, picked by the launching surface at session start
— a session is told its hold, never left to detect it at runtime:
ride solo|alongpicks by its--holdflag for both claude flavors — the managed Claude session append prompt and the--raw--system-prompt(flag semantics:bro/reference/ride.md)- the bro-native launch surfaces pick it through
bro/bro.py:BaseBro.system_prompt_for—run()defaults unattended,send()guided, with every launcher's--holdoverriding (per-surface defaults:bro/launch/AGENTS.md, "Display and holds")
hold.md (top level, not in _BASE_PROMPT_FILES) selects the per-level file in holds/ via an exhaustive {{iff #hold = …}} chain and {{include}}s it;
the three non-guided level files share holds/authorization.md, the full-authorization block, and the three interactive levels
— detached, attended, guided
— share fragments/interaction.md, the interaction policy.
bro.prompts.hold_fragment(hold, …facts) is the one rendering path
— session_fragment composes it, and native/bro/fork.py re-renders it to swap a resumed run's level —
and it is the only call that supplies the #hold fact, so all other text stays hold-neutral mechanically:
a stray #hold directive in a spell or procedure doc raises.
The level files are the single place the levels differ:
unattended carries the never-ask + raise convention, detached the carry-questions-into-the-report convention, attended the end-the-turn-at-pivotal-points convention, guided the confirm-each-significant-step convention.
Bare-session grounding fragment
grounding.md (top level, not in _BASE_PROMPT_FILES) is the tool-grounding rule for ride solo|along --raw sessions:
BaseBro appends it at the end of both composed bro prompt flavors
— last, where instruction recency is strongest
— and the file's own directives render its body only for the claude-bare surface (harness bro, wire mcp),
the flavor whose argv-seeded first turn can reach the model before its MCP servers connect (bro/reference/ride.md "Session-local MCP serving").
Top-level one-shot prompts
*.prompt / *.prompt.template files at the top level are framework one-shot prompts loaded by name from their callers.
Adding a prompt
- One-shot:
drop
<name>.prompt(or<name>.prompt.templateforstr.formatslots) at the top level. Load withget_prompt('<name>.prompt'[, **kwargs]) - Auto-injected into bros and
ride solo|alongsessions: drop a*.mdinshared/. Conventions that must hold for both surfaces at every hold (word choices, tone) belong here - Include fragment:
drop a
*.mdinfragments/and splice it with{{include fragments/<name>.md}}from each opting-in text. For a convention that applies only where a capability exists — e.g.task_tracker.md, included by every persona that mounts task-tracker tools rather than injected everywhere — or only at some holds, e.g.interaction.md, included by the interactive level files - Reference doc:
drop a
*.mdunder this package, then either add its relative name toride/ride/claude/system_prompt.py:_BASE_PROMPT_FILES(injected into everyride solo|alongsession) or declare aFileSourcefor it inbro/datasources/references.py— listed on a bro directly or asman('<topic>')(tool-served on demand, every harness — e.g.environment.md)