diff --git a/README.md b/README.md index 5a9a0be0..023ab83a 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Maestro [![Made with Maestro](docs/assets/made-with-maestro.svg)](https://github.com/pedramamini/Maestro) -[![Discord](https://img.shields.io/badge/Discord-Join%20Us-5865F2?logo=discord&logoColor=white)](https://discord.gg/SrBsykvG) +[![Discord](https://img.shields.io/badge/Discord-Join%20Us-5865F2?logo=discord&logoColor=white)](https://discord.gg/SVSRy593) [![User Docs](https://img.shields.io/badge/Docs-Usage%20%26%20Documentation-blue?logo=readthedocs&logoColor=white)](https://docs.runmaestro.ai/) > Maestro hones fractured attention into focused intent. @@ -163,7 +163,7 @@ Full documentation and usage guide available at **[docs.runmaestro.ai](https://d ## Community -- **Discord**: [Join Us](https://discord.gg/SrBsykvG) +- **Discord**: [Join Us](https://discord.gg/SVSRy593) - **GitHub Issues**: [Report bugs & request features](https://github.com/pedramamini/Maestro/issues) ## Contributing diff --git a/build/entitlements.mac.plist b/build/entitlements.mac.plist index d29ffd47..a8469946 100644 --- a/build/entitlements.mac.plist +++ b/build/entitlements.mac.plist @@ -10,5 +10,7 @@ com.apple.security.device.audio-input + com.apple.security.automation.apple-events + diff --git a/docs/docs.json b/docs/docs.json index 502015ff..54f07ef2 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -101,7 +101,7 @@ "links": [ { "label": "Discord", - "href": "https://discord.gg/SrBsykvG" + "href": "https://discord.gg/SVSRy593" }, { "label": "GitHub", @@ -120,7 +120,7 @@ }, "footer": { "socials": { - "discord": "https://discord.gg/SrBsykvG", + "discord": "https://discord.gg/SVSRy593", "github": "https://github.com/pedramamini/Maestro" } } diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 953d973c..7ede0d10 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -138,5 +138,5 @@ For new projects, always clone to the Linux filesystem from the start. ## Getting Help - **GitHub Issues**: [Report bugs or request features](https://github.com/pedramamini/Maestro/issues) -- **Discord**: [Join the community](https://discord.gg/SrBsykvG) +- **Discord**: [Join the community](https://discord.gg/SVSRy593) - **Documentation**: [Docs site](https://docs.runmaestro.ai), [CONTRIBUTING.md](https://github.com/pedramamini/Maestro/blob/main/CONTRIBUTING.md), and [ARCHITECTURE.md](https://github.com/pedramamini/Maestro/blob/main/ARCHITECTURE.md) diff --git a/src/main/agent-detector.ts b/src/main/agent-detector.ts index 94c4563c..30f6ac34 100644 --- a/src/main/agent-detector.ts +++ b/src/main/agent-detector.ts @@ -522,6 +522,99 @@ export class AgentDetector { return null; } + /** + * Detect Node version manager paths on Unix systems (macOS/Linux). + * Returns an array of bin paths from nvm, fnm, volta, mise, asdf, and n. + * These paths are needed to find npm-installed CLIs like codex, claude, gemini. + */ + private detectNodeVersionManagerBinPaths(): string[] { + const home = os.homedir(); + const detectedPaths: string[] = []; + + // nvm: Check for ~/.nvm and find installed node versions + const nvmDir = process.env.NVM_DIR || path.join(home, '.nvm'); + if (fs.existsSync(nvmDir)) { + const versionsDir = path.join(nvmDir, 'versions', 'node'); + if (fs.existsSync(versionsDir)) { + try { + const versions = fs.readdirSync(versionsDir).filter(v => v.startsWith('v')); + // Sort versions descending to check newest first + versions.sort((a, b) => { + const parseVersion = (v: string) => v.replace('v', '').split('.').map(n => parseInt(n, 10)); + const av = parseVersion(a); + const bv = parseVersion(b); + for (let i = 0; i < 3; i++) { + if ((bv[i] || 0) !== (av[i] || 0)) return (bv[i] || 0) - (av[i] || 0); + } + return 0; + }); + for (const version of versions) { + const versionBin = path.join(versionsDir, version, 'bin'); + if (fs.existsSync(versionBin)) { + detectedPaths.push(versionBin); + } + } + } catch { + // Ignore errors reading versions directory + } + } + // Also check nvm/current symlink + const nvmCurrentBin = path.join(nvmDir, 'current', 'bin'); + if (fs.existsSync(nvmCurrentBin) && !detectedPaths.includes(nvmCurrentBin)) { + detectedPaths.unshift(nvmCurrentBin); + } + } + + // fnm: Fast Node Manager + const fnmPaths = [ + path.join(home, 'Library', 'Application Support', 'fnm'), // macOS default + path.join(home, '.local', 'share', 'fnm'), // Linux default + path.join(home, '.fnm'), // Legacy/custom location + ]; + for (const fnmDir of fnmPaths) { + if (fs.existsSync(fnmDir)) { + const fnmCurrentBin = path.join(fnmDir, 'aliases', 'default', 'bin'); + if (fs.existsSync(fnmCurrentBin)) { + detectedPaths.push(fnmCurrentBin); + } + const fnmNodeVersions = path.join(fnmDir, 'node-versions'); + if (fs.existsSync(fnmNodeVersions)) { + try { + const versions = fs.readdirSync(fnmNodeVersions).filter(v => v.startsWith('v')); + for (const version of versions) { + const versionBin = path.join(fnmNodeVersions, version, 'installation', 'bin'); + if (fs.existsSync(versionBin)) { + detectedPaths.push(versionBin); + } + } + } catch { + // Ignore errors + } + } + } + } + + // volta: Uses ~/.volta/bin for shims + const voltaBin = path.join(home, '.volta', 'bin'); + if (fs.existsSync(voltaBin)) { + detectedPaths.push(voltaBin); + } + + // mise (formerly rtx): Uses ~/.local/share/mise/shims + const miseShims = path.join(home, '.local', 'share', 'mise', 'shims'); + if (fs.existsSync(miseShims)) { + detectedPaths.push(miseShims); + } + + // asdf: Uses ~/.asdf/shims + const asdfShims = path.join(home, '.asdf', 'shims'); + if (fs.existsSync(asdfShims)) { + detectedPaths.push(asdfShims); + } + + return detectedPaths; + } + /** * On macOS/Linux, directly probe known installation paths for a binary. * This is necessary because packaged Electron apps don't inherit shell aliases, @@ -531,6 +624,9 @@ export class AgentDetector { private async probeUnixPaths(binaryName: string): Promise { const home = os.homedir(); + // Get dynamic paths from Node version managers (nvm, fnm, volta, etc.) + const versionManagerPaths = this.detectNodeVersionManagerBinPaths(); + // Define known installation paths for each binary, in priority order const knownPaths: Record = { claude: [ @@ -546,6 +642,8 @@ export class AgentDetector { path.join(home, '.npm-global', 'bin', 'claude'), // User bin directory path.join(home, 'bin', 'claude'), + // Add paths from Node version managers (nvm, fnm, volta, etc.) + ...versionManagerPaths.map(p => path.join(p, 'claude')), ], codex: [ // User local bin @@ -555,6 +653,8 @@ export class AgentDetector { '/usr/local/bin/codex', // npm global path.join(home, '.npm-global', 'bin', 'codex'), + // Add paths from Node version managers (nvm, fnm, volta, etc.) + ...versionManagerPaths.map(p => path.join(p, 'codex')), ], opencode: [ // OpenCode installer default location @@ -566,12 +666,16 @@ export class AgentDetector { // Homebrew paths '/opt/homebrew/bin/opencode', '/usr/local/bin/opencode', + // Add paths from Node version managers (nvm, fnm, volta, etc.) + ...versionManagerPaths.map(p => path.join(p, 'opencode')), ], gemini: [ // npm global paths path.join(home, '.npm-global', 'bin', 'gemini'), '/opt/homebrew/bin/gemini', '/usr/local/bin/gemini', + // Add paths from Node version managers (nvm, fnm, volta, etc.) + ...versionManagerPaths.map(p => path.join(p, 'gemini')), ], aider: [ // pip installation @@ -579,6 +683,8 @@ export class AgentDetector { // Homebrew paths '/opt/homebrew/bin/aider', '/usr/local/bin/aider', + // Add paths from Node version managers (in case installed via npm) + ...versionManagerPaths.map(p => path.join(p, 'aider')), ], }; diff --git a/src/renderer/components/AboutModal.tsx b/src/renderer/components/AboutModal.tsx index ad5fe0cd..d0006574 100644 --- a/src/renderer/components/AboutModal.tsx +++ b/src/renderer/components/AboutModal.tsx @@ -130,7 +130,7 @@ export function AboutModal({ theme, autoRunStats, usageStats, handsOnTimeMs, onC