Appearance
Customization
Iris is designed to be customized. This guide covers modifying the AI personality, adding integrations, adjusting truth and memory behavior, and maintaining your changes through upgrades.
Customization Philosophy
Iris separates "core" from "custom" to help you extend the application without forking:
- Core files live in
app/,config/iris.php, andresources/views/prompts/ - Custom files go in
app/Extensions/,config/iris-custom.php, and your own directories
This separation means you can:
- Pull updates to Iris without losing your customizations
- Clearly see what you've changed vs what's stock
- Share customizations as standalone packages
Custom Configuration
Create a config/iris-custom.php file to override settings, add tools, or customize prompts without modifying core files. This is your application configuration -commit it to version control with the rest of your customizations.
php
// config/iris-custom.php
<?php
return [
'agent' => [
'model' => 'claude-opus-4', // Override the default model
],
'truths' => [
'max_dynamic' => 8, // Override specific settings
],
];How Merging Works
Different config keys use different merge strategies:
| Key | Strategy | Description |
|---|---|---|
prompts | Replace | Your list replaces the core list entirely |
tools | Append | Your tools are added to core tools |
disabled_tools | Filter | Listed tools are removed from the final set |
provider_tools | Replace | Your list replaces core provider tools |
| Everything else | Smart merge | Associative arrays merge recursively; indexed arrays replace entirely |
Smart merge details: For nested configuration like shell or truths, associative keys are merged recursively (your values override core values). However, indexed arrays (lists) like blocked_executables, blocked_patterns, or inherit_env_vars are replaced entirely—your list becomes the new list, rather than being combined with the core list.
php
// config/iris-custom.php
return [
'shell' => [
// This scalar value overrides the core value
'default_timeout' => 60,
// This indexed array REPLACES the core list entirely
'blocked_executables' => ['sudo'], // Only 'sudo' is blocked, not the full core list
// Omitted keys like 'blocked_patterns' keep their core values
],
];Adding Custom Tools
Add your own tools without modifying core config:
php
// config/iris-custom.php
return [
'tools' => [
App\Extensions\Tools\WeatherTool::class,
App\Extensions\Tools\HomeAssistantTool::class,
],
];Your tools are appended to the core tools, so Iris still has truths, memory, calendar, and image generation.
Disabling Tools
Remove tools you don't need:
php
// config/iris-custom.php
return [
'disabled_tools' => [
App\Tools\GenerateImageTool::class,
App\Tools\Calendar\CreateCalendarEventTool::class,
App\Tools\Shell\RunShellCommandTool::class, // Disable shell even if env enabled
App\Tools\Agent\DelegateTaskTool::class, // Disable task delegation
],
];Customizing Prompts
The prompts array defines system prompt classes that render in order. To customize, you replace the entire list (order matters for system prompts):
php
// config/iris-custom.php
return [
'prompts' => [
App\Prompts\IrisStaticPrompt::class, // Keep core static prompt
App\Prompts\MemoryPrompt::class, // Recalled memories
App\Prompts\SummaryPrompt::class, // Conversation summaries
App\Prompts\WorkContextPrompt::class, // Add your context
App\Prompts\CalendarPrompt::class, // Calendar events
App\Prompts\CurrentTimePrompt::class, // Current date/time
],
];Create your own prompt class by extending the base. Prompts are self-contained: they inject RequestContext and any services they need, then fetch their context in content():
php
// app/Extensions/Prompts/WorkContextPrompt.php
<?php
namespace App\Extensions\Prompts;
use App\Prompts\Prompt;
use App\ValueObjects\RequestContext;
use Illuminate\View\View;
class WorkContextPrompt extends Prompt
{
public function __construct(
protected RequestContext $requestContext,
protected ProjectService $projectService,
) {}
public function content(): View
{
return view('prompts.work-context', [
'projects' => $this->projectService->getActiveProjects(
$this->requestContext->user()?->id
),
]);
}
// Optional: enable caching for static content
public function providerOptions(): array
{
return [
'cacheType' => 'ephemeral',
];
}
}The RequestContext provides access to the current user and message:
php
$this->requestContext->user(); // Current User model (or null)
$this->requestContext->message(); // Current user message
$this->requestContext->images(); // Attached images arrayCustomizing Provider Tools
Provider tools (like Anthropic's web search and web fetch) are stored as arrays for config caching compatibility:
php
// config/iris.php default format
'provider_tools' => [
['type' => 'web_fetch_20250910', 'name' => 'web_fetch'],
['type' => 'web_search_20250305', 'name' => 'web_search'],
],To disable all provider tools:
php
// config/iris-custom.php
return [
'provider_tools' => [], // Empty array disables all
];To use only specific provider tools:
php
// config/iris-custom.php
return [
'provider_tools' => [
['type' => 'web_search_20250305', 'name' => 'web_search'],
// web_fetch disabled
],
];Complete Customization Example
Here's a complete example of customizing Iris for a development team assistant that tracks projects and integrates with internal tools.
1. Create the Custom Config
php
<?php
// config/iris-custom.php
return [
// Use a more capable model for technical discussions
'agent' => [
'model' => 'claude-sonnet-4-5',
],
// More aggressive context settings for work
'truths' => [
'max_dynamic' => 8, // More Truths per conversation
],
'memory' => [
'max_results' => 10, // More semantic search results
],
// Custom tools for internal systems
'tools' => [
App\Extensions\Tools\JiraIntegration\ListTicketsTool::class,
App\Extensions\Tools\JiraIntegration\CreateTicketTool::class,
App\Extensions\Tools\Slack\SendMessageTool::class,
App\Extensions\Tools\GitHub\ListPullRequestsTool::class,
],
// Disable image generation (not needed for dev work)
'disabled_tools' => [
App\Tools\GenerateImageTool::class,
],
// Custom prompt pipeline with work context
'prompts' => [
App\Extensions\Prompts\DevTeamStaticPrompt::class, // Custom identity
App\Prompts\MemoryPrompt::class,
App\Prompts\SummaryPrompt::class,
App\Extensions\Prompts\ProjectContextPrompt::class, // Current projects
App\Extensions\Prompts\TeamContextPrompt::class, // Team info
App\Prompts\CalendarPrompt::class,
App\Prompts\CurrentTimePrompt::class,
],
// Timezone for the team
'temporal' => [
'timezone' => 'America/Los_Angeles',
],
];2. Create Custom Prompts
php
<?php
// app/Extensions/Prompts/DevTeamStaticPrompt.php
namespace App\Extensions\Prompts;
use App\Prompts\Prompt;
class DevTeamStaticPrompt extends Prompt
{
public function content(): string
{
return view('prompts.extensions.dev-team-static')->render();
}
public function providerOptions(): array
{
return ['cacheType' => 'ephemeral'];
}
}blade
{{-- resources/views/prompts/extensions/dev-team-static.blade.php --}}
# Iris - Development Team Assistant
You are Iris, a technical assistant for the engineering team at Acme Corp. Your role is to help developers stay productive by:
- Tracking project status and deadlines
- Managing Jira tickets and GitHub pull requests
- Facilitating team communication via Slack
- Remembering technical decisions and context
## Communication Style
Be direct and technical. Skip pleasantries when discussing code or issues. Use precise terminology. When referencing tickets, include the ID (e.g., ACME-123).
## Team Standards
- All code changes require PR review
- Tickets move through: To Do → In Progress → Review → Done
- Sprint planning is every Monday at 10am
- Deployments happen on Tuesdays and Thursdays3. Create Custom Tools
See Custom Tools for the full guide. Here's a quick example:
php
<?php
// app/Extensions/Tools/JiraIntegration/ListTicketsTool.php
namespace App\Extensions\Tools\JiraIntegration;
use App\Models\User;
use Prism\Prism\Tool;
class ListTicketsTool extends Tool
{
public function __construct(
protected User $user,
protected JiraClient $jira,
) {
$this
->as('list_jira_tickets')
->for('List Jira tickets assigned to the user or in current sprint')
->withStringParameter('filter', 'Filter: mine, sprint, or project key (default: mine)')
->using($this);
}
public function __invoke(string $filter = 'mine'): string
{
$tickets = match ($filter) {
'mine' => $this->jira->getAssignedTo($this->user->email),
'sprint' => $this->jira->getCurrentSprint(),
default => $this->jira->getByProject($filter),
};
if ($tickets->isEmpty()) {
return 'No tickets found.';
}
return $tickets
->map(fn ($t) => "- [{$t->key}] {$t->summary} ({$t->status})")
->join("\n");
}
}4. Register Services
If your tools need external services, register them in a service provider:
php
<?php
// app/Providers/ExtensionsServiceProvider.php
namespace App\Providers;
use App\Extensions\Tools\JiraIntegration\JiraClient;
use Illuminate\Support\ServiceProvider;
class ExtensionsServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->singleton(JiraClient::class, function () {
return new JiraClient(
baseUrl: config('services.jira.url'),
apiToken: config('services.jira.token'),
);
});
}
}Upgrade Strategy
When Iris releases updates, follow this process to incorporate them while preserving your customizations.
Before Upgrading
- Review the changelog - Check for breaking changes or new features
- Backup your database - Especially if migrations are included
- Note your customizations - List what you've changed
Upgrade Process
bash
# Fetch the latest changes
git fetch origin
# Review what's changed
git diff main..origin/main
# Merge or rebase
git merge origin/main
# or
git rebase origin/main
# If conflicts arise in customized files, resolve them
# Your custom config (iris-custom.php) shouldn't conflict
# Custom classes in app/Extensions/ shouldn't conflict
# Run migrations
php artisan migrate
# Rebuild frontend if needed
npm run build
# Clear caches
php artisan config:clear
php artisan view:clearHandling Conflicts
Core prompts changed: If you've replaced the prompts array and core prompts changed, review the changes and update your custom config if needed.
New config options: New options in iris.php are automatically available. Override in iris-custom.php if you need different values.
Database migrations: Run migrations after every upgrade. Review migration files if you've modified the schema.
Recommended Git Strategy
Keep your customizations on a separate branch:
bash
# Create a customizations branch
git checkout -b customizations main
# Make your customizations
# Commit them to the customizations branch
# When upgrading:
git checkout main
git pull origin main
git checkout customizations
git rebase main