Commit graph

249 commits

Author SHA1 Message Date
Peter Steinberger
1095f488ea Fix macOS version requirement documentation
- Update to macOS 15.0+ (Sequoia) to match Package.swift
- Fix incorrect version in CHANGELOG.md
- Update README badges and requirements section

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:39:18 +01:00
Peter Steinberger
0bec93e364 Apply SwiftFormat changes for v1.0.0 release
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:35:27 +01:00
Peter Steinberger
1a2a817822 Prepare v1.0.0 stable release
- Update version to 1.0.0
- Add comprehensive changelog for stable release
- Mark project as production-ready

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:34:56 +01:00
Peter Steinberger
41fafd6d9f Fix ESLint violations for release
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:30:32 +01:00
Peter Steinberger
797fb3c7cf Add changelog entry for v1.0.0-beta.25
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:30:06 +01:00
Peter Steinberger
d72c9eefa9 Fix MCP server error handling for unexpected exceptions
- Ensure all errors return proper MCP response format
- Prevent 'No result received' when tool execution fails
- Handle special characters and edge cases gracefully

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 20:29:23 +01:00
Peter Steinberger
80e5ea338a Fixes a local only test 2025-06-08 20:26:05 +01:00
Peter Steinberger
b2c5c9ac96 ultimate error capture 2025-06-08 20:25:36 +01:00
Peter Steinberger
cc73b22c6f permission checks 2025-06-08 20:25:29 +01:00
Peter Steinberger
7567e81e3f Fix command preprocessing 2025-06-08 20:25:07 +01:00
Peter Steinberger
1084050f18 Improve error handling 2025-06-08 20:24:46 +01:00
Peter Steinberger
17e73f12f2 Revert to AsyncParsableCommand with parse-as-library
- Remove problematic AsyncAdapter that was causing continuation leaks
- Use AsyncParsableCommand directly with @main attribute
- Add -parse-as-library flag to Package.swift to enable @main
- This fixes the Swift continuation leak issue

Note: Integration tests still timeout in CI environment, likely due to
screen capture permissions or environment differences. The CLI works
correctly when run directly.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 12:09:43 +01:00
Peter Steinberger
3f4a7c864b Fix list tool server_status handling and improve robustness
- Remove empty string from item_type enum to prevent undefined values
- Add defensive programming to buildSwiftCliArgs to filter out undefined/null values
- Improve item type determination logic with explicit string checks
- Add debug logging for Swift CLI arguments
- Fix double --json-output flag issue

This fixes the "Unknown operation: undefined" error when calling list with server_status.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:50:00 +01:00
Peter Steinberger
e4b0c545e4 Add changelog entry for v1.0.0-beta.24
Document Swift 6 migration and async/sync adapter implementation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:39:01 +01:00
Peter Steinberger
3e8f787dbb v1.0.0-beta.24 2025-06-08 11:38:08 +01:00
Peter Steinberger
b0e33bcf0e 1.0.0-beta.24 2025-06-08 11:37:45 +01:00
Peter Steinberger
559349f198 Fix Swift 6 async execution with synchronous adapter
- Add AsyncAdapter.swift to bridge async/sync execution
- Change AsyncParsableCommand back to ParsableCommand
- Implement AsyncRunnable protocol for async execution
- Use DispatchSemaphore pattern for synchronous blocking
- Make ErrorBox thread-safe with @unchecked Sendable

This fixes the CLI execution issue where commands were showing help
instead of executing, by properly bridging the async/sync worlds
as required by ArgumentParser.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:34:53 +01:00
Peter Steinberger
8b46d11015 Update ApplicationFinderTests to use modern Swift Testing patterns
- Replace #expect(throws:) with more expressive error validation pattern
- Use #expect { } throws: { } for better error type checking
- Improve error handling in nonExistentAppThrowsError test

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:28:37 +01:00
Peter Steinberger
84df333f2b v1.0.0-beta.23 2025-06-08 11:24:44 +01:00
Peter Steinberger
07cf11be4e 1.0.0-beta.23 2025-06-08 11:24:12 +01:00
Peter Steinberger
92d3bdb1f7 Fix WindowManagerTests to use return instead of XCTSkip
- Replace XCTSkip with simple return for non-running apps
- This avoids dependency on XCTest framework in Swift Testing

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:24:01 +01:00
Peter Steinberger
612f69f459 Improve test assertions for better clarity
- Remove redundant bundle ID checks in ApplicationFinderTests
- Replace do-catch with #expect(throws:) for cleaner error testing
- Simplify permission test assertions to avoid false failures
- Remove unnecessary boolean comparisons in permission checks

