mirror of
https://github.com/samsonjs/Peekaboo.git
synced 2026-04-27 15:07:41 +00:00
Implement missing best practices
- Add npm run inspector script for MCP inspector tool - Synchronize Swift CLI version with package.json (1.0.0-beta.9) - Update macOS version requirement to v14 (Sonoma) for n-1 support - Add Swift compiler warnings check in prepare-release script - Convert tests/setup.ts from Jest to Vitest syntax - Update server status tests to match new format 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
b9f7e2da7a
commit
7bf63a225c
8 changed files with 77 additions and 49 deletions
|
|
@ -29,6 +29,7 @@
|
||||||
"lint:swift": "cd peekaboo-cli && swiftlint",
|
"lint:swift": "cd peekaboo-cli && swiftlint",
|
||||||
"format:swift": "cd peekaboo-cli && swiftformat .",
|
"format:swift": "cd peekaboo-cli && swiftformat .",
|
||||||
"prepare-release": "node ./scripts/prepare-release.js",
|
"prepare-release": "node ./scripts/prepare-release.js",
|
||||||
|
"inspector": "npx @modelcontextprotocol/inspector node dist/index.js",
|
||||||
"postinstall": "chmod +x dist/index.js 2>/dev/null || true"
|
"postinstall": "chmod +x dist/index.js 2>/dev/null || true"
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import PackageDescription
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "peekaboo",
|
name: "peekaboo",
|
||||||
platforms: [
|
platforms: [
|
||||||
.macOS(.v12)
|
.macOS(.v14)
|
||||||
],
|
],
|
||||||
products: [
|
products: [
|
||||||
.executable(
|
.executable(
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ struct PeekabooCommand: ParsableCommand {
|
||||||
static let configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
commandName: "peekaboo",
|
commandName: "peekaboo",
|
||||||
abstract: "A macOS utility for screen capture, application listing, and window management",
|
abstract: "A macOS utility for screen capture, application listing, and window management",
|
||||||
version: "1.1.1",
|
version: "1.0.0-beta.9",
|
||||||
subcommands: [ImageCommand.self, ListCommand.self],
|
subcommands: [ImageCommand.self, ListCommand.self],
|
||||||
defaultSubcommand: ImageCommand.self
|
defaultSubcommand: ImageCommand.self
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -203,6 +203,37 @@ function checkSwift() {
|
||||||
}
|
}
|
||||||
logSuccess('SwiftLint passed');
|
logSuccess('SwiftLint passed');
|
||||||
|
|
||||||
|
// Check for Swift compiler warnings/errors
|
||||||
|
log('Checking for Swift compiler warnings...', colors.cyan);
|
||||||
|
let swiftBuildOutput = '';
|
||||||
|
try {
|
||||||
|
// Capture build output to check for warnings
|
||||||
|
swiftBuildOutput = execSync('cd peekaboo-cli && swift build --arch arm64 -c release 2>&1', {
|
||||||
|
cwd: projectRoot,
|
||||||
|
encoding: 'utf8'
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
logError('Swift build failed during analyzer check');
|
||||||
|
if (error.stdout) console.log(error.stdout);
|
||||||
|
if (error.stderr) console.log(error.stderr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for warnings in the output
|
||||||
|
const warningMatches = swiftBuildOutput.match(/warning:|note:/gi);
|
||||||
|
if (warningMatches && warningMatches.length > 0) {
|
||||||
|
logWarning(`Found ${warningMatches.length} warnings/notes in Swift build`);
|
||||||
|
// Extract and show warning lines
|
||||||
|
const lines = swiftBuildOutput.split('\n');
|
||||||
|
lines.forEach(line => {
|
||||||
|
if (line.includes('warning:') || line.includes('note:')) {
|
||||||
|
console.log(` ${line.trim()}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logSuccess('No Swift compiler warnings found');
|
||||||
|
}
|
||||||
|
|
||||||
// Run Swift tests
|
// Run Swift tests
|
||||||
if (!execWithOutput('npm run test:swift', 'Swift tests')) {
|
if (!execWithOutput('npm run test:swift', 'Swift tests')) {
|
||||||
logError('Swift tests failed');
|
logError('Swift tests failed');
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,29 @@ export const listToolSchema = z
|
||||||
item_type: z
|
item_type: z
|
||||||
.enum(["running_applications", "application_windows", "server_status"])
|
.enum(["running_applications", "application_windows", "server_status"])
|
||||||
.default("running_applications")
|
.default("running_applications")
|
||||||
.describe("What to list. 'server_status' returns Peekaboo server info."),
|
.describe(
|
||||||
|
"Specifies the type of items to list. Valid options are:\n" +
|
||||||
|
"- `running_applications`: Lists all currently running applications with details like name, bundle ID, PID, active status, and window count.\n" +
|
||||||
|
"- `application_windows`: Lists open windows for a specific application. Requires the `app` parameter. Details can be customized with `include_window_details`.\n" +
|
||||||
|
"- `server_status`: Returns information about the Peekaboo MCP server itself, including its version and configured AI providers."
|
||||||
|
),
|
||||||
app: z
|
app: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
"Required if 'item_type' is 'application_windows'. Target application. Uses fuzzy matching.",
|
"Required when `item_type` is `application_windows`. " +
|
||||||
|
"Specifies the target application by its name (e.g., \"Safari\", \"TextEdit\") or bundle ID. " +
|
||||||
|
"Fuzzy matching is used, so partial names may work."
|
||||||
),
|
),
|
||||||
include_window_details: z
|
include_window_details: z
|
||||||
.array(z.enum(["off_screen", "bounds", "ids"]))
|
.array(z.enum(["off_screen", "bounds", "ids"]))
|
||||||
.optional()
|
.optional()
|
||||||
.describe(
|
.describe(
|
||||||
"Optional, for 'application_windows'. Additional window details. Example: ['bounds', 'ids']",
|
"Optional, only applicable when `item_type` is `application_windows`. " +
|
||||||
|
"Specifies additional details to include for each window. Provide an array of strings. Example: `[\"bounds\", \"ids\"]`.\n" +
|
||||||
|
"- `ids`: Include window ID.\n" +
|
||||||
|
"- `bounds`: Include window position and size (x, y, width, height).\n" +
|
||||||
|
"- `off_screen`: Indicate if the window is currently off-screen."
|
||||||
),
|
),
|
||||||
})
|
})
|
||||||
.refine(
|
.refine(
|
||||||
|
|
@ -54,6 +65,11 @@ export const listToolSchema = z
|
||||||
"'app' and 'include_window_details' not applicable for 'server_status'.",
|
"'app' and 'include_window_details' not applicable for 'server_status'.",
|
||||||
path: ["item_type"],
|
path: ["item_type"],
|
||||||
},
|
},
|
||||||
|
)
|
||||||
|
.describe(
|
||||||
|
"Lists various system items, providing situational awareness. " +
|
||||||
|
"Can retrieve running applications, windows of a specific app, or server status. " +
|
||||||
|
"App identifier uses fuzzy matching for convenience."
|
||||||
);
|
);
|
||||||
|
|
||||||
export type ListToolInput = z.infer<typeof listToolSchema>;
|
export type ListToolInput = z.infer<typeof listToolSchema>;
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ describe("Swift CLI Integration Tests", () => {
|
||||||
) {
|
) {
|
||||||
const firstContentItem = response.content[0] as PeekabooContentItem;
|
const firstContentItem = response.content[0] as PeekabooContentItem;
|
||||||
expect(firstContentItem.type).toBe("text");
|
expect(firstContentItem.type).toBe("text");
|
||||||
expect(firstContentItem.text).toContain("Peekaboo MCP Server Status");
|
expect(firstContentItem.text).toContain("Peekaboo MCP");
|
||||||
} else {
|
} else {
|
||||||
fail(
|
fail(
|
||||||
"Response content was not in the expected format for server_status",
|
"Response content was not in the expected format for server_status",
|
||||||
|
|
|
||||||
|
|
@ -1,30 +1,29 @@
|
||||||
// Jest setup file
|
// Vitest setup file
|
||||||
// Configure global test environment
|
// Configure global test environment
|
||||||
|
|
||||||
|
import { beforeEach, afterEach, vi } from 'vitest';
|
||||||
|
|
||||||
// Mock console methods to reduce noise during testing
|
// Mock console methods to reduce noise during testing
|
||||||
const originalConsole = global.console;
|
const originalConsole = globalThis.console;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
// Reset console mocks before each test
|
// Reset console mocks before each test
|
||||||
global.console = {
|
globalThis.console = {
|
||||||
...originalConsole,
|
...originalConsole,
|
||||||
log: jest.fn(),
|
log: vi.fn(),
|
||||||
error: jest.fn(),
|
error: vi.fn(),
|
||||||
warn: jest.fn(),
|
warn: vi.fn(),
|
||||||
info: jest.fn(),
|
info: vi.fn(),
|
||||||
debug: jest.fn(),
|
debug: vi.fn(),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
// Restore original console after each test
|
// Restore original console after each test
|
||||||
global.console = originalConsole;
|
globalThis.console = originalConsole;
|
||||||
jest.clearAllMocks();
|
vi.clearAllMocks();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Global test timeout
|
|
||||||
jest.setTimeout(10000);
|
|
||||||
|
|
||||||
// Mock environment variables for testing
|
// Mock environment variables for testing
|
||||||
process.env.NODE_ENV = "test";
|
process.env.NODE_ENV = "test";
|
||||||
process.env.PEEKABOO_AI_PROVIDERS = JSON.stringify([
|
process.env.PEEKABOO_AI_PROVIDERS = JSON.stringify([
|
||||||
|
|
@ -34,4 +33,4 @@ process.env.PEEKABOO_AI_PROVIDERS = JSON.stringify([
|
||||||
model: "llava",
|
model: "llava",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
@ -10,69 +10,50 @@ describe("Server Status Utility - generateServerStatusString", () => {
|
||||||
|
|
||||||
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is not set", () => {
|
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is not set", () => {
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using None Configured. Set PEEKABOO_AI_PROVIDERS ENV.`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): None Configured. Set PEEKABOO_AI_PROVIDERS ENV.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is an empty string", () => {
|
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is an empty string", () => {
|
||||||
process.env.PEEKABOO_AI_PROVIDERS = "";
|
process.env.PEEKABOO_AI_PROVIDERS = "";
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using None Configured. Set PEEKABOO_AI_PROVIDERS ENV.`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): None Configured. Set PEEKABOO_AI_PROVIDERS ENV.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is whitespace", () => {
|
it("should return status with default providers text when PEEKABOO_AI_PROVIDERS is whitespace", () => {
|
||||||
process.env.PEEKABOO_AI_PROVIDERS = " ";
|
process.env.PEEKABOO_AI_PROVIDERS = " ";
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using None Configured. Set PEEKABOO_AI_PROVIDERS ENV.`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): None Configured. Set PEEKABOO_AI_PROVIDERS ENV.",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should list a single provider from PEEKABOO_AI_PROVIDERS", () => {
|
it("should list a single provider from PEEKABOO_AI_PROVIDERS", () => {
|
||||||
process.env.PEEKABOO_AI_PROVIDERS = "ollama/llava";
|
process.env.PEEKABOO_AI_PROVIDERS = "ollama/llava";
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using ollama/llava`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): ollama/llava",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should list multiple providers from PEEKABOO_AI_PROVIDERS, trimmed and joined", () => {
|
it("should list multiple providers from PEEKABOO_AI_PROVIDERS, trimmed and joined", () => {
|
||||||
process.env.PEEKABOO_AI_PROVIDERS = "ollama/llava, openai/gpt-4o";
|
process.env.PEEKABOO_AI_PROVIDERS = "ollama/llava, openai/gpt-4o";
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using ollama/llava, openai/gpt-4o`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): ollama/llava, openai/gpt-4o",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should handle extra whitespace and empty segments in PEEKABOO_AI_PROVIDERS", () => {
|
it("should handle extra whitespace and empty segments in PEEKABOO_AI_PROVIDERS", () => {
|
||||||
process.env.PEEKABOO_AI_PROVIDERS =
|
process.env.PEEKABOO_AI_PROVIDERS =
|
||||||
" ollama/llava , ,, openai/gpt-4o ,anthropic/claude ";
|
" ollama/llava , ,, openai/gpt-4o ,anthropic/claude ";
|
||||||
const status = generateServerStatusString(testVersion);
|
const status = generateServerStatusString(testVersion);
|
||||||
expect(status).toContain(`Version: ${testVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${testVersion} using ollama/llava, openai/gpt-4o, anthropic/claude`);
|
||||||
expect(status).toContain(
|
|
||||||
"Configured AI Providers (from PEEKABOO_AI_PROVIDERS ENV): ollama/llava, openai/gpt-4o, anthropic/claude",
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should correctly include the provided version string", () => {
|
it("should correctly include the provided version string", () => {
|
||||||
const customVersion = "z.y.x";
|
const customVersion = "z.y.x";
|
||||||
const status = generateServerStatusString(customVersion);
|
const status = generateServerStatusString(customVersion);
|
||||||
expect(status).toContain(`Version: ${customVersion}`);
|
expect(status).toBe(`Peekaboo MCP ${customVersion} using None Configured. Set PEEKABOO_AI_PROVIDERS ENV.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should produce a trimmed multi-line string", () => {
|
it("should produce a trimmed string", () => {
|
||||||
const status = generateServerStatusString("0.0.1");
|
const status = generateServerStatusString("0.0.1");
|
||||||
expect(status.startsWith("---")).toBe(true);
|
|
||||||
expect(status.endsWith("---")).toBe(true);
|
|
||||||
expect(status).not.toMatch(/^\s/); // No leading whitespace
|
expect(status).not.toMatch(/^\s/); // No leading whitespace
|
||||||
expect(status).not.toMatch(/\s$/); // No trailing whitespace
|
expect(status).not.toMatch(/\s$/); // No trailing whitespace
|
||||||
|
expect(status.startsWith("Peekaboo MCP")).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
Loading…
Reference in a new issue