8.7 KiB
Contributing to Maestro
Thank you for your interest in contributing to Maestro! This document provides guidelines, setup instructions, and practical guidance for developers.
For architecture details, see ARCHITECTURE.md. For quick reference while coding, see CLAUDE.md.
Table of Contents
- Development Setup
- Project Structure
- Development Scripts
- Common Development Tasks
- Code Style
- Debugging Guide
- Commit Messages
- Pull Request Process
- Building for Release
Development Setup
Prerequisites
- Node.js 20+
- npm or yarn
- Git
Getting Started
# Fork and clone the repository
git clone <your-fork-url>
cd maestro
# Install dependencies
npm install
# Run in development mode with hot reload
npm run dev
Project Structure
maestro/
├── src/
│ ├── main/ # Electron main process (Node.js backend)
│ │ ├── index.ts # Entry point, IPC handlers
│ │ ├── process-manager.ts
│ │ ├── preload.ts # Secure IPC bridge
│ │ └── utils/ # Shared utilities
│ └── renderer/ # React frontend (UI)
│ ├── App.tsx # Main coordinator
│ ├── components/ # React components
│ ├── hooks/ # Custom React hooks
│ ├── services/ # IPC wrappers (git, process)
│ ├── contexts/ # React contexts
│ ├── constants/ # Themes, shortcuts, priorities
│ ├── types/ # TypeScript definitions
│ └── utils/ # Frontend utilities
├── build/ # Application icons
├── .github/workflows/ # CI/CD automation
└── dist/ # Build output (generated)
Development Scripts
npm run dev # Start dev server with hot reload
npm run build # Full production build
npm run build:main # Build main process only
npm run build:renderer # Build renderer only
npm start # Start built application
npm run clean # Clean build artifacts
npm run package # Package for all platforms
npm run package:mac # Package for macOS
npm run package:win # Package for Windows
npm run package:linux # Package for Linux
Common Development Tasks
Adding a New UI Feature
- Plan the state - Determine if it's per-session or global
- Add state management - In
useSettings.ts(global) or session state - Create persistence - Use wrapper function pattern for global settings
- Implement UI - Follow Tailwind + theme color pattern
- Add keyboard shortcuts - In
shortcuts.tsandApp.tsx - Test focus flow - Ensure Escape key navigation works
Adding a New Modal
- Create component in
src/renderer/components/ - Add priority in
src/renderer/constants/modalPriorities.ts:MY_MODAL: 600, - Register with layer stack (see ARCHITECTURE.md)
- Use proper ARIA attributes:
<div role="dialog" aria-modal="true" aria-label="My Modal">
Adding Keyboard Shortcuts
-
Add definition in
src/renderer/constants/shortcuts.ts:myShortcut: { id: 'myShortcut', label: 'My Action', keys: ['Meta', 'k'] }, -
Add handler in
App.tsxkeyboard event listener:else if (isShortcut(e, 'myShortcut')) { e.preventDefault(); // Handler code }
Supported modifiers: Meta (Cmd/Win), Ctrl, Alt, Shift
Arrow keys: ArrowLeft, ArrowRight, ArrowUp, ArrowDown
Adding a New Setting
-
Add state in
useSettings.ts:const [mySetting, setMySettingState] = useState(defaultValue); -
Create wrapper function:
const setMySetting = (value) => { setMySettingState(value); window.maestro.settings.set('mySetting', value); }; -
Load in useEffect:
const saved = await window.maestro.settings.get('mySetting'); if (saved !== undefined) setMySettingState(saved); -
Add to return object and export.
Adding a Slash Command
Add to src/renderer/slashCommands.ts:
{
command: '/mycommand',
description: 'Does something useful',
terminalOnly: false, // Optional: restrict to terminal mode
execute: (context) => {
const { activeSessionId, setSessions } = context;
// Your logic
}
}
Adding a New Theme
Add to src/renderer/constants/themes.ts:
'my-theme': {
id: 'my-theme',
name: 'My Theme',
mode: 'dark', // or 'light'
colors: {
bgMain: '#...',
bgSidebar: '#...',
bgActivity: '#...',
border: '#...',
textMain: '#...',
textDim: '#...',
accent: '#...',
accentDim: 'rgba(...)',
accentText: '#...',
success: '#...',
warning: '#...',
error: '#...',
}
}
Then add the ID to ThemeId type in src/renderer/types/index.ts.
Adding an IPC Handler
-
Add handler in
src/main/index.ts:ipcMain.handle('myNamespace:myAction', async (_, arg1, arg2) => { // Implementation return result; }); -
Expose in
src/main/preload.ts:myNamespace: { myAction: (arg1, arg2) => ipcRenderer.invoke('myNamespace:myAction', arg1, arg2), }, -
Add types to
MaestroAPIinterface in preload.ts.
Code Style
TypeScript
- Strict mode enabled
- Interface definitions for all data structures
- Export types via
preload.tsfor renderer
React Components
- Functional components with hooks
- Keep components focused and small
- Use Tailwind for layout, inline styles for theme colors
- Maintain keyboard accessibility
- Use
tabIndex={-1}+outline-nonefor programmatic focus
Security
- Always use
execFileNoThrowfor external commands (never shell-based execution) - Keep context isolation enabled
- Use preload script for all IPC
- Sanitize all user inputs
- Use
spawn()withshell: false
Debugging Guide
Focus Not Working
- Add
tabIndex={0}ortabIndex={-1}to element - Add
outline-noneclass to hide focus ring - Use
ref={(el) => el?.focus()}for auto-focus - Check for
e.stopPropagation()blocking events
Settings Not Persisting
- Ensure wrapper function calls
window.maestro.settings.set() - Check loading code in
useSettings.tsuseEffect - Verify the key name matches in both save and load
Modal Escape Not Working
- Register modal with layer stack (don't handle Escape locally)
- Check priority in
modalPriorities.ts - Use ref pattern to avoid re-registration:
const onCloseRef = useRef(onClose); onCloseRef.current = onClose;
Theme Colors Not Applying
- Use
style={{ color: theme.colors.textMain }}instead of Tailwind color classes - Check theme prop is passed to component
- Never use hardcoded hex colors for themed elements
Process Output Not Showing
- Check session ID matches (with
-aior-terminalsuffix) - Verify
onDatalistener is registered - Check process spawned successfully (check pid > 0)
- Look for errors in DevTools console
DevTools
Open via Quick Actions (Cmd+K → "Toggle DevTools") or set DEBUG=true env var.
Commit Messages
Use conventional commits:
feat: new feature
fix: bug fix
docs: documentation changes
refactor: code refactoring
test: test additions/changes
chore: build process or tooling changes
Example: feat: add context usage visualization
Pull Request Process
- Create a feature branch from
main - Make your changes following the code style
- Test thoroughly (keyboard navigation, themes, focus)
- Update documentation if needed
- Submit PR with clear description
- Wait for review
Building for Release
1. Prepare Icons
Place icons in build/ directory:
icon.icns- macOS (512x512 or 1024x1024)icon.ico- Windows (256x256)icon.png- Linux (512x512)
2. Update Version
Update in package.json:
{
"version": "0.1.0"
}
3. Build Distributables
npm run package # All platforms
npm run package:mac # macOS (.dmg, .zip)
npm run package:win # Windows (.exe)
npm run package:linux # Linux (.AppImage, .deb, .rpm)
Output in release/ directory.
GitHub Actions
Create a release tag to trigger automated builds:
git tag v0.1.0
git push origin v0.1.0
GitHub Actions will build for all platforms and create a release.
Questions?
Open a GitHub Discussion or create an Issue.