## CHANGES

- Added `dev:prod-data` to develop against real production sessions safely 🧪
- Dev mode now defaults to an isolated `maestro-dev` data directory 🗂️
- App can explicitly opt into production userData via `USE_PROD_DATA=1` 🔀
- Contributor docs now clearly map dev commands to their data directories 📚
- Reduced dev/production database lock conflicts when running side-by-side 🔒
- Tab hover overlay redesigned to look like a clean “open folder” panel 🗃️
- Removed tab title tooltip to streamline the tab interaction feel ✂️
- Toast logging now captures whether audio/TTS notifications were enabled 🎙️
- Toast logs include the exact audio command used for notifications 🧾
- TTS playback now reuses captured audio state for consistent behavior 🔊
This commit is contained in:
Pedram Amini
2025-12-28 08:51:42 -06:00
parent c398f6a778
commit d65b8d2c6b
6 changed files with 53 additions and 30 deletions

View File

@@ -27,15 +27,16 @@ Maestro is an Electron desktop app for managing multiple AI coding assistants (C
## Quick Commands
```bash
npm run dev # Development with hot reload
npm run dev:web # Web interface development
npm run build # Full production build
npm run clean # Clean build artifacts
npm run lint # TypeScript type checking (all configs)
npm run lint:eslint # ESLint code quality checks
npm run package # Package for all platforms
npm run test # Run test suite
npm run test:watch # Run tests in watch mode
npm run dev # Development with hot reload (isolated data, can run alongside production)
npm run dev:prod-data # Development using production data (close production app first)
npm run dev:web # Web interface development
npm run build # Full production build
npm run clean # Clean build artifacts
npm run lint # TypeScript type checking (all configs)
npm run lint:eslint # ESLint code quality checks
npm run package # Package for all platforms
npm run test # Run test suite
npm run test:watch # Run tests in watch mode
```
## Architecture at a Glance

View File

@@ -97,7 +97,8 @@ maestro/
## Development Scripts
```bash
npm run dev # Start dev server with hot reload
npm run dev # Start dev server with hot reload (isolated data directory)
npm run dev:prod-data # Start dev server using production data (requires closing production app)
npm run dev:demo # Start in demo mode (fresh settings, isolated data)
npm run dev:web # Start web interface dev server
npm run build # Full production build (main + renderer + web + CLI)
@@ -114,6 +115,21 @@ npm run package:win # Package for Windows
npm run package:linux # Package for Linux
```
### Development Data Directories
By default, `npm run dev` uses an isolated data directory (`~/Library/Application Support/maestro-dev/`) separate from production. This allows you to run both dev and production instances simultaneously—useful when using the production Maestro to work on the dev instance.
| Command | Data Directory | Can Run Alongside Production? |
|---------|---------------|-------------------------------|
| `npm run dev` | `maestro-dev/` | ✅ Yes |
| `npm run dev:prod-data` | `maestro/` (production) | ❌ No - close production first |
| `npm run dev:demo` | `/tmp/maestro-demo/` | ✅ Yes |
**When to use each:**
- **`npm run dev`** — Default for most development. Start fresh or use dev-specific test data.
- **`npm run dev:prod-data`** — Test with your real sessions and settings. Must close production app first to avoid database lock conflicts.
- **`npm run dev:demo`** — Screenshots, demos, or testing with completely fresh state.
### Demo Mode
Use demo mode to run Maestro with a fresh, isolated data directory - useful for demos, testing, or screenshots without affecting your real settings:

View File

@@ -17,8 +17,10 @@
},
"scripts": {
"dev": "concurrently \"npm run dev:main\" \"npm run dev:renderer\"",
"dev:prod-data": "USE_PROD_DATA=1 concurrently \"npm run dev:main:prod-data\" \"npm run dev:renderer\"",
"dev:demo": "MAESTRO_DEMO_DIR=/tmp/maestro-demo npm run dev",
"dev:main": "npm run build:prompts && tsc -p tsconfig.main.json && NODE_ENV=development electron .",
"dev:main:prod-data": "npm run build:prompts && tsc -p tsconfig.main.json && NODE_ENV=development USE_PROD_DATA=1 electron .",
"dev:renderer": "vite",
"dev:web": "vite --config vite.config.web.mts",
"build": "npm run build:prompts && npm run build:main && npm run build:renderer && npm run build:web && npm run build:cli",

View File

@@ -101,10 +101,13 @@ if (DEMO_MODE) {
// Development mode: use a separate data directory to allow running alongside production
// This prevents database lock conflicts (e.g., Service Worker storage)
if (isDevelopment && !DEMO_MODE) {
// Set USE_PROD_DATA=1 to use the production data directory instead (requires closing production app)
if (isDevelopment && !DEMO_MODE && !process.env.USE_PROD_DATA) {
const devDataPath = path.join(app.getPath('userData'), '..', 'maestro-dev');
app.setPath('userData', devDataPath);
console.log(`[DEV MODE] Using data directory: ${devDataPath}`);
} else if (isDevelopment && process.env.USE_PROD_DATA) {
console.log(`[DEV MODE] Using production data directory: ${app.getPath('userData')}`);
}
// Type definitions

View File

@@ -333,7 +333,6 @@ function Tab({
onDragOver={onDragOver}
onDragEnd={onDragEnd}
onDrop={onDrop}
title={tab.name || tab.agentSessionId || 'New tab'}
>
{/* Busy indicator - pulsing dot for tabs in write mode */}
{tab.state === 'busy' && (
@@ -429,23 +428,16 @@ function Tab({
setIsHovered(false);
}}
>
{/* Connector tab that visually bridges the gap between tab and overlay */}
{/* Main overlay content - connects directly to tab like an open folder */}
<div
className="shadow-xl overflow-hidden"
style={{
width: overlayPosition.tabWidth || 100,
height: '6px',
backgroundColor: theme.colors.bgSidebar,
borderLeft: `1px solid ${theme.colors.border}`,
borderRight: `1px solid ${theme.colors.border}`,
marginBottom: '-1px' // Overlap with main overlay to hide seam
}}
/>
{/* Main overlay content */}
<div
className="rounded-b-lg rounded-tr-lg shadow-xl border overflow-hidden"
style={{
backgroundColor: theme.colors.bgSidebar,
borderColor: theme.colors.border,
borderBottom: `1px solid ${theme.colors.border}`,
borderBottomLeftRadius: '8px',
borderBottomRightRadius: '8px',
minWidth: '220px'
}}
>
@@ -642,7 +634,7 @@ function Tab({
</button>
)}
</div>
</div>
</div>
</div>,
document.body
)}

View File

@@ -87,7 +87,10 @@ export function ToastProvider({ children, defaultDuration: initialDuration = 20
setToasts(prev => [...prev, newToast]);
}
// Log toast to system logs
// Capture audio feedback state for logging
const { enabled: audioEnabled, command: audioCommand } = audioFeedbackRef.current;
// Log toast to system logs (include audio notification info)
window.maestro.logger.toast(toast.title, {
type: toast.type,
message: toast.message,
@@ -95,13 +98,19 @@ export function ToastProvider({ children, defaultDuration: initialDuration = 20
project: toast.project,
taskDuration: toast.taskDuration,
agentSessionId: toast.agentSessionId,
tabName: toast.tabName
tabName: toast.tabName,
// Audio/TTS notification info
audioNotification: audioEnabled && audioCommand ? {
enabled: true,
command: audioCommand
} : {
enabled: false
}
});
// Speak toast via TTS if audio feedback is enabled and command is configured
const { enabled, command } = audioFeedbackRef.current;
if (enabled && command) {
window.maestro.notification.speak(toast.message, command).catch(err => {
if (audioEnabled && audioCommand) {
window.maestro.notification.speak(toast.message, audioCommand).catch(err => {
console.error('[ToastContext] Failed to speak toast:', err);
});
}