diff --git a/CHANGELOG.md b/CHANGELOG.md index 784301a..061cc43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Invalid format values now automatically fall back to PNG instead of returning an error - Empty strings, null values, and unrecognized format values are converted to PNG - This provides a better user experience by gracefully handling invalid inputs +- Enhanced error messages for ambiguous application identifiers + - When multiple applications match an identifier (e.g., "C" matches Calendar, Console, and Cursor), the error message now lists all matching applications with their bundle IDs + - This helps users quickly identify the correct application name to use + - Applies to both `image` and `list` tools ## [1.0.0-beta.18] - 2025-06-08 diff --git a/src/tools/image.ts b/src/tools/image.ts index 82f0f2d..8b0be60 100644 --- a/src/tools/image.ts +++ b/src/tools/image.ts @@ -63,11 +63,17 @@ export async function imageToolHandler( { error: swiftResponse.error }, "Swift CLI returned error for image capture", ); + const errorMessage = swiftResponse.error?.message || "Unknown error"; + const errorDetails = swiftResponse.error?.details; + const fullErrorMessage = errorDetails + ? `${errorMessage}\n${errorDetails}` + : errorMessage; + return { content: [ { type: "text", - text: `Image capture failed: ${swiftResponse.error?.message || "Unknown error"}`, + text: `Image capture failed: ${fullErrorMessage}`, }, ], isError: true, diff --git a/src/tools/list.ts b/src/tools/list.ts index 33fd4d9..94de8cc 100644 --- a/src/tools/list.ts +++ b/src/tools/list.ts @@ -119,11 +119,17 @@ export async function listToolHandler( if (!swiftResponse.success) { logger.error({ error: swiftResponse.error }, "Swift CLI returned error"); + const errorMessage = swiftResponse.error?.message || "Unknown error"; + const errorDetails = swiftResponse.error?.details; + const fullErrorMessage = errorDetails + ? `${errorMessage}\n${errorDetails}` + : errorMessage; + return { content: [ { type: "text" as const, - text: `List operation failed: ${swiftResponse.error?.message || "Unknown error"}`, + text: `List operation failed: ${fullErrorMessage}`, }, ], isError: true, diff --git a/tests/unit/tools/image.test.ts b/tests/unit/tools/image.test.ts index 694dde7..39c4581 100644 --- a/tests/unit/tools/image.test.ts +++ b/tests/unit/tools/image.test.ts @@ -1142,4 +1142,62 @@ describe("Image Tool", () => { ); }); }); + + describe("imageToolHandler - Error message handling", () => { + it("should include error details for ambiguous app identifier", async () => { + // Mock resolveImagePath + mockResolveImagePath.mockResolvedValue({ + effectivePath: MOCK_TEMP_IMAGE_DIR, + tempDirUsed: MOCK_TEMP_IMAGE_DIR, + }); + + // Mock Swift CLI returning ambiguous app error with details + mockExecuteSwiftCli.mockResolvedValue({ + success: false, + error: { + message: "Multiple applications match identifier 'C'. Please be more specific.", + code: "AMBIGUOUS_APP_IDENTIFIER", + details: "Matches found: Calendar (com.apple.iCal), Console (com.apple.Console), Cursor (com.todesktop.230313mzl4w4u92)" + } + }); + + const result = await imageToolHandler( + { app_target: "C" }, + mockContext, + ); + + expect(result.isError).toBe(true); + expect(result.content[0].type).toBe("text"); + // Should include both the main message and the details + expect(result.content[0].text).toContain("Multiple applications match identifier 'C'"); + expect(result.content[0].text).toContain("Matches found: Calendar (com.apple.iCal), Console (com.apple.Console), Cursor (com.todesktop.230313mzl4w4u92)"); + }); + + it("should handle errors without details gracefully", async () => { + // Mock resolveImagePath + mockResolveImagePath.mockResolvedValue({ + effectivePath: MOCK_TEMP_IMAGE_DIR, + tempDirUsed: MOCK_TEMP_IMAGE_DIR, + }); + + // Mock Swift CLI returning error without details + mockExecuteSwiftCli.mockResolvedValue({ + success: false, + error: { + message: "Application not found", + code: "APP_NOT_FOUND" + } + }); + + const result = await imageToolHandler( + { app_target: "NonExistent" }, + mockContext, + ); + + expect(result.isError).toBe(true); + expect(result.content[0].type).toBe("text"); + // Should only include the main message + expect(result.content[0].text).toBe("Image capture failed: Application not found"); + }); + }); }); \ No newline at end of file diff --git a/tests/unit/tools/list.test.ts b/tests/unit/tools/list.test.ts index fbba125..bf143f9 100644 --- a/tests/unit/tools/list.test.ts +++ b/tests/unit/tools/list.test.ts @@ -288,7 +288,7 @@ describe("List Tool", () => { // Assert expect(result.isError).toBe(true); expect(result.content[0].text).toBe( - "List operation failed: The specified application ('Ciursor') is not running or could not be found." + "List operation failed: The specified application ('Ciursor') is not running or could not be found.\nError: Application with name 'Ciursor' not found." ); }); @@ -865,4 +865,32 @@ describe("List Tool", () => { } }); }); + + describe("listToolHandler - Error message handling", () => { + it("should include error details for ambiguous app identifier", async () => { + // Mock Swift CLI returning ambiguous app error with details + mockExecuteSwiftCli.mockResolvedValue({ + success: false, + error: { + message: "Multiple applications match identifier 'C'. Please be more specific.", + code: "AMBIGUOUS_APP_IDENTIFIER", + details: "Matches found: Calendar (com.apple.iCal), Console (com.apple.Console), Cursor (com.todesktop.230313mzl4w4u92)" + } + }); + + const result = await listToolHandler( + { + item_type: "application_windows", + app: "C" + }, + mockContext, + ); + + expect(result.isError).toBe(true); + expect(result.content[0].type).toBe("text"); + // Should include both the main message and the details + expect(result.content[0].text).toContain("Multiple applications match identifier 'C'"); + expect(result.content[0].text).toContain("Matches found: Calendar (com.apple.iCal), Console (com.apple.Console), Cursor (com.todesktop.230313mzl4w4u92)"); + }); + }); });