feat: Enhanced error messages for ambiguous app identifiers

- Error messages now include the list of matching applications when multiple apps match an identifier
- Shows bundle IDs alongside app names to help users disambiguate (e.g., Calendar (com.apple.iCal))
- Applies to both image and list tools for consistent user experience
- Added comprehensive tests for error detail handling

This makes it much easier for users to understand which specific application to target when there are multiple matches.

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Peter Steinberger 2025-06-08 06:16:15 +01:00
parent dbb68e4294
commit 2676decf51
5 changed files with 105 additions and 3 deletions

View file

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

View file

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

View file

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

View file

@ -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");
});
});
});

View file

@ -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)");
});
});
});