dropped Aider across the code base and documentation

This commit is contained in:
Pedram Amini
2026-01-28 20:35:13 -05:00
parent b25419d74e
commit aed9fb7c95
39 changed files with 131 additions and 205 deletions

View File

@@ -9,10 +9,9 @@ Agent support documentation for the Maestro codebase. For the main guide, see [[
| `claude-code` | Claude Code | **Active** | Primary agent, `--print --verbose --output-format stream-json` |
| `codex` | OpenAI Codex | **Active** | Full support, `--json`, YOLO mode default |
| `opencode` | OpenCode | **Active** | Multi-provider support (75+ LLMs), stub session storage |
| `factory-droid` | Factory Droid | **Active** | Factory's AI coding assistant, `-o stream-json` |
| `terminal` | Terminal | Internal | Hidden from UI, used for shell sessions |
Additional `ToolType` values (`aider`, `claude`) are defined in types but not yet implemented in `agent-detector.ts`.
## Agent Capabilities
Each agent declares capabilities that control UI feature availability. See `src/main/agent-capabilities.ts` for the full interface.

View File

@@ -10,7 +10,7 @@ Maestro is a cross-platform desktop app for orchestrating your fleet of AI agent
Collaborate with AI to create detailed specification documents, then let Auto Run execute them automatically, each task in a fresh session with clean context. Allowing for long-running unattended sessions, my current record is nearly 24 hours of continuous runtime.
Run multiple agents in parallel with a Linear/Superhuman-level responsive interface. Currently supporting **Claude Code**, **OpenAI Codex**, and **OpenCode** with plans for additional agentic coding tools (Aider, Gemini CLI, Qwen3 Coder) based on user demand.
Run multiple agents in parallel with a Linear/Superhuman-level responsive interface. Currently supporting **Claude Code**, **OpenAI Codex**, **OpenCode**, and **Factory Droid** with plans for additional agentic coding tools (Gemini CLI, Qwen3 Coder) based on user demand.
<div align="center">
<a href="https://youtu.be/fmwwTOg7cyA?si=dJ89K54tGflKa5G4">
@@ -79,7 +79,7 @@ Run multiple agents in parallel with a Linear/Superhuman-level responsive interf
Additional interactions: Drag nodes to reposition, scroll to zoom, use mini-map for overview.
> **Note**: Maestro supports Claude Code, OpenAI Codex, and OpenCode. Support for additional agents (Aider, Gemini CLI, Qwen3 Coder) may be added in future releases based on community demand.
> **Note**: Maestro supports Claude Code, OpenAI Codex, OpenCode, and Factory Droid. Support for additional agents (Gemini CLI, Qwen3 Coder) may be added in future releases based on community demand.
## Quick Start

View File

@@ -34,4 +34,4 @@ icon: sparkles
- 📊 **[Usage Dashboard](./usage-dashboard)** - Comprehensive analytics for tracking AI usage patterns. View aggregated statistics, compare agent performance, analyze activity heatmaps, and export data to CSV. Access via `Opt+Cmd+U` / `Alt+Ctrl+U`.
- 🏆 **[Achievements](./achievements)** - Level up from Apprentice to Titan of the Baton based on cumulative Auto Run time. 11 conductor-themed ranks to unlock.
> **Note**: Maestro currently supports Claude Code, Codex (OpenAI), and OpenCode as fully-integrated providers. Support for additional providers (Aider, Gemini CLI, Qwen3 Coder) is planned for future releases based on community demand.
> **Note**: Maestro currently supports Claude Code, Codex (OpenAI), OpenCode, and Factory Droid as fully-integrated providers. Support for additional providers (Gemini CLI, Qwen3 Coder) is planned for future releases based on community demand.

View File

@@ -10,7 +10,7 @@ Maestro is a cross-platform desktop app for orchestrating your fleet of AI agent
Collaborate with AI to create detailed specification documents, then let Auto Run execute them automatically, each task in a fresh session with clean context. Allowing for long-running unattended sessions, my current record is nearly 24 hours of continuous runtime.
Run multiple agents in parallel with a Linear/Superhuman-level responsive interface. Currently supporting **Claude Code**, **Codex** (OpenAI), and **OpenCode** with plans for additional agentic coding tools (Aider, Gemini CLI, Qwen3 Coder) based on user demand.
Run multiple agents in parallel with a Linear/Superhuman-level responsive interface. Currently supporting **Claude Code**, **Codex** (OpenAI), **OpenCode**, and **Factory Droid** with plans for additional agentic coding tools (Gemini CLI, Qwen3 Coder) based on user demand.
![Main screen](./screenshots/main-screen.png)

View File

@@ -19,7 +19,8 @@ Download the latest release for your platform from the [Releases](https://github
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) — Anthropic's AI coding assistant (fully integrated)
- [Codex](https://github.com/openai/codex) — OpenAI's coding agent (fully integrated)
- [OpenCode](https://github.com/sst/opencode) — Open-source AI coding assistant (fully integrated)
- [Aider](https://github.com/paul-gauthier/aider), [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Qwen3 Coder](https://github.com/QwenLM/Qwen-Agent) — Planned support
- [Factory Droid](https://docs.factory.ai/cli) — Factory's AI coding assistant (fully integrated)
- [Gemini CLI](https://github.com/google-gemini/gemini-cli), [Qwen3 Coder](https://github.com/QwenLM/Qwen-Agent) — Planned support
- Git (optional, for git-aware features)
## WSL2 Users (Windows Subsystem for Linux)

View File

@@ -34,7 +34,7 @@ Commands support **template variables** that are automatically substituted at ru
| `{{AGENT_GROUP}}` | Agent's group name (if grouped) |
| `{{AGENT_SESSION_ID}}` | Agent session ID (for conversation continuity) |
| `{{TAB_NAME}}` | Custom tab name (alias: `SESSION_NAME`) |
| `{{TOOL_TYPE}}` | Agent type (claude-code, codex, opencode, aider) |
| `{{TOOL_TYPE}}` | Agent type (claude-code, codex, opencode, factory-droid) |
### Path Variables

View File

@@ -78,7 +78,7 @@ describe('list-agents command', () => {
it('should display agents in human-readable format', () => {
const mockSessions: SessionInfo[] = [
mockSession({ id: 'a1', name: 'Agent One', toolType: 'claude-code' }),
mockSession({ id: 'a2', name: 'Agent Two', toolType: 'aider' }),
mockSession({ id: 'a2', name: 'Agent Two', toolType: 'factory-droid' }),
];
vi.mocked(readSessions).mockReturnValue(mockSessions);
@@ -423,7 +423,7 @@ describe('list-agents command', () => {
});
it('should handle all tool types', () => {
const toolTypes = ['claude-code', 'aider', 'terminal', 'gemini-cli', 'qwen3-coder'];
const toolTypes = ['claude-code', 'factory-droid', 'terminal', 'gemini-cli', 'qwen3-coder'];
const mockSessions: SessionInfo[] = toolTypes.map((toolType, i) =>
mockSession({ id: `agent-${i}`, name: `Agent ${i}`, toolType: toolType as any })
);

View File

@@ -526,7 +526,7 @@ describe('jsonl output', () => {
const agent = {
id: 'agent-456',
name: 'Grouped Agent',
toolType: 'aider',
toolType: 'factory-droid',
cwd: '/path',
groupId: 'group-123',
};
@@ -576,7 +576,7 @@ describe('jsonl output', () => {
});
it('should handle different tool types', () => {
const toolTypes = ['claude-code', 'aider', 'terminal', 'gemini-cli', 'qwen3-coder'];
const toolTypes = ['claude-code', 'factory-droid', 'terminal', 'gemini-cli', 'qwen3-coder'];
toolTypes.forEach((toolType, index) => {
consoleSpy.mockClear();

View File

@@ -341,7 +341,7 @@ describe('storage service', () => {
const result = readAgentConfigs();
expect(result['claude-code']).toEqual({ customPath: '/custom/path' });
expect(result['aider']).toEqual({ setting: 'value' });
expect(result['factory-droid']).toEqual({ setting: 'value' });
});
it('should return empty object when file does not exist', () => {
@@ -389,7 +389,7 @@ describe('storage service', () => {
})
);
const result = getAgentCustomPath('aider');
const result = getAgentCustomPath('factory-droid');
expect(result).toBeUndefined();
});

View File

@@ -277,7 +277,7 @@ describe('agent-detector', () => {
const agents = await detector.detectAgents();
// Should have all 7 agents (terminal, claude-code, codex, gemini-cli, qwen3-coder, opencode, aider)
// Should have all 7 agents (terminal, claude-code, codex, gemini-cli, qwen3-coder, opencode, factory-droid)
expect(agents.length).toBe(7);
const agentIds = agents.map((a) => a.id);
@@ -287,7 +287,7 @@ describe('agent-detector', () => {
expect(agentIds).toContain('gemini-cli');
expect(agentIds).toContain('qwen3-coder');
expect(agentIds).toContain('opencode');
expect(agentIds).toContain('aider');
expect(agentIds).toContain('factory-droid');
});
it('should mark agents as available when binary is found', async () => {

View File

@@ -316,7 +316,7 @@ describe('agent-output-parser', () => {
expect(isValidToolType('codex')).toBe(true);
expect(isValidToolType('terminal')).toBe(true);
expect(isValidToolType('claude')).toBe(true);
expect(isValidToolType('aider')).toBe(true);
expect(isValidToolType('factory-droid')).toBe(true);
});
it('should return false for invalid agent IDs', () => {

View File

@@ -825,8 +825,8 @@ describe('error-patterns', () => {
],
};
registerErrorPatterns('aider', customPatterns);
const patterns = getErrorPatterns('aider');
registerErrorPatterns('factory-droid', customPatterns);
const patterns = getErrorPatterns('factory-droid');
expect(patterns).toBe(customPatterns);
});

View File

@@ -207,7 +207,7 @@ describe('DEFAULT_CONTEXT_WINDOWS', () => {
expect(DEFAULT_CONTEXT_WINDOWS['claude']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['codex']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['opencode']).toBe(128000);
expect(DEFAULT_CONTEXT_WINDOWS['aider']).toBe(128000);
expect(DEFAULT_CONTEXT_WINDOWS['factory-droid']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['terminal']).toBe(0);
});
});

View File

@@ -43,7 +43,7 @@ vi.mock('../../../renderer/services/contextGroomer', () => ({
'claude-code': 'Claude Code',
opencode: 'OpenCode',
codex: 'OpenAI Codex',
aider: 'Aider',
factory-droid: 'Factory Droid',
terminal: 'Terminal',
};
return names[toolType] || toolType;

View File

@@ -37,7 +37,7 @@ vi.mock('../../../renderer/services/contextGroomer', () => ({
'claude-code': 'Claude Code',
opencode: 'OpenCode',
codex: 'OpenAI Codex',
aider: 'Aider',
factory-droid: 'Factory Droid',
terminal: 'Terminal',
};
return names[toolType] || toolType;

View File

@@ -103,7 +103,7 @@ describe('AgentComparisonChart', () => {
// Use getAllByText since agent names appear in both bar labels and legend
expect(screen.getAllByText('claude-code').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('aider').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('factory-droid').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('terminal').length).toBeGreaterThanOrEqual(1);
});
@@ -167,7 +167,7 @@ describe('AgentComparisonChart', () => {
// In duration mode, claude-code has highest duration (2000000), then aider (1600000), then terminal (500000)
expect(agentNames[0]).toBe('claude-code');
expect(agentNames[1]).toBe('aider');
expect(agentNames[1]).toBe('factory-droid');
expect(agentNames[2]).toBe('terminal');
});
});

View File

@@ -76,7 +76,7 @@ function generateManyAgentsData(numAgents: number): StatsAggregation {
const byAgent: Record<string, { count: number; duration: number }> = {};
const agentNames = [
'claude-code',
'aider',
'factory-droid',
'opencode',
'gpt-engineer',
'cursor',

View File

@@ -885,7 +885,7 @@ describe('transfer edge cases', () => {
});
it('handles all supported agent types as targets', async () => {
const targetAgents: ToolType[] = ['opencode', 'aider', 'codex', 'claude'];
const targetAgents: ToolType[] = ['opencode', 'factory-droid', 'codex', 'claude'];
for (const targetAgent of targetAgents) {
vi.clearAllMocks();

View File

@@ -466,7 +466,6 @@ describe('AGENT_ARTIFACTS', () => {
it('should define artifacts for all agent types', () => {
const expectedAgents: ToolType[] = [
'claude-code',
'aider',
'opencode',
'codex',
'factory-droid',
@@ -496,14 +495,6 @@ describe('AGENT_ARTIFACTS', () => {
expect(artifacts).toContain('opus');
});
it('should include aider-specific commands', () => {
const artifacts = AGENT_ARTIFACTS['aider'];
expect(artifacts).toContain('/add');
expect(artifacts).toContain('/drop');
expect(artifacts).toContain('/commit');
expect(artifacts).toContain('Aider');
});
it('should include codex-specific references', () => {
const artifacts = AGENT_ARTIFACTS['codex'];
expect(artifacts).toContain('Codex');
@@ -521,9 +512,9 @@ describe('AGENT_TARGET_NOTES', () => {
it('should define notes for all agent types', () => {
const expectedAgents: ToolType[] = [
'claude-code',
'aider',
'opencode',
'codex',
'factory-droid',
'claude',
'terminal',
];
@@ -542,11 +533,10 @@ describe('AGENT_TARGET_NOTES', () => {
expect(notes).toContain('edit files');
});
it('should mention git workflow in aider notes', () => {
const notes = AGENT_TARGET_NOTES['aider'];
expect(notes).toContain('git');
expect(notes).toContain('/add');
expect(notes).toContain('/drop');
it('should mention Factory in factory-droid notes', () => {
const notes = AGENT_TARGET_NOTES['factory-droid'];
expect(notes).toContain('Factory');
expect(notes).toContain('AI coding assistant');
});
it('should mention reasoning models in codex notes', () => {
@@ -564,9 +554,9 @@ describe('AGENT_TARGET_NOTES', () => {
describe('getAgentDisplayName', () => {
it('should return correct display names for all agents', () => {
expect(getAgentDisplayName('claude-code')).toBe('Claude Code');
expect(getAgentDisplayName('aider')).toBe('Aider');
expect(getAgentDisplayName('opencode')).toBe('OpenCode');
expect(getAgentDisplayName('codex')).toBe('OpenAI Codex');
expect(getAgentDisplayName('factory-droid')).toBe('Factory Droid');
expect(getAgentDisplayName('claude')).toBe('Claude');
expect(getAgentDisplayName('terminal')).toBe('Terminal');
});
@@ -580,14 +570,14 @@ describe('getAgentDisplayName', () => {
describe('buildContextTransferPrompt', () => {
it('should include source and target agent names', () => {
const prompt = buildContextTransferPrompt('claude-code', 'aider');
const prompt = buildContextTransferPrompt('claude-code', 'opencode');
expect(prompt).toContain('Claude Code');
expect(prompt).toContain('Aider');
expect(prompt).toContain('OpenCode');
});
it('should include source agent artifacts', () => {
const prompt = buildContextTransferPrompt('claude-code', 'aider');
const prompt = buildContextTransferPrompt('claude-code', 'opencode');
// Should include Claude Code artifacts as bullet points
expect(prompt).toContain('"/clear"');
@@ -597,12 +587,11 @@ describe('buildContextTransferPrompt', () => {
});
it('should include target agent notes', () => {
const prompt = buildContextTransferPrompt('claude-code', 'aider');
const prompt = buildContextTransferPrompt('claude-code', 'opencode');
// Should include Aider target notes
expect(prompt).toContain('git repositories');
expect(prompt).toContain('/add');
expect(prompt).toContain('/drop');
// Should include OpenCode target notes
expect(prompt).toContain('multi-model');
expect(prompt).toContain('AI coding assistant');
});
it('should handle agents with no artifacts', () => {
@@ -623,7 +612,7 @@ describe('buildContextTransferPrompt', () => {
});
it('should work for all agent type combinations', () => {
const agents: ToolType[] = ['claude-code', 'aider', 'opencode', 'codex', 'factory-droid', 'claude', 'terminal'];
const agents: ToolType[] = ['claude-code', 'opencode', 'codex', 'factory-droid', 'claude', 'terminal'];
for (const source of agents) {
for (const target of agents) {

View File

@@ -95,10 +95,11 @@ describe('estimateContextUsage', () => {
expect(result).toBe(8);
});
it('should use aider default context window (128k)', () => {
it('should use factory-droid default context window (200k)', () => {
const stats = createStats({ contextWindow: 0 });
const result = estimateContextUsage(stats, 'aider');
expect(result).toBe(8);
const result = estimateContextUsage(stats, 'factory-droid');
// (10000 + 0 + 0) / 200000 = 5%
expect(result).toBe(5);
});
it('should return null for terminal agent', () => {
@@ -289,7 +290,7 @@ describe('DEFAULT_CONTEXT_WINDOWS', () => {
expect(DEFAULT_CONTEXT_WINDOWS['claude']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['codex']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['opencode']).toBe(128000);
expect(DEFAULT_CONTEXT_WINDOWS['aider']).toBe(128000);
expect(DEFAULT_CONTEXT_WINDOWS['factory-droid']).toBe(200000);
expect(DEFAULT_CONTEXT_WINDOWS['terminal']).toBe(0);
});
});

View File

@@ -171,9 +171,9 @@ describe('sessionValidation', () => {
];
// Different provider (aider) also gets a warning now
const result = validateNewSession(
'Aider Agent',
'Factory Droid Agent',
'/Users/test/project',
'aider',
'factory-droid',
existingSessions
);
expect(result.valid).toBe(true);
@@ -269,7 +269,7 @@ describe('sessionValidation', () => {
createMockSession({
name: 'Agent 2',
projectRoot: '/Users/test/project',
toolType: 'aider',
toolType: 'factory-droid',
}),
];
const result = validateNewSession(
@@ -299,7 +299,7 @@ describe('sessionValidation', () => {
projectRoot: '/path/two',
toolType: 'claude-code',
}),
createMockSession({ name: 'Session 3', projectRoot: '/path/three', toolType: 'aider' }),
createMockSession({ name: 'Session 3', projectRoot: '/path/three', toolType: 'factory-droid' }),
];
// Unique name and directory - no warning
@@ -331,7 +331,7 @@ describe('sessionValidation', () => {
const diffProviderResult = validateNewSession(
'Session 4',
'/path/two',
'aider',
'factory-droid',
existingSessions
);
expect(diffProviderResult.valid).toBe(true);
@@ -372,10 +372,10 @@ describe('sessionValidation', () => {
createMockSession({
name: 'Existing',
projectRoot: '/path',
toolType: 'aider',
toolType: 'factory-droid',
}),
];
const result = validateNewSession('New', '/path', 'aider', existingSessions);
const result = validateNewSession('New', '/path', 'factory-droid', existingSessions);
expect(result.valid).toBe(true);
expect(result.warning).toBeDefined();
expect(result.warning).toContain('Existing');

View File

@@ -183,7 +183,7 @@ describe('substituteTemplateVariables', () => {
it('should replace {{TOOL_TYPE}} with session.toolType', () => {
const context = createTestContext({
session: createTestSession({ toolType: 'aider' }),
session: createTestSession({ toolType: 'factory-droid' }),
});
const result = substituteTemplateVariables('Tool: {{TOOL_TYPE}}', context);
expect(result).toBe('Tool: aider');

View File

@@ -246,35 +246,6 @@ export const AGENT_CAPABILITIES: Record<string, AgentCapabilities> = {
supportsContextExport: false, // Not yet investigated - PLACEHOLDER
},
/**
* Aider - AI pair programming in your terminal
* https://github.com/paul-gauthier/aider
*
* PLACEHOLDER: Most capabilities set to false until Aider integration is
* implemented. Update this configuration when integrating the agent.
*/
aider: {
supportsResume: false, // Not yet investigated
supportsReadOnlyMode: false, // Not yet investigated
supportsJsonOutput: false, // Not yet investigated
supportsSessionId: false, // Not yet investigated
supportsImageInput: true, // Aider supports vision models
supportsImageInputOnResume: false, // Not yet investigated
supportsSlashCommands: true, // Aider has /commands
supportsSessionStorage: false, // Not yet investigated
supportsCostTracking: true, // Aider tracks costs
supportsUsageStats: true, // Aider shows token usage
supportsBatchMode: false, // Not yet investigated
requiresPromptToStart: false, // Not yet investigated
supportsStreaming: true, // Likely streams
supportsResultMessages: false, // Not yet investigated
supportsModelSelection: true, // --model flag
supportsStreamJsonInput: false,
supportsThinkingDisplay: false, // Not yet investigated
supportsContextMerge: false, // Not yet investigated - PLACEHOLDER
supportsContextExport: false, // Not yet investigated - PLACEHOLDER
},
/**
* OpenCode - Open source coding assistant
* https://github.com/opencode-ai/opencode

View File

@@ -172,13 +172,6 @@ export const AGENT_DEFINITIONS: Omit<AgentConfig, 'available' | 'path' | 'capabi
},
],
},
{
id: 'aider',
name: 'Aider',
binaryName: 'aider',
command: 'aider',
args: [], // Base args (placeholder - to be configured when implemented)
},
{
id: 'factory-droid',
name: 'Factory Droid',
@@ -512,7 +505,7 @@ export class AgentDetector {
// User local programs
path.join(localAppData, 'Programs'),
path.join(localAppData, 'Microsoft', 'WindowsApps'),
// Python/pip user installs (for Aider)
// Python/pip user installs
path.join(appData, 'Python', 'Scripts'),
path.join(localAppData, 'Programs', 'Python', 'Python312', 'Scripts'),
path.join(localAppData, 'Programs', 'Python', 'Python311', 'Scripts'),
@@ -624,13 +617,6 @@ export class AgentDetector {
path.join(appData, 'npm', 'gemini.cmd'),
path.join(localAppData, 'npm', 'gemini.cmd'),
],
aider: [
// pip installation
path.join(appData, 'Python', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python312', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python311', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python310', 'Scripts', 'aider.exe'),
],
droid: [
// Factory Droid installation paths
path.join(home, '.factory', 'bin', 'droid.exe'),
@@ -720,15 +706,6 @@ export class AgentDetector {
// Add paths from Node version managers (nvm, fnm, volta, etc.)
...versionManagerPaths.map((p) => path.join(p, 'gemini')),
],
aider: [
// pip installation
path.join(home, '.local', 'bin', 'aider'),
// Homebrew paths
'/opt/homebrew/bin/aider',
'/usr/local/bin/aider',
// Add paths from Node version managers (in case installed via npm)
...versionManagerPaths.map((p) => path.join(p, 'aider')),
],
droid: [
// Factory Droid installation paths
path.join(home, '.factory', 'bin', 'droid'),

View File

@@ -31,7 +31,7 @@ export interface WindowsDiagnosticsInfo {
codex: AgentProbeResult;
opencode: AgentProbeResult;
gemini: AgentProbeResult;
aider: AgentProbeResult;
droid: AgentProbeResult;
};
whereResults?: {
// Results from 'where' command for each agent
@@ -39,7 +39,7 @@ export interface WindowsDiagnosticsInfo {
codex: WhereResult;
opencode: WhereResult;
gemini: WhereResult;
aider: WhereResult;
droid: WhereResult;
};
npmInfo?: {
npmGlobalPrefix: string | null; // npm config get prefix (sanitized)
@@ -105,7 +105,7 @@ export async function collectWindowsDiagnostics(): Promise<WindowsDiagnosticsInf
codex: await probeAgentPaths('codex'),
opencode: await probeAgentPaths('opencode'),
gemini: await probeAgentPaths('gemini'),
aider: await probeAgentPaths('aider'),
droid: await probeAgentPaths('droid'),
};
// Run 'where' command for each agent
@@ -114,7 +114,7 @@ export async function collectWindowsDiagnostics(): Promise<WindowsDiagnosticsInf
codex: await runWhereCommand('codex'),
opencode: await runWhereCommand('opencode'),
gemini: await runWhereCommand('gemini'),
aider: await runWhereCommand('aider'),
droid: await runWhereCommand('droid'),
};
// Collect npm info
@@ -179,11 +179,13 @@ async function probeAgentPaths(binaryName: string): Promise<AgentProbeResult> {
path.join(appData, 'npm', 'opencode.cmd'),
],
gemini: [path.join(appData, 'npm', 'gemini.cmd'), path.join(localAppData, 'npm', 'gemini.cmd')],
aider: [
path.join(appData, 'Python', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python312', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python311', 'Scripts', 'aider.exe'),
path.join(localAppData, 'Programs', 'Python', 'Python310', 'Scripts', 'aider.exe'),
droid: [
path.join(home, '.factory', 'bin', 'droid.exe'),
path.join(localAppData, 'Factory', 'droid.exe'),
path.join(appData, 'Factory', 'droid.exe'),
path.join(home, '.local', 'bin', 'droid.exe'),
path.join(appData, 'npm', 'droid.cmd'),
path.join(localAppData, 'npm', 'droid.cmd'),
],
};

View File

@@ -41,7 +41,6 @@ const VALID_TOOL_TYPES: ToolType[] = [
'codex',
'terminal',
'claude',
'aider',
'factory-droid',
];

View File

@@ -26,7 +26,6 @@ import { logger } from '../utils/logger';
const VALID_TOOL_TYPES = new Set<string>([
'claude',
'claude-code',
'aider',
'opencode',
'codex',
'terminal',

View File

@@ -46,7 +46,6 @@ export const DEFAULT_CONTEXT_WINDOWS: Record<ToolType, number> = {
claude: 200000, // Legacy Claude
codex: 200000, // OpenAI o3/o4-mini context window
opencode: 128000, // OpenCode (depends on model, 128k is conservative default)
aider: 128000, // Aider (varies by model, 128k is conservative default)
terminal: 0, // Terminal has no context window
'factory-droid': 200000, // Factory Droid (Claude Opus 4.5 default context)
};

View File

@@ -1490,7 +1490,7 @@ export function EditAgentModal({
'claude-code': 'Claude Code',
codex: 'Codex',
opencode: 'OpenCode',
aider: 'Aider',
'factory-droid': 'Factory Droid',
};
const agentName = agentNameMap[session.toolType] || session.toolType;

View File

@@ -90,9 +90,10 @@ function formatAgentName(toolType: ToolType): string {
'claude-code': 'Claude Code',
opencode: 'OpenCode',
'openai-codex': 'OpenAI Codex',
codex: 'Codex',
'gemini-cli': 'Gemini CLI',
'qwen3-coder': 'Qwen3 Coder',
aider: 'Aider',
'factory-droid': 'Factory Droid',
terminal: 'Terminal',
};
return names[toolType] || toolType;

View File

@@ -68,16 +68,9 @@ export const AGENT_TILES: AgentTile[] = [
name: 'Factory Droid',
supported: true,
description: "Factory's AI coding assistant",
brandColor: '#8B5CF6', // Purple/violet
brandColor: '#3B82F6', // Factory blue
},
// Coming soon agents at the bottom
{
id: 'aider',
name: 'Aider',
supported: false,
description: 'Coming soon',
brandColor: '#14B8A6', // Teal
},
{
id: 'gemini-cli',
name: 'Gemini CLI',
@@ -94,9 +87,9 @@ export const AGENT_TILES: AgentTile[] = [
},
];
// Grid dimensions for keyboard navigation (3 cols for 7 items)
// Grid dimensions for keyboard navigation (3 cols for 6 items)
const GRID_COLS = 3;
const GRID_ROWS = 3;
const GRID_ROWS = 2;
/**
* Get SVG logo for an agent with brand colors
@@ -155,33 +148,6 @@ export function AgentLogo({
</svg>
);
case 'aider':
// Aider - chat bubble with code brackets
return (
<svg
className="w-12 h-12"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ opacity }}
>
{/* Chat bubble with code brackets */}
<path
d="M8 12C8 9.79 9.79 8 12 8H36C38.21 8 40 9.79 40 12V28C40 30.21 38.21 32 36 32H20L12 40V32H12C9.79 32 8 30.21 8 28V12Z"
stroke={color}
strokeWidth="2"
fill="none"
/>
<path
d="M18 16L14 20L18 24M30 16L34 20L30 24"
stroke={color}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
case 'gemini-cli':
// Gemini - Google's sparkle/star logo
return (
@@ -254,6 +220,60 @@ export function AgentLogo({
</svg>
);
case 'factory-droid':
// Factory Droid - pinwheel/flower logo
return (
<svg
className="w-12 h-12"
viewBox="0 0 48 48"
fill="none"
xmlns="http://www.w3.org/2000/svg"
style={{ opacity }}
>
{/* Factory Droid pinwheel logo - 6 petals radiating from center */}
<circle cx="24" cy="24" r="3" fill={color} />
{/* Petals - elliptical shapes radiating outward */}
<ellipse cx="24" cy="12" rx="4" ry="8" fill={color} fillOpacity="0.9" />
<ellipse
cx="34.4"
cy="18"
rx="4"
ry="8"
fill={color}
fillOpacity="0.9"
transform="rotate(60 34.4 18)"
/>
<ellipse
cx="34.4"
cy="30"
rx="4"
ry="8"
fill={color}
fillOpacity="0.9"
transform="rotate(120 34.4 30)"
/>
<ellipse cx="24" cy="36" rx="4" ry="8" fill={color} fillOpacity="0.9" />
<ellipse
cx="13.6"
cy="30"
rx="4"
ry="8"
fill={color}
fillOpacity="0.9"
transform="rotate(60 13.6 30)"
/>
<ellipse
cx="13.6"
cy="18"
rx="4"
ry="8"
fill={color}
fillOpacity="0.9"
transform="rotate(120 13.6 18)"
/>
</svg>
);
default:
return (
<div className="w-12 h-12 rounded-full border-2" style={{ borderColor: color, opacity }} />

View File

@@ -41,7 +41,6 @@ export const AGENT_ICONS: Record<string, string> = {
// Open-source alternatives
opencode: '📟',
aider: '🛠️',
// Enterprise
'factory-droid': '🏭',

View File

@@ -74,8 +74,8 @@ function getAgentIcon(agentId: string): string {
return '⬡';
case 'opencode':
return '📟';
case 'aider':
return '🛠️';
case 'factory-droid':
return '🏭';
default:
return '🔧';
}

View File

@@ -59,28 +59,6 @@ export const AGENT_ARTIFACTS: Record<ToolType, string[]> = {
'Claude Code',
'CLAUDE.md',
],
aider: [
// Slash commands
'/add',
'/drop',
'/commit',
'/diff',
'/undo',
'/clear',
'/run',
'/voice',
'/help',
'/quit',
'/ls',
'/map',
// Brand references
'Aider',
'aider',
// Model references
'gpt-4',
'gpt-3.5',
'GPT',
],
opencode: [
// Slash commands
'/help',
@@ -145,12 +123,6 @@ export const AGENT_TARGET_NOTES: Record<ToolType, string> = {
It can read and edit files, run terminal commands, search code, and interact with git.
It uses slash commands like /compact, /clear, /cost for session management.
It can handle large codebases and multi-file changes.
`,
aider: `
Aider is an AI pair programming tool.
It works with git repositories and can make commits.
It uses /add to include files in context and /drop to remove them.
It focuses on code changes and git workflow.
`,
opencode: `
OpenCode is a multi-model AI coding assistant.
@@ -186,7 +158,6 @@ export const AGENT_TARGET_NOTES: Record<ToolType, string> = {
export function getAgentDisplayName(agentType: ToolType): string {
const names: Record<ToolType, string> = {
'claude-code': 'Claude Code',
aider: 'Aider',
opencode: 'OpenCode',
codex: 'OpenAI Codex',
'factory-droid': 'Factory Droid',

View File

@@ -457,8 +457,8 @@ export interface Session {
// Usage statistics from AI responses
usageStats?: UsageStats;
inputMode: 'terminal' | 'ai';
// AI process PID (for non-batch agents like Aider)
// For Claude batch mode, this is 0 since processes spawn per-message
// AI process PID (for agents with persistent processes)
// For batch mode agents, this is 0 since processes spawn per-message
aiPid: number;
// Terminal uses runCommand() which spawns fresh shells per command
// This field is kept for backwards compatibility but is always 0

View File

@@ -17,7 +17,6 @@ export const DEFAULT_CONTEXT_WINDOWS: Record<ToolType, number> = {
claude: 200000, // Legacy Claude
codex: 200000, // OpenAI o3/o4-mini context window
opencode: 128000, // OpenCode (depends on model, 128k is conservative default)
aider: 128000, // Aider (varies by model, 128k is conservative default)
'factory-droid': 200000, // Factory Droid (varies by model, defaults to Claude Opus)
terminal: 0, // Terminal has no context window
};

View File

@@ -111,7 +111,6 @@ export function getProviderDisplayName(toolType: ToolType): string {
const displayNames: Record<ToolType, string> = {
'claude-code': 'Claude Code',
claude: 'Claude',
aider: 'Aider',
opencode: 'OpenCode',
codex: 'Codex',
'factory-droid': 'Factory Droid',

View File

@@ -10,7 +10,7 @@
* {{AGENT_SESSION_ID}} - Agent session ID (for conversation continuity)
* {{AGENT_HISTORY_PATH}} - Path to agent's history JSON file (for task recall)
* {{TAB_NAME}} - Custom tab name (alias: SESSION_NAME)
* {{TOOL_TYPE}} - Agent type (claude-code, aider, etc.)
* {{TOOL_TYPE}} - Agent type (claude-code, codex, opencode, factory-droid)
*
* Path Variables:
* {{CWD}} - Current working directory

View File

@@ -1,7 +1,7 @@
// Shared type definitions for Maestro CLI and Electron app
// These types are used by both the CLI tool and the renderer process
export type ToolType = 'claude' | 'claude-code' | 'aider' | 'opencode' | 'codex' | 'terminal' | 'factory-droid';
export type ToolType = 'claude' | 'claude-code' | 'opencode' | 'codex' | 'terminal' | 'factory-droid';
/**
* ThinkingMode controls how AI reasoning/thinking content is displayed.