These changes make the tests more maintainable and less prone to
environment-specific failures.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:23:43 +01:00
Peter Steinberger
c04b8e7af0 Migrate to Swift 6 with strict concurrency
- Update to swift-tools-version 6.0 and enable StrictConcurrency
- Make all data models and types Sendable for concurrency safety
- Migrate commands from ParsableCommand to AsyncParsableCommand
- Remove AsyncUtils.swift and synchronous bridging patterns
- Update WindowBounds property names to snake_case for consistency
- Ensure all error types conform to Sendable protocol
- Add comprehensive Swift 6 migration documentation

This migration enables full Swift 6 concurrency checking and data race
safety while maintaining backward compatibility with the existing API.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 11:23:10 +01:00
Peter Steinberger
50984f8dc2 Fix async concurrency issues without semaphores
- Replace problematic DispatchSemaphore usage with NSCondition-based async bridge
- Revert to ParsableCommand for compatibility while maintaining async operations
- Use CGWindowListCopyWindowInfo for sync permission checking instead of async ScreenCaptureKit
- Remove all RunLoop workarounds in favor of proper Task.runBlocking pattern
- Eliminate all deadlock sources while preserving async capture functionality
2025-06-08 10:10:04 +01:00
Peter Steinberger
d2fb50b289 Fix deadlock in PermissionsChecker by replacing semaphore with RunLoop
- Replace DispatchSemaphore usage in checkScreenRecordingPermission with RunLoop pattern
- This was the root cause of CLI hangs affecting all commands that check permissions
- Use same async-to-sync bridging pattern as ImageCommand for consistency
2025-06-08 09:53:11 +01:00
Peter Steinberger
fafa8fdc2a Convert test to use proper async/await instead of semaphore
- Replace DispatchSemaphore usage in ScreenshotValidationTests with async/await
- Make test functions async and use Task.sleep instead of RunLoop/Thread.sleep
- Use proper Swift Testing async patterns for better compatibility
2025-06-08 09:47:58 +01:00
Peter Steinberger
fbc478f75e Add changelog entry for 1.0.0-beta.22 release 2025-06-08 09:42:48 +01:00
Peter Steinberger
932642b30f Update package-lock.json to match version 1.0.0-beta.22 2025-06-08 09:42:27 +01:00
Peter Steinberger
96f268abbd Bump version to 1.0.0-beta.22 for release 2025-06-08 09:42:13 +01:00
Peter Steinberger
40acc9669b Fix deadlock in ImageCommand by replacing semaphore with RunLoop
- Remove DispatchSemaphore usage that violated Swift concurrency rules
- Implement RunLoop-based async-to-sync bridging in runAsyncCapture()
- Convert all capture methods to async/await patterns
- Replace Thread.sleep with Task.sleep in async contexts
- Keep ParsableCommand for compatibility, avoid AsyncParsableCommand issues
- Add comprehensive tests and documentation
- Improve error handling and browser helper filtering

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 09:41:50 +01:00
Peter Steinberger
3a837c7159 test: Add comprehensive tests for browser helper filtering
Documents the expected behavior and ensures browser helper filtering works correctly:
- Tests browser-specific error messages when main browser isn't running
- Verifies successful capture when main browser is found (not helpers)
- Documents the problem this fixes (no more confusing 'no capturable windows' errors)
- Ensures non-browser applications continue to work normally

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:48:29 +01:00
Peter Steinberger
5bdb2092ca feat: Add browser helper filtering for improved Chrome/Safari matching
Addresses the issue where searching for 'Chrome' or 'Safari' would incorrectly
match helper processes (like 'Google Chrome Helper (Renderer)') instead of the
main browser application, leading to confusing 'no capturable windows' errors.

Key improvements:
- Added filterBrowserHelpers() method that filters out helper processes for browser searches
- Supports common browsers: chrome, safari, firefox, edge, brave, arc, opera
- Filters out processes containing: helper, renderer, utility, plugin, service, crashpad, gpu, background
- Provides browser-specific error messages when main browser isn't running
- Only applies filtering to browser identifiers, preserves normal matching for other apps
- Comprehensive test coverage for browser filtering scenarios

