diff --git a/AGENTS.md b/AGENTS.md index 681311eb9c..ceb2b988dc 120000 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1 @@ -CLAUDE.md \ No newline at end of file +CLAUDE.md diff --git a/docs/releases.md b/docs/releases.md index d053eb9477..81e0e7f113 100644 --- a/docs/releases.md +++ b/docs/releases.md @@ -17,60 +17,60 @@ Maestro can update itself automatically! This feature was introduced in **v0.8.7 **Latest: v0.15.2** | Released March 12, 2026 -# Major 0.15.x Additions - -๐ŸŽถ **Maestro Symphony** โ€” Contribute to open source with AI assistance! Browse curated issues from projects with the `runmaestro.ai` label, clone repos with one click, and automatically process the relevant Auto Run playbooks. Track your contributions, streaks, and stats. You're contributing CPU and tokens towards your favorite open source projects and features. - -๐ŸŽฌ **Director's Notes** โ€” Aggregates history across all agents into a unified timeline with search, filters, and an activity graph. Includes an AI Overview tab that generates a structured synopsis of recent work. Off by default, gated behind a new "Encore Features" panel under settings. This is a precursor to an eventual plugin system, allowing for extensions and customizations without bloating the core app. - -๐Ÿท๏ธ **Conductor Profile** โ€” Available under Settings > General. Provide a short description on how Maestro agents should interface with you. - -๐Ÿง  **Three-State Thinking Toggle** โ€” The thinking toggle now cycles through three modes: off, on, and sticky. Sticky mode keeps thinking content visible after the response completes. Cycle with CMD/CTRL+SHIFT+K. - -๐Ÿค– **Factory.ai Droid Support** โ€” Added support for the [Factory.ai](https://factory.ai/product/cli) droid agent. Full session management and output parsing integration. - -## Change in v0.15.2 - -Patch release with bug fixes, UX improvements, and cherry-picks from the 0.16.0 RC. - -### New Features - -- **Cmd+0 โ†’ Last Tab:** Remapped Cmd+0 to jump to last tab; Cmd+Shift+0 now resets font size -- **Unsent draft protection:** Confirm dialog before closing tabs with unsent draft input -- **Read-only CLI flag:** Added `--read-only` flag to `maestro-cli send` command -- **Gemini read-only enforcement:** Gemini `-y` flag now works in read-only mode -- **Capability-based providers:** Replaced hardcoded agent ID checks with capability flags and shared metadata - -### Bug Fixes - -- **Sticky overlay scroll:** Fixed sticky overlays breaking tab scroll-into-view -- **Director's Notes stats:** Count only agents with entries in lookback window -- **SSH remote config:** Check `sessionSshRemoteConfig` as primary SSH remote ID source -- **.maestro file tree:** Always show .maestro directory even when dotfiles are hidden -- **Provider hardening:** Prototype safety, capability gates, stale map cleanup -- **Session search:** Per-session error resilience and metadata-based title matching -- **File tree stale loads:** Load sequence counter prevents stale file tree updates -- **File tree Unicode:** NFC normalization prevents duplicate entries -- **File tree duplicates:** Tree-structured data resolves duplicate entries -- **File tree auto-refresh:** Timer no longer destroyed on right panel tab switch -- **Menu z-index:** Branding header menu renders above sidebar content -- **Dropdown clipping:** Fixed hamburger menu and live overlay dropdown clipping -- **Font size shortcuts:** Restored Cmd+/- font size shortcuts lost with custom menu -- **Draft input preservation:** Replaying a previous message no longer discards current draft -- **SSH directory collision:** Skip warning when agents are on different SSH hosts -- **IPC error handling:** Handle expected IPC errors gracefully -- **Auto-focus on mode switch:** Input field auto-focuses when toggling AI/Shell mode -- **OpenCode parser:** Preserve JSON error events; reset resultEmitted on step_start -- **NDJSON performance:** Eliminated triple JSON parsing on hot path -- **Agent config overrides:** Apply config overrides in context groomer before spawning -- **Stale closure fix:** Resolved model not saving in wizard agent config - -### Visual Polish - -- **Light theme contrast:** Improved syntax highlighting contrast across all light themes -- **Context warning sash:** Dark text colors in light mode for readability -- **Session name dimming:** Use `textMain` color to prevent visual dimming -- **Session name pill:** Allow shrinking so date doesn't collide with type pill +# Major 0.15.x Additions + +๐ŸŽถ **Maestro Symphony** โ€” Contribute to open source with AI assistance! Browse curated issues from projects with the `runmaestro.ai` label, clone repos with one click, and automatically process the relevant Auto Run playbooks. Track your contributions, streaks, and stats. You're contributing CPU and tokens towards your favorite open source projects and features. + +๐ŸŽฌ **Director's Notes** โ€” Aggregates history across all agents into a unified timeline with search, filters, and an activity graph. Includes an AI Overview tab that generates a structured synopsis of recent work. Off by default, gated behind a new "Encore Features" panel under settings. This is a precursor to an eventual plugin system, allowing for extensions and customizations without bloating the core app. + +๐Ÿท๏ธ **Conductor Profile** โ€” Available under Settings > General. Provide a short description on how Maestro agents should interface with you. + +๐Ÿง  **Three-State Thinking Toggle** โ€” The thinking toggle now cycles through three modes: off, on, and sticky. Sticky mode keeps thinking content visible after the response completes. Cycle with CMD/CTRL+SHIFT+K. + +๐Ÿค– **Factory.ai Droid Support** โ€” Added support for the [Factory.ai](https://factory.ai/product/cli) droid agent. Full session management and output parsing integration. + +## Change in v0.15.2 + +Patch release with bug fixes, UX improvements, and cherry-picks from the 0.16.0 RC. + +### New Features + +- **Cmd+0 โ†’ Last Tab:** Remapped Cmd+0 to jump to last tab; Cmd+Shift+0 now resets font size +- **Unsent draft protection:** Confirm dialog before closing tabs with unsent draft input +- **Read-only CLI flag:** Added `--read-only` flag to `maestro-cli send` command +- **Gemini read-only enforcement:** Gemini `-y` flag now works in read-only mode +- **Capability-based providers:** Replaced hardcoded agent ID checks with capability flags and shared metadata + +### Bug Fixes + +- **Sticky overlay scroll:** Fixed sticky overlays breaking tab scroll-into-view +- **Director's Notes stats:** Count only agents with entries in lookback window +- **SSH remote config:** Check `sessionSshRemoteConfig` as primary SSH remote ID source +- **.maestro file tree:** Always show .maestro directory even when dotfiles are hidden +- **Provider hardening:** Prototype safety, capability gates, stale map cleanup +- **Session search:** Per-session error resilience and metadata-based title matching +- **File tree stale loads:** Load sequence counter prevents stale file tree updates +- **File tree Unicode:** NFC normalization prevents duplicate entries +- **File tree duplicates:** Tree-structured data resolves duplicate entries +- **File tree auto-refresh:** Timer no longer destroyed on right panel tab switch +- **Menu z-index:** Branding header menu renders above sidebar content +- **Dropdown clipping:** Fixed hamburger menu and live overlay dropdown clipping +- **Font size shortcuts:** Restored Cmd+/- font size shortcuts lost with custom menu +- **Draft input preservation:** Replaying a previous message no longer discards current draft +- **SSH directory collision:** Skip warning when agents are on different SSH hosts +- **IPC error handling:** Handle expected IPC errors gracefully +- **Auto-focus on mode switch:** Input field auto-focuses when toggling AI/Shell mode +- **OpenCode parser:** Preserve JSON error events; reset resultEmitted on step_start +- **NDJSON performance:** Eliminated triple JSON parsing on hot path +- **Agent config overrides:** Apply config overrides in context groomer before spawning +- **Stale closure fix:** Resolved model not saving in wizard agent config + +### Visual Polish + +- **Light theme contrast:** Improved syntax highlighting contrast across all light themes +- **Context warning sash:** Dark text colors in light mode for readability +- **Session name dimming:** Use `textMain` color to prevent visual dimming +- **Session name pill:** Allow shrinking so date doesn't collide with type pill - **Scroll-to-bottom arrow:** Removed noisy indicator from terminal output view ### Previous Releases in this Series @@ -83,41 +83,41 @@ Patch release with bug fixes, UX improvements, and cherry-picks from the 0.16.0 **Latest: v0.14.5** | Released January 24, 2026 -Changes in this point release include: - -- Desktop app performance improvements (more to come on this, we want Maestro blazing fast) ๐ŸŒ -- Added local manifest feature for custom playbooks ๐Ÿ“– -- Agents are now inherently aware of your activity history as seen in the history panel ๐Ÿ“œ (this is built-in cross context memory!) -- Added markdown rendering support for AI responses in mobile view ๐Ÿ“ฑ -- Bugfix in tracking costs from JSONL files that were aged out ๐Ÿฆ -- Added BlueSky social media handle for leaderboard ๐Ÿฆ‹ -- Added options to disable GPU rendering and confetti ๐ŸŽŠ -- Better handling of large files in preview ๐Ÿ—„๏ธ -- Bug fix in Claude context calculation ๐Ÿงฎ -- Addressed bug in OpenSpec version reporting ๐Ÿ› - -The major contributions to 0.14.x remain: - -๐Ÿ—„๏ธ Document Graphs. Launch from file preview or from the FIle tree panel. Explore relationships between Markdown documents that contain links between documents and to URLs. - -๐Ÿ“ถ SSH support for agents. Manage a remote agent with feature parity over SSH. Includes support for Git and File tree panels. Manage agents on remote systems or in containers. This even works for Group Chat, which is rad as hell. - -๐Ÿง™โ€โ™‚๏ธ Added an in-tab wizard for generating Auto Run Playbooks via `/wizard` or a new button in the Auto Run panel. - -# Smaller Changes in 014.x - -- Improved User Dashboard, available from hamburger menu, command palette or hotkey ๐ŸŽ›๏ธ -- Leaderboard tracking now works across multiple systems and syncs level from cloud ๐Ÿ† -- Agent duplication. Pro tip: Consider a group of unused "Template" agents โœŒ๏ธ -- New setting to prevent system from going to sleep while agents are active ๐Ÿ›๏ธ -- The tab menu has a new "Publish as GitHub Gist" option ๐Ÿ“ -- The tab menu has options to move the tab to the first or last position ๐Ÿ”€ -- [Maestro-Playbooks](https://github.com/pedramamini/Maestro-Playbooks) can now contain non-markdown assets ๐Ÿ“™ -- Improved default shell detection ๐Ÿš -- Added logic to prevent overlapping TTS notifications ๐Ÿ’ฌ -- Added "Toggle Bookmark" shortcut (CTRL/CMD+SHIFT+B) โŒจ๏ธ -- Gist publishing now shows previous URLs with copy button ๐Ÿ“‹ - +Changes in this point release include: + +- Desktop app performance improvements (more to come on this, we want Maestro blazing fast) ๐ŸŒ +- Added local manifest feature for custom playbooks ๐Ÿ“– +- Agents are now inherently aware of your activity history as seen in the history panel ๐Ÿ“œ (this is built-in cross context memory!) +- Added markdown rendering support for AI responses in mobile view ๐Ÿ“ฑ +- Bugfix in tracking costs from JSONL files that were aged out ๐Ÿฆ +- Added BlueSky social media handle for leaderboard ๐Ÿฆ‹ +- Added options to disable GPU rendering and confetti ๐ŸŽŠ +- Better handling of large files in preview ๐Ÿ—„๏ธ +- Bug fix in Claude context calculation ๐Ÿงฎ +- Addressed bug in OpenSpec version reporting ๐Ÿ› + +The major contributions to 0.14.x remain: + +๐Ÿ—„๏ธ Document Graphs. Launch from file preview or from the FIle tree panel. Explore relationships between Markdown documents that contain links between documents and to URLs. + +๐Ÿ“ถ SSH support for agents. Manage a remote agent with feature parity over SSH. Includes support for Git and File tree panels. Manage agents on remote systems or in containers. This even works for Group Chat, which is rad as hell. + +๐Ÿง™โ€โ™‚๏ธ Added an in-tab wizard for generating Auto Run Playbooks via `/wizard` or a new button in the Auto Run panel. + +# Smaller Changes in 014.x + +- Improved User Dashboard, available from hamburger menu, command palette or hotkey ๐ŸŽ›๏ธ +- Leaderboard tracking now works across multiple systems and syncs level from cloud ๐Ÿ† +- Agent duplication. Pro tip: Consider a group of unused "Template" agents โœŒ๏ธ +- New setting to prevent system from going to sleep while agents are active ๐Ÿ›๏ธ +- The tab menu has a new "Publish as GitHub Gist" option ๐Ÿ“ +- The tab menu has options to move the tab to the first or last position ๐Ÿ”€ +- [Maestro-Playbooks](https://github.com/pedramamini/Maestro-Playbooks) can now contain non-markdown assets ๐Ÿ“™ +- Improved default shell detection ๐Ÿš +- Added logic to prevent overlapping TTS notifications ๐Ÿ’ฌ +- Added "Toggle Bookmark" shortcut (CTRL/CMD+SHIFT+B) โŒจ๏ธ +- Gist publishing now shows previous URLs with copy button ๐Ÿ“‹ + Thanks for the contributions: @t1mmen @aejfager @Crumbgrabber @whglaser @b3nw @deandebeer @shadown @breki @charles-dyfis-net @ronaldeddings @jlengrand @ksylvan ### Previous Releases in this Series @@ -136,20 +136,22 @@ Thanks for the contributions: @t1mmen @aejfager @Crumbgrabber @whglaser @b3nw @d ### Changes -- TAKE TWO! Fixed Linux ARM64 build architecture contamination issues ๐Ÿ—๏ธ - -### v0.13.1 Changes -- Fixed Linux ARM64 build architecture contamination issues ๐Ÿ—๏ธ -- Enhanced error handling for Auto Run batch processing ๐Ÿšจ - -### v0.13.0 Changes -- Added a global usage dashboard, data collection begins with this install ๐ŸŽ›๏ธ -- Added a Playbook Exchange for downloading pre-defined Auto Run playbooks from [Maestro-Playbooks](https://github.com/pedramamini/Maestro-Playbooks) ๐Ÿ“• -- Bundled OpenSpec commands for structured change proposals ๐Ÿ“ -- Added pre-release channel support for beta/RC updates ๐Ÿงช -- Implemented global hands-on time tracking across sessions โฑ๏ธ -- Added new keyboard shortcut for agent settings (Opt+Cmd+, | Ctrl+Alt+,) โŒจ๏ธ -- Added directory size calculation with file/folder counts in file explorer ๐Ÿ“Š +- TAKE TWO! Fixed Linux ARM64 build architecture contamination issues ๐Ÿ—๏ธ + +### v0.13.1 Changes + +- Fixed Linux ARM64 build architecture contamination issues ๐Ÿ—๏ธ +- Enhanced error handling for Auto Run batch processing ๐Ÿšจ + +### v0.13.0 Changes + +- Added a global usage dashboard, data collection begins with this install ๐ŸŽ›๏ธ +- Added a Playbook Exchange for downloading pre-defined Auto Run playbooks from [Maestro-Playbooks](https://github.com/pedramamini/Maestro-Playbooks) ๐Ÿ“• +- Bundled OpenSpec commands for structured change proposals ๐Ÿ“ +- Added pre-release channel support for beta/RC updates ๐Ÿงช +- Implemented global hands-on time tracking across sessions โฑ๏ธ +- Added new keyboard shortcut for agent settings (Opt+Cmd+, | Ctrl+Alt+,) โŒจ๏ธ +- Added directory size calculation with file/folder counts in file explorer ๐Ÿ“Š - Added sleep detection to exclude laptop sleep from time tracking โฐ ### Previous Releases in this Series @@ -163,22 +165,26 @@ Thanks for the contributions: @t1mmen @aejfager @Crumbgrabber @whglaser @b3nw @d **Latest: v0.12.3** | Released December 28, 2025 -The big changes in the v0.12.x line are the following three: - -## Show Thinking -๐Ÿค” There is now a toggle to show thinking for the agent, the default for new tabs is off, though this can be changed under Settings > General. The toggle shows next to History and Read-Only. Very similar pattern. This has been the #1 most requested feature, though personally, I don't think I'll use it as I prefer to not see the details of the work, but the results of the work. Just as we work with our colleagues. - -## GitHub Spec-Kit Integration -๐ŸŽฏ Added [GitHub Spec-Kit](https://github.com/github/spec-kit) commands into Maestro with a built in updater to grab the latest prompts from the repository. We do override `/speckit-implement` (the final step) to create Auto Run docs and guide the user through their execution, which thanks to Wortrees from v0.11.x allows us to run in parallel! - -## Context Management Tools -๐Ÿ“– Added context management options from tab right-click menu. You can now compress, merge, and transfer contexts between agents. You will received (configurable) warnings at 60% and 80% context consumption with a hint to compact. - -## Changes Specific to v0.12.3: -- We now have hosted documentation through Mintlify ๐Ÿ“š -- Export any tab conversation as self-contained themed HTML file ๐Ÿ“„ -- Publish files as private/public Gists ๐ŸŒ -- Added tab hover overlay menu with close operations and export ๐Ÿ“‹ +The big changes in the v0.12.x line are the following three: + +## Show Thinking + +๐Ÿค” There is now a toggle to show thinking for the agent, the default for new tabs is off, though this can be changed under Settings > General. The toggle shows next to History and Read-Only. Very similar pattern. This has been the #1 most requested feature, though personally, I don't think I'll use it as I prefer to not see the details of the work, but the results of the work. Just as we work with our colleagues. + +## GitHub Spec-Kit Integration + +๐ŸŽฏ Added [GitHub Spec-Kit](https://github.com/github/spec-kit) commands into Maestro with a built in updater to grab the latest prompts from the repository. We do override `/speckit-implement` (the final step) to create Auto Run docs and guide the user through their execution, which thanks to Wortrees from v0.11.x allows us to run in parallel! + +## Context Management Tools + +๐Ÿ“– Added context management options from tab right-click menu. You can now compress, merge, and transfer contexts between agents. You will received (configurable) warnings at 60% and 80% context consumption with a hint to compact. + +## Changes Specific to v0.12.3: + +- We now have hosted documentation through Mintlify ๐Ÿ“š +- Export any tab conversation as self-contained themed HTML file ๐Ÿ“„ +- Publish files as private/public Gists ๐ŸŒ +- Added tab hover overlay menu with close operations and export ๐Ÿ“‹ - Added social handles to achievement share images ๐Ÿ† ### Previous Releases in this Series @@ -192,12 +198,12 @@ The big changes in the v0.12.x line are the following three: **Latest: v0.11.0** | Released December 22, 2025 -๐ŸŒณ Github Worktree support was added. Any agent bound to a Git repository has the option to enable worktrees, each of which show up as a sub-agent with their own write-lock and Auto Run capability. Now you can truly develop in parallel on the same project and issue PRs when you're ready, all from within Maestro. Huge improvement, major thanks to @petersilberman. - -# Other Changes - -- @ file mentions now include documents from your Auto Run folder (which may not live in your agent working directory) ๐Ÿ—„๏ธ -- The wizard is now capable of detecting and continuing on past started projects ๐Ÿง™ +๐ŸŒณ Github Worktree support was added. Any agent bound to a Git repository has the option to enable worktrees, each of which show up as a sub-agent with their own write-lock and Auto Run capability. Now you can truly develop in parallel on the same project and issue PRs when you're ready, all from within Maestro. Huge improvement, major thanks to @petersilberman. + +# Other Changes + +- @ file mentions now include documents from your Auto Run folder (which may not live in your agent working directory) ๐Ÿ—„๏ธ +- The wizard is now capable of detecting and continuing on past started projects ๐Ÿง™ - Bug fixes ๐Ÿ›๐Ÿœ๐Ÿž --- @@ -208,14 +214,14 @@ The big changes in the v0.12.x line are the following three: ### Changes -- Export group chats as self-contained HTML โฌ‡๏ธ -- Enhanced system process viewer now has details view with full process args ๐Ÿ’ป -- Update button hides until platform binaries are available in releases. โณ -- Added Auto Run stall detection at the loop level, if no documents are updated after a loop ๐Ÿ” -- Improved Codex session discovery ๐Ÿ” -- Windows compatibility fixes ๐Ÿ› -- 64-bit Linux ARM build issue fixed (thanks @LilYoopug) ๐Ÿœ -- Addressed session enumeration issues with Codex and OpenCode ๐Ÿž +- Export group chats as self-contained HTML โฌ‡๏ธ +- Enhanced system process viewer now has details view with full process args ๐Ÿ’ป +- Update button hides until platform binaries are available in releases. โณ +- Added Auto Run stall detection at the loop level, if no documents are updated after a loop ๐Ÿ” +- Improved Codex session discovery ๐Ÿ” +- Windows compatibility fixes ๐Ÿ› +- 64-bit Linux ARM build issue fixed (thanks @LilYoopug) ๐Ÿœ +- Addressed session enumeration issues with Codex and OpenCode ๐Ÿž - Addressed pathing issues around gh command (thanks @oliveiraantoniocc) ๐Ÿ ### Previous Releases in this Series @@ -231,13 +237,13 @@ The big changes in the v0.12.x line are the following three: ### Changes -- Add Sentry crashing reporting monitoring with opt-out ๐Ÿ› -- Stability fixes on v0.9.0 along with all the changes it brought along, including... - - Major refactor to enable supporting of multiple providers ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ - - Added OpenAI Codex support ๐Ÿ‘จโ€๐Ÿ’ป - - Added OpenCode support ๐Ÿ‘ฉโ€๐Ÿ’ป - - Error handling system detects and recovers from agent failures ๐Ÿšจ - - Added option to specify CLI arguments to AI providers โœจ +- Add Sentry crashing reporting monitoring with opt-out ๐Ÿ› +- Stability fixes on v0.9.0 along with all the changes it brought along, including... + - Major refactor to enable supporting of multiple providers ๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ฆ + - Added OpenAI Codex support ๐Ÿ‘จโ€๐Ÿ’ป + - Added OpenCode support ๐Ÿ‘ฉโ€๐Ÿ’ป + - Error handling system detects and recovers from agent failures ๐Ÿšจ + - Added option to specify CLI arguments to AI providers โœจ - Bunch of other little tweaks and additions ๐Ÿ’Ž ### Previous Releases in this Series @@ -252,19 +258,19 @@ The big changes in the v0.12.x line are the following three: ### Changes -- Added "Nudge" messages. Short static copy to include with every interactive message sent, perhaps to remind the agent on how to work ๐Ÿ“Œ -- Addressed various resource consumption issues to reduce battery cost ๐Ÿ“‰ -- Implemented fuzzy file search in quick actions for instant navigation ๐Ÿ” -- Added "clear" command support to clean terminal shell logs ๐Ÿงน -- Simplified search highlighting by integrating into markdown pipeline โœจ -- Enhanced update checker to filter prerelease tags like -rc, -beta ๐Ÿš€ -- Fixed RPM package compatibility for OpenSUSE Tumbleweed ๐Ÿง (H/T @JOduMonT) -- Added libuuid1 support alongside standard libuuid dependency ๐Ÿ“ฆ -- Introduced Cmd+Shift+U shortcut for tab unread toggle โŒจ๏ธ -- Enhanced keyboard navigation for marking tabs unread ๐ŸŽฏ -- Expanded Linux distribution support with smart dependencies ๐ŸŒ -- Major underlying code re-structuring for maintainability ๐Ÿงน -- Improved stall detection to allow for individual docs to stall out while not affecting the entire playbook ๐Ÿ“– (H/T @mattjay) +- Added "Nudge" messages. Short static copy to include with every interactive message sent, perhaps to remind the agent on how to work ๐Ÿ“Œ +- Addressed various resource consumption issues to reduce battery cost ๐Ÿ“‰ +- Implemented fuzzy file search in quick actions for instant navigation ๐Ÿ” +- Added "clear" command support to clean terminal shell logs ๐Ÿงน +- Simplified search highlighting by integrating into markdown pipeline โœจ +- Enhanced update checker to filter prerelease tags like -rc, -beta ๐Ÿš€ +- Fixed RPM package compatibility for OpenSUSE Tumbleweed ๐Ÿง (H/T @JOduMonT) +- Added libuuid1 support alongside standard libuuid dependency ๐Ÿ“ฆ +- Introduced Cmd+Shift+U shortcut for tab unread toggle โŒจ๏ธ +- Enhanced keyboard navigation for marking tabs unread ๐ŸŽฏ +- Expanded Linux distribution support with smart dependencies ๐ŸŒ +- Major underlying code re-structuring for maintainability ๐Ÿงน +- Improved stall detection to allow for individual docs to stall out while not affecting the entire playbook ๐Ÿ“– (H/T @mattjay) - Added option to select a static listening port for remote control ๐ŸŽฎ (H/T @b3nw) ### Previous Releases in this Series @@ -284,35 +290,40 @@ The big changes in the v0.12.x line are the following three: **Latest: v0.7.4** | Released December 12, 2025 -Minor bugfixes on top of v0.7.3: - -# Onboarding, Wizard, and Tours -- Implemented comprehensive onboarding wizard with integrated tour system ๐Ÿš€ -- Added project-understanding confidence display to wizard UI ๐ŸŽจ -- Enhanced keyboard navigation across all wizard screens โŒจ๏ธ -- Added analytics tracking for wizard and tour completion ๐Ÿ“ˆ -- Added First Run Celebration modal with confetti animation ๐ŸŽ‰ - -# UI / UX Enhancements -- Added expand-to-fullscreen button for Auto Run interface ๐Ÿ–ฅ๏ธ -- Created dedicated modal component and improved modal priority constants for expanded Auto Run view ๐Ÿ“ -- Enhanced user experience with fullscreen editing capabilities โœจ -- Fixed tab name display to correctly show full name for active tabs ๐Ÿท๏ธ -- Added performance optimizations with throttling and caching for scrolling โšก -- Implemented drag-and-drop reordering for execution queue items ๐ŸŽฏ -- Enhanced toast context with agent name for OS notifications ๐Ÿ“ข - -# Auto Run Workflow Improvements -- Created phase document generation for Auto Run workflow ๐Ÿ“„ -- Added real-time log streaming to the LogViewer component ๐Ÿ“Š - -# Application Behavior / Core Fixes -- Added validation to prevent nested worktrees inside the main repository ๐Ÿšซ -- Fixed process manager to properly emit exit events on errors ๐Ÿ”ง -- Fixed process exit handling to ensure proper cleanup ๐Ÿงน - -# Update System -- Implemented automatic update checking on application startup ๐Ÿš€ +Minor bugfixes on top of v0.7.3: + +# Onboarding, Wizard, and Tours + +- Implemented comprehensive onboarding wizard with integrated tour system ๐Ÿš€ +- Added project-understanding confidence display to wizard UI ๐ŸŽจ +- Enhanced keyboard navigation across all wizard screens โŒจ๏ธ +- Added analytics tracking for wizard and tour completion ๐Ÿ“ˆ +- Added First Run Celebration modal with confetti animation ๐ŸŽ‰ + +# UI / UX Enhancements + +- Added expand-to-fullscreen button for Auto Run interface ๐Ÿ–ฅ๏ธ +- Created dedicated modal component and improved modal priority constants for expanded Auto Run view ๐Ÿ“ +- Enhanced user experience with fullscreen editing capabilities โœจ +- Fixed tab name display to correctly show full name for active tabs ๐Ÿท๏ธ +- Added performance optimizations with throttling and caching for scrolling โšก +- Implemented drag-and-drop reordering for execution queue items ๐ŸŽฏ +- Enhanced toast context with agent name for OS notifications ๐Ÿ“ข + +# Auto Run Workflow Improvements + +- Created phase document generation for Auto Run workflow ๐Ÿ“„ +- Added real-time log streaming to the LogViewer component ๐Ÿ“Š + +# Application Behavior / Core Fixes + +- Added validation to prevent nested worktrees inside the main repository ๐Ÿšซ +- Fixed process manager to properly emit exit events on errors ๐Ÿ”ง +- Fixed process exit handling to ensure proper cleanup ๐Ÿงน + +# Update System + +- Implemented automatic update checking on application startup ๐Ÿš€ - Added settings toggle for enabling/disabling startup update checks โš™๏ธ ### Previous Releases in this Series @@ -328,38 +339,40 @@ Minor bugfixes on top of v0.7.3: **Latest: v0.6.1** | Released December 4, 2025 -In this release... -- Added recursive subfolder support for Auto Run markdown files ๐Ÿ—‚๏ธ -- Enhanced document tree display with expandable folder navigation ๐ŸŒณ -- Enabled creating documents in subfolders with path selection ๐Ÿ“ -- Improved batch runner UI with inline progress bars and loop indicators ๐Ÿ“Š -- Fixed execution queue display bug for immediate command processing ๐Ÿ› -- Added folder icons and better visual hierarchy for document browser ๐ŸŽจ -- Implemented dynamic task re-counting for batch run loop iterations ๐Ÿ”„ -- Enhanced create document modal with location selector dropdown ๐Ÿ“ -- Improved progress tracking with per-document completion visualization ๐Ÿ“ˆ -- Added support for nested folder structures in document management ๐Ÿ—๏ธ - -Plus the pre-release ALPHA... -- Template vars now set context in default autorun prompt ๐Ÿš€ -- Added Enter key support for queued message confirmation dialog โŒจ๏ธ -- Kill process capability added to System Process Monitor ๐Ÿ’€ -- Toggle markdown rendering added to Cmd+K Quick Actions ๐Ÿ“ -- Fixed cloudflared detection in packaged app environments ๐Ÿ”ง -- Added debugging logs for process exit diagnostics ๐Ÿ› -- Tab switcher shows last activity timestamps and filters by project ๐Ÿ• -- Slash commands now fill text on Tab/Enter instead of executing โšก -- Added GitHub Actions workflow for auto-assigning issues/PRs ๐Ÿค– -- Graceful handling for playbooks with missing documents implemented โœจ -- Added multi-document batch processing for Auto Run ๐Ÿš€ -- Introduced Git worktree support for parallel execution ๐ŸŒณ -- Created playbook system for saving run configurations ๐Ÿ“š -- Implemented document reset-on-completion with loop mode ๐Ÿ”„ -- Added drag-and-drop document reordering interface ๐ŸŽฏ -- Built Auto Run folder selector with file management ๐Ÿ“ -- Enhanced progress tracking with per-document metrics ๐Ÿ“Š -- Integrated PR creation after worktree completion ๐Ÿ”€ -- Added undo/redo support in document editor โ†ฉ๏ธ +In this release... + +- Added recursive subfolder support for Auto Run markdown files ๐Ÿ—‚๏ธ +- Enhanced document tree display with expandable folder navigation ๐ŸŒณ +- Enabled creating documents in subfolders with path selection ๐Ÿ“ +- Improved batch runner UI with inline progress bars and loop indicators ๐Ÿ“Š +- Fixed execution queue display bug for immediate command processing ๐Ÿ› +- Added folder icons and better visual hierarchy for document browser ๐ŸŽจ +- Implemented dynamic task re-counting for batch run loop iterations ๐Ÿ”„ +- Enhanced create document modal with location selector dropdown ๐Ÿ“ +- Improved progress tracking with per-document completion visualization ๐Ÿ“ˆ +- Added support for nested folder structures in document management ๐Ÿ—๏ธ + +Plus the pre-release ALPHA... + +- Template vars now set context in default autorun prompt ๐Ÿš€ +- Added Enter key support for queued message confirmation dialog โŒจ๏ธ +- Kill process capability added to System Process Monitor ๐Ÿ’€ +- Toggle markdown rendering added to Cmd+K Quick Actions ๐Ÿ“ +- Fixed cloudflared detection in packaged app environments ๐Ÿ”ง +- Added debugging logs for process exit diagnostics ๐Ÿ› +- Tab switcher shows last activity timestamps and filters by project ๐Ÿ• +- Slash commands now fill text on Tab/Enter instead of executing โšก +- Added GitHub Actions workflow for auto-assigning issues/PRs ๐Ÿค– +- Graceful handling for playbooks with missing documents implemented โœจ +- Added multi-document batch processing for Auto Run ๐Ÿš€ +- Introduced Git worktree support for parallel execution ๐ŸŒณ +- Created playbook system for saving run configurations ๐Ÿ“š +- Implemented document reset-on-completion with loop mode ๐Ÿ”„ +- Added drag-and-drop document reordering interface ๐ŸŽฏ +- Built Auto Run folder selector with file management ๐Ÿ“ +- Enhanced progress tracking with per-document metrics ๐Ÿ“Š +- Integrated PR creation after worktree completion ๐Ÿ”€ +- Added undo/redo support in document editor โ†ฉ๏ธ - Implemented auto-save with 5-second debounce ๐Ÿ’พ ### Previous Releases in this Series @@ -374,15 +387,15 @@ Plus the pre-release ALPHA... ### Changes -- Added "Made with Maestro" badge to README header ๐ŸŽฏ -- Redesigned app icon with darker purple color scheme ๐ŸŽจ -- Created new SVG badge for project attribution ๐Ÿท๏ธ -- Added side-by-side image diff viewer for git changes ๐Ÿ–ผ๏ธ -- Enhanced confetti animation with realistic cannon-style bursts ๐ŸŽŠ -- Fixed z-index layering for standing ovation overlay ๐Ÿ“Š -- Improved tab switcher to show all named sessions ๐Ÿ” -- Enhanced batch synopsis prompts for cleaner summaries ๐Ÿ“ -- Added binary file detection in git diff parser ๐Ÿ”ง +- Added "Made with Maestro" badge to README header ๐ŸŽฏ +- Redesigned app icon with darker purple color scheme ๐ŸŽจ +- Created new SVG badge for project attribution ๐Ÿท๏ธ +- Added side-by-side image diff viewer for git changes ๐Ÿ–ผ๏ธ +- Enhanced confetti animation with realistic cannon-style bursts ๐ŸŽŠ +- Fixed z-index layering for standing ovation overlay ๐Ÿ“Š +- Improved tab switcher to show all named sessions ๐Ÿ” +- Enhanced batch synopsis prompts for cleaner summaries ๐Ÿ“ +- Added binary file detection in git diff parser ๐Ÿ”ง - Implemented git file reading at specific refs ๐Ÿ“ ### Previous Releases in this Series @@ -397,24 +410,24 @@ Plus the pre-release ALPHA... ### Changes -- Added Tab Switcher modal for quick navigation between AI tabs ๐Ÿš€ -- Implemented @ mention file completion for AI mode references ๐Ÿ“ -- Added navigation history with back/forward through sessions and tabs โฎ๏ธ -- Introduced tab completion filters for branches, tags, and files ๐ŸŒณ -- Added unread tab indicators and filtering for better organization ๐Ÿ“ฌ -- Implemented token counting display with human-readable formatting ๐Ÿ”ข -- Added markdown rendering toggle for AI responses in terminal ๐Ÿ“ -- Removed built-in slash commands in favor of custom AI commands ๐ŸŽฏ -- Added context menu for sessions with rename, bookmark, move options ๐Ÿ–ฑ๏ธ -- Enhanced file preview with stats showing size, tokens, timestamps ๐Ÿ“Š -- Added token counting with js-tiktoken for file preview stats bar ๐Ÿ”ข -- Implemented Tab Switcher modal for fuzzy-search navigation (Opt+Cmd+T) ๐Ÿ” -- Added Save to History toggle (Cmd+S) for automatic work synopsis tracking ๐Ÿ’พ -- Enhanced tab completion with @ mentions for file references in AI prompts ๐Ÿ“Ž -- Implemented navigation history with back/forward shortcuts (Cmd+Shift+,/.) ๐Ÿ”™ -- Added git branches and tags to intelligent tab completion system ๐ŸŒฟ -- Enhanced markdown rendering with syntax highlighting and toggle view ๐Ÿ“ -- Added right-click context menus for session management and organization ๐Ÿ–ฑ๏ธ +- Added Tab Switcher modal for quick navigation between AI tabs ๐Ÿš€ +- Implemented @ mention file completion for AI mode references ๐Ÿ“ +- Added navigation history with back/forward through sessions and tabs โฎ๏ธ +- Introduced tab completion filters for branches, tags, and files ๐ŸŒณ +- Added unread tab indicators and filtering for better organization ๐Ÿ“ฌ +- Implemented token counting display with human-readable formatting ๐Ÿ”ข +- Added markdown rendering toggle for AI responses in terminal ๐Ÿ“ +- Removed built-in slash commands in favor of custom AI commands ๐ŸŽฏ +- Added context menu for sessions with rename, bookmark, move options ๐Ÿ–ฑ๏ธ +- Enhanced file preview with stats showing size, tokens, timestamps ๐Ÿ“Š +- Added token counting with js-tiktoken for file preview stats bar ๐Ÿ”ข +- Implemented Tab Switcher modal for fuzzy-search navigation (Opt+Cmd+T) ๐Ÿ” +- Added Save to History toggle (Cmd+S) for automatic work synopsis tracking ๐Ÿ’พ +- Enhanced tab completion with @ mentions for file references in AI prompts ๐Ÿ“Ž +- Implemented navigation history with back/forward shortcuts (Cmd+Shift+,/.) ๐Ÿ”™ +- Added git branches and tags to intelligent tab completion system ๐ŸŒฟ +- Enhanced markdown rendering with syntax highlighting and toggle view ๐Ÿ“ +- Added right-click context menus for session management and organization ๐Ÿ–ฑ๏ธ - Improved mobile app with better WebSocket reconnection and status badges ๐Ÿ“ฑ ### Previous Releases in this Series @@ -429,15 +442,15 @@ Plus the pre-release ALPHA... ### Changes -- Fixed tab handling requiring explicitly selected Claude session ๐Ÿ”ง -- Added auto-scroll navigation for slash command list selection โšก -- Implemented TTS audio feedback for toast notifications speak ๐Ÿ”Š -- Fixed shortcut case sensitivity using lowercase key matching ๐Ÿ”ค -- Added Cmd+Shift+J shortcut to jump to bottom instantly โฌ‡๏ธ -- Sorted shortcuts alphabetically in help modal for discovery ๐Ÿ“‘ -- Display full commit message body in git log view ๐Ÿ“ -- Added expand/collapse all buttons to process tree header ๐ŸŒณ -- Support synopsis process type in process tree parsing ๐Ÿ” +- Fixed tab handling requiring explicitly selected Claude session ๐Ÿ”ง +- Added auto-scroll navigation for slash command list selection โšก +- Implemented TTS audio feedback for toast notifications speak ๐Ÿ”Š +- Fixed shortcut case sensitivity using lowercase key matching ๐Ÿ”ค +- Added Cmd+Shift+J shortcut to jump to bottom instantly โฌ‡๏ธ +- Sorted shortcuts alphabetically in help modal for discovery ๐Ÿ“‘ +- Display full commit message body in git log view ๐Ÿ“ +- Added expand/collapse all buttons to process tree header ๐ŸŒณ +- Support synopsis process type in process tree parsing ๐Ÿ” - Renamed "No Group" to "UNGROUPED" for better clarity โœจ ### Previous Releases in this Series @@ -450,15 +463,15 @@ Plus the pre-release ALPHA... **Latest: v0.2.3** | Released November 29, 2025 -โ€ข Enhanced mobile web interface with session sync and history panel ๐Ÿ“ฑ -โ€ข Added ThinkingStatusPill showing real-time token counts and elapsed time โฑ๏ธ -โ€ข Implemented task count badges and session deduplication for batch runner ๐Ÿ“Š -โ€ข Added TTS stop control and improved voice synthesis compatibility ๐Ÿ”Š -โ€ข Created image lightbox with navigation, clipboard, and delete features ๐Ÿ–ผ๏ธ -โ€ข Fixed UI bugs in search, auto-scroll, and sidebar interactions ๐Ÿ› -โ€ข Added global Claude stats with streaming updates across projects ๐Ÿ“ˆ -โ€ข Improved markdown checkbox styling and collapsed palette hover UX โœจ -โ€ข Enhanced scratchpad with search, image paste, and attachment support ๐Ÿ” +โ€ข Enhanced mobile web interface with session sync and history panel ๐Ÿ“ฑ +โ€ข Added ThinkingStatusPill showing real-time token counts and elapsed time โฑ๏ธ +โ€ข Implemented task count badges and session deduplication for batch runner ๐Ÿ“Š +โ€ข Added TTS stop control and improved voice synthesis compatibility ๐Ÿ”Š +โ€ข Created image lightbox with navigation, clipboard, and delete features ๐Ÿ–ผ๏ธ +โ€ข Fixed UI bugs in search, auto-scroll, and sidebar interactions ๐Ÿ› +โ€ข Added global Claude stats with streaming updates across projects ๐Ÿ“ˆ +โ€ข Improved markdown checkbox styling and collapsed palette hover UX โœจ +โ€ข Enhanced scratchpad with search, image paste, and attachment support ๐Ÿ” โ€ข Added splash screen with logo and progress bar during startup ๐ŸŽจ ### Previous Releases in this Series @@ -473,15 +486,15 @@ Plus the pre-release ALPHA... **Latest: v0.1.6** | Released November 27, 2025 -โ€ข Added template variables for dynamic AI command customization ๐ŸŽฏ -โ€ข Implemented session bookmarking with star icons and dedicated section โญ -โ€ข Enhanced Git Log Viewer with smarter date formatting ๐Ÿ“… -โ€ข Improved GitHub release workflow to handle partial failures gracefully ๐Ÿ”ง -โ€ข Added collapsible template documentation in AI Commands panel ๐Ÿ“š -โ€ข Updated default commit command with session ID traceability ๐Ÿ” -โ€ข Added tag indicators for custom-named sessions visually ๐Ÿท๏ธ -โ€ข Improved Git Log search UX with better focus handling ๐ŸŽจ -โ€ข Fixed input placeholder spacing for better readability ๐Ÿ“ +โ€ข Added template variables for dynamic AI command customization ๐ŸŽฏ +โ€ข Implemented session bookmarking with star icons and dedicated section โญ +โ€ข Enhanced Git Log Viewer with smarter date formatting ๐Ÿ“… +โ€ข Improved GitHub release workflow to handle partial failures gracefully ๐Ÿ”ง +โ€ข Added collapsible template documentation in AI Commands panel ๐Ÿ“š +โ€ข Updated default commit command with session ID traceability ๐Ÿ” +โ€ข Added tag indicators for custom-named sessions visually ๐Ÿท๏ธ +โ€ข Improved Git Log search UX with better focus handling ๐ŸŽจ +โ€ข Fixed input placeholder spacing for better readability ๐Ÿ“ โ€ข Updated documentation with new features and template references ๐Ÿ“– ### Previous Releases in this Series @@ -500,6 +513,7 @@ Plus the pre-release ALPHA... All releases are available on the [GitHub Releases page](https://github.com/RunMaestro/Maestro/releases). Maestro is available for: + - **macOS** - Apple Silicon (arm64) and Intel (x64) - **Windows** - x64 - **Linux** - x64 and arm64, AppImage, deb, and rpm packages diff --git a/src/__tests__/cli/services/agent-sessions.test.ts b/src/__tests__/cli/services/agent-sessions.test.ts index f454fea463..8a71527699 100644 --- a/src/__tests__/cli/services/agent-sessions.test.ts +++ b/src/__tests__/cli/services/agent-sessions.test.ts @@ -8,6 +8,7 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import * as fs from 'fs'; import * as os from 'os'; +import * as path from 'path'; import { listClaudeSessions } from '../../../cli/services/agent-sessions'; // Mock fs @@ -28,7 +29,7 @@ describe('listClaudeSessions', () => { const projectPath = '/path/to/project'; // encodeClaudeProjectPath: replace all non-alphanumeric with - const encodedPath = '-path-to-project'; - const sessionsDir = `/home/testuser/.claude/projects/${encodedPath}`; + const sessionsDir = path.join('/home/testuser', '.claude', 'projects', encodedPath); const makeJsonlContent = ( opts: { diff --git a/src/__tests__/cli/services/storage.test.ts b/src/__tests__/cli/services/storage.test.ts index c597857dd0..6d6bf3bf5e 100644 --- a/src/__tests__/cli/services/storage.test.ts +++ b/src/__tests__/cli/services/storage.test.ts @@ -13,6 +13,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import * as fs from 'fs'; import * as os from 'os'; +import * as path from 'path'; import type { Group, SessionInfo, HistoryEntry } from '../../../shared/types'; // Store original env values @@ -98,7 +99,9 @@ describe('storage service', () => { const result = getConfigDirectory(); - expect(result).toBe('/Users/testuser/Library/Application Support/Maestro'); + expect(result).toBe( + path.join('/Users/testuser', 'Library', 'Application Support', 'Maestro') + ); }); it('should return Windows config path with APPDATA', () => { @@ -130,7 +133,7 @@ describe('storage service', () => { const result = getConfigDirectory(); - expect(result).toBe('/home/testuser/.custom-config/Maestro'); + expect(result).toBe(path.join('/home/testuser/.custom-config', 'Maestro')); }); it('should return Linux config path fallback without XDG_CONFIG_HOME', () => { @@ -140,7 +143,7 @@ describe('storage service', () => { const result = getConfigDirectory(); - expect(result).toBe('/home/testuser/.config/Maestro'); + expect(result).toBe(path.join('/home/testuser', '.config', 'Maestro')); }); it('should use Linux path for unknown platforms', () => { @@ -150,7 +153,7 @@ describe('storage service', () => { const result = getConfigDirectory(); - expect(result).toBe('/home/testuser/.config/Maestro'); + expect(result).toBe(path.join('/home/testuser', '.config', 'Maestro')); }); }); @@ -829,7 +832,9 @@ describe('storage service', () => { const writeCall = vi.mocked(fs.writeFileSync).mock.calls[0]; expect(writeCall[0]).toContain('maestro-history.json'); - expect(writeCall[0]).toContain('/Users/testuser/Library/Application Support/Maestro'); + expect(writeCall[0]).toContain( + path.join('/Users/testuser', 'Library', 'Application Support', 'Maestro') + ); }); }); @@ -919,11 +924,11 @@ describe('storage service', () => { beforeEach(() => { // Enable migrated mode by making existsSync return true for migration marker vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('history-migrated.json')) { return true; } - if (pathStr.includes('/history/')) { + if (pathStr.includes(`${path.sep}history${path.sep}`)) { // Check specific session file existence return pathStr.includes('session-123.json') || pathStr.includes('session-456.json'); } @@ -971,15 +976,15 @@ describe('storage service', () => { ] as unknown as fs.Dirent[]); vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); - if (pathStr.includes('history-migrated.json') || pathStr.includes('/history')) { + const pathStr = path.normalize(String(filepath)); + if (pathStr.includes('history-migrated.json') || pathStr.includes(`${path.sep}history`)) { return true; } return false; }); vi.mocked(fs.readFileSync).mockImplementation((filepath) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('session-123.json')) { return JSON.stringify({ version: 1, @@ -1016,15 +1021,15 @@ describe('storage service', () => { ] as unknown as fs.Dirent[]); vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); - if (pathStr.includes('history-migrated.json') || pathStr.includes('/history')) { + const pathStr = path.normalize(String(filepath)); + if (pathStr.includes('history-migrated.json') || pathStr.includes(`${path.sep}history`)) { return true; } return false; }); vi.mocked(fs.readFileSync).mockImplementation((filepath) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('session-123.json')) { return JSON.stringify({ version: 1, @@ -1066,14 +1071,14 @@ describe('storage service', () => { it('should write entry to session-specific file', () => { vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('history-migrated.json')) { return true; } - if (pathStr.includes('/history/session-123.json')) { + if (pathStr.includes(`${path.sep}history${path.sep}session-123.json`)) { return true; } - return pathStr.includes('/history'); + return pathStr.includes(`${path.sep}history`); }); const existingData = { @@ -1093,7 +1098,7 @@ describe('storage service', () => { expect(fs.writeFileSync).toHaveBeenCalled(); const writeCall = vi.mocked(fs.writeFileSync).mock.calls[0]; - expect(writeCall[0]).toContain('/history/session-123.json'); + expect(writeCall[0]).toContain(path.join('history', 'session-123.json')); const writtenData = JSON.parse(writeCall[1] as string); expect(writtenData.entries).toHaveLength(2); expect(writtenData.entries[0].id).toBe('new-entry'); // New entry at beginning @@ -1101,7 +1106,7 @@ describe('storage service', () => { it('should create history directory if it does not exist', () => { vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('history-migrated.json')) { return true; } @@ -1115,18 +1120,18 @@ describe('storage service', () => { }); addHistoryEntry(newEntry); - expect(fs.mkdirSync).toHaveBeenCalledWith(expect.stringContaining('/history'), { + expect(fs.mkdirSync).toHaveBeenCalledWith(expect.stringContaining(`${path.sep}history`), { recursive: true, }); }); it('should skip entries without sessionId', () => { vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('history-migrated.json')) { return true; } - return pathStr.includes('/history'); + return pathStr.includes(`${path.sep}history`); }); const newEntry = mockHistoryEntry({ @@ -1143,11 +1148,11 @@ describe('storage service', () => { it('should enforce max entries limit (5000)', () => { vi.mocked(fs.existsSync).mockImplementation((filepath: fs.PathLike) => { - const pathStr = String(filepath); + const pathStr = path.normalize(String(filepath)); if (pathStr.includes('history-migrated.json')) { return true; } - return pathStr.includes('/history'); + return pathStr.includes(`${path.sep}history`); }); // Create 5000 existing entries diff --git a/src/__tests__/main/agents/session-storage.test.ts b/src/__tests__/main/agents/session-storage.test.ts index 28ff3f88b3..2276ba1ba3 100644 --- a/src/__tests__/main/agents/session-storage.test.ts +++ b/src/__tests__/main/agents/session-storage.test.ts @@ -19,9 +19,11 @@ import type { ToolType } from '../../../shared/types'; vi.mock('os', async () => { const actual = await vi.importActual('os'); + const actualOs = 'default' in actual && actual.default ? actual.default : actual; const mocked = { - ...actual, + ...actualOs, homedir: vi.fn(() => '/tmp/maestro-session-storage-home'), + tmpdir: vi.fn(() => '/tmp'), }; return { ...mocked, @@ -731,38 +733,42 @@ describe('CodexSessionStorage SSH Remote Support', () => { expect(Array.isArray(withoutSsh)).toBe(true); }); - it('should verify SshRemoteConfig interface is properly accepted', async () => { - const { CodexSessionStorage } = await import('../../../main/storage/codex-session-storage'); - const storage = new CodexSessionStorage(); - - // Full SshRemoteConfig object - const fullConfig = { - id: 'full-config-test', - name: 'Full Config Test', - host: 'remote.example.com', - port: 2222, - username: 'admin', - useSshConfig: true, - enabled: true, - }; - - // Should work with full config - const sessions = await storage.listSessions('/project', fullConfig); - expect(Array.isArray(sessions)).toBe(true); - - // Should work with minimal config - const minimalConfig = { - id: 'minimal', - name: 'Minimal', - host: 'host', - port: 22, - username: 'user', - useSshConfig: false, - enabled: true, - }; - const sessionsMinimal = await storage.listSessions('/project', minimalConfig); - expect(Array.isArray(sessionsMinimal)).toBe(true); - }); + it( + 'should verify SshRemoteConfig interface is properly accepted', + { timeout: 20000 }, + async () => { + const { CodexSessionStorage } = await import('../../../main/storage/codex-session-storage'); + const storage = new CodexSessionStorage(); + + // Full SshRemoteConfig object + const fullConfig = { + id: 'full-config-test', + name: 'Full Config Test', + host: 'remote.example.com', + port: 2222, + username: 'admin', + useSshConfig: true, + enabled: true, + }; + + // Should work with full config + const sessions = await storage.listSessions('/project', fullConfig); + expect(Array.isArray(sessions)).toBe(true); + + // Should work with minimal config + const minimalConfig = { + id: 'minimal', + name: 'Minimal', + host: 'host', + port: 22, + username: 'user', + useSshConfig: false, + enabled: true, + }; + const sessionsMinimal = await storage.listSessions('/project', minimalConfig); + expect(Array.isArray(sessionsMinimal)).toBe(true); + } + ); }); describe('Remote sessions directory path', () => { @@ -1043,42 +1049,46 @@ describe('OpenCodeSessionStorage SSH Remote Support', () => { expect(localPath).not.toContain('~'); // Verify local path is absolute - expect(localPath?.startsWith('/') || localPath?.match(/^[A-Z]:\\/)).toBeTruthy(); - }); - - it('should verify SshRemoteConfig interface is properly accepted', async () => { - const { OpenCodeSessionStorage } = - await import('../../../main/storage/opencode-session-storage'); - const storage = new OpenCodeSessionStorage(); - - // Full SshRemoteConfig object - const fullConfig = { - id: 'full-config-test', - name: 'Full Config Test', - host: 'remote.example.com', - port: 2222, - username: 'admin', - useSshConfig: true, - enabled: true, - }; - - // Should work with full config - const path = storage.getSessionPath('/project', 'session-id', fullConfig); - expect(path).toBe('~/.local/share/opencode/storage/message/session-id'); - - // Should work with minimal config - const minimalConfig = { - id: 'minimal', - name: 'Minimal', - host: 'host', - port: 22, - username: 'user', - useSshConfig: false, - enabled: true, - }; - const pathMinimal = storage.getSessionPath('/project', 'session-id', minimalConfig); - expect(pathMinimal).toBe('~/.local/share/opencode/storage/message/session-id'); - }); + expect(path.isAbsolute(localPath!)).toBeTruthy(); + }); + + it( + 'should verify SshRemoteConfig interface is properly accepted', + { timeout: 20000 }, + async () => { + const { OpenCodeSessionStorage } = + await import('../../../main/storage/opencode-session-storage'); + const storage = new OpenCodeSessionStorage(); + + // Full SshRemoteConfig object + const fullConfig = { + id: 'full-config-test', + name: 'Full Config Test', + host: 'remote.example.com', + port: 2222, + username: 'admin', + useSshConfig: true, + enabled: true, + }; + + // Should work with full config + const path = storage.getSessionPath('/project', 'session-id', fullConfig); + expect(path).toBe('~/.local/share/opencode/storage/message/session-id'); + + // Should work with minimal config + const minimalConfig = { + id: 'minimal', + name: 'Minimal', + host: 'host', + port: 22, + username: 'user', + useSshConfig: false, + enabled: true, + }; + const pathMinimal = storage.getSessionPath('/project', 'session-id', minimalConfig); + expect(pathMinimal).toBe('~/.local/share/opencode/storage/message/session-id'); + } + ); }); }); @@ -1410,42 +1420,46 @@ describe('FactoryDroidSessionStorage SSH Remote Support', () => { expect(localPath).not.toContain('~'); // Verify local path is absolute - expect(localPath?.startsWith('/') || localPath?.match(/^[A-Z]:\\/)).toBeTruthy(); - }); - - it('should verify SshRemoteConfig interface is properly accepted', async () => { - const { FactoryDroidSessionStorage } = - await import('../../../main/storage/factory-droid-session-storage'); - const storage = new FactoryDroidSessionStorage(); - - // Full SshRemoteConfig object - const fullConfig = { - id: 'full-config-test', - name: 'Full Config Test', - host: 'remote.example.com', - port: 2222, - username: 'admin', - useSshConfig: true, - enabled: true, - }; - - // Should work with full config - const path = storage.getSessionPath('/project', 'session-id', fullConfig); - expect(path).toContain('~/.factory/sessions/'); - - // Should work with minimal config - const minimalConfig = { - id: 'minimal', - name: 'Minimal', - host: 'host', - port: 22, - username: 'user', - useSshConfig: false, - enabled: true, - }; - const pathMinimal = storage.getSessionPath('/project', 'session-id', minimalConfig); - expect(pathMinimal).toContain('~/.factory/sessions/'); - }); + expect(path.isAbsolute(localPath!)).toBeTruthy(); + }); + + it( + 'should verify SshRemoteConfig interface is properly accepted', + { timeout: 20000 }, + async () => { + const { FactoryDroidSessionStorage } = + await import('../../../main/storage/factory-droid-session-storage'); + const storage = new FactoryDroidSessionStorage(); + + // Full SshRemoteConfig object + const fullConfig = { + id: 'full-config-test', + name: 'Full Config Test', + host: 'remote.example.com', + port: 2222, + username: 'admin', + useSshConfig: true, + enabled: true, + }; + + // Should work with full config + const path = storage.getSessionPath('/project', 'session-id', fullConfig); + expect(path).toContain('~/.factory/sessions/'); + + // Should work with minimal config + const minimalConfig = { + id: 'minimal', + name: 'Minimal', + host: 'host', + port: 22, + username: 'user', + useSshConfig: false, + enabled: true, + }; + const pathMinimal = storage.getSessionPath('/project', 'session-id', minimalConfig); + expect(pathMinimal).toContain('~/.factory/sessions/'); + } + ); }); describe('Remote sessions directory path', () => { @@ -1757,7 +1771,7 @@ describe('SSH Config Integration Flow Verification', () => { // Without SSH config - local path const localPath = storage.getSessionPath(projectPath, sessionId); expect(localPath).not.toContain('~'); // Local paths are absolute - expect(localPath?.startsWith('/') || localPath?.match(/^[A-Z]:\\/)).toBeTruthy(); + expect(path.isAbsolute(localPath!)).toBeTruthy(); }); it('should correctly differentiate local and remote paths for Factory Droid', async () => { @@ -1777,7 +1791,7 @@ describe('SSH Config Integration Flow Verification', () => { // Without SSH config - local path const localPath = storage.getSessionPath(projectPath, sessionId); expect(localPath).not.toContain('~'); // Local paths are absolute - expect(localPath?.startsWith('/') || localPath?.match(/^[A-Z]:\\/)).toBeTruthy(); + expect(path.isAbsolute(localPath!)).toBeTruthy(); expect(localPath).toContain('.factory'); }); }); @@ -1907,8 +1921,8 @@ describe('SSH Config Integration Flow Verification', () => { expect(factoryPath).not.toContain('~'); // Should be absolute paths - expect(openCodePath?.startsWith('/') || openCodePath?.match(/^[A-Z]:\\/)).toBeTruthy(); - expect(factoryPath?.startsWith('/') || factoryPath?.match(/^[A-Z]:\\/)).toBeTruthy(); + expect(path.isAbsolute(openCodePath!)).toBeTruthy(); + expect(path.isAbsolute(factoryPath!)).toBeTruthy(); }); it('should handle all pagination options correctly without sshConfig', async () => { @@ -2031,37 +2045,41 @@ describe('SSH Config Integration Flow Verification', () => { } }); - it('should support all search modes with SSH config across all agents', async () => { - const { OpenCodeSessionStorage } = - await import('../../../main/storage/opencode-session-storage'); - const { CodexSessionStorage } = await import('../../../main/storage/codex-session-storage'); - const { FactoryDroidSessionStorage } = - await import('../../../main/storage/factory-droid-session-storage'); - - const openCode = new OpenCodeSessionStorage(); - const codex = new CodexSessionStorage(); - const factoryDroid = new FactoryDroidSessionStorage(); - - const searchModes: Array<'title' | 'user' | 'assistant' | 'all'> = [ - 'title', - 'user', - 'assistant', - 'all', - ]; - - for (const mode of searchModes) { - // All should accept the mode with SSH config without throwing - await expect( - openCode.searchSessions('/test', 'query', mode, integrationSshConfig) - ).resolves.toBeDefined(); - await expect( - codex.searchSessions('/test', 'query', mode, integrationSshConfig) - ).resolves.toBeDefined(); - await expect( - factoryDroid.searchSessions('/test', 'query', mode, integrationSshConfig) - ).resolves.toBeDefined(); + it( + 'should support all search modes with SSH config across all agents', + { timeout: 20000 }, + async () => { + const { OpenCodeSessionStorage } = + await import('../../../main/storage/opencode-session-storage'); + const { CodexSessionStorage } = await import('../../../main/storage/codex-session-storage'); + const { FactoryDroidSessionStorage } = + await import('../../../main/storage/factory-droid-session-storage'); + + const openCode = new OpenCodeSessionStorage(); + const codex = new CodexSessionStorage(); + const factoryDroid = new FactoryDroidSessionStorage(); + + const searchModes: Array<'title' | 'user' | 'assistant' | 'all'> = [ + 'title', + 'user', + 'assistant', + 'all', + ]; + + for (const mode of searchModes) { + // All should accept the mode with SSH config without throwing + await expect( + openCode.searchSessions('/test', 'query', mode, integrationSshConfig) + ).resolves.toBeDefined(); + await expect( + codex.searchSessions('/test', 'query', mode, integrationSshConfig) + ).resolves.toBeDefined(); + await expect( + factoryDroid.searchSessions('/test', 'query', mode, integrationSshConfig) + ).resolves.toBeDefined(); + } } - }); + ); }); describe('Remote Path Format Verification', () => { diff --git a/src/__tests__/main/ipc/handlers/attachments.test.ts b/src/__tests__/main/ipc/handlers/attachments.test.ts index 61a6288f79..7e9843cbd3 100644 --- a/src/__tests__/main/ipc/handlers/attachments.test.ts +++ b/src/__tests__/main/ipc/handlers/attachments.test.ts @@ -1,3 +1,4 @@ +import path from 'path'; import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { ipcMain } from 'electron'; @@ -15,7 +16,7 @@ vi.mock('electron', () => ({ // Mock app const mockApp = { - getPath: vi.fn().mockReturnValue('/mock/userData'), + getPath: vi.fn().mockReturnValue(path.resolve('/mock/userData')), }; // Mock fs/promises module diff --git a/src/__tests__/main/ipc/handlers/marketplace.test.ts b/src/__tests__/main/ipc/handlers/marketplace.test.ts index 7adce890bc..4f154c47f2 100644 --- a/src/__tests__/main/ipc/handlers/marketplace.test.ts +++ b/src/__tests__/main/ipc/handlers/marketplace.test.ts @@ -914,11 +914,11 @@ describe('marketplace IPC handlers', () => { // Verify documents were READ FROM LOCAL FILESYSTEM (not fetched from GitHub) // The fs.readFile mock should have been called for the document paths expect(fs.readFile).toHaveBeenCalledWith( - path.normalize('/Users/test/custom-playbooks/my-playbook/phase-1.md'), + path.resolve('/Users/test/custom-playbooks/my-playbook', 'phase-1.md'), 'utf-8' ); expect(fs.readFile).toHaveBeenCalledWith( - path.normalize('/Users/test/custom-playbooks/my-playbook/phase-2.md'), + path.resolve('/Users/test/custom-playbooks/my-playbook', 'phase-2.md'), 'utf-8' ); diff --git a/src/__tests__/main/ipc/handlers/system.test.ts b/src/__tests__/main/ipc/handlers/system.test.ts index b2415d5927..ad8babc8ee 100644 --- a/src/__tests__/main/ipc/handlers/system.test.ts +++ b/src/__tests__/main/ipc/handlers/system.test.ts @@ -13,6 +13,7 @@ */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import * as path from 'path'; import { ipcMain, dialog, shell, BrowserWindow, App } from 'electron'; import Store from 'electron-store'; import { @@ -593,7 +594,7 @@ describe('system IPC handlers', () => { const handler = handlers.get('shell:showItemInFolder'); await handler!({} as any, '/path/to/file.txt'); - expect(shell.showItemInFolder).toHaveBeenCalledWith('/path/to/file.txt'); + expect(shell.showItemInFolder).toHaveBeenCalledWith(path.resolve('/path/to/file.txt')); }); it('should throw error for empty path', async () => { @@ -621,7 +622,7 @@ describe('system IPC handlers', () => { const handler = handlers.get('shell:trashItem'); await handler!({} as any, '/path/to/file.txt'); - expect(shell.trashItem).toHaveBeenCalledWith('/path/to/file.txt'); + expect(shell.trashItem).toHaveBeenCalledWith(path.resolve('/path/to/file.txt')); }); it('should throw error for empty path', async () => { @@ -663,7 +664,7 @@ describe('system IPC handlers', () => { const handler = handlers.get('shell:openPath'); await handler!({} as any, '/path/to/file.txt'); - expect(shell.openPath).toHaveBeenCalledWith('/path/to/file.txt'); + expect(shell.openPath).toHaveBeenCalledWith(path.resolve('/path/to/file.txt')); }); it('should throw error for empty path', async () => { diff --git a/src/__tests__/main/stats/paths.test.ts b/src/__tests__/main/stats/paths.test.ts index 6e94cfc6d1..823a9e6a3f 100644 --- a/src/__tests__/main/stats/paths.test.ts +++ b/src/__tests__/main/stats/paths.test.ts @@ -347,7 +347,7 @@ describe('Cross-platform database path resolution (macOS, Windows, Linux)', () = const { StatsDB } = await import('../../../main/stats'); const db = new StatsDB(); - expect(path.dirname(db.getDbPath())).toBe(testUserData); + expect(path.dirname(db.getDbPath())).toBe(path.normalize(testUserData)); }); }); @@ -362,7 +362,9 @@ describe('Cross-platform database path resolution (macOS, Windows, Linux)', () = const db = new StatsDB(); db.initialize(); - expect(mockFsMkdirSync).toHaveBeenCalledWith(macOsUserData, { recursive: true }); + expect(mockFsMkdirSync).toHaveBeenCalledWith(path.normalize(macOsUserData), { + recursive: true, + }); }); it('should create directory on Windows if it does not exist', async () => { @@ -388,7 +390,9 @@ describe('Cross-platform database path resolution (macOS, Windows, Linux)', () = const db = new StatsDB(); db.initialize(); - expect(mockFsMkdirSync).toHaveBeenCalledWith(linuxUserData, { recursive: true }); + expect(mockFsMkdirSync).toHaveBeenCalledWith(path.normalize(linuxUserData), { + recursive: true, + }); }); it('should use recursive option for deeply nested paths', async () => { @@ -401,7 +405,7 @@ describe('Cross-platform database path resolution (macOS, Windows, Linux)', () = const db = new StatsDB(); db.initialize(); - expect(mockFsMkdirSync).toHaveBeenCalledWith(deepPath, { recursive: true }); + expect(mockFsMkdirSync).toHaveBeenCalledWith(path.normalize(deepPath), { recursive: true }); }); }); @@ -548,7 +552,9 @@ describe('Cross-platform database path resolution (macOS, Windows, Linux)', () = const db = new StatsDB(); db.initialize(); - expect(mockFsMkdirSync).toHaveBeenCalledWith(platformPath, { recursive: true }); + expect(mockFsMkdirSync).toHaveBeenCalledWith(path.normalize(platformPath), { + recursive: true, + }); } }); }); diff --git a/src/__tests__/main/utils/cliDetection.test.ts b/src/__tests__/main/utils/cliDetection.test.ts index b519167afd..4ef496d241 100644 --- a/src/__tests__/main/utils/cliDetection.test.ts +++ b/src/__tests__/main/utils/cliDetection.test.ts @@ -414,8 +414,8 @@ describe('cliDetection.ts', () => { // Should still have the additional paths if (process.platform === 'win32') { - expect(path).toContain('C:\\Program Files\\dotnet'); - expect(path).toContain('C:\\WINDOWS\\System32\\OpenSSH'); + expect(path.toLowerCase()).toContain('c:\\program files\\dotnet'); + expect(path.toLowerCase()).toContain('c:\\windows\\system32\\openssh'); } else { expect(path).toContain('/opt/homebrew/bin'); expect(path).toContain('/usr/local/bin'); diff --git a/src/__tests__/shared/pathUtils.test.ts b/src/__tests__/shared/pathUtils.test.ts index 39c2c9acf7..9c96e87eb7 100644 --- a/src/__tests__/shared/pathUtils.test.ts +++ b/src/__tests__/shared/pathUtils.test.ts @@ -13,6 +13,7 @@ */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import * as path from 'path'; import * as os from 'os'; import { expandTilde, @@ -262,7 +263,7 @@ describe('buildExpandedPath', () => { process.env.PATH = '/opt/homebrew/bin:/usr/bin'; const result = buildExpandedPath(); - const pathParts = result.split(':'); + const pathParts = result.split(path.delimiter); const homebrewCount = pathParts.filter((p) => p === '/opt/homebrew/bin').length; expect(homebrewCount).toBe(1); }); diff --git a/src/main/app-lifecycle/window-manager.ts b/src/main/app-lifecycle/window-manager.ts index 9adcc893cd..1c9ed44278 100644 --- a/src/main/app-lifecycle/window-manager.ts +++ b/src/main/app-lifecycle/window-manager.ts @@ -261,12 +261,6 @@ export function createWindowManager(deps: WindowManagerDependencies): WindowMana logger.info('Window became responsive again', 'Window'); }); - // Handle page crashes (less severe than render-process-gone) - mainWindow.webContents.on('crashed', (_event, killed) => { - logger.error('WebContents crashed', 'Window', { killed }); - reportCrashToSentry('WebContents crashed', killed ? 'warning' : 'error', { killed }); - }); - // Handle page load failures (network issues, invalid URLs, etc.) mainWindow.webContents.on( 'did-fail-load', diff --git a/src/main/process-listeners/__tests__/wakatime-listener.test.ts b/src/main/process-listeners/__tests__/wakatime-listener.test.ts index a705ae9277..5441a97219 100644 --- a/src/main/process-listeners/__tests__/wakatime-listener.test.ts +++ b/src/main/process-listeners/__tests__/wakatime-listener.test.ts @@ -8,6 +8,7 @@ */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import * as path from 'path'; import { setupWakaTimeListener } from '../wakatime-listener'; import type { ProcessManager } from '../../process-manager'; import type { WakaTimeManager } from '../../wakatime-manager'; @@ -494,7 +495,7 @@ describe('WakaTime Listener', () => { } as QueryCompleteData); expect(mockWakaTimeManager.sendFileHeartbeats).toHaveBeenCalledWith( - [{ filePath: '/home/user/project/src/utils.ts', timestamp: 1000 }], + [{ filePath: path.resolve('/home/user/project', 'src/utils.ts'), timestamp: 1000 }], 'project', '/home/user/project', 'user' @@ -873,7 +874,7 @@ describe('WakaTime Listener', () => { vi.advanceTimersByTime(500); expect(mockWakaTimeManager.sendFileHeartbeats).toHaveBeenCalledWith( - [{ filePath: '/home/user/project/src/utils.ts', timestamp: 1000 }], + [{ filePath: path.resolve('/home/user/project', 'src/utils.ts'), timestamp: 1000 }], 'project', '/home/user/project', undefined @@ -900,7 +901,7 @@ describe('WakaTime Listener', () => { vi.advanceTimersByTime(500); expect(mockWakaTimeManager.sendFileHeartbeats).toHaveBeenCalledWith( - [{ filePath: '/home/user/fallback/src/utils.ts', timestamp: 1000 }], + [{ filePath: path.resolve('/home/user/fallback', 'src/utils.ts'), timestamp: 1000 }], 'fallback', '/home/user/fallback', undefined