diff --git a/.github/workflows/publish-schemas.yml b/.github/workflows/publish-schemas.yml new file mode 100644 index 00000000..de2cbd3c --- /dev/null +++ b/.github/workflows/publish-schemas.yml @@ -0,0 +1,66 @@ +name: Publish Schemas + +permissions: + contents: read + +on: + push: + branches: [main] + paths: + - 'schemas/**' + - '.github/workflows/publish-schemas.yml' + workflow_dispatch: + +jobs: + publish: + runs-on: ubuntu-latest + env: + WEBSITE_REPO: getsentry/xcodebuildmcp.com + WEBSITE_BRANCH: main + TARGET_DIR: public/schemas/structured-output + steps: + - name: Checkout source repository + uses: actions/checkout@v4 + + - name: Fail if deploy key is missing + env: + DEPLOY_KEY: ${{ secrets.XCODEBUILDMCP_WEBSITE_DEPLOY_KEY }} + run: | + set -euo pipefail + if [ -z "$DEPLOY_KEY" ]; then + echo "XCODEBUILDMCP_WEBSITE_DEPLOY_KEY is required to publish schemas." >&2 + exit 1 + fi + + - name: Configure SSH for website repository + uses: webfactory/ssh-agent@v0.9.0 + with: + ssh-private-key: ${{ secrets.XCODEBUILDMCP_WEBSITE_DEPLOY_KEY }} + + - name: Clone website repository + run: | + set -euo pipefail + git clone "git@github.com:${WEBSITE_REPO}.git" website-repo + cd website-repo + git checkout "$WEBSITE_BRANCH" + git pull --ff-only origin "$WEBSITE_BRANCH" + + - name: Sync schema files into website public directory + run: | + set -euo pipefail + mkdir -p "website-repo/${TARGET_DIR}" + rsync -a --delete schemas/structured-output/ "website-repo/${TARGET_DIR}/" + + - name: Commit and push website update + run: | + set -euo pipefail + cd website-repo + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add "$TARGET_DIR" + if git diff --cached --quiet; then + echo "Schema publish target already up to date." + exit 0 + fi + git commit -m "Publish structured output schemas from ${GITHUB_REPOSITORY}@${GITHUB_SHA}" + git push origin "$WEBSITE_BRANCH" diff --git a/docs/SCHEMA_VERSIONING.md b/docs/SCHEMA_VERSIONING.md new file mode 100644 index 00000000..44b8ae69 --- /dev/null +++ b/docs/SCHEMA_VERSIONING.md @@ -0,0 +1,217 @@ +# Structured JSON Schema versioning and publishing + +This document defines how XcodeBuildMCP versions and publishes the JSON Schemas for +structured output fixtures and runtime payloads. + +## Goals + +- Keep schema contracts stable and predictable for external consumers. +- Make published schema URLs real, durable, and safe to reference. +- Let the website serve schemas directly from `https://xcodebuildmcp.com/schemas/...`. +- Avoid ambiguous compatibility rules. + +## Canonical schema identity + +Each schema has two stable identifiers: + +1. The payload metadata: + ```json + { + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1" + } + ``` +2. The published schema file path: + ```text + https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json + ``` + +The in-payload `schema` and `schemaVersion` values must always match the published +schema document that validates that payload. + +## Version format + +`schemaVersion` uses integer strings only: + +- `"1"` +- `"2"` +- `"3"` + +Do not use semver-style schema versions such as `"1.1"` or `"2.0"`. + +The version number is a contract version, not a release number. + +## Versioning rules + +### Published versions are immutable + +Once a schema version is published, do not make breaking changes to that file. + +Breaking changes include: + +- removing a property +- making an optional property required +- narrowing allowed values or enums +- changing object shape incompatibly +- changing field meaning in a way that could break clients + +If any of those changes are needed, publish a new version instead: + +```text +schemas/structured-output/xcodebuildmcp.output.build-result/2.schema.json +``` + +and emit: + +```json +"schemaVersion": "2" +``` + +### Old versions remain available + +Previously published schema files must continue to be hosted. + +Do not remove old schema versions from the website once consumers may rely on them. + +### Additive changes + +Additive, optional fields can be compatible, but use caution. + +If a new field is truly optional and old clients can safely ignore it, it may remain +within the same schema version. If there is any doubt about compatibility or meaning, +bump the schema version. + +Bias toward a new version when the contract meaning changes. + +## Directory layout + +Source schemas in this repository live under: + +```text +schemas/structured-output/ +``` + +Published schemas on the website live under: + +```text +public/schemas/structured-output/ +``` + +A source file such as: + +```text +schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json +``` + +is published to: + +```text +public/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json +``` + +which is then served at: + +```text +https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json +``` + +## Publishing workflow + +Schema publishing is handled from this repository by a GitHub Actions workflow. + +Trigger conditions: + +- push to `main` when files under `schemas/**` change +- manual `workflow_dispatch` + +Publishing steps: + +1. Check out this repository. +2. Clone `getsentry/xcodebuildmcp.com` over SSH. +3. Sync `schemas/structured-output/` from this repository into + `public/schemas/structured-output/` in the website repository. +4. Commit the website change if the published files changed. +5. Push to the website repository `main` branch. +6. Let Vercel deploy the website normally. + +This keeps schema authoring in the main project repository while using the website +repository as the deployment surface. + +## Required secret + +The publishing workflow requires this repository secret: + +```text +XCODEBUILDMCP_WEBSITE_DEPLOY_KEY +``` + +This secret must contain an SSH private key with write access to: + +```text +git@github.com:getsentry/xcodebuildmcp.com.git +``` + +The corresponding public key should be installed as a deploy key on the website +repository with write access. + +### Deploy key setup + +1. Generate a dedicated SSH key pair for schema publishing. + ```bash + ssh-keygen -t ed25519 -C "schema-publisher" -f ./xcodebuildmcp-website-deploy-key + ``` +2. In `getsentry/xcodebuildmcp.com`, add the public key as a deploy key with write + access. + - GitHub, Settings, Deploy keys + - Add `xcodebuildmcp-website-deploy-key.pub` + - Enable write access +3. In `getsentry/XcodeBuildMCP`, add the private key as an actions secret named: + ```text + XCODEBUILDMCP_WEBSITE_DEPLOY_KEY + ``` +4. Trigger the `Publish Schemas` workflow manually once to verify SSH access and sync. +5. Confirm that the website repository receives the commit and Vercel deploys it. +6. Confirm a final URL resolves, for example: + ```text + https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json + ``` + +Use a dedicated deploy key for this workflow only. Do not reuse a personal SSH key. + +## Consumer guidance + +Consumers should branch on both `schema` and `schemaVersion`. + +Example: + +```ts +switch (`${payload.schema}@${payload.schemaVersion}`) { + case "xcodebuildmcp.output.build-result@1": + // validate using v1 schema + break + case "xcodebuildmcp.output.build-result@2": + // validate using v2 schema + break + default: + throw new Error("Unsupported schema version") +} +``` + +These JSON Schemas describe payload shapes. They are not an OpenAPI description by +themselves. If an HTTP API is introduced later, OpenAPI should reference the schema +files as component schemas instead of trying to infer endpoints from them. + +## Maintenance checklist + +When updating schemas: + +1. Decide whether the change is compatible or breaking. +2. If breaking, add a new versioned schema file instead of changing the old one. +3. Update fixture payloads to emit the correct `schemaVersion`. +4. Run: + ```bash + npm run test:schema-fixtures + ``` +5. Merge to `main`. +6. Confirm the publish workflow updated the website repo. +7. Confirm the final schema URL resolves on the website. diff --git a/docs/dev/STRUCTURED_JSON_OUTPUT_PLAN.md b/docs/dev/STRUCTURED_JSON_OUTPUT_PLAN.md new file mode 100644 index 00000000..1afdd972 --- /dev/null +++ b/docs/dev/STRUCTURED_JSON_OUTPUT_PLAN.md @@ -0,0 +1,951 @@ +# Structured JSON Output Plan + +## Goal + +Change CLI output modes so that: + +- `--output text` stays as the current human-readable output +- `--output jsonl` becomes the current event-stream output (today exposed as `json`) +- `--output json` becomes a new structured JSON document representing the tool's primary result +- `--output raw` stays unchanged + +This plan is based on the current implementation, not the stale `docs/dev/RENDERING_PIPELINE_REFACTOR.md` redesign. + +## Requirements + +1. Rename the current `json` output mode to `jsonl` +2. Add a new structured `json` mode +3. Preserve current text output behavior without regressions +4. Preserve current event-stream behavior without regressions +5. Exclude auxiliary output from structured JSON: + - no next steps + - no frontmatter + - no event stream passthrough +6. Standardize structured JSON around a common envelope +7. Codify per-tool outputs as versioned schemas +8. Add snapshot coverage for structured JSON output +9. Prefer the cleanest, lowest-churn design over a broad pipeline rewrite + +## Current architecture + +The current flow is already good enough to support this change incrementally: + +- tools emit `PipelineEvent`s through `ToolHandlerContext.emit` +- CLI creates a `RenderSession` +- the session accumulates: + - events + - attachments + - error state +- `postProcessSession()` appends `next-steps` as another event +- CLI decides how output is rendered/printed based on `--output` + +Today: + +- `--output text` uses `cli-text` +- `--output raw` uses `text` +- `--output json` uses `cli-json`, which actually prints one JSON event per line + +So current `json` is semantically JSONL already. + +## Recommended architecture + +### Decision + +Implement structured JSON as a CLI-boundary projection over the final `RenderSession`. + +That means: + +1. run the tool normally +2. let it emit its normal `PipelineEvent`s +3. let daemon-backed tools replay into the same CLI-side session as they do today +4. let `postProcessSession()` run as it does today +5. for `--output json`, build one structured JSON object from the final session state + +### Why this is the right design + +This avoids the wrong kind of complexity. + +We should **not**: + +- rewrite tool handlers to return new result types +- refactor MCP output +- change daemon protocol +- add a separate deep rendering pipeline for structured JSON +- parse rendered text back into JSON + +We **should**: + +- keep text and jsonl on the existing event/session path +- add structured JSON only at the final CLI output boundary + +This keeps the design simple: + +- one event production path +- one session capture model +- two presentation styles from the same source: + - streamed event output (`jsonl`) + - final projected output (`json`) + +## Output model + +## Structured JSON envelope + +All structured JSON responses should use one envelope shape: + +```json +{ + "schema": "xcodebuildmcp.output.simulator-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": {} +} +``` + +### Fields + +- `schema` + - stable identifier for the payload contract + - example: `xcodebuildmcp.output.simulator-list` +- `schemaVersion` + - manual contract version + - start at `"1"` + - bump only for breaking changes +- `didError` + - whether the command failed +- `error` + - standardized human-readable failure string + - `null` on success +- `data` + - the primary structured result + - `null` when no meaningful primary data exists + +## Rules for structured JSON + +- emit exactly one JSON document +- pretty-print with 2-space indentation +- include a trailing newline +- never include next steps +- never include `_meta` +- never include raw `PipelineEvent[]` +- never include rendered human text blocks just to mirror the text mode + +## Error shape + +Structured JSON should always remain parseable. + +If the tool fails: + +```json +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": true, + "error": "App path lookup failed", + "data": null +} +``` + +If structured-output generation itself fails internally, still emit a valid envelope with: + +- `didError: true` +- `error: "Structured output generation failed: ..."` +- `data: null` + +## Schema design + +## Per-tool schema definitions + +Define structured output schemas in code, not in manifest YAML. + +Recommended module layout: + +- `src/structured-output/types.ts` +- `src/structured-output/helpers.ts` +- `src/structured-output/registry.ts` +- `src/structured-output/index.ts` +- `src/structured-output/definitions/common.ts` +- workflow-specific definition files: + - `coverage.ts` + - `debugging.ts` + - `device.ts` + - `doctor.ts` + - `macos.ts` + - `project-discovery.ts` + - `project-scaffolding.ts` + - `session-management.ts` + - `simulator.ts` + - `simulator-management.ts` + - `swift-package.ts` + - `ui-automation.ts` + - `utilities.ts` + - `workflow-discovery.ts` + - `xcode-ide.ts` + +## Definition shape + +Each schema definition should include: + +- schema id +- schema version +- associated tool ids +- a Zod schema for the `data` payload +- a builder that projects data from the final event session + +Conceptually: + +```ts +interface StructuredOutputDefinition { + schema: string; + schemaVersion: string; + toolKeys: readonly string[]; + dataSchema: z.ZodType; + build(input: StructuredOutputBuildInput, view: StructuredEventView): TData | null; +} +``` + +## Event view + +Create one indexed event view from the final session events, excluding `next-steps`. + +That view should expose grouped access to: + +- header +- status lines +- summaries +- detail trees +- tables +- sections +- file refs +- compiler errors +- compiler warnings +- test discovery +- test failures + +This avoids scattered ad hoc event scanning in every tool definition. + +## Shared schema families + +Not every tool should get its own bespoke `data` shape. + +A core goal of this design is that consumers should be able to write a small number of parsers for shared output families, not hundreds of parsers for equivalent data exposed by different tools. + +That means: + +- prefer shared schema families over per-tool schemas +- prefer shared field names across related tool results +- use tool-specific schemas only when the primary data is genuinely different +- compose from common sub-schemas rather than inventing new top-level structures casually + +Examples: + +- `xcodebuildmcp.output.app-path` + - `get_sim_app_path` + - `get_device_app_path` + - `get_mac_app_path` +- `xcodebuildmcp.output.bundle-id` + - `get_app_bundle_id` + - `get_mac_bundle_id` +- `xcodebuildmcp.output.simulator-list` + - simulator list commands with the same payload shape +- `xcodebuildmcp.output.launch-result` + - `launch_app_sim` + - `launch_app_device` + - `launch_mac_app` +- `xcodebuildmcp.output.build-result` + - `build_sim` + - `build_device` + - `build_macos` + - `swift_package_build` + +Use shared contracts where it reduces duplication cleanly. + +## Common base shapes + +The doc should explicitly bias toward reusable base shapes. + +Recommended common sub-schemas: + +- `summary` + - status + - duration + - optional test counts +- `diagnostics` + - warnings + - errors + - testFailures when relevant +- `artifacts` + - appPath + - bundleId + - processId + - simulatorId + - deviceId + - buildLogPath + - runtimeLogPath + - osLogPath +- `entries` + - ordered key/value outputs +- `items` + - normalized list outputs + +The important rule is consistency: + +- if `appPath` is part of a result, it should live in the same place for every schema family that uses it +- if `processId` is part of a result, it should live in the same place for every schema family that uses it +- if `buildLogPath` is emitted, it should not move around between sibling schemas + +### Normalization rule for durable outputs + +For durable outputs that are artifacts of the command result, prefer nesting them under `artifacts` rather than placing them at the top level opportunistically. + +That means the plan should normalize toward shapes like: + +```json +{ + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 5234 + }, + "artifacts": { + "appPath": "...", + "bundleId": "com.example.CalculatorApp", + "processId": 12345, + "simulatorId": "...", + "buildLogPath": "..." + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} +``` + +For simple lookup-only results, a reduced version of the same idea still applies: + +```json +{ + "data": { + "artifacts": { + "appPath": "..." + } + } +} +``` + +This is preferable to having one tool emit `data.appPath` and another emit `data.artifacts.appPath` unless there is a very strong reason. + +## Schema-family matrix + +The implementation should begin with an audit that groups tools into shared result families before any code is written. + +A recommended initial family matrix is: + +| Schema family | Shared `data` shape intent | Example tools | +|---|---|---| +| `app-path` | `artifacts.appPath` | `get_sim_app_path`, `get_device_app_path`, `get_mac_app_path` | +| `bundle-id` | `artifacts.bundleId` | `get_app_bundle_id`, `get_mac_bundle_id` | +| `launch-result` | `artifacts.bundleId`, `artifacts.processId`, target id | `launch_app_sim`, `launch_app_device`, `launch_mac_app` | +| `install-result` | target id + installed artifact identity where available | `install_app_sim`, `install_app_device` | +| `stop-result` | target id + stopped process/app identity where available | `stop_app_sim`, `stop_app_device`, `stop_mac_app`, `swift_package_stop` | +| `build-result` | `summary`, `artifacts`, `diagnostics` | `build_sim`, `build_device`, `build_macos`, `swift_package_build`, `clean` | +| `test-result` | `summary`, `diagnostics`, optional discoveries/artifacts | `test_sim`, `test_device`, `test_macos`, `swift_package_test` | +| `build-run-result` | `summary`, `artifacts`, `diagnostics` | `build_run_sim`, `build_run_device`, `build_run_macos`, possibly `swift_package_run` if it fits cleanly | +| `simulator-list` | `simulators[]` normalized records | `list_sims` | +| `device-list` | `devices[]` normalized records | `list_devices` | +| `scheme-list` | `schemes[]` | `list_schemes` | +| `project-list` | `projects[]` | `discover_projects` | +| `settings-entries` | ordered `entries[]` | `show_build_settings`, session/defaults-style key-value outputs where appropriate | +| `coverage-result` | summary + coverage entries | `get_coverage_report`, `get_file_coverage` | +| `ui-action-result` | action target + artifact refs if produced | `tap`, `swipe`, `touch`, `long_press`, `button`, `gesture`, `type_text`, `key_press`, `key_sequence` | +| `capture-result` | artifact paths and capture metadata | `screenshot`, `snapshot_ui`, `record_sim_video` | +| `normalized-content` | generic fallback for proxy/dynamic tools | dynamic xcode-ide style tools | + +This matrix should be refined from a real tool audit, but the principle should not change: group by result semantics, not by command name. + +## Versioning policy + +### Start point + +Every new schema starts at version `"1"`. + +### Bump `schemaVersion` for breaking changes + +Examples: + +- renaming a field +- removing a field +- changing a field type +- changing the structure of arrays/objects incompatibly +- changing semantic meaning of an existing field + +### Do not bump for non-breaking changes + +Examples: + +- internal extraction refactors +- text formatting changes +- adding truly optional fields + +## Zod validation + +Each `data` payload should be validated against a strict Zod schema before emission. + +That gives us: + +- fail-fast schema drift detection +- a clear version boundary +- confidence that fixtures match actual contracts + +## How structured JSON is derived + +## Derivation point + +Derive structured JSON in the CLI command handler after tool invocation completes. + +Practically, this means wiring it in `src/cli/register-tool-commands.ts` after `await invoker.invokeDirect(...)` returns. + +At that point the CLI already has: + +- `session.getEvents()` +- `session.getAttachments()` +- `session.isError()` + +That is the cleanest seam because it works equally for: + +- direct CLI tools +- daemon-backed CLI tools + +without changing daemon or MCP contracts. + +## Filtering + +Structured JSON builders must ignore: + +- `next-steps` +- any future auxiliary presentation-only event types + +The structured payload should represent only the tool's primary result. + +## Extraction strategy + +Prefer data extraction in this order: + +1. typed event fields directly + - detail tree items + - table rows + - file refs + - summaries + - test/diagnostic events +2. command args where the result is basically the performed action +3. tool-specific parsing of `SectionEvent.title` and `SectionEvent.lines` only when needed +4. never parse rendered text output + +This is an important constraint: structured JSON should be projected from the event model, not reverse-engineered from final text. + +## CLI changes + +## Output option changes + +Change CLI output choices from: + +- `text` +- `json` +- `raw` + +to: + +- `text` +- `json` +- `jsonl` +- `raw` + +### Semantics + +- `text`: current human-readable CLI output +- `jsonl`: current event stream output, one JSON event per line +- `json`: new structured result envelope +- `raw`: unchanged + +## Internal render strategy rename + +Rename internal render strategy naming to match the new CLI terminology: + +- `cli-json` -> `cli-jsonl` + +Behavior remains the same. + +## `--output json` behavior + +For `json` mode: + +- use a silent capture session rather than the streaming JSONL session +- do not print streamed output +- after execution, build the structured JSON envelope from the final session +- print the envelope once +- set exit code from the structured result/session error state + +## Early CLI validation failures + +Current CLI validation has early `console.error(...)` branches for things like: + +- invalid `--json` +- unknown defaults profile +- missing required arguments +- unexpected args + +These must be updated so that when `--output json` is selected they emit a structured error envelope instead of plain text. + +This is required to keep machine output clean and parseable. + +For other output modes, existing behavior should remain unchanged. + +## Tool schema guidance + +Use consistent field naming across tools. + +Preferred common field names: + +- `appPath` +- `bundleId` +- `processId` +- `simulatorId` +- `deviceId` +- `buildLogPath` +- `runtimeLogPath` +- `osLogPath` + +Collections should use plural nouns: + +- `simulators` +- `devices` +- `schemes` +- `projects` +- `tests` +- `entries` + +For open-ended map-like outputs, prefer deterministic entry arrays over unordered freeform objects where snapshot stability matters. + +Example: + +```json +{ + "entries": [ + { "key": "PRODUCT_NAME", "value": "CalculatorApp" } + ] +} +``` + +## Dynamic and proxy tools + +For dynamic xcode-ide style tools, do not block this work on designing perfect bespoke schemas for every dynamic result. + +Use a generic normalized-content schema as a fallback for proxy-style tools, based on normalized event content such as: + +- header +- detail trees +- tables +- sections +- file refs +- summary + +This should still exclude next steps and raw event passthrough. + +## Example tool responses + +The doc needs concrete examples because the envelope by itself is not the hard part. The important part is what goes in `data`. + +These are representative target shapes, not final frozen contracts. + +### Example: `get_sim_app_path` + +Schema: + +- `schema: "xcodebuildmcp.output.app-path"` +- `schemaVersion: "1"` + +```json +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "~/Library/Developer/Xcode/DerivedData/.../Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "simulatorId": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE" + } + } +} +``` + +Notes: + +- shared schema family with device/macOS app-path tools is fine +- this now follows the same `artifacts` convention as other durable outputs +- if target identity is known and useful, keep it in `artifacts`; otherwise omit it rather than inventing a new layout +### Example: `list_sims` + +Schema: + +- `schema: "xcodebuildmcp.output.simulator-list"` +- `schemaVersion: "1"` + +```json +{ + "schema": "xcodebuildmcp.output.simulator-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "simulators": [ + { + "name": "iPhone 16", + "simulatorId": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + "state": "Shutdown", + "isAvailable": true, + "runtime": "iOS 18.0" + }, + { + "name": "iPhone 16 Pro", + "simulatorId": "FFFFFFFF-1111-2222-3333-444444444444", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 18.0" + } + ] + } +} +``` + +Notes: + +- this is a good example of primary data replacing presentation-only text/grouping +- text mode can keep emojis and grouped display; structured JSON should just expose normalized data +- if the current event stream does not expose enough data directly, this is the kind of case where we may need a small tool-specific extractor against event payloads + +### Example: `launch_app_sim` + +Schema: + +- `schema: "xcodebuildmcp.output.launch-result"` +- `schemaVersion: "1"` + +```json +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "bundleId": "com.example.CalculatorApp", + "simulatorId": "AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE", + "processId": 12345 + } + } +} +``` + +Notes: + +- this is a simple action-result shape +- it now uses the same `artifacts` nesting as build-style results +- snapshot helpers can still reuse these fields; they just read them from a consistent location +### Example: `show_build_settings` + +Schema: + +- `schema: "xcodebuildmcp.output.build-settings"` +- `schemaVersion: "1"` + +```json +{ + "schema": "xcodebuildmcp.output.build-settings", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "entries": [ + { "key": "PRODUCT_NAME", "value": "CalculatorApp" }, + { "key": "PRODUCT_BUNDLE_IDENTIFIER", "value": "com.example.CalculatorApp" }, + { "key": "SDKROOT", "value": "iphonesimulator" } + ] + } +} +``` + +Notes: + +- use ordered `entries` rather than a large freeform object for better snapshot stability +- if we later decide consumers strongly prefer an object map, that would be a schema decision and versioning question, not something to drift into accidentally + +### Example: `build_sim` or `build_macos` + +Schema: + +- `schema: "xcodebuildmcp.output.build-result"` +- `schemaVersion: "1"` + +```json +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 5234 + }, + "artifacts": { + "appPath": "~/Library/Developer/Xcode/DerivedData/.../Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "buildLogPath": "~/Library/Logs/XcodeBuildMCP/build.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} +``` + +Notes: + +- for build/test-style tools, `data` should focus on durable result data, not transient stage events +- build stages belong in `jsonl`, not structured `json` +- this keeps the split between event stream and result document clear + +### Example: failed build + +```json +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 8123 + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Cannot find 'FooBar' in scope", + "location": "Sources/App/ContentView.swift:42:13" + } + ] + }, + "artifacts": { + "buildLogPath": "~/Library/Logs/XcodeBuildMCP/build.log" + } + } +} +``` + +Notes: + +- failure does not have to force `data` to `null` +- if we have durable structured failure data, we should keep it +- `error` stays the standardized top-level quick summary; `data.diagnostics` carries the details + +### Example: CLI validation failure before tool execution + +This is the case that currently goes through `console.error(...)` and needs special handling in structured mode. + +```json +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": true, + "error": "Missing required argument: simulator-id", + "data": null +} +``` + +Notes: + +- this is why `--output json` needs custom early-error handling +- machine consumers should still get one valid envelope even when the tool never started + +## Testing plan + +## Unit tests + +Add unit coverage for: + +- output mode wiring +- `json` vs `jsonl` dispatch +- structured error envelope generation +- schema registry uniqueness +- envelope building behavior +- event filtering that excludes `next-steps` +- manifest coverage so every CLI-exposed tool has a structured-output definition + +That manifest coverage test is important. It prevents future drift when new tools are added. + +## Snapshot tests + +### Principle + +Keep the existing text snapshots intact. + +Add a parallel fixture set for CLI structured JSON rather than replacing current fixtures. + +### Fixture layout + +Keep current fixtures: + +- `src/snapshot-tests/__fixtures__/cli/.../*.txt` +- `src/snapshot-tests/__fixtures__/mcp/.../*.txt` + +Add new structured fixtures: + +- `src/snapshot-tests/__fixtures__/cli-json/.../*.json` + +### Harness changes + +Extend the snapshot harness to accept an output mode: + +- default remains `text` +- add `json` +- add `jsonl` if we want explicit event-stream snapshot coverage later + +### Normalization + +Add JSON-aware normalization that: + +- parses the envelope +- recursively normalizes paths, UUID-like values, and other unstable values +- re-serializes deterministically with 2-space indentation + +Do not normalize structured JSON with regex over raw text. + +### Parser helpers + +Update snapshot parser helpers so they first check structured JSON fields, then fall back to existing text parsing. + +For example: + +- `extractAppPathFromSnapshotOutput()` should first look at `data.appPath` +- `extractProcessIdFromSnapshotOutput()` should first look at `data.processId` + +This is another reason to keep field naming consistent. + +## Docs and migration + +## Documentation updates + +Update: + +- `docs/CLI.md` +- `docs/dev/RENDERING_PIPELINE.md` +- `README.md` +- any examples or tests that currently describe `--output json` as the event stream mode + +## Changelog + +Record this as a breaking CLI contract change: + +- `--output json` now means structured JSON +- previous event-stream behavior moves to `--output jsonl` + +## Stale doc cleanup + +`docs/dev/RENDERING_PIPELINE_REFACTOR.md` is stale and should be removed or replaced after this plan is implemented, so it does not keep misleading future work. + +## Risks and tradeoffs + +## Main risk + +The main risk is not the CLI wiring. The main risk is extracting clean structured payloads for every tool from the current event model without introducing brittle special cases. + +Mitigation: + +- build the schema registry first +- add coverage tests so every tool must be mapped +- prefer shared schema families +- only add tool-specific extraction when needed +- avoid changing text output unless absolutely necessary + +## What not to do + +- do not do a broad renderer refactor +- do not change daemon protocol unless a real blocker appears +- do not change MCP response contracts for this work +- do not parse rendered text into JSON +- do not include next steps in structured JSON +- do not scatter schema logic across manifests and runtime code + +## Implementation plan + +### Phase 1: rename and wiring groundwork + +1. Rename internal render strategy `cli-json` -> `cli-jsonl` +2. Extend CLI output enum to include `jsonl` +3. Update CLI help text and output selection wiring +4. Preserve existing event-stream behavior under `jsonl` + +### Phase 2: structured-output core + +1. Add `src/structured-output/` module +2. Define envelope type and schema definition contract +3. Add event indexing helpers +4. Add registry lookup by tool id +5. Add envelope/error-building helpers + +### Phase 3: per-tool schema definitions + +1. Implement workflow/family schema definitions +2. Prefer shared contracts where sensible +3. Add fallback normalized-content schema for proxy/dynamic tools +4. Add a manifest coverage test to require complete tool coverage + +### Phase 4: CLI structured JSON integration + +1. Wire `--output json` to build a single envelope from the final session +2. Update early validation failures to emit structured error envelopes in `json` mode +3. Ensure stdout remains clean machine output in structured mode +4. Preserve exit code behavior + +### Phase 5: snapshot coverage + +1. Extend snapshot harness/contracts for output mode selection +2. Add JSON normalization support +3. Add `cli-json` fixture tree +4. Add structured JSON snapshot coverage across CLI-capable suites +5. Keep existing text fixtures unchanged + +### Phase 6: docs and migration + +1. Update CLI docs and README examples +2. Update rendering pipeline documentation +3. Add changelog entry +4. Remove or replace stale output/refactor docs + +## Suggested quality gates + +Before handoff, run the relevant non-doc checks for the implementation work: + +- `npm run typecheck` +- `npm run test` +- `npm run test:snapshot` +- any targeted smoke coverage if output-mode CLI tests exist + +For this planning-only change, no checks are required. + +## Final recommendation + +Treat structured JSON as a projection layer over the finished CLI session, not as a new renderer or a pipeline redesign. + +That gives the cleanest maintainable system: + +- one event production model +- one shared session capture model +- text output preserved +- jsonl output preserved +- structured json added with low regression risk +- versioned per-tool contracts that can evolve intentionally over time diff --git a/package-lock.json b/package-lock.json index 1a66dde5..246b4417 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,6 +32,7 @@ "@types/yargs": "^17.0.33", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", + "ajv": "^8.18.0", "eslint": "^9.23.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.5", @@ -711,6 +712,30 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/@eslint/js": { "version": "9.33.0", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.33.0.tgz", @@ -1023,28 +1048,6 @@ } } }, - "node_modules/@modelcontextprotocol/sdk/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@modelcontextprotocol/sdk/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.3.tgz", @@ -3103,16 +3106,15 @@ } }, "node_modules/ajv": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", - "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", - "dev": true, + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -3136,28 +3138,6 @@ } } }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -3985,6 +3965,23 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/eslint/node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", @@ -3998,6 +3995,13 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", @@ -4981,10 +4985,9 @@ "license": "MIT" }, "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-schema-typed": { diff --git a/package.json b/package.json index 2c517d89..a699c167 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "license:check": "npx -y license-checker --production --onlyAllow 'MIT;ISC;BSD-2-Clause;BSD-3-Clause;Apache-2.0;Unlicense;FSL-1.1-MIT'", "knip": "knip", "test": "vitest run", + "test:schema-fixtures": "vitest run --config vitest.snapshot.config.ts src/snapshot-tests/__tests__/json-fixture-schema.test.ts", "test:snapshot": "npm run build && node build/cli.js daemon stop 2>/dev/null; vitest run --config vitest.snapshot.config.ts", "test:snapshot:update": "npm run build && node build/cli.js daemon stop 2>/dev/null; UPDATE_SNAPSHOTS=1 vitest run --config vitest.snapshot.config.ts", "test:smoke": "npm run build && vitest run --config vitest.smoke.config.ts", @@ -99,6 +100,7 @@ "@types/yargs": "^17.0.33", "@vitest/coverage-v8": "^3.2.4", "@vitest/ui": "^3.2.4", + "ajv": "^8.18.0", "eslint": "^9.23.0", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.5", diff --git a/schemas/structured-output/_defs/common.schema.json b/schemas/structured-output/_defs/common.schema.json new file mode 100644 index 00000000..0e2f63e4 --- /dev/null +++ b/schemas/structured-output/_defs/common.schema.json @@ -0,0 +1,388 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json", + "type": "object", + "additionalProperties": false, + "properties": {}, + "$defs": { + "errorConsistency": { + "type": "object", + "properties": { + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + } + }, + "allOf": [ + { + "if": { + "properties": { + "didError": { + "const": false + } + }, + "required": [ + "didError" + ] + }, + "then": { + "properties": { + "error": { + "type": "null" + } + }, + "required": [ + "error" + ] + } + }, + { + "if": { + "properties": { + "didError": { + "const": true + } + }, + "required": [ + "didError" + ] + }, + "then": { + "properties": { + "error": { + "type": "string", + "minLength": 1 + } + }, + "required": [ + "error" + ] + } + } + ] + }, + "diagnosticEntry": { + "type": "object", + "additionalProperties": false, + "properties": { + "message": { + "type": "string" + }, + "location": { + "type": "string" + } + }, + "required": [ + "message" + ] + }, + "basicDiagnostics": { + "type": "object", + "additionalProperties": false, + "properties": { + "warnings": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/diagnosticEntry" + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/diagnosticEntry" + } + } + }, + "required": [ + "warnings", + "errors" + ] + }, + "testFailureEntry": { + "type": "object", + "additionalProperties": false, + "properties": { + "suite": { + "type": "string" + }, + "test": { + "type": "string" + }, + "message": { + "type": "string" + }, + "location": { + "type": "string" + } + }, + "required": [ + "suite", + "test", + "message" + ] + }, + "testDiagnostics": { + "type": "object", + "additionalProperties": false, + "properties": { + "warnings": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/diagnosticEntry" + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/diagnosticEntry" + } + }, + "testFailures": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/testFailureEntry" + } + } + }, + "required": [ + "warnings", + "errors", + "testFailures" + ] + }, + "statusSummary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + } + }, + "required": [ + "status" + ] + }, + "counts": { + "type": "object", + "additionalProperties": false, + "properties": { + "passed": { + "type": "integer", + "minimum": 0 + }, + "failed": { + "type": "integer", + "minimum": 0 + }, + "skipped": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "passed", + "failed", + "skipped" + ] + }, + "orderedEntry": { + "type": "object", + "additionalProperties": false, + "properties": { + "key": { + "type": "string" + }, + "value": { + "type": "string" + } + }, + "required": [ + "key", + "value" + ] + }, + "point": { + "type": "object", + "additionalProperties": false, + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "x", + "y" + ] + }, + "pathItem": { + "type": "object", + "additionalProperties": false, + "properties": { + "path": { + "type": "string" + } + }, + "required": [ + "path" + ] + }, + "sessionDefaultsProfile": { + "type": "object", + "additionalProperties": false, + "properties": { + "projectPath": { + "type": [ + "string", + "null" + ] + }, + "workspacePath": { + "type": [ + "string", + "null" + ] + }, + "scheme": { + "type": [ + "string", + "null" + ] + }, + "configuration": { + "type": [ + "string", + "null" + ] + }, + "simulatorName": { + "type": [ + "string", + "null" + ] + }, + "simulatorId": { + "type": [ + "string", + "null" + ] + }, + "simulatorPlatform": { + "anyOf": [ + { + "const": null + }, + { + "enum": [ + "iOS Simulator", + "watchOS Simulator", + "tvOS Simulator", + "visionOS Simulator" + ] + } + ] + }, + "deviceId": { + "type": [ + "string", + "null" + ] + }, + "useLatestOS": { + "type": [ + "boolean", + "null" + ] + }, + "arch": { + "anyOf": [ + { + "const": null + }, + { + "enum": [ + "arm64", + "x86_64" + ] + } + ] + }, + "suppressWarnings": { + "type": [ + "boolean", + "null" + ] + }, + "derivedDataPath": { + "type": [ + "string", + "null" + ] + }, + "preferXcodebuild": { + "type": [ + "boolean", + "null" + ] + }, + "platform": { + "type": [ + "string", + "null" + ] + }, + "bundleId": { + "type": [ + "string", + "null" + ] + }, + "env": { + "anyOf": [ + { + "const": null + }, + { + "type": "object", + "propertyNames": { + "type": "string", + "minLength": 1 + }, + "additionalProperties": { + "type": "string" + } + } + ] + } + }, + "required": [ + "projectPath", + "workspacePath", + "scheme", + "configuration", + "simulatorName", + "simulatorId", + "simulatorPlatform", + "deviceId", + "useLatestOS", + "arch", + "suppressWarnings", + "derivedDataPath", + "preferXcodebuild", + "platform", + "bundleId", + "env" + ] + } + } +} diff --git a/schemas/structured-output/xcodebuildmcp.output.app-path/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.app-path/1.schema.json new file mode 100644 index 00000000..6d4d41b8 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.app-path/1.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.app-path/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.app-path" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + } + }, + "required": [ + "appPath" + ] + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [], + "anyOf": [ + { + "properties": { + "artifacts": true + }, + "required": [ + "artifacts" + ] + }, + { + "properties": { + "diagnostics": true + }, + "required": [ + "diagnostics" + ] + } + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json new file mode 100644 index 00000000..0eb850a7 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json @@ -0,0 +1,115 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.build-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "durationMs": { + "type": "integer", + "minimum": 0 + }, + "target": { + "enum": [ + "simulator", + "device", + "macos", + "swift-package" + ] + } + }, + "required": [ + "status" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + }, + "bundleId": { + "type": "string" + }, + "buildLogPath": { + "type": "string" + }, + "packagePath": { + "type": "string" + }, + "workspacePath": { + "type": "string" + }, + "scheme": { + "type": "string" + }, + "configuration": { + "type": "string" + }, + "platform": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.build-run-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.build-run-result/1.schema.json new file mode 100644 index 00000000..e81397ba --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.build-run-result/1.schema.json @@ -0,0 +1,137 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-run-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.build-run-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "durationMs": { + "type": "integer", + "minimum": 0 + }, + "target": { + "enum": [ + "simulator", + "device", + "macos", + "swift-package" + ] + } + }, + "required": [ + "status" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + }, + "bundleId": { + "type": "string" + }, + "processId": { + "type": "integer", + "minimum": 1 + }, + "simulatorId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "buildLogPath": { + "type": "string" + }, + "runtimeLogPath": { + "type": "string" + }, + "osLogPath": { + "type": "string" + }, + "packagePath": { + "type": "string" + }, + "executablePath": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + }, + "output": { + "type": "object", + "additionalProperties": false, + "properties": { + "stdout": { + "type": "array", + "items": { + "type": "string" + } + }, + "stderr": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "stdout", + "stderr" + ] + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.build-settings/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.build-settings/1.schema.json new file mode 100644 index 00000000..517fd550 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.build-settings/1.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.build-settings/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.build-settings" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "workspacePath": { + "type": "string" + }, + "scheme": { + "type": "string" + } + }, + "required": [ + "workspacePath", + "scheme" + ] + }, + "entries": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/orderedEntry" + } + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "artifacts", + "entries" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.bundle-id/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.bundle-id/1.schema.json new file mode 100644 index 00000000..7598d1e6 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.bundle-id/1.schema.json @@ -0,0 +1,59 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.bundle-id/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.bundle-id" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + }, + "bundleId": { + "type": "string" + } + }, + "required": [ + "appPath" + ] + } + }, + "required": [ + "artifacts" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.capture-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.capture-result/1.schema.json new file mode 100644 index 00000000..f2f1cf6f --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.capture-result/1.schema.json @@ -0,0 +1,226 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.capture-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "$defs": { + "frame": { + "type": "object", + "additionalProperties": false, + "properties": { + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "width": { + "type": "number" + }, + "height": { + "type": "number" + } + }, + "required": [ + "x", + "y", + "width", + "height" + ] + }, + "accessibilityNode": { + "type": "object", + "additionalProperties": true, + "properties": { + "frame": { + "$ref": "#/$defs/frame" + }, + "type": { + "type": "string" + }, + "role": { + "type": "string" + }, + "children": { + "type": "array", + "items": { + "$ref": "#/$defs/accessibilityNode" + } + }, + "enabled": { + "type": "boolean" + }, + "custom_actions": { + "type": "array", + "items": { + "type": "string" + } + }, + "AXFrame": { + "type": "string" + }, + "AXUniqueId": { + "type": [ + "string", + "null" + ] + }, + "role_description": { + "type": [ + "string", + "null" + ] + }, + "AXLabel": { + "type": [ + "string", + "null" + ] + }, + "content_required": { + "type": "boolean" + }, + "title": { + "type": [ + "string", + "null" + ] + }, + "help": { + "type": [ + "string", + "null" + ] + }, + "AXValue": { + "type": [ + "string", + "null" + ] + }, + "subrole": { + "type": [ + "string", + "null" + ] + }, + "pid": { + "type": "number" + } + }, + "required": [ + "frame", + "type", + "role", + "children", + "enabled", + "custom_actions" + ] + } + }, + "properties": { + "schema": { + "const": "xcodebuildmcp.output.capture-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulatorId": { + "type": "string" + }, + "screenshotPath": { + "type": "string" + } + }, + "required": [ + "simulatorId" + ] + }, + "capture": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "format": { + "type": "string" + }, + "width": { + "type": "integer", + "minimum": 0 + }, + "height": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "format", + "width", + "height" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "ui-hierarchy" + }, + "uiHierarchy": { + "type": "array", + "items": { + "$ref": "#/$defs/accessibilityNode" + } + } + }, + "required": [ + "type", + "uiHierarchy" + ] + } + ] + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.coverage-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.coverage-result/1.schema.json new file mode 100644 index 00000000..18598937 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.coverage-result/1.schema.json @@ -0,0 +1,229 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.coverage-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.coverage-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "coveragePct": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "coveredLines": { + "type": "integer", + "minimum": 0 + }, + "executableLines": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "status" + ] + }, + "coverageScope": { + "enum": [ + "report", + "file" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "xcresultPath": { + "type": "string" + }, + "target": { + "type": "string" + }, + "file": { + "type": "string" + }, + "sourceFilePath": { + "type": "string" + } + }, + "required": [ + "xcresultPath" + ] + }, + "targets": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "coveragePct": { + "type": "number", + "minimum": 0, + "maximum": 100 + }, + "coveredLines": { + "type": "integer", + "minimum": 0 + }, + "executableLines": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "name", + "coveragePct", + "coveredLines", + "executableLines" + ] + } + }, + "functions": { + "type": "object", + "additionalProperties": false, + "properties": { + "notCovered": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "line": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string" + }, + "coveredLines": { + "type": "integer", + "minimum": 0 + }, + "executableLines": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "line", + "name", + "coveredLines", + "executableLines" + ] + } + }, + "partialCoverage": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "line": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string" + }, + "coveredLines": { + "type": "integer", + "minimum": 0 + }, + "executableLines": { + "type": "integer", + "minimum": 0 + }, + "coveragePct": { + "type": "number", + "minimum": 0, + "maximum": 100 + } + }, + "required": [ + "line", + "name", + "coveragePct", + "coveredLines", + "executableLines" + ] + } + }, + "fullCoverageCount": { + "type": "integer", + "minimum": 0 + }, + "notCoveredFunctionCount": { + "type": "integer", + "minimum": 0 + }, + "notCoveredLineCount": { + "type": "integer", + "minimum": 0 + }, + "partialCoverageFunctionCount": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "fullCoverageCount", + "notCoveredFunctionCount", + "notCoveredLineCount", + "partialCoverageFunctionCount" + ] + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "coverageScope", + "artifacts" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.debug-breakpoint-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.debug-breakpoint-result/1.schema.json new file mode 100644 index 00000000..033ab2ba --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.debug-breakpoint-result/1.schema.json @@ -0,0 +1,171 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.debug-breakpoint-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "$defs": { + "addFileLineBreakpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kind": { + "const": "file-line" + }, + "file": { + "type": "string" + }, + "line": { + "type": "integer", + "minimum": 1 + }, + "breakpointId": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "kind", + "file", + "line" + ] + }, + "addFunctionBreakpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "kind": { + "const": "function" + }, + "name": { + "type": "string" + }, + "breakpointId": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "kind", + "name" + ] + }, + "removeBreakpoint": { + "type": "object", + "additionalProperties": false, + "properties": { + "breakpointId": { + "type": "integer", + "minimum": 1 + } + }, + "required": [ + "breakpointId" + ] + } + }, + "properties": { + "schema": { + "const": "xcodebuildmcp.output.debug-breakpoint-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "action": { + "enum": [ + "add", + "remove" + ] + }, + "breakpoint": { + "oneOf": [ + { + "$ref": "#/$defs/addFileLineBreakpoint" + }, + { + "$ref": "#/$defs/addFunctionBreakpoint" + }, + { + "$ref": "#/$defs/removeBreakpoint" + } + ] + } + }, + "required": [ + "action", + "breakpoint" + ], + "allOf": [ + { + "if": { + "properties": { + "action": { + "const": "add" + } + }, + "required": [ + "action" + ] + }, + "then": { + "properties": { + "breakpoint": { + "oneOf": [ + { + "$ref": "#/$defs/addFileLineBreakpoint" + }, + { + "$ref": "#/$defs/addFunctionBreakpoint" + } + ] + } + } + } + }, + { + "if": { + "properties": { + "action": { + "const": "remove" + } + }, + "required": [ + "action" + ] + }, + "then": { + "properties": { + "breakpoint": { + "$ref": "#/$defs/removeBreakpoint" + } + } + } + } + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.debug-command-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.debug-command-result/1.schema.json new file mode 100644 index 00000000..22e71d43 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.debug-command-result/1.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.debug-command-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.debug-command-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "command": { + "type": "string" + }, + "outputLines": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "command", + "outputLines" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.debug-session-action/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.debug-session-action/1.schema.json new file mode 100644 index 00000000..37142c38 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.debug-session-action/1.schema.json @@ -0,0 +1,91 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.debug-session-action/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.debug-session-action" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "action": { + "enum": [ + "attach", + "continue", + "detach" + ] + }, + "session": { + "type": "object", + "additionalProperties": false, + "properties": { + "debugSessionId": { + "type": "string" + }, + "connectionState": { + "enum": [ + "attached", + "detached" + ] + }, + "executionState": { + "enum": [ + "paused", + "running" + ] + } + }, + "required": [ + "debugSessionId", + "connectionState" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulatorId": { + "type": "string" + }, + "processId": { + "type": "integer", + "minimum": 1 + } + }, + "required": [], + "minProperties": 1 + } + }, + "required": [ + "action" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.debug-stack-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.debug-stack-result/1.schema.json new file mode 100644 index 00000000..c8e04507 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.debug-stack-result/1.schema.json @@ -0,0 +1,100 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.debug-stack-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.debug-stack-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "threads": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "threadId": { + "type": "integer", + "minimum": 1 + }, + "name": { + "type": "string" + }, + "truncated": { + "type": "boolean" + }, + "frames": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "index": { + "type": "integer", + "minimum": 0 + }, + "symbol": { + "type": "string" + }, + "displayLocation": { + "type": "string" + } + }, + "required": [ + "index", + "symbol", + "displayLocation" + ] + } + } + }, + "required": [ + "threadId", + "name", + "truncated", + "frames" + ] + } + } + }, + "required": [ + "threads" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.debug-variables-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.debug-variables-result/1.schema.json new file mode 100644 index 00000000..2fa675f3 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.debug-variables-result/1.schema.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.debug-variables-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.debug-variables-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "anyOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "scopes": { + "type": "object", + "additionalProperties": false, + "properties": { + "locals": { + "type": "object", + "additionalProperties": false, + "properties": { + "variables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "required": [ + "variables" + ] + }, + "globals": { + "type": "object", + "additionalProperties": false, + "properties": { + "variables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "required": [ + "variables" + ] + }, + "registers": { + "type": "object", + "additionalProperties": false, + "properties": { + "groups": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "variables": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": true + } + } + }, + "required": [ + "name", + "variables" + ] + } + } + }, + "required": [ + "groups" + ] + } + }, + "required": [ + "locals", + "globals", + "registers" + ] + } + }, + "required": [ + "scopes" + ] + }, + { + "type": "null" + } + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.device-list/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.device-list/1.schema.json new file mode 100644 index 00000000..de1eb517 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.device-list/1.schema.json @@ -0,0 +1,79 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.device-list/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.device-list" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "devices": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "platform": { + "type": "string" + }, + "state": { + "type": "string" + }, + "isAvailable": { + "type": "boolean" + }, + "osVersion": { + "type": "string" + } + }, + "required": [ + "name", + "deviceId", + "platform", + "state", + "isAvailable", + "osVersion" + ] + } + } + }, + "required": [ + "devices" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.install-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.install-result/1.schema.json new file mode 100644 index 00000000..7858eeae --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.install-result/1.schema.json @@ -0,0 +1,70 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.install-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.install-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + }, + "simulatorId": { + "type": "string" + }, + "deviceId": { + "type": "string" + } + }, + "required": [ + "appPath" + ] + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.launch-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.launch-result/1.schema.json new file mode 100644 index 00000000..632a3378 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.launch-result/1.schema.json @@ -0,0 +1,82 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.launch-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.launch-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "appPath": { + "type": "string" + }, + "bundleId": { + "type": "string" + }, + "processId": { + "type": "integer", + "minimum": 1 + }, + "simulatorId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "runtimeLogPath": { + "type": "string" + }, + "osLogPath": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.process-list/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.process-list/1.schema.json new file mode 100644 index 00000000..67b05f4b --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.process-list/1.schema.json @@ -0,0 +1,94 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.process-list/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.process-list" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "runningProcessCount": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "runningProcessCount" + ] + }, + "processes": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "processId": { + "type": "integer", + "minimum": 1 + }, + "uptimeSeconds": { + "type": "integer", + "minimum": 0 + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "packagePath": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + } + }, + "required": [ + "name", + "processId", + "uptimeSeconds" + ] + } + } + }, + "required": [ + "summary", + "processes" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.project-list/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.project-list/1.schema.json new file mode 100644 index 00000000..db1e2644 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.project-list/1.schema.json @@ -0,0 +1,106 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.project-list/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.project-list" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "maxDepth": { + "type": "integer", + "minimum": 0 + }, + "projectCount": { + "type": "integer", + "minimum": 0 + }, + "workspaceCount": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "status", + "maxDepth" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "workspaceRoot": { + "type": "string" + }, + "scanPath": { + "type": "string" + } + }, + "required": [ + "workspaceRoot", + "scanPath" + ] + }, + "projects": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/pathItem" + } + }, + "workspaces": { + "type": "array", + "items": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/pathItem" + } + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts", + "projects", + "workspaces" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.scaffold-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.scaffold-result/1.schema.json new file mode 100644 index 00000000..54fba356 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.scaffold-result/1.schema.json @@ -0,0 +1,86 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.scaffold-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.scaffold-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "platform": { + "enum": [ + "iOS", + "macOS" + ] + } + }, + "required": [ + "status", + "platform" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "projectName": { + "type": "string" + }, + "outputPath": { + "type": "string" + }, + "workspacePath": { + "type": "string" + } + }, + "required": [ + "projectName", + "outputPath" + ] + } + }, + "required": [ + "summary", + "artifacts" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.scheme-list/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.scheme-list/1.schema.json new file mode 100644 index 00000000..e111aa95 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.scheme-list/1.schema.json @@ -0,0 +1,66 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.scheme-list/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.scheme-list" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "workspacePath": { + "type": "string" + } + }, + "required": [ + "workspacePath" + ] + }, + "schemes": { + "type": "array", + "items": { + "type": "string" + } + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "artifacts", + "schemes" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.session-defaults/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.session-defaults/1.schema.json new file mode 100644 index 00000000..55878fd9 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.session-defaults/1.schema.json @@ -0,0 +1,62 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.session-defaults/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.session-defaults" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "currentProfile": { + "type": "string" + }, + "profiles": { + "type": "object", + "properties": { + "(default)": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/sessionDefaultsProfile" + } + }, + "required": [ + "(default)" + ], + "additionalProperties": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/sessionDefaultsProfile" + } + } + }, + "required": [ + "currentProfile", + "profiles" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.session-profile/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.session-profile/1.schema.json new file mode 100644 index 00000000..8603e812 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.session-profile/1.schema.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.session-profile/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.session-profile" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "previousProfile": { + "type": "string" + }, + "currentProfile": { + "type": "string" + } + }, + "required": [ + "previousProfile", + "currentProfile" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.simulator-action-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.simulator-action-result/1.schema.json new file mode 100644 index 00000000..c0309c50 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.simulator-action-result/1.schema.json @@ -0,0 +1,178 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.simulator-action-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.simulator-action-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "action": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "boot" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "erase" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "open" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "reset-location" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "set-location" + }, + "coordinates": { + "type": "object", + "additionalProperties": false, + "properties": { + "latitude": { + "type": "number", + "minimum": -90, + "maximum": 90 + }, + "longitude": { + "type": "number", + "minimum": -180, + "maximum": 180 + } + }, + "required": [ + "latitude", + "longitude" + ] + } + }, + "required": [ + "type", + "coordinates" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "set-appearance" + }, + "appearance": { + "type": "string" + } + }, + "required": [ + "type", + "appearance" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "statusbar" + }, + "dataNetwork": { + "type": "string" + } + }, + "required": [ + "type" + ] + } + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulatorId": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "action" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.simulator-list/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.simulator-list/1.schema.json new file mode 100644 index 00000000..acdc1937 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.simulator-list/1.schema.json @@ -0,0 +1,75 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.simulator-list/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.simulator-list" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulators": { + "type": "array", + "items": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "simulatorId": { + "type": "string" + }, + "state": { + "type": "string" + }, + "isAvailable": { + "type": "boolean" + }, + "runtime": { + "type": "string" + } + }, + "required": [ + "name", + "simulatorId", + "state", + "isAvailable", + "runtime" + ] + } + } + }, + "required": [ + "simulators" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.stop-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.stop-result/1.schema.json new file mode 100644 index 00000000..b27053bf --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.stop-result/1.schema.json @@ -0,0 +1,76 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.stop-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.stop-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulatorId": { + "type": "string" + }, + "deviceId": { + "type": "string" + }, + "processId": { + "type": "integer", + "minimum": 1 + }, + "bundleId": { + "type": "string" + }, + "appName": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.test-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.test-result/1.schema.json new file mode 100644 index 00000000..3c294925 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.test-result/1.schema.json @@ -0,0 +1,129 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.test-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.test-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "type": "object", + "additionalProperties": false, + "properties": { + "status": { + "enum": [ + "SUCCEEDED", + "FAILED" + ] + }, + "durationMs": { + "type": "integer", + "minimum": 0 + }, + "target": { + "enum": [ + "simulator", + "device", + "macos", + "swift-package" + ] + }, + "counts": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/counts" + } + }, + "required": [ + "status" + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "deviceId": { + "type": "string" + }, + "buildLogPath": { + "type": "string" + }, + "packagePath": { + "type": "string" + } + }, + "required": [], + "minProperties": 1 + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/testDiagnostics" + }, + "tests": { + "type": "object", + "additionalProperties": false, + "properties": { + "selected": { + "type": "array", + "items": { + "type": "string" + } + }, + "discovered": { + "type": "object", + "additionalProperties": false, + "properties": { + "total": { + "type": "integer", + "minimum": 0 + }, + "items": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "total", + "items" + ] + } + }, + "required": [] + } + }, + "required": [ + "summary", + "artifacts", + "diagnostics" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/schemas/structured-output/xcodebuildmcp.output.ui-action-result/1.schema.json b/schemas/structured-output/xcodebuildmcp.output.ui-action-result/1.schema.json new file mode 100644 index 00000000..1c5f2230 --- /dev/null +++ b/schemas/structured-output/xcodebuildmcp.output.ui-action-result/1.schema.json @@ -0,0 +1,231 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://xcodebuildmcp.com/schemas/structured-output/xcodebuildmcp.output.ui-action-result/1.schema.json", + "type": "object", + "additionalProperties": false, + "allOf": [ + { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/errorConsistency" + } + ], + "properties": { + "schema": { + "const": "xcodebuildmcp.output.ui-action-result" + }, + "schemaVersion": { + "const": "1" + }, + "didError": { + "type": "boolean" + }, + "error": { + "type": [ + "string", + "null" + ] + }, + "data": { + "type": "object", + "additionalProperties": false, + "properties": { + "summary": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/statusSummary" + }, + "action": { + "oneOf": [ + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "tap" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "swipe" + }, + "from": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/point" + }, + "to": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/point" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "touch" + }, + "event": { + "type": "string" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "long-press" + }, + "x": { + "type": "number" + }, + "y": { + "type": "number" + }, + "durationMs": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "type", + "x", + "y", + "durationMs" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "button" + }, + "button": { + "type": "string" + } + }, + "required": [ + "type", + "button" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "gesture" + }, + "gesture": { + "type": "string" + } + }, + "required": [ + "type", + "gesture" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "type-text" + } + }, + "required": [ + "type" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "key-press" + }, + "keyCode": { + "type": "integer", + "minimum": 0 + } + }, + "required": [ + "type", + "keyCode" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { + "type": { + "const": "key-sequence" + }, + "keyCodes": { + "type": "array", + "items": { + "type": "integer", + "minimum": 0 + } + } + }, + "required": [ + "type", + "keyCodes" + ] + } + ] + }, + "artifacts": { + "type": "object", + "additionalProperties": false, + "properties": { + "simulatorId": { + "type": "string" + } + }, + "required": [ + "simulatorId" + ] + }, + "diagnostics": { + "$ref": "https://xcodebuildmcp.com/schemas/structured-output/_defs/common.schema.json#/$defs/basicDiagnostics" + } + }, + "required": [ + "summary", + "action", + "artifacts" + ] + } + }, + "required": [ + "schema", + "schemaVersion", + "didError", + "error", + "data" + ] +} diff --git a/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--error-invalid-bundle.json b/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--error-invalid-bundle.json new file mode 100644 index 00000000..7943baa9 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--error-invalid-bundle.json @@ -0,0 +1,23 @@ +{ + "schema": "xcodebuildmcp.output.coverage-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get coverage report: Failed to load result bundle", + "data": { + "summary": { + "status": "FAILED" + }, + "coverageScope": "report", + "artifacts": { + "xcresultPath": "/invalid.xcresult" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Error Domain=XCCovErrorDomain Code=0 \"Failed to load result bundle\"" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--success.json b/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--success.json new file mode 100644 index 00000000..37b58e8a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/coverage/get-coverage-report--success.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.coverage-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "coveragePct": 94.9, + "coveredLines": 371, + "executableLines": 391 + }, + "coverageScope": "report", + "artifacts": { + "xcresultPath": "/TestResults.xcresult", + "target": "CalculatorAppTests" + }, + "targets": [ + { + "name": "CalculatorAppTests.xctest", + "coveragePct": 94.9, + "coveredLines": 371, + "executableLines": 391 + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--error-invalid-bundle.json b/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--error-invalid-bundle.json new file mode 100644 index 00000000..a191b28f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--error-invalid-bundle.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.coverage-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get file coverage: Failed to load result bundle", + "data": { + "summary": { + "status": "FAILED" + }, + "coverageScope": "file", + "artifacts": { + "xcresultPath": "/invalid.xcresult", + "file": "SomeFile.swift" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Error Domain=XCCovErrorDomain Code=0 \"Failed to load result bundle\"" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--success.json b/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--success.json new file mode 100644 index 00000000..a7359d85 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/coverage/get-file-coverage--success.json @@ -0,0 +1,56 @@ +{ + "schema": "xcodebuildmcp.output.coverage-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "coveragePct": 77.8, + "coveredLines": 147, + "executableLines": 189 + }, + "coverageScope": "file", + "artifacts": { + "xcresultPath": "/TestResults.xcresult", + "file": "CalculatorService.swift", + "sourceFilePath": "example_projects/iOS_Calculator/CalculatorAppPackage/Sources/CalculatorAppFeature/CalculatorService.swift" + }, + "functions": { + "notCovered": [ + { + "line": 159, + "name": "CalculatorService.deleteLastDigit()", + "coveredLines": 0, + "executableLines": 16 + }, + { + "line": 178, + "name": "CalculatorService.setError(_:)", + "coveredLines": 0, + "executableLines": 5 + } + ], + "partialCoverage": [ + { + "line": 63, + "name": "CalculatorService.inputDecimal()", + "coveragePct": 71.4, + "coveredLines": 10, + "executableLines": 14 + }, + { + "line": 93, + "name": "CalculatorService.calculate()", + "coveragePct": 84.2, + "coveredLines": 32, + "executableLines": 38 + } + ], + "fullCoverageCount": 27, + "notCoveredFunctionCount": 8, + "notCoveredLineCount": 27, + "partialCoverageFunctionCount": 4 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--error-no-session.json new file mode 100644 index 00000000..8be35662 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--error-no-session.json @@ -0,0 +1,14 @@ +{ + "schema": "xcodebuildmcp.output.debug-breakpoint-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to add breakpoint: No active debug session. Provide debugSessionId or attach first.", + "data": { + "action": "add", + "breakpoint": { + "kind": "file-line", + "file": "ContentView.swift", + "line": 42 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--success.json b/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--success.json new file mode 100644 index 00000000..da8962ab --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/add-breakpoint--success.json @@ -0,0 +1,15 @@ +{ + "schema": "xcodebuildmcp.output.debug-breakpoint-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "add", + "breakpoint": { + "breakpointId": 1, + "kind": "file-line", + "file": "ContentView.swift", + "line": 42 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/attach--error-no-process.json b/src/snapshot-tests/__fixtures__/json/debugging/attach--error-no-process.json new file mode 100644 index 00000000..624c0c66 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/attach--error-no-process.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": true, + "error": "Failed to resolve simulator PID: Invalid device: ", + "data": { + "action": "attach", + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/attach--success-continue.json b/src/snapshot-tests/__fixtures__/json/debugging/attach--success-continue.json new file mode 100644 index 00000000..9649852c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/attach--success-continue.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "attach", + "session": { + "debugSessionId": "", + "connectionState": "attached", + "executionState": "running" + }, + "artifacts": { + "simulatorId": "", + "processId": 99999 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/attach--success.json b/src/snapshot-tests/__fixtures__/json/debugging/attach--success.json new file mode 100644 index 00000000..10ba7af4 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/attach--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "attach", + "session": { + "debugSessionId": "", + "connectionState": "attached", + "executionState": "paused" + }, + "artifacts": { + "simulatorId": "", + "processId": 99999 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/continue--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/continue--error-no-session.json new file mode 100644 index 00000000..b79b9c03 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/continue--error-no-session.json @@ -0,0 +1,9 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": true, + "error": "Failed to resume debugger: No active debug session. Provide debugSessionId or attach first.", + "data": { + "action": "continue" + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/continue--success.json b/src/snapshot-tests/__fixtures__/json/debugging/continue--success.json new file mode 100644 index 00000000..516b6e95 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/continue--success.json @@ -0,0 +1,14 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "continue", + "session": { + "debugSessionId": "", + "connectionState": "attached", + "executionState": "running" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/detach--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/detach--error-no-session.json new file mode 100644 index 00000000..d5921f1d --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/detach--error-no-session.json @@ -0,0 +1,9 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": true, + "error": "Failed to detach debugger: No active debug session. Provide debugSessionId or attach first.", + "data": { + "action": "detach" + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/detach--success.json b/src/snapshot-tests/__fixtures__/json/debugging/detach--success.json new file mode 100644 index 00000000..752f3772 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/detach--success.json @@ -0,0 +1,13 @@ +{ + "schema": "xcodebuildmcp.output.debug-session-action", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "detach", + "session": { + "debugSessionId": "", + "connectionState": "detached" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--error-no-session.json new file mode 100644 index 00000000..10baa910 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--error-no-session.json @@ -0,0 +1,10 @@ +{ + "schema": "xcodebuildmcp.output.debug-command-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to run LLDB command: No active debug session. Provide debugSessionId or attach first.", + "data": { + "command": "breakpoint list", + "outputLines": [] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--success.json b/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--success.json new file mode 100644 index 00000000..5da13860 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/lldb-command--success.json @@ -0,0 +1,15 @@ +{ + "schema": "xcodebuildmcp.output.debug-command-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "command": "breakpoint list", + "outputLines": [ + "Current breakpoints:", + "1: file = 'ContentView.swift', line = 42, exact_match = 0, locations = ", + " Names:", + " dap" + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--error-no-session.json new file mode 100644 index 00000000..6d9dd6f6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--error-no-session.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.debug-breakpoint-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to remove breakpoint: No active debug session. Provide debugSessionId or attach first.", + "data": { + "action": "remove", + "breakpoint": { + "breakpointId": 1 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--success.json b/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--success.json new file mode 100644 index 00000000..8b795c75 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/remove-breakpoint--success.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.debug-breakpoint-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "action": "remove", + "breakpoint": { + "breakpointId": 1 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/stack--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/stack--error-no-session.json new file mode 100644 index 00000000..e51cdac7 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/stack--error-no-session.json @@ -0,0 +1,7 @@ +{ + "schema": "xcodebuildmcp.output.debug-stack-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get stack: No active debug session. Provide debugSessionId or attach first.", + "data": null +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/stack--success.json b/src/snapshot-tests/__fixtures__/json/debugging/stack--success.json new file mode 100644 index 00000000..390d7e28 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/stack--success.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.debug-stack-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "threads": [ + { + "threadId": 1, + "name": "Thread 1", + "truncated": true, + "frames": [ + { + "index": 0, + "symbol": "static CalculatorApp.$main()", + "displayLocation": "/Library/Developer/CoreSimulator/Devices//data/Containers/Bundle/Application//CalculatorApp.app/CalculatorApp.debug.dylib`static CalculatorApp.CalculatorApp.$main() -> ():" + }, + { + "index": 1, + "symbol": "main", + "displayLocation": "/Library/Developer/CoreSimulator/Devices//data/Containers/Bundle/Application//CalculatorApp.app/CalculatorApp.debug.dylib`main:" + } + ] + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/variables--error-no-session.json b/src/snapshot-tests/__fixtures__/json/debugging/variables--error-no-session.json new file mode 100644 index 00000000..d43b6aa6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/variables--error-no-session.json @@ -0,0 +1,7 @@ +{ + "schema": "xcodebuildmcp.output.debug-variables-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get variables: No active debug session. Provide debugSessionId or attach first.", + "data": null +} diff --git a/src/snapshot-tests/__fixtures__/json/debugging/variables--success.json b/src/snapshot-tests/__fixtures__/json/debugging/variables--success.json new file mode 100644 index 00000000..06d6ac7a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/debugging/variables--success.json @@ -0,0 +1,40 @@ +{ + "schema": "xcodebuildmcp.output.debug-variables-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "scopes": { + "locals": { + "variables": [] + }, + "globals": { + "variables": [] + }, + "registers": { + "groups": [ + { + "name": "General Purpose Registers", + "variables": [] + }, + { + "name": "Floating Point Registers", + "variables": [] + }, + { + "name": "Exception State Registers", + "variables": [] + }, + { + "name": "Scalable Vector Extension Registers", + "variables": [] + }, + { + "name": "Scalable Matrix Extension Registers", + "variables": [] + } + ] + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/device/build--error-wrong-scheme.json new file mode 100644 index 00000000..396e8066 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/build--error-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/build--success.json b/src/snapshot-tests/__fixtures__/json/device/build--success.json new file mode 100644 index 00000000..a1e76935 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/build--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/build-and-run--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/device/build-and-run--error-wrong-scheme.json new file mode 100644 index 00000000..fa7b1a8f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/build-and-run--error-wrong-scheme.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "deviceId": "", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/build-and-run--success.json b/src/snapshot-tests/__fixtures__/json/device/build-and-run--success.json new file mode 100644 index 00000000..fa9b0e0e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/build-and-run--success.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "device" + }, + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphoneos/CalculatorApp.app", + "bundleId": "io.sentry.calculatorapp", + "processId": 99999, + "deviceId": "", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_device__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} \ No newline at end of file diff --git a/src/snapshot-tests/__fixtures__/json/device/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/device/get-app-path--error-wrong-scheme.json new file mode 100644 index 00000000..282833bc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/get-app-path--error-wrong-scheme.json @@ -0,0 +1,16 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": true, + "error": "Query failed", + "data": { + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/get-app-path--success.json b/src/snapshot-tests/__fixtures__/json/device/get-app-path--success.json new file mode 100644 index 00000000..9ef2c208 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/get-app-path--success.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphoneos/CalculatorApp.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/install--error-invalid-app.json b/src/snapshot-tests/__fixtures__/json/device/install--error-invalid-app.json new file mode 100644 index 00000000..3a24297c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/install--error-invalid-app.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.install-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to install app", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "deviceId": "", + "appPath": "/tmp/nonexistent.app" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Failed to load provisioning paramter list due to error: Error Domain=com.apple.dt.CoreDeviceError Code=1002 \"No provider was found.\" UserInfo={NSLocalizedDescription=No provider was found.}." + }, + { + "message": "`devicectl manage create` may support a reduced set of arguments." + }, + { + "message": "ERROR: The specified device was not found. (Name: ) (com.apple.dt.CoreDeviceError error 1000 (0x3E8))" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/install--success.json b/src/snapshot-tests/__fixtures__/json/device/install--success.json new file mode 100644 index 00000000..33533b50 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/install--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.install-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "deviceId": "", + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphoneos/CalculatorApp.app" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/launch--error-invalid-bundle.json b/src/snapshot-tests/__fixtures__/json/device/launch--error-invalid-bundle.json new file mode 100644 index 00000000..09ab4484 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/launch--error-invalid-bundle.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to launch app", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "bundleId": "com.nonexistent.app", + "deviceId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Failed to load provisioning paramter list due to error: Error Domain=com.apple.dt.CoreDeviceError Code=1002 \"No provider was found.\" UserInfo={NSLocalizedDescription=No provider was found.}." + }, + { + "message": "`devicectl manage create` may support a reduced set of arguments." + }, + { + "message": "ERROR: The specified device was not found. (Name: ) (com.apple.dt.CoreDeviceError error 1000 (0x3E8))" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/launch--success.json b/src/snapshot-tests/__fixtures__/json/device/launch--success.json new file mode 100644 index 00000000..595ddc83 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/launch--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "bundleId": "io.sentry.calculatorapp", + "deviceId": "", + "processId": 99999 + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/list--success.json b/src/snapshot-tests/__fixtures__/json/device/list--success.json new file mode 100644 index 00000000..c3f5402c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/list--success.json @@ -0,0 +1,42 @@ +{ + "schema": "xcodebuildmcp.output.device-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "devices": [ + { + "name": "Cameron’s iPhone 16 Pro Max", + "deviceId": "", + "platform": "iOS", + "state": "connected", + "isAvailable": true, + "osVersion": "26.3.1 (a)" + }, + { + "name": "iPhone", + "deviceId": "", + "platform": "iOS", + "state": "disconnected", + "isAvailable": false, + "osVersion": "26.1" + }, + { + "name": "Cameron’s Apple Watch", + "deviceId": "", + "platform": "watchOS", + "state": "disconnected", + "isAvailable": false, + "osVersion": "10.6.1" + }, + { + "name": "Cameron’s Apple Watch", + "deviceId": "", + "platform": "watchOS", + "state": "connected", + "isAvailable": true, + "osVersion": "26.3" + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/stop--error-no-app.json b/src/snapshot-tests/__fixtures__/json/device/stop--error-no-app.json new file mode 100644 index 00000000..074be632 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/stop--error-no-app.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to stop app", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "deviceId": "", + "processId": 99999 + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Failed to load provisioning paramter list due to error: Error Domain=com.apple.dt.CoreDeviceError Code=1002 \"No provider was found.\" UserInfo={NSLocalizedDescription=No provider was found.}." + }, + { + "message": "`devicectl manage create` may support a reduced set of arguments." + }, + { + "message": "ERROR: The specified device was not found. (Name: ) (com.apple.dt.CoreDeviceError error 1000 (0x3E8))" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/stop--success.json b/src/snapshot-tests/__fixtures__/json/device/stop--success.json new file mode 100644 index 00000000..2dbc4791 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/stop--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "deviceId": "", + "processId": 99999 + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/device/test--failure.json b/src/snapshot-tests/__fixtures__/json/device/test--failure.json new file mode 100644 index 00000000..8d99ea9f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/test--failure.json @@ -0,0 +1,53 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "counts": { + "passed": 48, + "failed": 2, + "skipped": 2 + }, + "target": "device" + }, + "artifacts": { + "deviceId": "", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_device__pid.log" + }, + "tests": { + "discovered": { + "total": 52, + "items": [ + "CalculatorAppFeatureTests/CalculatorBasicTests/testClear", + "CalculatorAppFeatureTests/CalculatorBasicTests/testInitialState", + "CalculatorAppFeatureTests/CalculatorBasicTests/testIntentionalFailure", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testChainCalculations", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testComplexCalculation", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testExpressionDisplay" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [ + { + "suite": "CalculatorAppTests", + "test": "testCalculatorServiceFailure", + "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", + "location": "example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52" + }, + { + "suite": "IntentionalFailureTests", + "test": "test", + "message": "XCTAssertTrue failed - This test should fail to verify error reporting", + "location": "example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286" + } + ] + } + } +} \ No newline at end of file diff --git a/src/snapshot-tests/__fixtures__/json/device/test--success.json b/src/snapshot-tests/__fixtures__/json/device/test--success.json new file mode 100644 index 00000000..47da262d --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/device/test--success.json @@ -0,0 +1,38 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "counts": { + "passed": 1, + "failed": 0, + "skipped": 0 + }, + "target": "device" + }, + "artifacts": { + "deviceId": "", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_device__pid.log" + }, + "tests": { + "selected": [ + "CalculatorAppTests/CalculatorAppTests/testAddition" + ], + "discovered": { + "total": 1, + "items": [ + "CalculatorAppTests/CalculatorAppTests/testAddition" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/macos/build--error-wrong-scheme.json new file mode 100644 index 00000000..7c14553e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/build--error-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/build--success.json b/src/snapshot-tests/__fixtures__/json/macos/build--success.json new file mode 100644 index 00000000..07ed33fe --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/build--success.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "bundleId": "io.sentry.MCPTest.macOS", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/build-and-run--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/macos/build-and-run--error-wrong-scheme.json new file mode 100644 index 00000000..cc2cc8aa --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/build-and-run--error-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/build-and-run--success.json b/src/snapshot-tests/__fixtures__/json/macos/build-and-run--success.json new file mode 100644 index 00000000..c2d53e67 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/build-and-run--success.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug/MCPTest.app", + "bundleId": "io.sentry.MCPTest.macOS", + "processId": 99999, + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_macos__pid.log" + }, + "output": { + "stdout": [], + "stderr": [] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/macos/get-app-path--error-wrong-scheme.json new file mode 100644 index 00000000..75d7d8c6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/get-app-path--error-wrong-scheme.json @@ -0,0 +1,16 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": true, + "error": "Query failed", + "data": { + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/get-app-path--success.json b/src/snapshot-tests/__fixtures__/json/macos/get-app-path--success.json new file mode 100644 index 00000000..f7f8e7b0 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/get-app-path--success.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug/MCPTest.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--error-missing-app.json b/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--error-missing-app.json new file mode 100644 index 00000000..07f5c0ca --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--error-missing-app.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": true, + "error": "File not found: '/nonexistent/path/Fake.app'. Please check the path and try again.", + "data": { + "artifacts": { + "appPath": "/nonexistent/path/Fake.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--success.json b/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--success.json new file mode 100644 index 00000000..ffe86063 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/get-macos-bundle-id--success.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/BundleTest.app", + "bundleId": "com.test.snapshot-macos" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/launch--error-invalid-app.json b/src/snapshot-tests/__fixtures__/json/macos/launch--error-invalid-app.json new file mode 100644 index 00000000..3f8dd16d --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/launch--error-invalid-app.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": true, + "error": "File not found: '/NonExistent.app'. Please check the path and try again.", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "appPath": "/NonExistent.app" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/launch--success.json b/src/snapshot-tests/__fixtures__/json/macos/launch--success.json new file mode 100644 index 00000000..20365102 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/launch--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug/MCPTest.app", + "bundleId": "io.sentry.MCPTest.macOS", + "processId": 99999 + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/stop--error-no-app.json b/src/snapshot-tests/__fixtures__/json/macos/stop--error-no-app.json new file mode 100644 index 00000000..4a7c4050 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/stop--error-no-app.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": true, + "error": "Stop macOS app operation failed: kill: 999999: No such process", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "processId": 999999 + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/stop--success.json b/src/snapshot-tests/__fixtures__/json/macos/stop--success.json new file mode 100644 index 00000000..263381fb --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/stop--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "appName": "MCPTest" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/macos/test--error-wrong-scheme.json new file mode 100644 index 00000000..73236ccf --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/test--error-wrong-scheme.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_macos__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The project named \"MCPTest\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the project." + } + ], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/test--failure.json b/src/snapshot-tests/__fixtures__/json/macos/test--failure.json new file mode 100644 index 00000000..d68101cc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/test--failure.json @@ -0,0 +1,50 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "counts": { + "passed": 2, + "failed": 2, + "skipped": 0 + }, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_macos__pid.log" + }, + "tests": { + "discovered": { + "total": 4, + "items": [ + "MCPTestTests/MCPTestTests/appNameIsCorrect", + "MCPTestTests/MCPTestTests/deliberateFailure", + "MCPTestTests/MCPTestsXCTests/testAppNameIsCorrect", + "MCPTestTests/MCPTestsXCTests/testDeliberateFailure" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [ + { + "suite": "MCPTestsXCTests", + "test": "testDeliberateFailure", + "message": "XCTAssertTrue failed - This test is designed to fail for snapshot testing", + "location": "MCPTestsXCTests.swift:11" + }, + { + "suite": "MCPTestTests", + "test": "deliberateFailure", + "message": "Expectation failed: 1 == 2: This test is designed to fail for snapshot testing", + "location": "MCPTestTests.swift:11" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/macos/test--success.json b/src/snapshot-tests/__fixtures__/json/macos/test--success.json new file mode 100644 index 00000000..fdf934aa --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/macos/test--success.json @@ -0,0 +1,39 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "counts": { + "passed": 2, + "failed": 0, + "skipped": 0 + }, + "target": "macos" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_macos__pid.log" + }, + "tests": { + "selected": [ + "MCPTestTests/MCPTestTests/appNameIsCorrect()", + "MCPTestTests/MCPTestsXCTests/testAppNameIsCorrect" + ], + "discovered": { + "total": 2, + "items": [ + "MCPTestTests/MCPTestTests/appNameIsCorrect", + "MCPTestTests/MCPTestsXCTests/testAppNameIsCorrect" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--error-invalid-root.json b/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--error-invalid-root.json new file mode 100644 index 00000000..568f2e88 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--error-invalid-root.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.project-list", + "schemaVersion": "1", + "didError": true, + "error": "Failed to access scan path: /nonexistent/path", + "data": { + "summary": { + "status": "FAILED", + "maxDepth": 3 + }, + "artifacts": { + "workspaceRoot": "/nonexistent/path/Fake.app", + "scanPath": "/nonexistent/path" + }, + "projects": [], + "workspaces": [], + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "ENOENT: no such file or directory, stat '/nonexistent/path'" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--success.json b/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--success.json new file mode 100644 index 00000000..ccd80630 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/discover-projs--success.json @@ -0,0 +1,28 @@ +{ + "schema": "xcodebuildmcp.output.project-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "projectCount": 1, + "workspaceCount": 1, + "maxDepth": 3 + }, + "artifacts": { + "workspaceRoot": "", + "scanPath": "" + }, + "projects": [ + { + "path": "example_projects/iOS_Calculator/CalculatorApp.xcodeproj" + } + ], + "workspaces": [ + { + "path": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace" + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--error-missing-app.json b/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--error-missing-app.json new file mode 100644 index 00000000..07f5c0ca --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--error-missing-app.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": true, + "error": "File not found: '/nonexistent/path/Fake.app'. Please check the path and try again.", + "data": { + "artifacts": { + "appPath": "/nonexistent/path/Fake.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--success.json b/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--success.json new file mode 100644 index 00000000..125a0b2f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/get-app-bundle-id--success.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/BundleTest.app", + "bundleId": "com.test.snapshot" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--error-missing-app.json b/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--error-missing-app.json new file mode 100644 index 00000000..07f5c0ca --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--error-missing-app.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": true, + "error": "File not found: '/nonexistent/path/Fake.app'. Please check the path and try again.", + "data": { + "artifacts": { + "appPath": "/nonexistent/path/Fake.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--success.json b/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--success.json new file mode 100644 index 00000000..125a0b2f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/get-macos-bundle-id--success.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.bundle-id", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/BundleTest.app", + "bundleId": "com.test.snapshot" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--error-invalid-workspace.json b/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--error-invalid-workspace.json new file mode 100644 index 00000000..57944cfe --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--error-invalid-workspace.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.scheme-list", + "schemaVersion": "1", + "didError": true, + "error": "xcodebuild: error: '/nonexistent/path/Fake.xcworkspace' does not exist.", + "data": { + "artifacts": { + "workspacePath": "/nonexistent/path/Fake.xcworkspace" + }, + "schemes": [], + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "xcodebuild: error: '/nonexistent/path/Fake.xcworkspace' does not exist." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--success.json b/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--success.json new file mode 100644 index 00000000..186ccd48 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/list-schemes--success.json @@ -0,0 +1,15 @@ +{ + "schema": "xcodebuildmcp.output.scheme-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace" + }, + "schemes": [ + "CalculatorApp", + "CalculatorAppFeature" + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--error-wrong-scheme.json new file mode 100644 index 00000000..1fdbd0bc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--error-wrong-scheme.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.build-settings", + "schemaVersion": "1", + "didError": true, + "error": "xcodebuild: error: The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\".", + "data": { + "artifacts": { + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "NONEXISTENT" + }, + "entries": [], + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "xcodebuild: error: The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--success.json b/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--success.json new file mode 100644 index 00000000..bd7c508e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-discovery/show-build-settings--success.json @@ -0,0 +1,62 @@ +{ + "schema": "xcodebuildmcp.output.build-settings", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "CalculatorApp" + }, + "entries": [ + { + "key": "ACTION", + "value": "build" + }, + { + "key": "CONFIGURATION", + "value": "Debug" + }, + { + "key": "SDKROOT", + "value": "/Applications/Xcode-26.4.0.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS26.4.sdk" + }, + { + "key": "PRODUCT_NAME", + "value": "CalculatorApp" + }, + { + "key": "PRODUCT_BUNDLE_IDENTIFIER", + "value": "io.sentry.calculatorapp" + }, + { + "key": "TARGET_NAME", + "value": "CalculatorApp" + }, + { + "key": "WORKSPACE_DIR", + "value": "/example_projects/iOS_Calculator" + }, + { + "key": "PROJECT_DIR", + "value": "/example_projects/iOS_Calculator" + }, + { + "key": "SWIFT_VERSION", + "value": "5.0" + }, + { + "key": "IPHONEOS_DEPLOYMENT_TARGET", + "value": "17.0" + }, + { + "key": "TARGET_DEVICE_IDENTIFIER", + "value": "" + }, + { + "key": "PATH", + "value": "" + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--error-existing.json b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--error-existing.json new file mode 100644 index 00000000..2703d487 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--error-existing.json @@ -0,0 +1,16 @@ +{ + "schema": "xcodebuildmcp.output.scaffold-result", + "schemaVersion": "1", + "didError": true, + "error": "Xcode project files already exist in /ios-existing", + "data": { + "summary": { + "status": "FAILED", + "platform": "iOS" + }, + "artifacts": { + "projectName": "SnapshotTestApp", + "outputPath": "/ios-existing" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--success.json b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--success.json new file mode 100644 index 00000000..e5fc0c7c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-ios--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.scaffold-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "platform": "iOS" + }, + "artifacts": { + "projectName": "SnapshotTestApp", + "outputPath": "/ios", + "workspacePath": "/ios/SnapshotTestApp.xcworkspace" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--error-existing.json b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--error-existing.json new file mode 100644 index 00000000..820763f5 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--error-existing.json @@ -0,0 +1,16 @@ +{ + "schema": "xcodebuildmcp.output.scaffold-result", + "schemaVersion": "1", + "didError": true, + "error": "Xcode project files already exist in /macos-existing", + "data": { + "summary": { + "status": "FAILED", + "platform": "macOS" + }, + "artifacts": { + "projectName": "SnapshotTestMacApp", + "outputPath": "/macos-existing" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--success.json b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--success.json new file mode 100644 index 00000000..533cb7f2 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/project-scaffolding/scaffold-macos--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.scaffold-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "platform": "macOS" + }, + "artifacts": { + "projectName": "SnapshotTestMacApp", + "outputPath": "/macos", + "workspacePath": "/macos/SnapshotTestMacApp.xcworkspace" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-clear-defaults--success.json b/src/snapshot-tests/__fixtures__/json/session-management/session-clear-defaults--success.json new file mode 100644 index 00000000..6b2f23e8 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-clear-defaults--success.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": null, + "scheme": null, + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--scheme.json b/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--scheme.json new file mode 100644 index 00000000..6c03aa73 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--scheme.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": null, + "scheme": "CalculatorApp", + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--success.json b/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--success.json new file mode 100644 index 00000000..4695f0b4 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-set-defaults--success.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "CalculatorApp", + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--empty.json b/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--empty.json new file mode 100644 index 00000000..6b2f23e8 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--empty.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": null, + "scheme": null, + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--success.json b/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--success.json new file mode 100644 index 00000000..9b9d5b72 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-show-defaults--success.json @@ -0,0 +1,47 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "CalculatorApp", + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + }, + "MyCustomProfile": { + "projectPath": null, + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "CalculatorApp", + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": null, + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-sync-xcode-defaults--success.json b/src/snapshot-tests/__fixtures__/json/session-management/session-sync-xcode-defaults--success.json new file mode 100644 index 00000000..bf76083c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-sync-xcode-defaults--success.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.session-defaults", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "currentProfile": "(default)", + "profiles": { + "(default)": { + "projectPath": null, + "workspacePath": null, + "scheme": "CalculatorApp", + "configuration": null, + "simulatorName": null, + "simulatorId": null, + "simulatorPlatform": null, + "deviceId": null, + "useLatestOS": null, + "arch": null, + "suppressWarnings": null, + "derivedDataPath": null, + "preferXcodebuild": null, + "platform": null, + "bundleId": "io.sentry.calculatorapp", + "env": null + } + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/session-management/session-use-defaults-profile--success.json b/src/snapshot-tests/__fixtures__/json/session-management/session-use-defaults-profile--success.json new file mode 100644 index 00000000..d38c34e3 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/session-management/session-use-defaults-profile--success.json @@ -0,0 +1,10 @@ +{ + "schema": "xcodebuildmcp.output.session-profile", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "previousProfile": "(default)", + "currentProfile": "MyCustomProfile" + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/boot--error-invalid-id.json b/src/snapshot-tests/__fixtures__/json/simulator-management/boot--error-invalid-id.json new file mode 100644 index 00000000..01a6dbef --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/boot--error-invalid-id.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Boot simulator operation failed: Invalid device or device pair: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "boot" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device or device pair: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/boot--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/boot--success.json new file mode 100644 index 00000000..81734db6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/boot--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "boot" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/erase--error-invalid-id.json b/src/snapshot-tests/__fixtures__/json/simulator-management/erase--error-invalid-id.json new file mode 100644 index 00000000..3bd1e8bd --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/erase--error-invalid-id.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to erase simulator: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "erase" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/erase--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/erase--success.json new file mode 100644 index 00000000..969951cb --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/erase--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "erase" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/list--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/list--success.json new file mode 100644 index 00000000..4964c72a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/list--success.json @@ -0,0 +1,87 @@ +{ + "schema": "xcodebuildmcp.output.simulator-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "simulators": [ + { + "name": "iPhone 17 Pro", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17 Pro Max", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17e", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone Air", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Pro 13-inch (M5)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Pro 11-inch (M5)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad mini (A17 Pro)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Air 13-inch (M4)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Air 11-inch (M4)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad (A16)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/open--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/open--success.json new file mode 100644 index 00000000..32cd4583 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/open--success.json @@ -0,0 +1,14 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "open" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--error-invalid-simulator.json b/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--error-invalid-simulator.json new file mode 100644 index 00000000..65ec8e92 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--error-invalid-simulator.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to reset simulator location: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "reset-location" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--success.json new file mode 100644 index 00000000..7c99e5bd --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/reset-location--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "reset-location" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--error-invalid-simulator.json b/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--error-invalid-simulator.json new file mode 100644 index 00000000..92be66a6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--error-invalid-simulator.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to set simulator appearance: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "set-appearance", + "appearance": "dark" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--success.json new file mode 100644 index 00000000..4b3817f0 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/set-appearance--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "set-appearance", + "appearance": "dark" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--error-invalid-simulator.json b/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--error-invalid-simulator.json new file mode 100644 index 00000000..535ec591 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--error-invalid-simulator.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to set simulator location: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "set-location", + "coordinates": { + "latitude": 37.7749, + "longitude": -122.4194 + } + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--success.json new file mode 100644 index 00000000..78f12d43 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/set-location--success.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "set-location", + "coordinates": { + "latitude": 37.7749, + "longitude": -122.4194 + } + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--error-invalid-simulator.json b/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--error-invalid-simulator.json new file mode 100644 index 00000000..26540d08 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--error-invalid-simulator.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to set status bar: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "statusbar", + "dataNetwork": "wifi" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Invalid device: " + } + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--success.json b/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--success.json new file mode 100644 index 00000000..137b409a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator-management/statusbar--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.simulator-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "statusbar", + "dataNetwork": "wifi" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/build--error-missing-params.json b/src/snapshot-tests/__fixtures__/json/simulator/build--error-missing-params.json new file mode 100644 index 00000000..b605548c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/build--error-missing-params.json @@ -0,0 +1,7 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Input validation error: Invalid arguments for tool build_sim: expected string for scheme", + "data": null +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/build--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/simulator/build--error-wrong-scheme.json new file mode 100644 index 00000000..ce990752 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/build--error-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/build--success.json b/src/snapshot-tests/__fixtures__/json/simulator/build--success.json new file mode 100644 index 00000000..83ddd9f4 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/build--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--error-wrong-scheme.json new file mode 100644 index 00000000..86093358 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--error-wrong-scheme.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--success.json b/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--success.json new file mode 100644 index 00000000..7a84be3e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/build-and-run--success.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphonesimulator/CalculatorApp.app", + "bundleId": "io.sentry.calculatorapp", + "processId": 99999, + "simulatorId": "", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_sim__pid.log", + "runtimeLogPath": "/Library/Developer/XcodeBuildMCP/logs/io.sentry.calculatorapp__pid.log", + "osLogPath": "/Library/Developer/XcodeBuildMCP/logs/io.sentry.calculatorapp_oslog__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} \ No newline at end of file diff --git a/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--error-wrong-scheme.json new file mode 100644 index 00000000..acb8f795 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--error-wrong-scheme.json @@ -0,0 +1,16 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get app path", + "data": { + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--success.json b/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--success.json new file mode 100644 index 00000000..0c096cbc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/get-app-path--success.json @@ -0,0 +1,11 @@ +{ + "schema": "xcodebuildmcp.output.app-path", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "artifacts": { + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphonesimulator/CalculatorApp.app" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/install--error-invalid-app.json b/src/snapshot-tests/__fixtures__/json/simulator/install--error-invalid-app.json new file mode 100644 index 00000000..28701824 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/install--error-invalid-app.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.install-result", + "schemaVersion": "1", + "didError": true, + "error": "Install app in simulator operation failed: Missing bundle ID.", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "simulatorId": "", + "appPath": "/NotAnApp.app" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Simulator device failed to install the application." + }, + { + "message": "Failed to get bundle ID from /NotAnApp.app" + }, + { + "message": "Missing bundle ID." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/install--success.json b/src/snapshot-tests/__fixtures__/json/simulator/install--success.json new file mode 100644 index 00000000..5a71e725 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/install--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.install-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "simulatorId": "", + "appPath": "/Library/Developer/XcodeBuildMCP/DerivedData/Build/Products/Debug-iphonesimulator/CalculatorApp.app" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/launch-app--error-not-installed.json b/src/snapshot-tests/__fixtures__/json/simulator/launch-app--error-not-installed.json new file mode 100644 index 00000000..e374c4f2 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/launch-app--error-not-installed.json @@ -0,0 +1,23 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": true, + "error": "App is not installed on the simulator. Please use install_app_sim before launching. Workflow: build -> install -> launch.", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "simulatorId": "", + "bundleId": "com.nonexistent.app" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "App is not installed on the simulator." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/launch-app--success.json b/src/snapshot-tests/__fixtures__/json/simulator/launch-app--success.json new file mode 100644 index 00000000..a1fc54a6 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/launch-app--success.json @@ -0,0 +1,22 @@ +{ + "schema": "xcodebuildmcp.output.launch-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "simulatorId": "", + "bundleId": "io.sentry.calculatorapp", + "processId": 99999, + "runtimeLogPath": "/Library/Developer/XcodeBuildMCP/logs/io.sentry.calculatorapp__pid.log", + "osLogPath": "/Library/Developer/XcodeBuildMCP/logs/io.sentry.calculatorapp_oslog__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/list--success.json b/src/snapshot-tests/__fixtures__/json/simulator/list--success.json new file mode 100644 index 00000000..297d1a21 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/list--success.json @@ -0,0 +1,87 @@ +{ + "schema": "xcodebuildmcp.output.simulator-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "simulators": [ + { + "name": "iPhone 17 Pro", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17 Pro Max", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17e", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone Air", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPhone 17", + "simulatorId": "", + "state": "Booted", + "isAvailable": true, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Pro 13-inch (M5)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Pro 11-inch (M5)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad mini (A17 Pro)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Air 13-inch (M4)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad Air 11-inch (M4)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + }, + { + "name": "iPad (A16)", + "simulatorId": "", + "state": "Shutdown", + "isAvailable": false, + "runtime": "iOS 26.4" + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/screenshot--error-invalid-simulator.json b/src/snapshot-tests/__fixtures__/json/simulator/screenshot--error-invalid-simulator.json new file mode 100644 index 00000000..b08286bc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/screenshot--error-invalid-simulator.json @@ -0,0 +1,22 @@ +{ + "schema": "xcodebuildmcp.output.capture-result", + "schemaVersion": "1", + "didError": true, + "error": "System error executing screenshot: Failed to capture screenshot: Invalid device: ", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Failed to capture screenshot: Invalid device: " + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/screenshot--success.json b/src/snapshot-tests/__fixtures__/json/simulator/screenshot--success.json new file mode 100644 index 00000000..4a997444 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/screenshot--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.capture-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "simulatorId": "", + "screenshotPath": "" + }, + "capture": { + "format": "image/jpeg", + "width": 368, + "height": 800 + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/stop--error-no-app.json b/src/snapshot-tests/__fixtures__/json/simulator/stop--error-no-app.json new file mode 100644 index 00000000..f37ee86c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/stop--error-no-app.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": true, + "error": "Stop app in simulator operation failed: found nothing to terminate", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "simulatorId": "", + "bundleId": "com.nonexistent.app" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "Simulator device failed to terminate com.nonexistent.app." + }, + { + "message": "The request to terminate \"com.nonexistent.app\" failed. found nothing to terminate" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/stop--success.json b/src/snapshot-tests/__fixtures__/json/simulator/stop--success.json new file mode 100644 index 00000000..a5cd4add --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/stop--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "simulatorId": "", + "bundleId": "io.sentry.calculatorapp" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/test--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/simulator/test--error-wrong-scheme.json new file mode 100644 index 00000000..96bf5aec --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/test--error-wrong-scheme.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_sim__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/simulator/test--failure.json b/src/snapshot-tests/__fixtures__/json/simulator/test--failure.json new file mode 100644 index 00000000..87a8f138 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/test--failure.json @@ -0,0 +1,52 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "counts": { + "passed": 48, + "failed": 2, + "skipped": 2 + }, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_sim__pid.log" + }, + "tests": { + "discovered": { + "total": 52, + "items": [ + "CalculatorAppFeatureTests/CalculatorBasicTests/testClear", + "CalculatorAppFeatureTests/CalculatorBasicTests/testInitialState", + "CalculatorAppFeatureTests/CalculatorBasicTests/testIntentionalFailure", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testChainCalculations", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testComplexCalculation", + "CalculatorAppFeatureTests/CalculatorIntegrationTests/testExpressionDisplay" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [ + { + "suite": "CalculatorAppTests", + "test": "testCalculatorServiceFailure", + "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", + "location": "example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:52" + }, + { + "suite": "IntentionalFailureTests", + "test": "test", + "message": "XCTAssertTrue failed - This test should fail to verify error reporting", + "location": "example_projects/iOS_Calculator/CalculatorAppTests/CalculatorAppTests.swift:286" + } + ] + } + } +} \ No newline at end of file diff --git a/src/snapshot-tests/__fixtures__/json/simulator/test--success.json b/src/snapshot-tests/__fixtures__/json/simulator/test--success.json new file mode 100644 index 00000000..83937edc --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/simulator/test--success.json @@ -0,0 +1,34 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "counts": { + "passed": 1, + "failed": 0, + "skipped": 0 + }, + "target": "simulator" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/test_sim__pid.log" + }, + "tests": { + "discovered": { + "total": 1, + "items": [ + "CalculatorAppTests/CalculatorAppTests/testAddition" + ] + } + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/build--error-bad-path.json b/src/snapshot-tests/__fixtures__/json/swift-package/build--error-bad-path.json new file mode 100644 index 00000000..8930eed3 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/build--error-bad-path.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/NONEXISTENT", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_spm__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/build--success.json b/src/snapshot-tests/__fixtures__/json/swift-package/build--success.json new file mode 100644 index 00000000..e4009fc8 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/build--success.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/spm", + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_spm__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/clean--error-bad-path.json b/src/snapshot-tests/__fixtures__/json/swift-package/clean--error-bad-path.json new file mode 100644 index 00000000..34f25256 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/clean--error-bad-path.json @@ -0,0 +1,23 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Swift package clean failed: error: chdir error: No such file or directory (2): /example_projects/NONEXISTENT", + "data": { + "summary": { + "status": "FAILED", + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/NONEXISTENT" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/clean--success.json b/src/snapshot-tests/__fixtures__/json/swift-package/clean--success.json new file mode 100644 index 00000000..abf6c659 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/clean--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/spm" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/list--no-processes.json b/src/snapshot-tests/__fixtures__/json/swift-package/list--no-processes.json new file mode 100644 index 00000000..3e568e51 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/list--no-processes.json @@ -0,0 +1,12 @@ +{ + "schema": "xcodebuildmcp.output.process-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "runningProcessCount": 0 + }, + "processes": [] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/list--success.json b/src/snapshot-tests/__fixtures__/json/swift-package/list--success.json new file mode 100644 index 00000000..a985d7c3 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/list--success.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.process-list", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "runningProcessCount": 1 + }, + "processes": [ + { + "name": "spm", + "processId": 99999, + "uptimeSeconds": 3600, + "artifacts": { + "packagePath": "/example_projects/spm" + } + } + ] + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/run--error-bad-executable.json b/src/snapshot-tests/__fixtures__/json/swift-package/run--error-bad-executable.json new file mode 100644 index 00000000..01c0313e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/run--error-bad-executable.json @@ -0,0 +1,24 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": true, + "error": "Build failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/spm" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "no executable product named 'nonexistent-executable'" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/run--success.json b/src/snapshot-tests/__fixtures__/json/swift-package/run--success.json new file mode 100644 index 00000000..bfa44f68 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/run--success.json @@ -0,0 +1,29 @@ +{ + "schema": "xcodebuildmcp.output.build-run-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "target": "swift-package" + }, + "artifacts": { + "packagePath": "/example_projects/spm", + "executablePath": "example_projects/spm/.build/arm64-apple-macosx/debug/spm", + "processId": 99999, + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/build_run_spm__pid.log" + }, + "output": { + "stdout": [ + "Hello, world!" + ], + "stderr": [] + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/stop--error-no-process.json b/src/snapshot-tests/__fixtures__/json/swift-package/stop--error-no-process.json new file mode 100644 index 00000000..c32e026b --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/stop--error-no-process.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.stop-result", + "schemaVersion": "1", + "didError": true, + "error": "No running process found with PID 999999. Use swift_package_list to check active processes.", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "processId": 99999 + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/test--error-bad-path.json b/src/snapshot-tests/__fixtures__/json/swift-package/test--error-bad-path.json new file mode 100644 index 00000000..298972ee --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/test--error-bad-path.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "target": "swift-package" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/swift_package_test__pid.log", + "packagePath": "/example_projects/NONEXISTENT" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "chdir error: No such file or directory (2): /example_projects/NONEXISTENT" + } + ], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/test--failure.json b/src/snapshot-tests/__fixtures__/json/swift-package/test--failure.json new file mode 100644 index 00000000..bac3975e --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/test--failure.json @@ -0,0 +1,39 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": true, + "error": "Tests failed", + "data": { + "summary": { + "status": "FAILED", + "durationMs": 1234, + "counts": { + "passed": 8, + "failed": 2, + "skipped": 0 + }, + "target": "swift-package" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/swift_package_test__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [ + { + "suite": "CalculatorAppTests", + "test": "testCalculatorServiceFailure", + "message": "XCTAssertEqual failed: (\"0\") is not equal to (\"999\") - This test should fail - display should be 0, not 999", + "location": "example_projects/spm/Tests/TestLibTests/SimpleTests.swift:49" + }, + { + "suite": "Unknown Suite", + "test": "test", + "message": "Expectation failed: Bool(false)", + "location": "SimpleTests.swift:57" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/swift-package/test--success.json b/src/snapshot-tests/__fixtures__/json/swift-package/test--success.json new file mode 100644 index 00000000..46f4dc45 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/swift-package/test--success.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.test-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED", + "durationMs": 1234, + "counts": { + "passed": 1, + "failed": 0, + "skipped": 0 + }, + "target": "swift-package" + }, + "artifacts": { + "buildLogPath": "/Library/Developer/XcodeBuildMCP/logs/swift_package_test__pid.log" + }, + "diagnostics": { + "warnings": [], + "errors": [], + "testFailures": [] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/button--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/button--error-no-simulator.json new file mode 100644 index 00000000..90c0e794 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/button--error-no-simulator.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to press button 'home': axe command 'button' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "button", + "button": "home" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/button--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/button--success.json new file mode 100644 index 00000000..6d617298 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/button--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "button", + "button": "home" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--error-no-simulator.json new file mode 100644 index 00000000..6606ab1f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--error-no-simulator.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to execute gesture 'scroll-down': axe command 'gesture' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "gesture", + "gesture": "scroll-down" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--success.json new file mode 100644 index 00000000..a35585b8 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/gesture--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "gesture", + "gesture": "scroll-down" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--error-no-simulator.json new file mode 100644 index 00000000..68698083 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--error-no-simulator.json @@ -0,0 +1,26 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to simulate key press (code: 4): axe command 'key' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "key-press", + "keyCode": 4 + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--success.json new file mode 100644 index 00000000..a0b984d9 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/key-press--success.json @@ -0,0 +1,18 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "key-press", + "keyCode": 4 + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--error-no-simulator.json new file mode 100644 index 00000000..f5b0648c --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--error-no-simulator.json @@ -0,0 +1,30 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to execute key sequence: axe command 'key-sequence' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "key-sequence", + "keyCodes": [ + 4, + 5, + 6 + ] + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--success.json new file mode 100644 index 00000000..82632eec --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/key-sequence--success.json @@ -0,0 +1,22 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "key-sequence", + "keyCodes": [ + 4, + 5, + 6 + ] + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--error-no-simulator.json new file mode 100644 index 00000000..df3701af --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--error-no-simulator.json @@ -0,0 +1,28 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to simulate long press at (100, 400): axe command 'touch' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "long-press", + "x": 100, + "y": 400, + "durationMs": 500 + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--success.json new file mode 100644 index 00000000..51dbdb7f --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/long-press--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "long-press", + "x": 100, + "y": 400, + "durationMs": 500 + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--error-no-simulator.json new file mode 100644 index 00000000..2639e661 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--error-no-simulator.json @@ -0,0 +1,22 @@ +{ + "schema": "xcodebuildmcp.output.capture-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to get accessibility hierarchy: axe command 'describe-ui' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--success.json new file mode 100644 index 00000000..d829d953 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/snapshot-ui--success.json @@ -0,0 +1,309 @@ +{ + "schema": "xcodebuildmcp.output.capture-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "simulatorId": "" + }, + "capture": { + "type": "ui-hierarchy", + "uiHierarchy": [ + { + "AXFrame": "{{0, 0}, {402, 874}}", + "AXUniqueId": null, + "frame": { + "y": 0, + "x": 0, + "width": 402, + "height": 874 + }, + "role_description": "application", + "AXLabel": " ", + "content_required": false, + "type": "Application", + "title": null, + "help": null, + "custom_actions": [], + "AXValue": null, + "enabled": true, + "role": "AXApplication", + "children": [ + { + "AXFrame": "{{28.333333333333371, 88}, {68, 90.666666666666657}}", + "AXUniqueId": "Fitness", + "frame": { + "y": 88, + "x": 28.3, + "width": 68, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Fitness", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{120.66666666666674, 88}, {68, 90.666666666666657}}", + "AXUniqueId": "Watch", + "frame": { + "y": 88, + "x": 120.7, + "width": 68, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Watch", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{211.33333333333326, 88}, {72.333333333333314, 90.666666666666657}}", + "AXUniqueId": "Contacts", + "frame": { + "y": 88, + "x": 211.3, + "width": 72.3, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Contacts", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{306, 88}, {68, 90.666666666666657}}", + "AXUniqueId": "Files", + "frame": { + "y": 88, + "x": 306, + "width": 68, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Files", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{28.333333333333371, 188.33333333333334}, {68, 90.666666666666714}}", + "AXUniqueId": "Preview", + "frame": { + "y": 188.3, + "x": 28.3, + "width": 68, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Preview", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{120.66666666666674, 188.33333333333334}, {68, 90.666666666666714}}", + "AXUniqueId": "Utilities", + "frame": { + "y": 188.3, + "x": 120.7, + "width": 68, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Utilities folder", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "2 apps", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{207.66666666666663, 188.33333333333334}, {79.333333333333314, 90.666666666666714}}", + "AXUniqueId": "Calculator", + "frame": { + "y": 188.3, + "x": 207.7, + "width": 79.3, + "height": 90.7 + }, + "role_description": "button", + "AXLabel": "Calculator", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{162, 703.66666666666674}, {78, 30}}", + "AXUniqueId": "spotlight-pill", + "frame": { + "y": 703.7, + "x": 162, + "width": 78, + "height": 30 + }, + "role_description": "slider", + "AXLabel": "Search", + "content_required": false, + "type": "Slider", + "title": null, + "help": "Activate to open Spotlight.", + "custom_actions": [], + "AXValue": "Page 2 of 2", + "enabled": true, + "role": "AXSlider", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{123, 771.33333333333326}, {68, 68}}", + "AXUniqueId": "Safari", + "frame": { + "y": 771.3, + "x": 123, + "width": 68, + "height": 68 + }, + "role_description": "button", + "AXLabel": "Safari", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + }, + { + "AXFrame": "{{211, 771.33333333333326}, {68, 68}}", + "AXUniqueId": "Messages", + "frame": { + "y": 771.3, + "x": 211, + "width": 68, + "height": 68 + }, + "role_description": "button", + "AXLabel": "Messages", + "content_required": false, + "type": "Button", + "title": null, + "help": "Double-tap to open", + "custom_actions": [ + "Edit mode", + "Today", + "App Library" + ], + "AXValue": "", + "enabled": true, + "role": "AXButton", + "children": [], + "subrole": null, + "pid": 99999 + } + ], + "subrole": null, + "pid": 99999 + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--error-no-simulator.json new file mode 100644 index 00000000..87bebb9a --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--error-no-simulator.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to simulate swipe: axe command 'swipe' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "swipe" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--success.json new file mode 100644 index 00000000..fa572bb1 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/swipe--success.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "swipe", + "from": { + "x": 200, + "y": 400 + }, + "to": { + "x": 200, + "y": 200 + } + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/tap--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/tap--error-no-simulator.json new file mode 100644 index 00000000..3fd7ff16 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/tap--error-no-simulator.json @@ -0,0 +1,27 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to simulate tap at (100, 100): axe command 'tap' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "tap", + "x": 100, + "y": 100 + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/tap--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/tap--success.json new file mode 100644 index 00000000..e3b9ec3d --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/tap--success.json @@ -0,0 +1,19 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "tap", + "x": 100, + "y": 400 + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/touch--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/touch--error-no-simulator.json new file mode 100644 index 00000000..bf80e9c7 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/touch--error-no-simulator.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to execute touch event: axe command 'touch' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "touch" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/touch--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/touch--success.json new file mode 100644 index 00000000..b7fed1ba --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/touch--success.json @@ -0,0 +1,20 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "touch", + "event": "touch down+up", + "x": 100, + "y": 400 + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--error-no-simulator.json b/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--error-no-simulator.json new file mode 100644 index 00000000..e4706043 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--error-no-simulator.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": true, + "error": "Failed to simulate text typing: axe command 'type' failed.", + "data": { + "summary": { + "status": "FAILED" + }, + "action": { + "type": "type-text" + }, + "artifacts": { + "simulatorId": "" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "CLIError(errorDescription: \"Simulator with UDID not found in set.\")" + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--success.json b/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--success.json new file mode 100644 index 00000000..4e9ddb72 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/ui-automation/type-text--success.json @@ -0,0 +1,17 @@ +{ + "schema": "xcodebuildmcp.output.ui-action-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "action": { + "type": "type-text" + }, + "artifacts": { + "simulatorId": "" + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/utilities/clean--error-wrong-scheme.json b/src/snapshot-tests/__fixtures__/json/utilities/clean--error-wrong-scheme.json new file mode 100644 index 00000000..b6092a17 --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/utilities/clean--error-wrong-scheme.json @@ -0,0 +1,25 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": true, + "error": "Clean failed: xcodebuild: error: The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\".", + "data": { + "summary": { + "status": "FAILED" + }, + "artifacts": { + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "NONEXISTENT", + "configuration": "Debug", + "platform": "iOS" + }, + "diagnostics": { + "warnings": [], + "errors": [ + { + "message": "xcodebuild: error: The workspace named \"CalculatorApp\" does not contain a scheme named \"NONEXISTENT\". The \"-list\" option can be used to find the names of the schemes in the workspace." + } + ] + } + } +} diff --git a/src/snapshot-tests/__fixtures__/json/utilities/clean--success.json b/src/snapshot-tests/__fixtures__/json/utilities/clean--success.json new file mode 100644 index 00000000..b258ecdb --- /dev/null +++ b/src/snapshot-tests/__fixtures__/json/utilities/clean--success.json @@ -0,0 +1,21 @@ +{ + "schema": "xcodebuildmcp.output.build-result", + "schemaVersion": "1", + "didError": false, + "error": null, + "data": { + "summary": { + "status": "SUCCEEDED" + }, + "artifacts": { + "workspacePath": "example_projects/iOS_Calculator/CalculatorApp.xcworkspace", + "scheme": "CalculatorApp", + "configuration": "Debug", + "platform": "iOS" + }, + "diagnostics": { + "warnings": [], + "errors": [] + } + } +} diff --git a/src/snapshot-tests/__tests__/json-fixture-schema.test.ts b/src/snapshot-tests/__tests__/json-fixture-schema.test.ts new file mode 100644 index 00000000..0b7a1e99 --- /dev/null +++ b/src/snapshot-tests/__tests__/json-fixture-schema.test.ts @@ -0,0 +1,21 @@ +import { describe, expect, it } from 'vitest'; +import { createStructuredFixtureSchemaValidator } from '../json-schema-validation.ts'; + +const validator = createStructuredFixtureSchemaValidator(); + +describe('structured JSON fixture schemas', () => { + it('discovers JSON fixtures', () => { + expect(validator.fixtures.length).toBeGreaterThan(0); + }); + + it('compiles all schema documents', () => { + expect(() => validator.compileAllSchemas()).not.toThrow(); + }); + + it.each(validator.fixtures.map((fixture) => [fixture.relativePath, fixture] as const))( + 'validates %s', + (_relativePath, fixture) => { + validator.validateFixture(fixture); + }, + ); +}); diff --git a/src/snapshot-tests/json-schema-validation.ts b/src/snapshot-tests/json-schema-validation.ts new file mode 100644 index 00000000..bffd6ebc --- /dev/null +++ b/src/snapshot-tests/json-schema-validation.ts @@ -0,0 +1,253 @@ +import fs from 'node:fs'; +import path from 'node:path'; +import { Ajv2020 } from 'ajv/dist/2020.js'; +import type { ErrorObject, ValidateFunction } from 'ajv'; +import { globSync } from 'glob'; + +const FIXTURE_ROOT = path.resolve(process.cwd(), 'src/snapshot-tests/__fixtures__/json'); +const SCHEMA_ROOT = path.resolve(process.cwd(), 'schemas/structured-output'); +const SCHEMA_PATTERN = /^xcodebuildmcp\.output\.[a-z0-9-]+$/; +const SCHEMA_VERSION_PATTERN = /^[0-9]+$/; + +export interface JsonFixtureEnvelopeBootstrap { + schema: string; + schemaVersion: string; + didError: boolean; + error: string | null; + data: unknown; +} + +export interface DiscoveredJsonFixture { + absolutePath: string; + relativePath: string; + envelope: JsonFixtureEnvelopeBootstrap; + schemaPath: string; +} + +interface DiscoveredSchemaDocument { + absolutePath: string; + relativePath: string; + schemaId: string; +} + +export interface StructuredFixtureSchemaValidator { + fixtures: readonly DiscoveredJsonFixture[]; + compileAllSchemas(): void; + validateFixture(fixture: DiscoveredJsonFixture): void; +} + +function toRelative(absolutePath: string): string { + return path.relative(process.cwd(), absolutePath).split(path.sep).join('/'); +} + +function isRecord(value: unknown): value is Record { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function readJsonDocument(absolutePath: string, label: string): unknown { + let raw: string; + try { + raw = fs.readFileSync(absolutePath, 'utf8'); + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to read ${label}: ${message}`); + } + + try { + return JSON.parse(raw) as unknown; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + throw new Error(`Failed to parse ${label}: ${message}`); + } +} + +function assertBootstrapEnvelope( + value: unknown, + relativePath: string, +): JsonFixtureEnvelopeBootstrap { + if (!isRecord(value)) { + throw new Error(`${relativePath}: fixture root must be a JSON object.`); + } + + const { schema, schemaVersion, didError, error, data } = value; + + if (typeof schema !== 'string') { + throw new Error(`${relativePath}: fixture must declare a string schema.`); + } + if (typeof schemaVersion !== 'string') { + throw new Error(`${relativePath}: fixture must declare a string schemaVersion.`); + } + if (typeof didError !== 'boolean') { + throw new Error(`${relativePath}: fixture must declare a boolean didError.`); + } + if (!(typeof error === 'string' || error === null)) { + throw new Error(`${relativePath}: fixture error must be a string or null.`); + } + if (!Object.prototype.hasOwnProperty.call(value, 'data')) { + throw new Error(`${relativePath}: fixture must declare a data field.`); + } + + return { schema, schemaVersion, didError, error, data }; +} + +function assertValidSchemaRoute(fixture: JsonFixtureEnvelopeBootstrap, relativePath: string): void { + if (!SCHEMA_PATTERN.test(fixture.schema)) { + throw new Error( + `${relativePath}: schema "${fixture.schema}" does not match ${SCHEMA_PATTERN.source}.`, + ); + } + if (!SCHEMA_VERSION_PATTERN.test(fixture.schemaVersion)) { + throw new Error( + `${relativePath}: schemaVersion "${fixture.schemaVersion}" does not match ${SCHEMA_VERSION_PATTERN.source}.`, + ); + } +} + +function discoverSchemaDocuments(): DiscoveredSchemaDocument[] { + const relativePaths = globSync('**/*.schema.json', { + cwd: SCHEMA_ROOT, + nodir: true, + }).sort(); + + return relativePaths.map((relativePath) => { + const absolutePath = path.join(SCHEMA_ROOT, relativePath); + const document = readJsonDocument(absolutePath, `schema ${toRelative(absolutePath)}`); + if (!isRecord(document) || typeof document.$id !== 'string' || document.$id.length === 0) { + throw new Error(`${toRelative(absolutePath)}: schema must declare a non-empty $id.`); + } + + return { + absolutePath, + relativePath: relativePath.split(path.sep).join('/'), + schemaId: document.$id, + }; + }); +} + +function discoverJsonFixtures(knownSchemaPaths: Set): DiscoveredJsonFixture[] { + const relativePaths = globSync('**/*.json', { + cwd: FIXTURE_ROOT, + nodir: true, + }).sort(); + + return relativePaths.map((relativePath) => { + const absolutePath = path.join(FIXTURE_ROOT, relativePath); + const repoRelativePath = toRelative(absolutePath); + const parsed = readJsonDocument(absolutePath, `fixture ${repoRelativePath}`); + const envelope = assertBootstrapEnvelope(parsed, repoRelativePath); + assertValidSchemaRoute(envelope, repoRelativePath); + + const schemaPath = path.join( + SCHEMA_ROOT, + envelope.schema, + `${envelope.schemaVersion}.schema.json`, + ); + + if (!knownSchemaPaths.has(schemaPath)) { + throw new Error( + `${repoRelativePath}: declared schema ${envelope.schema}@${envelope.schemaVersion} maps to missing schema file ${toRelative(schemaPath)}.`, + ); + } + + return { + absolutePath, + relativePath: relativePath.split(path.sep).join('/'), + envelope, + schemaPath, + }; + }); +} + +function formatAjvErrors(errors: ErrorObject[] | null | undefined): string { + if (!errors || errors.length === 0) { + return '- (no AJV errors reported)'; + } + + return errors + .map((error) => { + const instancePath = error.instancePath.length > 0 ? error.instancePath : '/'; + const params = Object.keys(error.params).length > 0 ? ` ${JSON.stringify(error.params)}` : ''; + return `- ${instancePath}: ${error.message ?? 'validation error'}${params}`; + }) + .join('\n'); +} + +export function createStructuredFixtureSchemaValidator(): StructuredFixtureSchemaValidator { + const schemaDocuments = discoverSchemaDocuments(); + const schemaIdsByPath = new Map( + schemaDocuments.map((schema) => [schema.absolutePath, schema.schemaId]), + ); + const knownSchemaPaths = new Set(schemaDocuments.map((schema) => schema.absolutePath)); + const fixtures = discoverJsonFixtures(knownSchemaPaths); + + const ajv = new Ajv2020({ + allErrors: true, + strict: true, + validateSchema: true, + }); + + const validatorCache = new Map(); + + for (const schema of schemaDocuments) { + const document = readJsonDocument( + schema.absolutePath, + `schema ${toRelative(schema.absolutePath)}`, + ); + if (!isRecord(document)) { + throw new Error(`${toRelative(schema.absolutePath)}: schema root must be a JSON object.`); + } + ajv.addSchema(document); + } + + function validatorForSchemaPath(schemaPath: string): ValidateFunction { + const cached = validatorCache.get(schemaPath); + if (cached) { + return cached; + } + + const schemaId = schemaIdsByPath.get(schemaPath); + if (!schemaId) { + throw new Error(`No registered schema found for ${toRelative(schemaPath)}.`); + } + + const validator = ajv.getSchema(schemaId); + if (!validator) { + throw new Error(`AJV failed to compile schema ${schemaId} from ${toRelative(schemaPath)}.`); + } + + validatorCache.set(schemaPath, validator); + return validator; + } + + return { + fixtures, + compileAllSchemas(): void { + for (const schema of schemaDocuments) { + validatorForSchemaPath(schema.absolutePath); + } + }, + validateFixture(fixture: DiscoveredJsonFixture): void { + const validate = validatorForSchemaPath(fixture.schemaPath); + const parsed = readJsonDocument( + fixture.absolutePath, + `fixture ${toRelative(fixture.absolutePath)}`, + ); + + if (validate(parsed)) { + return; + } + + const schemaId = schemaIdsByPath.get(fixture.schemaPath) ?? '(unknown schema id)'; + throw new Error( + [ + `Fixture validation failed: ${fixture.relativePath}`, + `Declared schema: ${fixture.envelope.schema}@${fixture.envelope.schemaVersion}`, + `Resolved schema: ${toRelative(fixture.schemaPath)}`, + `Schema $id: ${schemaId}`, + 'AJV errors:', + formatAjvErrors(validate.errors), + ].join('\n'), + ); + }, + }; +}