From 2667cbdd77cb668827f7997813c874a44389de38 Mon Sep 17 00:00:00 2001 From: Kayvan Sylvan Date: Thu, 22 Jan 2026 10:14:48 -0800 Subject: [PATCH] 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) --------- Co-authored-by: Claude Sonnet 4.5 (1M context) --- .gitignore | 8 +- .vscode/extensions.json | 7 -- .vscode/settings.json | 47 ----------- CONTRIBUTING.md | 21 ++++- docs/git-worktrees.md | 8 ++ .../renderer/components/AboutModal.test.tsx | 1 - .../UsageDashboard/SummaryCards.test.tsx | 5 -- .../chart-accessibility.test.tsx | 4 +- .../UsageDashboard/responsive-layout.test.tsx | 1 - .../state-transition-animations.test.tsx | 6 +- .../components/UsageDashboardModal.test.tsx | 1 - src/main/index.ts | 5 +- src/renderer/index.html | 4 +- vite.config.mts | 79 ++++++++++--------- vite.config.web.mts | 17 +++- 15 files changed, 94 insertions(+), 120 deletions(-) delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 3fe49f5e..d50f4f67 100644 --- a/.gitignore +++ b/.gitignore @@ -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 \ No newline at end of file +.qodo diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index 9c86b8bc..00000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "recommendations": [ - "esbenp.prettier-vscode", - "dbaeumer.vscode-eslint", - "editorconfig.editorconfig" - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index d7b1963a..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -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 -} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc6f442f..36ce619b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -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: diff --git a/docs/git-worktrees.md b/docs/git-worktrees.md index 8161b3cb..16c43255 100644 --- a/docs/git-worktrees.md +++ b/docs/git-worktrees.md @@ -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 + ``` diff --git a/src/__tests__/renderer/components/AboutModal.test.tsx b/src/__tests__/renderer/components/AboutModal.test.tsx index 9ae8b6e2..5576265b 100644 --- a/src/__tests__/renderer/components/AboutModal.test.tsx +++ b/src/__tests__/renderer/components/AboutModal.test.tsx @@ -1111,5 +1111,4 @@ describe('AboutModal', () => { expect(screen.getByText('$12,345,678.90')).toBeInTheDocument(); }); }); - }); diff --git a/src/__tests__/renderer/components/UsageDashboard/SummaryCards.test.tsx b/src/__tests__/renderer/components/UsageDashboard/SummaryCards.test.tsx index a9c176f0..cccb25b8 100644 --- a/src/__tests__/renderer/components/UsageDashboard/SummaryCards.test.tsx +++ b/src/__tests__/renderer/components/UsageDashboard/SummaryCards.test.tsx @@ -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', () => { diff --git a/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx b/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx index 3973e8d8..37bbff61 100644 --- a/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx +++ b/src/__tests__/renderer/components/UsageDashboard/chart-accessibility.test.tsx @@ -70,7 +70,6 @@ const mockStatsData: StatsAggregation = { avgSessionDuration: 288000, byAgentByDay: {}, bySessionByDay: {}, - }; describe('Chart Accessibility - AgentComparisonChart', () => { @@ -411,10 +410,9 @@ describe('Chart Accessibility - General ARIA Patterns', () => { sessionsByDay: [], avgSessionDuration: 0, byAgentByDay: {}, - bySessionByDay: {}, + bySessionByDay: {}, }; - render(); expect(screen.getByText(/no agent data available/i)).toBeInTheDocument(); }); diff --git a/src/__tests__/renderer/components/UsageDashboard/responsive-layout.test.tsx b/src/__tests__/renderer/components/UsageDashboard/responsive-layout.test.tsx index 6267e20b..6dcc0ceb 100644 --- a/src/__tests__/renderer/components/UsageDashboard/responsive-layout.test.tsx +++ b/src/__tests__/renderer/components/UsageDashboard/responsive-layout.test.tsx @@ -212,7 +212,6 @@ const createSampleData = () => ({ avgSessionDuration: 144000, byAgentByDay: {}, bySessionByDay: {}, - }); describe('UsageDashboard Responsive Layout', () => { diff --git a/src/__tests__/renderer/components/UsageDashboard/state-transition-animations.test.tsx b/src/__tests__/renderer/components/UsageDashboard/state-transition-animations.test.tsx index d3109777..ee73a056 100644 --- a/src/__tests__/renderer/components/UsageDashboard/state-transition-animations.test.tsx +++ b/src/__tests__/renderer/components/UsageDashboard/state-transition-animations.test.tsx @@ -159,10 +159,9 @@ beforeEach(() => { ], avgSessionDuration: 180000, byAgentByDay: {}, - bySessionByDay: {}, + bySessionByDay: {}, }); mockStats.getDatabaseSize.mockResolvedValue(1024 * 1024); // 1 MB - }); afterEach(() => { @@ -282,7 +281,7 @@ describe('Usage Dashboard State Transition Animations', () => { sessionsByDay: [], avgSessionDuration: 240000, byAgentByDay: {}, - bySessionByDay: {}, + bySessionByDay: {}, }; it('applies dashboard-card-enter class to metric cards', () => { @@ -589,5 +588,4 @@ describe('Usage Dashboard State Transition Animations', () => { expect(totalMaxDuration).toBeLessThan(1000); }); }); - }); diff --git a/src/__tests__/renderer/components/UsageDashboardModal.test.tsx b/src/__tests__/renderer/components/UsageDashboardModal.test.tsx index 4d82507f..3b902670 100644 --- a/src/__tests__/renderer/components/UsageDashboardModal.test.tsx +++ b/src/__tests__/renderer/components/UsageDashboardModal.test.tsx @@ -147,7 +147,6 @@ const createSampleData = () => ({ avgSessionDuration: 144000, byAgentByDay: {}, bySessionByDay: {}, - }); describe('UsageDashboardModal', () => { diff --git a/src/main/index.ts b/src/main/index.ts index e5b80162..c949ea7b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -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'); diff --git a/src/renderer/index.html b/src/renderer/index.html index 2a1a073a..dd23f837 100644 --- a/src/renderer/index.html +++ b/src/renderer/index.html @@ -211,8 +211,8 @@