Adding Capabilities
Capabilities define what tasks Iris can perform. Each capability is a TOML file containing a prompt and output schema. This guide shows you how to add a new capability to Git-Iris.
What is a Capability?
A capability combines three elements:
- Task Prompt — Instructions for Iris on how to approach the task
- Output Type — Structured format for the response (JSON schema)
- Tool Access — Which tools Iris can use for this task
Capability Structure
Basic TOML Format
name = "my_capability"
description = "Brief description of what this does"
output_type = "MyOutputType"
task_prompt = """
Instructions for Iris go here.
## Tools Available
- `git_diff()` - Get code changes
- `file_analyzer()` - Analyze specific files
## Output Requirements
Your requirements for the output format.
## JSON Output
Return a `MyOutputType` with: field1, field2, etc.
"""Real Example: Commit Message Generation
From src/agents/capabilities/commit.toml:
name = "commit"
description = "Generate commit messages from staged changes"
output_type = "GeneratedMessage"
task_prompt = """
Generate a commit message for the staged changes.
## MANDATORY FIRST STEP
**ALWAYS call `project_docs(doc_type="context")` FIRST** before any other tool.
This fetches README + AGENTS.md/CLAUDE.md containing project conventions you MUST follow.
Do not skip this step.
## Tools Available
- `project_docs(doc_type="context")` - **CALL FIRST** - Get README + context
- `git_diff()` - Get staged changes with relevance scores
- `git_log(count=5)` - Recent commits for style reference
## Workflow
1. **FIRST**: Call `project_docs(doc_type="context")`
2. Call `git_diff()` to see what changed
3. Generate the commit message following project conventions
## Output Requirements
- **Subject line**: Imperative mood, max 72 chars, no period
- **Body**: Explain WHY, not what. Wrap at 72 chars.
- **Plain text only**: No markdown, no code fences
## JSON Output
Return a `GeneratedMessage` with: `emoji` (string or null), `title` (subject), `message` (body)
"""Step-by-Step: Adding a New Capability
Teaching Example
This section walks through creating a hypothetical "Feature Summary" capability. This capability does not exist in the current codebase — it's an example to illustrate the pattern. Follow along to learn how capabilities work.
Step 1: Create the TOML File
Create src/agents/capabilities/feature_summary.toml:
name = "feature_summary"
description = "Generate a high-level summary of a feature branch"
output_type = "FeatureSummary"
task_prompt = """
You are Iris, an AI assistant analyzing a feature branch to create a high-level summary.
## MANDATORY FIRST STEP
**ALWAYS call `project_docs(doc_type="context")` FIRST** to understand the project.
## Tools Available
- `project_docs(doc_type="context")` - Get project context (README, conventions)
- `git_diff(from, to)` - Get changes between branches
- `git_log(count=N)` - Get commit history
- `file_analyzer(paths)` - Analyze specific files in detail
## Workflow
1. Call `project_docs(doc_type="context")` to understand the project
2. Get the diff between main and the feature branch
3. Identify key files and patterns
4. Summarize the feature's purpose, implementation approach, and impact
## Output Requirements
- **Purpose**: 1-2 sentences on what this feature does
- **Approach**: Brief technical overview
- **Files Changed**: Count and categorization
- **Impact**: User-facing changes, API changes, internal refactors
## JSON Output
Return a `FeatureSummary` with:
- `purpose` (string)
- `approach` (string)
- `files_changed` (number)
- `impact` (string)
"""Step 2: Define the Output Type
Create or update src/types/feature_summary.rs:
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
/// Feature summary response
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct FeatureSummary {
/// What this feature does (1-2 sentences)
pub purpose: String,
/// Technical approach overview
pub approach: String,
/// Number of files changed
pub files_changed: usize,
/// User-facing and technical impact
pub impact: String,
}
impl FeatureSummary {
/// Format as markdown for display
pub fn format(&self) -> String {
format!(
"# Feature Summary\n\n\
## Purpose\n{}\n\n\
## Approach\n{}\n\n\
## Impact\n{}\n\n\
Files changed: {}\n",
self.purpose,
self.approach,
self.impact,
self.files_changed
)
}
}Add to src/types/mod.rs:
pub mod feature_summary;
pub use feature_summary::FeatureSummary;Step 3: Register in StructuredResponse
Edit src/agents/iris.rs:
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StructuredResponse {
CommitMessage(crate::types::GeneratedMessage),
PullRequest(crate::types::MarkdownPullRequest),
Changelog(crate::types::MarkdownChangelog),
ReleaseNotes(crate::types::MarkdownReleaseNotes),
MarkdownReview(crate::types::MarkdownReview),
SemanticBlame(String),
PlainText(String),
// Add your new type:
FeatureSummary(crate::types::FeatureSummary),
}
impl fmt::Display for StructuredResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
// ... existing arms ...
StructuredResponse::FeatureSummary(summary) => {
write!(f, "{}", summary.format())
}
}
}
}Step 4: Load the Capability
Add the embedded TOML constant in src/agents/iris.rs:
const CAPABILITY_COMMIT: &str = include_str!("capabilities/commit.toml");
const CAPABILITY_PR: &str = include_str!("capabilities/pr.toml");
// ... existing capabilities ...
const CAPABILITY_FEATURE_SUMMARY: &str = include_str!("capabilities/feature_summary.toml");Register in the capability map (find the capability loading code):
capabilities.insert("feature_summary", CAPABILITY_FEATURE_SUMMARY);Step 5: Add Execution Logic
In src/agents/iris.rs, find the execute_task method and add a match arm:
match capability.output_type.as_str() {
"GeneratedMessage" => {
let response: GeneratedMessage = self.execute_with_schema(prompt).await?;
Ok(StructuredResponse::CommitMessage(response))
}
// ... existing arms ...
"FeatureSummary" => {
let response: FeatureSummary = self.execute_with_schema(prompt).await?;
Ok(StructuredResponse::FeatureSummary(response))
}
_ => Err(anyhow::anyhow!("Unknown output type: {}", capability.output_type)),
}Step 6: Test Your Capability
# Build
cargo build
# Test in CLI (you may need to add a CLI command for your capability)
cargo run -- feature-summary main..feature-branch
# Or test in Studio (if you add a mode for it)
cargo run -- studioBest Practices
Prompt Engineering
Be specific about workflow:
## Workflow
1. Call `project_docs(doc_type="context")` first
2. Get the diff with `git_diff()`
3. For files over 500 lines, use `file_analyzer()` for targeted analysis
4. Synthesize findings into output formatProvide clear output requirements:
## Output Requirements
- **Title**: Max 100 chars, action-oriented
- **Summary**: 2-3 paragraphs, focus on impact
- **No uncertain language**: State facts definitively, not "likely" or "probably"Give examples:
Example output:
{
"title": "Add user authentication with JWT",
"summary": "Implements JWT-based authentication...",
"impact": "Breaking: All API endpoints now require auth headers"
}Context Strategy
Guide Iris on how to handle different changeset sizes:
## Context Strategy by Size
- **Small** (≤3 files, <100 lines): Consider all changes equally
- **Medium** (≤10 files, <500 lines): Focus on files with >60% relevance
- **Large** (>10 files or >500 lines): Use top 5-7 highest-relevance files
- **Very Large** (>20 files): Use `parallel_analyze` to distribute workTool Selection
Only list tools relevant to the task:
## Tools Available
- `git_diff()` - Get changes (use detail="summary" first)
- `git_log(count=5)` - Recent commits for context
- `file_analyzer(paths)` - Deep analysis of specific files
# Don't list every possible tool—keep it focusedCertainty Standards
Enforce definitive language:
## Writing Standards
- **NEVER use uncertain language**: Avoid "likely", "probably", "possibly",
"might", "may", "seems", "appears to", "presumably", "could be"
- You have full code access—investigate until you can state facts definitively
- If unsure, use tools to gather more contextOutput Type Patterns
Simple JSON Schema
For structured data:
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct SimpleOutput {
pub field1: String,
pub field2: Vec<String>,
pub field3: Option<usize>,
}Markdown Wrapper
For LLM-driven formatting:
#[derive(Serialize, Deserialize, JsonSchema, Debug, Clone)]
pub struct MarkdownOutput {
/// Markdown content (LLM controls structure)
pub content: String,
}
impl MarkdownOutput {
pub fn raw_content(&self) -> &str {
&self.content
}
}Use markdown wrappers when you want the LLM to control the exact structure while still having parseable output.
Common Patterns
Multi-Stage Analysis
## Workflow
1. Initial scan: `git_diff(detail="summary")` for overview
2. Identify key areas from relevance scores
3. Deep dive: `file_analyzer()` on top 5 files
4. Synthesize into structured outputParallel Processing
For large changesets:
## Very Large Changesets (>20 files)
Use `parallel_analyze` to distribute work:
parallel_analyze({
"tasks": [
"Analyze API changes in src/api/",
"Review database schema changes",
"Check frontend component updates"
]
})
Each subagent analyzes independently, then you synthesize.Style Adaptation
Allow preset-based customization:
## Style Adaptation
If STYLE INSTRUCTIONS are provided, prioritize that style in your output.
The structural requirements still apply, but adapt tone and word choice.Integration with Studio
If you add a Studio mode for your capability:
- Add mode variant to
Modeenum insrc/studio/state/mod.rs - Create state struct in
src/studio/state/modes.rs - Implement handler and renderer (see Adding Studio Modes)
Troubleshooting
Iris doesn't use the right tools
Make the tool list more explicit and add workflow steps that require specific tools.
Output parsing fails
Ensure your JSON schema matches exactly. Test with --debug to see raw responses.
Responses are too vague
Add certainty standards and specific output requirements. Show examples.
Context is incomplete
Guide Iris on when to gather more context. Add size-based strategies.
Examples in Codebase
Study these real capabilities:
commit.toml— Simple structured output, style adaptationreview.toml— Markdown wrapper, parallel analysis, size strategiespr.toml— Multi-stage workflow, context gatheringchangelog.toml— Version comparison, structured formatting
Next Steps
- Add tools to give Iris new context sources → Adding Tools
- Create a mode to surface your capability in Studio → Adding Studio Modes
- Contribute your capability back → Contributing
