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 @@