Example: Searching for 'chrome' now finds 'Google Chrome' instead of
'Google Chrome Helper (Renderer)' which has no capturable windows.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:47:09 +01:00
Peter Steinberger
d5b40c1550 feat: Implement proper frontmost window capture
Adds support for capturing the frontmost window of the frontmost application
instead of falling back to screen capture mode.

Changes:
- Added 'frontmost' case to CaptureMode enum in Swift CLI
- Implemented captureFrontmostWindow() method using NSWorkspace.shared.frontmostApplication
- Updated TypeScript to use --mode frontmost instead of defaulting to screen mode
- Added comprehensive test coverage for frontmost functionality
- Updated existing tests to reflect new behavior

The frontmost mode now:
1. Detects the currently active application
2. Captures only its frontmost window (index 0)
3. Returns a single image file with proper metadata

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:42:43 +01:00
Peter Steinberger
34dac65d2a fix: Handle empty string item_type parameter in list tool
Fixes issue where item_type: '' was not properly defaulting to the correct operation.
Empty strings and whitespace-only strings now fall back to the proper default logic:
- If app is provided: defaults to 'application_windows'
- If no app: defaults to 'running_applications'

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:33:44 +01:00
Peter Steinberger
c958f91bf0 chore: Update package-lock.json for version 1.0.0-beta.21 2025-06-08 08:23:43 +01:00
Peter Steinberger
fcd424b8cc chore: Prepare release v1.0.0-beta.21 2025-06-08 08:23:27 +01:00
Peter Steinberger
4a2d802977 fix: Handle empty provider_config gracefully and improve case-insensitive targets
- Fix "Cannot convert undefined or null to object" error when provider_config is empty
- Make frontmost target case-insensitive (frontmost, FRONTMOST, Frontmost)
- Make window specifiers case-insensitive (WINDOW_TITLE, window_title, Window_Title)
- Add comprehensive test coverage for empty/null provider_config scenarios
- Improve error handling to prevent spread operator failures on undefined _meta

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:20:30 +01:00
Peter Steinberger
17dea6ad79 fix: Prevent security vulnerability from malformed app targets
Addresses critical edge case where malformed app targets with multiple leading colons
(e.g., "::::::::::::::::Finder") created empty app names that would match ALL system
processes. This could potentially expose sensitive information or cause unintended
system-wide captures.

Key improvements:
- Enhanced app target parsing to validate non-empty app names
- Added fallback logic to extract valid app names from malformed inputs
- Default to screen mode when all parts are empty (security-first approach)
- Comprehensive test coverage for edge cases
- Improved backward compatibility with hidden path parameters

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:16:39 +01:00
Peter Steinberger
dd680eb638 feat: Improve window title matching and error messages for URLs with ports
When users search for windows with URLs containing ports (e.g., 'http://example.com:8080'),
the system now provides much better debugging information when the window isn't found.

Key improvements:
- Enhanced window not found errors now list all available window titles
- Added specific guidance for URL-based searches (try without protocol)
- New CaptureError.windowTitleNotFound with detailed debugging info
- Comprehensive test coverage for colon parsing in app targets
- Better error messages help users understand why matching failed

Example improved error:
"Window with title containing 'http://example.com:8080' not found in Google Chrome.
Available windows: 'example.com:8080 - Google Chrome', 'New Tab - Google Chrome'.
Note: For URLs, try without the protocol (e.g., 'example.com:8080' instead of 'http://example.com:8080')."

This addresses the common issue where browsers display simplified URLs in window titles
without the protocol, making it easier for users to find the correct matching pattern.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:09:47 +01:00
Peter Steinberger
822ea1cce7 fix: Correct error handling for path traversal and file system errors
Previously, path traversal attempts like `../../../../../../../etc/passwd` were incorrectly
reported as screen recording permission errors instead of file system errors.

Changes:
- Modified ScreenCapture error handling to distinguish between CaptureError types and ScreenCaptureKit errors
- CaptureError.fileWriteError now bypasses screen recording permission detection
- Added path validation in OutputPathResolver to detect and log path traversal attempts
- Added logging for system-sensitive path access attempts
- Comprehensive test coverage for various path traversal patterns and error scenarios

