.claude/skills/rig-agent-scaffold/SKILL.md
<!-- This file is generated by scripts/sync_agent_instruction_files.sh. --> <!-- Do not edit this file directly; update AGENTS.md and re-run the sync script. --> --- name: rig-agent-scaffold description: > Scaffold a Rig agent implementation. Use when creating agents, chatbots, RAG pipelines, or tool-calling workflows with Rig's builder pattern. argument-hint: "[agent-description]" allowed-tools: - Read - Glob - Grep - Edit - Write - Bash --- # Rig Agent Scaffold Create or refa
npx skillsauth add 0xplaygrounds/rig .claude/skills/rig-agent-scaffoldInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
name: rig-agent-scaffold description: > Scaffold a Rig agent implementation. Use when creating agents, chatbots, RAG pipelines, or tool-calling workflows with Rig's builder pattern. argument-hint: "[agent-description]" allowed-tools:
Create or refactor a Rig agent implementation.
Execution requirements:
WasmCompatSend / WasmCompatSync bounds where applicable..unwrap() / .expect() unless impossible.cargo fmt, cargo clippy --all-targets --all-features, and cargo test.Rig is built on several core principles that all contributions must respect:
Rig uses traits to define provider-agnostic interfaces:
CompletionModel - For text completion/chat modelsEmbeddingModel - For embedding generationVectorStoreIndex - For vector similarity searchTool - For defining callable toolsNew implementations should implement these traits rather than creating parallel abstractions.
Nearly every configurable type uses the builder pattern:
let agent = client
.agent(openai::GPT_5_2)
.preamble("System prompt")
.tool(my_tool)
.temperature(0.8)
.build();
Follow this pattern for any new configurable types.
The client system uses a generic architecture with provider extensions:
pub struct Client<Ext = Nothing, H = reqwest::Client> {
// ...
}
Where Ext is a provider extension defining capabilities and H is the HTTP backend.
Providers declare their capabilities explicitly:
impl<H> Capabilities<H> for MyProviderExt {
type Completion = Capable<CompletionModel<H>>; // Supported
type Embeddings = Nothing; // Not supported
// ...
}
Use Capable<T> for supported features and Nothing for unsupported ones.
Rig supports WebAssembly targets. Since WASM is single-threaded, Send/Sync bounds are unnecessary and can prevent compilation.
#[cfg(not(target_family = "wasm"))]
pub trait WasmCompatSend: Send {} // native
#[cfg(target_family = "wasm")]
pub trait WasmCompatSend {} // wasm
#[cfg(not(target_family = "wasm"))]
pub trait WasmCompatSync: Sync {} // native
#[cfg(target_family = "wasm")]
pub trait WasmCompatSync {} // wasm
Always use WasmCompatSend and WasmCompatSync instead of raw Send and Sync in trait bounds.
// Correct
pub trait MyTrait: WasmCompatSend + WasmCompatSync {
fn do_thing(&self) -> impl Future<Output = ()> + WasmCompatSend;
}
// Incorrect - will break WASM builds
pub trait MyTrait: Send + Sync {
fn do_thing(&self) -> impl Future<Output = ()> + Send;
}
Use WasmBoxedFuture for boxed futures:
pub type WasmBoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>; // native
pub type WasmBoxedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + 'a>>; // wasm
Some error types need platform-specific bounds:
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[cfg(not(target_family = "wasm"))]
#[error("Error: {0}")]
Inner(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
#[cfg(target_family = "wasm")]
#[error("Error: {0}")]
Inner(#[from] Box<dyn std::error::Error + 'static>),
}
Prompt hooks allow observing and controlling the agent's execution lifecycle.
PromptHook<M> provides callbacks for both streaming and non-streaming requests. All methods have default implementations — implement only what you need.
on_completion_call - Before sending prompt to model (returns HookAction)on_completion_response - After receiving response (returns HookAction)on_tool_call - Before invoking a tool (returns ToolCallHookAction)on_tool_result - After tool returns result (returns HookAction)on_text_delta - During streaming, on each text chunk (returns HookAction)on_tool_call_delta - During streaming, on each tool call chunk (returns HookAction)ToolCallHookAction::Continue (or .cont()) - Allow tool executionToolCallHookAction::Skip { reason } (or .skip("reason")) - Reject tool; reason becomes tool resultToolCallHookAction::Terminate { reason } (or .terminate("reason")) - Terminate the agentic loop earlyHookAction::Continue (or .cont()) - Continue normal executionHookAction::Terminate { reason } (or .terminate("reason")) - Terminate the agentic loop early.with_hook(), not globallyWasmCompatSend/WasmCompatSync boundsUse full where clause syntax for readability:
// Correct
impl<T> CompletionModel for MyModel<T>
where
T: HttpClientExt + Clone + WasmCompatSend + Debug + Default + 'static,
{
// ...
}
// Avoid inline bounds for complex signatures
impl<T: HttpClientExt + Clone + WasmCompatSend + Debug + Default + 'static> CompletionModel for MyModel<T> {
// ...
}
DO NOT use String as an error type. Define proper error enums:
// Correct
#[derive(Debug, thiserror::Error)]
pub enum MyError {
#[error("Failed to parse response: {0}")]
ParseError(#[from] serde_json::Error),
#[error("Provider returned error: {0}")]
ProviderError(String),
}
// Absolutely forbidden
fn do_thing() -> Result<(), String> // NO
DO NOT stub out error handling:
// Forbidden
let result = fallible_operation().unwrap(); // NO
let result = fallible_operation().expect("this should work"); // NO unless truly impossible
// Correct
let result = fallible_operation()?;
let result = fallible_operation().map_err(MyError::from)?;
Comments explain WHY, not WHAT.
If you need to explain what code is doing, the code itself is unclear. Rename variables, extract functions, or restructure.
// Bad - explains what
// Increment counter by one
counter += 1;
// Bad - explains what
// Check if user is admin
if user.role == Role::Admin {
// Good - explains why
// Rate limiting resets at midnight UTC, so we need the day boundary
let boundary = timestamp.truncate_to_day();
// Good - explains non-obvious business logic
// Providers may return tool calls split across multiple chunks,
// so we accumulate them by index until the stream ends
///) to all public items//!) to modulesTODO items are not allowed in submitted code.
If you need a TODO, the implementation is incomplete. Either:
// Forbidden
fn process() {
// TODO: handle edge case
}
// Forbidden
fn process() {
unimplemented!("will add later")
}
Run these commands and fix all issues before submitting:
cargo fmt
cargo clippy --all-targets --all-features
cargo test
If you cannot run these commands (e.g., environment limitations), explicitly ask the user to run them before the PR is submitted.
If your change involves:
Discuss with the user first. Do not implement major architectural changes without explicit approval. Open an issue for discussion if needed.
Before considering code complete:
.unwrap() or .expect() on fallible operations unless truly impossible)String error typesWasmCompatSend/WasmCompatSync instead of Send/Synccargo fmt passescargo clippy --all-targets --all-features passescargo test passesDO NOT MAKE COMMITS UNLESS THE USER HAS ASKED YOU TO DO SO. Users should be able to manually verify that what you have done has worked before proceeding with a commit, and may also want to write their own commit messages.
If the PR contains breaking changes, the PR message must list the public API items that have broken and the migration path for each.
PRs will be rejected if they contain:
String error types, .unwrap() everywhere, incomplete handlingSend/Sync instead of WasmCompat* variantsRemember: The quality bar exists because Rig is used in production by many projects. Every contribution must maintain that standard.
tools
Build LLM-powered applications with Rig, the Rust AI framework. Use when creating agents, RAG pipelines, tool-calling workflows, structured extraction, or streaming completions. Covers all providers with a unified API.
tools
<!-- This file is generated by scripts/sync_agent_instruction_files.sh. --> <!-- Do not edit this file directly; update AGENTS.md and re-run the sync script. --> --- name: rig-wasm-check description: > Audit Rig code for WebAssembly compatibility. Use when verifying that trait bounds use WasmCompatSend/WasmCompatSync, futures use WasmBoxedFuture, and error types have proper conditional compilation. allowed-tools: - Read - Glob - Grep --- # Rig WASM Compatibility Check Audit code fo
tools
<!-- This file is generated by scripts/sync_agent_instruction_files.sh. --> <!-- Do not edit this file directly; update AGENTS.md and re-run the sync script. --> --- name: rig-vector-store description: > Implement a Rig vector store companion crate. Use when adding a new vector database backend (MongoDB, LanceDB, Qdrant, etc.) or reviewing an existing vector store integration. argument-hint: "[store-backend]" allowed-tools: - Read - Glob - Grep - Edit - Write - Bash --- # Rig
tools
<!-- This file is generated by scripts/sync_agent_instruction_files.sh. --> <!-- Do not edit this file directly; update AGENTS.md and re-run the sync script. --> --- name: rig-review description: > Review Rig changes against mandatory quality gates before opening a PR. Use before submitting code to verify error handling, WASM compatibility, coding style, and required checks pass. allowed-tools: - Read - Glob - Grep - Bash --- # Rig Pre-PR Review Perform a Rig-focused pre-PR revie