Developer Experience Improvements (multi-worktree simultaneous development) (#209)

* docs: add git hash display and configurable dev server port

## CHANGES

- Add `VITE_PORT` env variable to configure dev server port
- Display git commit hash in About modal next to version
- Add `__GIT_HASH__` build-time constant to both Vite configs
- Document running multiple Maestro instances with git worktrees
- Update CONTRIBUTING.md with parallel development instructions

* feat: add configurable ports for dev servers

- Allow VITE_PORT to configure main dev server port
- Update main window to load from configurable port
- Enable VITE_WEB_PORT for web interface dev server
- Add note in CONTRIBUTING.md about port configuration
- Log port usage in development mode

* docs: update CONTRIBUTING.md section and fix React DevTools script initialization

## CHANGES

- Rename "Linting" section to "Linting & Pre-commit Hooks" in table of contents
- Move script variable declaration outside conditional block
- Fix React DevTools script initialization order in index.html

* chore: update `.vscode/settings.json` with new markdownlint config

* fix: disable biome linting. Project uses ESLint

* chore: Update baseline-browser-mapping (>2 months old, warning message on "npm run build:web")

* chore: add .vscode/ to gitignore

* chore: fix gitignore to ignore .cscode/* files properly

* fix

* chore: stop tracking .vscode/ files, respect gitignore

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kayvan Sylvan
2026-01-22 10:14:48 -08:00
committed by GitHub
parent 5778a5b34b
commit 2667cbdd77
15 changed files with 94 additions and 120 deletions

6
.gitignore vendored
View File

@@ -33,11 +33,6 @@ scratch/
Thumbs.db
# IDE
# .vscode/ is tracked for shared settings (settings.json, extensions.json)
# But ignore personal/local files
.vscode/*
!.vscode/settings.json
!.vscode/extensions.json
.idea/
*.swp
*.swo
@@ -55,5 +50,6 @@ yarn-debug.log*
yarn-error.log*
#VS Code
.vscode/
.VSCodeCounter
.qodo

View File

@@ -1,7 +0,0 @@
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"editorconfig.editorconfig"
]
}

47
.vscode/settings.json vendored
View File

@@ -1,47 +0,0 @@
{
// Format on save with Prettier
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
// Use tabs (matches .prettierrc and .editorconfig)
"editor.tabSize": 2,
"editor.insertSpaces": false,
"editor.detectIndentation": false,
// ESLint configuration
"eslint.enable": true,
"eslint.validate": ["javascript", "javascriptreact", "typescript", "typescriptreact"],
// Don't let ESLint format - let Prettier handle it
"eslint.format.enable": false,
// File-specific formatters
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// Recommended extensions
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
// Files to exclude from search/watch
"files.exclude": {
"dist": true,
"release": true,
"node_modules": true
},
// TypeScript settings
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

View File

@@ -22,7 +22,7 @@ See [Performance Guidelines](#performance-guidelines) for specific practices.
- [Project Structure](#project-structure)
- [Development Scripts](#development-scripts)
- [Testing](#testing)
- [Linting](#linting)
- [Linting & Pre-commit Hooks](#linting--pre-commit-hooks)
- [Common Development Tasks](#common-development-tasks)
- [Adding a New AI Agent](#adding-a-new-ai-agent)
- [Code Style](#code-style)
@@ -150,6 +150,25 @@ You can also specify a custom demo directory via environment variable:
MAESTRO_DEMO_DIR=~/Desktop/my-demo npm run dev
```
### Running Multiple Instances (Git Worktrees)
When working with multiple git worktrees, you can run Maestro instances in parallel by specifying different ports using the `VITE_PORT` environment variable:
```bash
# In the main worktree (uses default port 5173)
npm run dev
# In worktree 2 (in another directory and terminal)
VITE_PORT=5174 npm run dev
# In worktree 3
VITE_PORT=5175 npm run dev
```
This allows you to develop and test different branches simultaneously without port conflicts.
**Note:** The web interface dev server (`npm run dev:web`) uses a separate port (default 5174) and can be configured with `VITE_WEB_PORT` if needed.
## Testing
Run the test suite with Jest:

View File

@@ -121,3 +121,11 @@ The confirmation dialog shows the full path to the worktree directory so you kno
- **Use a dedicated worktree folder** — Keep all worktrees in one place outside the main repo
- **Clean up when done** — Remove worktree agents after merging PRs to avoid clutter
- **Watch for Changes** — Enable file watching to keep the file tree in sync with worktree activity
- **Run multiple dev instances** — Use `VITE_PORT` environment variable to run Maestro in multiple worktrees simultaneously:
```bash
# In main worktree
npm run dev
# In worktree 2 (different terminal/directory)
VITE_PORT=5174 npm run dev
```

View File

@@ -1111,5 +1111,4 @@ describe('AboutModal', () => {
expect(screen.getByText('$12,345,678.90')).toBeInTheDocument();
});
});
});

View File

@@ -42,7 +42,6 @@ const mockData: StatsAggregation = {
avgSessionDuration: 288000,
byAgentByDay: {},
bySessionByDay: {},
};
// Empty data for edge case testing
@@ -61,7 +60,6 @@ const emptyData: StatsAggregation = {
avgSessionDuration: 0,
byAgentByDay: {},
bySessionByDay: {},
};
// Data with large numbers
@@ -83,7 +81,6 @@ const largeNumbersData: StatsAggregation = {
avgSessionDuration: 7200000,
byAgentByDay: {},
bySessionByDay: {},
};
// Single agent data
@@ -104,7 +101,6 @@ const singleAgentData: StatsAggregation = {
avgSessionDuration: 360000,
byAgentByDay: {},
bySessionByDay: {},
};
// Only auto queries
@@ -125,7 +121,6 @@ const onlyAutoData: StatsAggregation = {
avgSessionDuration: 360000,
byAgentByDay: {},
bySessionByDay: {},
};
describe('SummaryCards', () => {

View File

@@ -70,7 +70,6 @@ const mockStatsData: StatsAggregation = {
avgSessionDuration: 288000,
byAgentByDay: {},
bySessionByDay: {},
};
describe('Chart Accessibility - AgentComparisonChart', () => {
@@ -414,7 +413,6 @@ describe('Chart Accessibility - General ARIA Patterns', () => {
bySessionByDay: {},
};
render(<AgentComparisonChart data={emptyData} theme={mockTheme} />);
expect(screen.getByText(/no agent data available/i)).toBeInTheDocument();
});

View File

@@ -212,7 +212,6 @@ const createSampleData = () => ({
avgSessionDuration: 144000,
byAgentByDay: {},
bySessionByDay: {},
});
describe('UsageDashboard Responsive Layout', () => {

View File

@@ -162,7 +162,6 @@ beforeEach(() => {
bySessionByDay: {},
});
mockStats.getDatabaseSize.mockResolvedValue(1024 * 1024); // 1 MB
});
afterEach(() => {
@@ -589,5 +588,4 @@ describe('Usage Dashboard State Transition Animations', () => {
expect(totalMaxDuration).toBeLessThan(1000);
});
});
});

View File

@@ -147,7 +147,6 @@ const createSampleData = () => ({
avgSessionDuration: 144000,
byAgentByDay: {},
bySessionByDay: {},
});
describe('UsageDashboardModal', () => {

View File

@@ -642,9 +642,10 @@ function createWindow() {
logger.warn(`Failed to load electron-devtools-installer: ${err.message}`, 'Window')
);
mainWindow.loadURL('http://localhost:5173');
const vitePort = process.env.VITE_PORT || '5173';
mainWindow.loadURL(`http://localhost:${vitePort}`);
// DevTools can be opened via Command-K menu instead of automatically on startup
logger.info('Loading development server', 'Window');
logger.info(`Loading development server on port ${vitePort}`, 'Window');
} else {
mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
logger.info('Loading production build', 'Window');

View File

@@ -211,8 +211,8 @@
<!-- React DevTools: connects to standalone react-devtools app (npm install -g react-devtools) -->
<!-- Only attempts connection in dev mode (Vite serves on localhost:5173) -->
<script>
if (window.location.hostname === 'localhost') {
var script = document.createElement('script');
if (window.location.hostname === 'localhost') {
script.src = 'http://localhost:8097';
script.async = false;
document.head.appendChild(script);

View File

@@ -36,7 +36,8 @@ export default defineConfig(({ mode }) => ({
alias: {
// In development, use wdyr.dev.ts which loads why-did-you-render
// In production, use wdyr.ts which is empty (prevents bundling the library)
'./wdyr': mode === 'development'
'./wdyr':
mode === 'development'
? path.join(__dirname, 'src/renderer/wdyr.dev.ts')
: path.join(__dirname, 'src/renderer/wdyr.ts'),
},
@@ -50,7 +51,7 @@ export default defineConfig(({ mode }) => ({
emptyOutDir: true,
},
server: {
port: 5173,
port: process.env.VITE_PORT ? parseInt(process.env.VITE_PORT) : 5173,
hmr: !disableHmr,
// Disable file watching entirely when HMR is disabled to prevent any reloads
watch: disableHmr ? null : undefined,

View File

@@ -11,6 +11,7 @@ import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';
import { readFileSync } from 'fs';
import { execFileSync } from 'child_process';
// Read version from package.json
const packageJson = JSON.parse(
@@ -18,6 +19,19 @@ const packageJson = JSON.parse(
);
const appVersion = process.env.VITE_APP_VERSION || packageJson.version;
// Get git hash
function getGitHash() {
try {
return execFileSync('git', ['rev-parse', '--short=8', 'HEAD'], {
encoding: 'utf8',
stdio: ['pipe', 'pipe', 'pipe']
}).trim();
} catch {
return 'unknown';
}
}
const gitHash = getGitHash();
export default defineConfig(({ mode }) => ({
plugins: [react()],
@@ -33,6 +47,7 @@ export default defineConfig(({ mode }) => ({
define: {
__APP_VERSION__: JSON.stringify(appVersion),
__GIT_HASH__: JSON.stringify(gitHash),
},
esbuild: {
@@ -128,7 +143,7 @@ export default defineConfig(({ mode }) => ({
// Development server (for testing web interface standalone)
server: {
port: 5174, // Different from renderer dev server (5173)
port: process.env.VITE_WEB_PORT ? parseInt(process.env.VITE_WEB_PORT) : 5174, // Different from renderer dev server (5173)
strictPort: true,
// Proxy API calls to the running Maestro app during development
proxy: {