This ensures users get accurate error messages that guide them to the actual problem
(invalid paths, missing directories, file permissions) rather than misleading
screen recording permission prompts.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:05:03 +01:00
Peter Steinberger
4afd15279c feat: Capture all windows from multiple exact app matches instead of erroring
When multiple applications have exact matches (e.g., "claude" and "Claude"), the system now:
- Captures all windows from all matching applications instead of throwing an ambiguous match error
- Maintains sequential window indices across all matched applications
- Preserves original application names in saved file metadata
- Only returns errors for truly ambiguous fuzzy matches

This provides more useful behavior for common scenarios where users have multiple apps with
similar names (different case, etc.) and want to capture windows from all of them.

Updates:
- Added `captureWindowsFromMultipleApps` method to handle multi-app capture logic
- Modified error handling in both single window and multi-window capture modes
- Updated documentation (spec.md, CHANGELOG.md) to reflect new behavior
- Comprehensive test suite covering various multiple match scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 08:00:44 +01:00
Peter Steinberger
089d96ce22 fix: Handle edge cases for invalid screen index and JSON null paths
- Invalid screen index (e.g., screen:99) now properly falls back to capturing all screens with unique filenames
- String "null" in path parameter is now correctly treated as undefined instead of literal path
- Added fallback-aware filename generation to prevent file overwrites when screen index is out of bounds
- Comprehensive test coverage for both edge cases

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:53:21 +01:00
Peter Steinberger
000da1e2c1 Fix file extension correction when format is changed
- Automatically correct file extensions when format gets preprocessed/corrected
- When invalid format like 'bmp' is provided with path ending in .bmp,
  the path is corrected to end in .png to match the actual output format
- Add Swift CLI path initialization to invalid-format-integration.test.ts
- Add conditional skipping for non-macOS platforms
- Integration tests now pass: files are created with correct .png extensions

This fixes the issue where providing format: "bmp" with path: "test.bmp"
would create a PNG file named "test.bmp", which was confusing for users.
Now it creates "test.png" to match the actual file format.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:46:32 +01:00
Peter Steinberger
ab882069b4 fix: Add defensive validation for invalid image formats with automatic PNG fallback
Implements robust handling for invalid image formats (like 'bmp', 'gif', 'webp') that bypass schema validation:

- Added defensive format validation in image tool handler
- Automatic path correction to ensure file extensions match actual format used
- Warning messages in response when format fallback occurs
- Comprehensive unit and integration test coverage for edge cases

This ensures invalid formats automatically fall back to PNG as requested, preventing
Swift CLI rejection and incorrect file extensions in output paths.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:44:17 +01:00
Peter Steinberger
977c22d37a Fix array parameter parsing for include_window_details
- Add preprocessing to handle JSON string arrays from MCP clients
- Support multiple input formats: JSON string, comma-separated, single value
- Handle empty strings and null/undefined values gracefully
- Add comprehensive test coverage for all parsing scenarios
- Fixes "Expected array, received string" error when MCP clients send JSON string arrays

This resolves the issue shown in the test screenshot where include_window_details
was sent as '["ids", "bounds", "off_screen"]' (JSON string) instead of a proper array.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:40:14 +01:00
Peter Steinberger
d8a0e10b02 Clarify format parameter behavior for screen vs app captures
- Update README.md to clearly explain that screen captures cannot use format: "data"
- Clarify that screen captures always save to files (temp or specified path)
- Update spec.md to distinguish behavior between app window captures and screen captures
- Make it clear that empty format string defaults to PNG file format for screen captures
- Address confusion where documentation suggested format defaults to "data" when path not given

This resolves the apparent contradiction between documentation and actual behavior
shown in the test screenshot where format: "" resulted in file saving rather than
data format for a screen capture.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:37:50 +01:00
Peter Steinberger
9837e7bea8 style: Apply SwiftFormat formatting
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:29:37 +01:00
Peter Steinberger
141502d668 style: Fix linting errors
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:29:11 +01:00
Peter Steinberger
c3e03a730b chore: Bump version to 1.0.0-beta.20
🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:27:43 +01:00
Peter Steinberger
5e3d4d3c76 Update documentation for timeout handling feature
- Add timeout handling details to CHANGELOG.md under Unreleased section
- Document PEEKABOO_CLI_TIMEOUT environment variable in spec.md
- Update spec.md handler pattern to include timeout behavior
- Add SWIFT_CLI_TIMEOUT error code documentation

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-08 07:26:17 +01:00