diff --git a/.github/workflows/open-vsx.yml b/.github/workflows/open-vsx.yml
new file mode 100644
index 0000000..18c3994
--- /dev/null
+++ b/.github/workflows/open-vsx.yml
@@ -0,0 +1,72 @@
+name: Publish Open VSX
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: Existing XBridge GitHub release tag (for example, v1.4.2)
+ required: true
+ type: string
+
+permissions:
+ contents: read
+
+jobs:
+ publish:
+ name: Publish release VSIX to Open VSX
+ runs-on: ubuntu-latest
+ env:
+ RELEASE_TAG: ${{ inputs.tag }}
+
+ steps:
+ - name: Set up Node.js
+ uses: actions/setup-node@v5
+ with:
+ node-version: 22
+
+ - name: Check configuration and release tag
+ env:
+ OVSX_PAT: ${{ secrets.OVSX_PAT }}
+ run: |
+ if [ -z "$OVSX_PAT" ]; then
+ echo "::error::Add an Open VSX access token as the OVSX_PAT repository secret."
+ exit 1
+ fi
+ if [[ ! "$RELEASE_TAG" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+ echo "::error::Use an existing XBridge release tag such as v1.4.2."
+ exit 1
+ fi
+
+ - name: Download and verify the GitHub release VSIX
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ VERSION="${RELEASE_TAG#v}"
+ PACKAGE="builds/xbridge-${VERSION}.vsix"
+ mkdir -p builds
+ gh release download "$RELEASE_TAG" --repo "$GITHUB_REPOSITORY" --pattern "xbridge-${VERSION}.vsix" --dir builds
+ test -s "$PACKAGE"
+ IDENTITY=$(unzip -p "$PACKAGE" extension/package.json | node -e 'let data = ""; process.stdin.on("data", chunk => data += chunk).on("end", () => { const manifest = JSON.parse(data); console.log(`${manifest.publisher}.${manifest.name}@${manifest.version}`); })')
+ if [ "$IDENTITY" != "chaiwithcode.xbridge@$VERSION" ]; then
+ echo "::error::The release VSIX identity does not match chaiwithcode.xbridge@$VERSION."
+ exit 1
+ fi
+
+ - name: Create the namespace if needed
+ env:
+ OVSX_PAT: ${{ secrets.OVSX_PAT }}
+ run: |
+ HTTP_STATUS=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' https://open-vsx.org/api/chaiwithcode)
+ case "$HTTP_STATUS" in
+ 200) echo "The chaiwithcode namespace already exists." ;;
+ 404) npx --yes ovsx@1.1.1 create-namespace chaiwithcode ;;
+ *) echo "::error::Open VSX namespace lookup returned HTTP $HTTP_STATUS."; exit 1 ;;
+ esac
+
+ - name: Publish to Open VSX
+ env:
+ OVSX_PAT: ${{ secrets.OVSX_PAT }}
+ run: |
+ VERSION="${RELEASE_TAG#v}"
+ npx --yes ovsx@1.1.1 publish "builds/xbridge-${VERSION}.vsix" --skip-duplicate
+ echo "[Check the Open VSX listing](https://open-vsx.org/extension/chaiwithcode/xbridge)" >> "$GITHUB_STEP_SUMMARY"
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a5d4069..320d6a2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -54,6 +54,6 @@ jobs:
- name: Publish to Open VSX
if: env.OVSX_PAT != ''
- run: npx ovsx publish builds/*.vsix --skip-duplicate
+ run: npx --yes ovsx@1.1.1 publish builds/*.vsix --skip-duplicate
env:
OVSX_PAT: ${{ secrets.OVSX_PAT }}
diff --git a/.vscodeignore b/.vscodeignore
index aa1677c..8fa4baf 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -13,3 +13,5 @@ esbuild.js
Makefile
**/*.tsbuildinfo
node_modules/**
+builds/**
+examples/**
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8832d65..7560ecc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
# Changelog
+## 1.4.2
+
+- Replaced retired Visual Studio Marketplace metadata badges with supported release and install badges.
+- Added a direct Marketplace installation link and extension identifier to the README.
+- Excluded local build outputs from published VSIX packages.
+
## 1.4.1
- Replaced the generic crossed-tools artwork with a distinctive connected-X brand mark.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 6c0f69f..d2bbb7d 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -99,6 +99,8 @@ git switch -c feature/my-feature
Open the pull request against `develop`. When preparing a release, create `release/` from `develop`, complete final verification, and open a pull request into `main`. After release, merge `main` back into `develop` so both branches contain the release commit and tag.
+Maintainers preparing the second registry listing can follow the [Open VSX publishing guide](docs/open-vsx.md).
+
---
## Submitting Pull Requests
diff --git a/README.md b/README.md
index bc814f5..afb04c6 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,8 @@
-
-
+
+
@@ -17,6 +17,8 @@
+ Installation •
+ Demo •
Features •
AI Tools •
Physical Devices •
@@ -27,6 +29,22 @@
---
+## Installation
+
+Install [XBridge from the Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=chaiwithcode.xbridge), or open the Extensions view in VS Code and search for `chaiwithcode.xbridge`.
+
+---
+
+## See XBridge in action
+
+
+
+The tour uses the included [XBridgeDemo Xcode project](examples/XBridgeDemo). View the full-resolution captures: [project and destination controls](docs/images/overview.png), [XCTest and Swift Testing in Test Explorer](docs/images/tests.png), [build and run](docs/images/build-and-run.png), and [the iPhone app](docs/images/demo-app.png).
+
+[Explore every XBridge command and AI tool](docs/capabilities.md).
+
+---
+
## Why XBridge?
- Run the core Xcode workflow from the editor where you write Swift.
diff --git a/docs/capabilities.md b/docs/capabilities.md
new file mode 100644
index 0000000..0c3be71
--- /dev/null
+++ b/docs/capabilities.md
@@ -0,0 +1,80 @@
+# XBridge capabilities
+
+XBridge puts the everyday Xcode workflow in VS Code. These are the commands and tools available in version 1.4.2. Open the Command Palette and type `XBridge:` to find a command; the most common actions also appear in the XBridge sidebar and status bar.
+
+## Project and workflow
+
+| Command | What it does |
+|:---|:---|
+| `XBridge: Select Xcode Project or Workspace` | Choose an `.xcodeproj`, `.xcworkspace`, or Swift package when more than one is available. |
+| `XBridge: Select Scheme` | Choose the Xcode scheme. |
+| `XBridge: Select Build Configuration` | Switch between configurations such as Debug and Release. |
+| `XBridge: Select Scheme, Configuration or Destination` | Open one picker for the active build target. |
+| `XBridge: Refresh Schemes` | Reload schemes and targets from Xcode. |
+| `XBridge: Open in Xcode` | Open the active project or workspace in Xcode. |
+| `XBridge: Getting Started` | Open the guided VS Code walkthrough. |
+
+## Build, run, test, and packages
+
+| Command | What it does |
+|:---|:---|
+| `XBridge: Build` | Compile the active scheme. |
+| `XBridge: Build & Run` | Build, install, and launch the app on the selected destination. |
+| `XBridge: Stop` | Cancel the active operation or stop the running app. |
+| `XBridge: Stop Running App` | Terminate the launched app without shutting down the simulator. |
+| `XBridge: Clean Build Folder` | Remove the active project's DerivedData build products. |
+| `XBridge: Run Tests` | Run the selected test plan or target with `xcodebuild`. |
+| `XBridge: Select Test Plan` | Choose an Xcode test plan, if the scheme has one. |
+| `XBridge: Select Test Target` | Limit a run to one test target. |
+| `XBridge: Resolve Package Dependencies` | Resolve Swift Package Manager dependencies through Xcode. |
+
+The native VS Code Test Explorer discovers XCTest methods and Swift Testing `@Test` methods, shows per-test results, and lets you run individual tests. Build errors and test failures appear in Problems and at their source locations.
+
+## Results and logs
+
+| Command | What it does |
+|:---|:---|
+| `XBridge: Show Build Output` | Open the `XBridge Build` output channel. |
+| `XBridge: Show Build Issues` | Browse errors and warnings and jump to the source line. |
+| `XBridge: Show App Console` | Open the launched app's console output. |
+
+The status bar also shows the selected scheme and destination, build activity, and error and warning counts.
+
+## Simulators and devices
+
+| Command | What it does |
+|:---|:---|
+| `XBridge: Select Destination (Simulator/Device)` | Choose a simulator or connected physical device. |
+| `XBridge: Set as Destination` | Use the selected device from the Destinations tree. |
+| `XBridge: Boot Simulator` | Start a simulator. |
+| `XBridge: Shutdown Simulator` | Shut down a simulator. |
+| `XBridge: Open Simulator.app` | Bring Apple's Simulator app forward. |
+| `XBridge: Refresh Simulators` | Reload the local simulator list. |
+| `XBridge: Refresh Devices & Simulators` | Reload simulator and connected-device discovery. |
+| `XBridge: Erase Simulator Content` | Reset a simulator's content and settings. |
+| `XBridge: Copy Simulator UDID` | Copy the simulator identifier. |
+| `XBridge: Open URL or Deep Link…` | Open a URL in a simulator. |
+| `XBridge: Take Screenshot` | Save a PNG from a booted simulator. |
+| `XBridge: Start / Stop Screen Recording` | Save a simulator recording as video. |
+| `XBridge: Toggle Light / Dark Appearance` | Change the simulator appearance. |
+| `XBridge: Open App Data Container` | Open the installed app's container in Finder. |
+| `XBridge: Uninstall App from Simulator` | Remove the installed app. |
+
+Connected iPhones and iPads appear in Destinations through Apple's `devicectl`. Physical-device builds require working code signing and Developer Mode.
+
+## AI agent tools
+
+Compatible VS Code AI agents can call these eight tools directly:
+
+| Tool | Capability |
+|:---|:---|
+| `xbridge_build` | Build and return compiler diagnostics. |
+| `xbridge_test` | Run tests and return failures. |
+| `xbridge_run` | Build and launch an app. |
+| `xbridge_listSchemes` | List schemes, configurations, and targets. |
+| `xbridge_listSimulators` | List available simulators and status. |
+| `xbridge_bootSimulator` | Boot a simulator. |
+| `xbridge_readDiagnostics` | Read the last build or test diagnostics. |
+| `xbridge_clean` | Clean the Xcode build folder. |
+
+See the [main README](../README.md#settings) for the eleven configurable settings and [XBridgeDemo](../examples/XBridgeDemo) for a project you can try locally.
diff --git a/docs/images/build-and-run.png b/docs/images/build-and-run.png
new file mode 100644
index 0000000..2b6772c
Binary files /dev/null and b/docs/images/build-and-run.png differ
diff --git a/docs/images/demo-app.png b/docs/images/demo-app.png
new file mode 100644
index 0000000..af59a8a
Binary files /dev/null and b/docs/images/demo-app.png differ
diff --git a/docs/images/feature-tour.gif b/docs/images/feature-tour.gif
new file mode 100644
index 0000000..7dcf4e0
Binary files /dev/null and b/docs/images/feature-tour.gif differ
diff --git a/docs/images/overview.png b/docs/images/overview.png
new file mode 100644
index 0000000..cc1011c
Binary files /dev/null and b/docs/images/overview.png differ
diff --git a/docs/images/tests.png b/docs/images/tests.png
new file mode 100644
index 0000000..0de32bd
Binary files /dev/null and b/docs/images/tests.png differ
diff --git a/docs/open-vsx.md b/docs/open-vsx.md
new file mode 100644
index 0000000..541f96c
--- /dev/null
+++ b/docs/open-vsx.md
@@ -0,0 +1,24 @@
+# Publishing XBridge to Open VSX
+
+Open VSX is a separate registry from the Visual Studio Marketplace. XBridge's extension ID is `chaiwithcode.xbridge` in both registries, but the Marketplace publisher account and token do not authorize Open VSX publishing.
+
+## One-time publisher setup
+
+1. Follow the [Open VSX publisher guide](https://github.com/eclipse-openvsx/openvsx/wiki/Publishing-Extensions): sign in to [open-vsx.org](https://open-vsx.org/) with the GitHub account that owns XBridge, connect the matching Eclipse account, and accept the Open VSX Publisher Agreement.
+2. Generate an Open VSX access token in your Open VSX account settings. Add it to this repository's GitHub Actions secrets as `OVSX_PAT`. Do not put the token in the repository, a command argument, or an issue.
+3. Merge the `Publish Open VSX` workflow into the repository's default branch. In GitHub Actions, run **Publish Open VSX** with an existing GitHub release tag such as `v1.4.2`. The workflow downloads that release's VSIX, verifies its publisher, name, and version, creates the `chaiwithcode` namespace if necessary, and uploads the exact release artifact. Re-running it skips a duplicate version.
+4. Inspect the [XBridge Open VSX listing](https://open-vsx.org/extension/chaiwithcode/xbridge), including the icon, description, README, license, and version. If you want the verified-publisher shield, [claim ownership of the namespace](https://github.com/eclipse-openvsx/openvsx/wiki/Namespace-Access); creating the namespace alone does not grant that status.
+
+The existing `v1.4.2` GitHub release contains `xbridge-1.4.2.vsix`. Its SHA-256 is `aa68331c49396d0014bae908e4348b0995f995b1d9c67e159d5a5f03add700cc`. Use that release artifact for a first `v1.4.2` submission. A new package built from `develop` includes README screenshots that are not yet on the default branch, so those links would remain unavailable until a later release merges the assets into `main`.
+
+## Future releases
+
+The tag-triggered [release workflow](../.github/workflows/release.yml) builds one VSIX for the GitHub release, the Visual Studio Marketplace, and Open VSX. When `OVSX_PAT` is configured, its Open VSX step publishes the same VSIX after a `v*` tag is pushed. Each registry accepts a given extension version only once; use a new version for updated contents.
+
+The [Open VSX CLI](https://github.com/eclipse-openvsx/openvsx/blob/main/cli/README.md) also supports publishing an already packaged VSIX locally with `OVSX_PAT` set in the environment:
+
+```sh
+npx --yes ovsx@1.1.1 publish path/to/xbridge-version.vsix --skip-duplicate
+```
+
+The manual GitHub workflow is preferred for a release because it uses the exact VSIX already attached to that release. After publication, verify the version through the listing or the [registry API](https://open-vsx.org/api/chaiwithcode/xbridge).
diff --git a/examples/XBridgeDemo/.gitignore b/examples/XBridgeDemo/.gitignore
new file mode 100644
index 0000000..445b095
--- /dev/null
+++ b/examples/XBridgeDemo/.gitignore
@@ -0,0 +1,4 @@
+.build/
+.vscode/
+*.xcuserstate
+xcuserdata/
diff --git a/examples/XBridgeDemo/README.md b/examples/XBridgeDemo/README.md
new file mode 100644
index 0000000..87dcb3e
--- /dev/null
+++ b/examples/XBridgeDemo/README.md
@@ -0,0 +1,9 @@
+# XBridge Demo
+
+A small, buildable SwiftUI app used to demonstrate XBridge in VS Code. It includes one XCTest and one Swift Testing test so both appear in Test Explorer.
+
+1. Open this folder in VS Code on a Mac with Xcode installed.
+2. Select the `XBridgeDemo` scheme and an iOS Simulator in the XBridge sidebar.
+3. Use **Build & Run** to launch the app, or open Test Explorer and run both tests.
+
+The app has no external packages or signing requirement for Simulator. Xcode build products and local VS Code settings are ignored by Git.
diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..bb2437b
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/project.pbxproj
@@ -0,0 +1,459 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; };
+ 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */; };
+ 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */; };
+ 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */; };
+ 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C94386DDF7FC57A34733389 /* ContentView.swift */; };
+ A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D21DF1F5E075569C85A5A80D /* Foundation.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 926908D120004E2CDF95387F /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 38C28A02E02E1A716344BAFD;
+ remoteInfo = XBridgeDemo;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoApp.swift; sourceTree = ""; };
+ 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ModernTests.swift; sourceTree = ""; };
+ 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XBridgeDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XBridgeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = XBridgeDemoTests.swift; sourceTree = ""; };
+ 8C94386DDF7FC57A34733389 /* ContentView.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ D21DF1F5E075569C85A5A80D /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 5623CC290D6BB5B3555C9E37 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ A056ED2D15E03B6267D834B4 /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 797F4E7CC0A84E8ADD920E9C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 3339FC342000793B43D0BD6E /* Foundation.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 4F75EA34634134EB2327C973 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */,
+ 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 5AABD117A77BFAA3F72F4001 = {
+ isa = PBXGroup;
+ children = (
+ 4F75EA34634134EB2327C973 /* Products */,
+ 76777653C83C84E6DFCCB739 /* Frameworks */,
+ A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */,
+ 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */,
+ );
+ sourceTree = "";
+ };
+ 76777653C83C84E6DFCCB739 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ C4778F146AEF9B99291A3F90 /* iOS */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ 79D6CE832ACE6D6DC6AC9F05 /* XBridgeDemoTests */ = {
+ isa = PBXGroup;
+ children = (
+ 89D0980295DEE023A32B5DC9 /* XBridgeDemoTests.swift */,
+ 1ABB7018A6B79EA9E51BB8FB /* ModernTests.swift */,
+ );
+ name = XBridgeDemoTests;
+ path = XBridgeDemoTests;
+ sourceTree = "";
+ };
+ A4B20FD811B3FAA4740D5F89 /* XBridgeDemo */ = {
+ isa = PBXGroup;
+ children = (
+ 15A1711653BCBC7EB5A424C4 /* XBridgeDemoApp.swift */,
+ 8C94386DDF7FC57A34733389 /* ContentView.swift */,
+ );
+ name = XBridgeDemo;
+ path = XBridgeDemo;
+ sourceTree = "";
+ };
+ C4778F146AEF9B99291A3F90 /* iOS */ = {
+ isa = PBXGroup;
+ children = (
+ D21DF1F5E075569C85A5A80D /* Foundation.framework */,
+ );
+ name = iOS;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 38C28A02E02E1A716344BAFD /* XBridgeDemo */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */;
+ buildPhases = (
+ 3ED4D77EFBB1BAF4890B3D9F /* Sources */,
+ 797F4E7CC0A84E8ADD920E9C /* Frameworks */,
+ 5A26789469FEB06DBB37C2A2 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = XBridgeDemo;
+ productName = XBridgeDemo;
+ productReference = 6238FA757782EB5EADFF0828 /* XBridgeDemo.app */;
+ productType = "com.apple.product-type.application";
+ };
+ E343816C6E356CED400EAB1F /* XBridgeDemoTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */;
+ buildPhases = (
+ 95855EDEB98C88C5E50A19C9 /* Sources */,
+ 5623CC290D6BB5B3555C9E37 /* Frameworks */,
+ 56C8A09B750384342F4A89E2 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */,
+ );
+ name = XBridgeDemoTests;
+ productName = XBridgeDemoTests;
+ productReference = 2C5D9B1CEA7CDD627FE83B70 /* XBridgeDemoTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 926908D120004E2CDF95387F /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 2660;
+ LastUpgradeCheck = 2660;
+ };
+ buildConfigurationList = 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 5AABD117A77BFAA3F72F4001;
+ minimizedProjectReferenceProxies = 0;
+ preferredProjectObjectVersion = 77;
+ productRefGroup = 4F75EA34634134EB2327C973 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 38C28A02E02E1A716344BAFD /* XBridgeDemo */,
+ E343816C6E356CED400EAB1F /* XBridgeDemoTests */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 56C8A09B750384342F4A89E2 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 5A26789469FEB06DBB37C2A2 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 3ED4D77EFBB1BAF4890B3D9F /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7A85D9FDE9A8B9352A26476B /* XBridgeDemoApp.swift in Sources */,
+ 9F3DFB026CACCEA2DD8BF023 /* ContentView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ 95855EDEB98C88C5E50A19C9 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 7A99EADFB04BDDA6FD8EA929 /* XBridgeDemoTests.swift in Sources */,
+ 42D7BFFFA5CBABA2F46D7E50 /* ModernTests.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ A3AC2A892FD3D0A005C28BE6 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ name = XBridgeDemo;
+ target = 38C28A02E02E1A716344BAFD /* XBridgeDemo */;
+ targetProxy = 5744DE6251B303F1CB3C9DF3 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+ 03F3B0CAB58D500D2D3DCF86 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ENABLE_OBJC_WEAK = NO;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo";
+ };
+ name = Debug;
+ };
+ 44B5F68238D33181F33D81DC /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 5915172CE0D64BA5F77731AE /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_ENABLE_OBJC_WEAK = NO;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 7E6DCA70F2C65BCFF5E627F8 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CLANG_ENABLE_OBJC_WEAK = NO;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemoTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/XBridgeDemo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/XBridgeDemo";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ B3BC7A1BB930F45E88B8E6F9 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ C0216E6A1D67B593D084CC55 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
+ CLANG_ENABLE_OBJC_WEAK = NO;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_TEAM = "";
+ GENERATE_INFOPLIST_FILE = YES;
+ INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
+ INFOPLIST_KEY_UILaunchScreen_Generation = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 17.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = com.chaiwithcode.XBridgeDemo;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SDKROOT = iphoneos;
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 4FE1DAE27A9BF849A7EFCED6 /* Build configuration list for PBXProject "XBridgeDemo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B3BC7A1BB930F45E88B8E6F9 /* Debug */,
+ 44B5F68238D33181F33D81DC /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 974B1B4BE0A6E8DBF5A93B89 /* Build configuration list for PBXNativeTarget "XBridgeDemoTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 7E6DCA70F2C65BCFF5E627F8 /* Release */,
+ 03F3B0CAB58D500D2D3DCF86 /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 9B8353B98D321DDF7007E5DD /* Build configuration list for PBXNativeTarget "XBridgeDemo" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ C0216E6A1D67B593D084CC55 /* Release */,
+ 5915172CE0D64BA5F77731AE /* Debug */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 926908D120004E2CDF95387F /* Project object */;
+}
diff --git a/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme
new file mode 100644
index 0000000..be305a4
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemo.xcodeproj/xcshareddata/xcschemes/XBridgeDemo.xcscheme
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/examples/XBridgeDemo/XBridgeDemo/ContentView.swift b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift
new file mode 100644
index 0000000..775e19f
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemo/ContentView.swift
@@ -0,0 +1,71 @@
+import SwiftUI
+
+struct ContentView: View {
+ private let features = [
+ Feature(title: "Build", detail: "Compile with xcodebuild", symbol: "hammer.fill", color: .blue),
+ Feature(title: "Test", detail: "XCTest & Swift Testing", symbol: "checkmark.seal.fill", color: .green),
+ Feature(title: "Run", detail: "Simulator or device", symbol: "play.circle.fill", color: .indigo),
+ Feature(title: "Diagnose", detail: "Inline build issues", symbol: "waveform.path.ecg", color: .orange),
+ ]
+
+ var body: some View {
+ NavigationStack {
+ ScrollView {
+ VStack(alignment: .leading, spacing: 24) {
+ VStack(alignment: .leading, spacing: 8) {
+ Image(systemName: "point.3.connected.trianglepath.dotted")
+ .font(.system(size: 42, weight: .semibold))
+ .foregroundStyle(.blue.gradient)
+
+ Text("Xcode power.\nVS Code flow.")
+ .font(.system(size: 36, weight: .bold, design: .rounded))
+
+ Text("A tiny project built to demonstrate XBridge.")
+ .foregroundStyle(.secondary)
+ }
+
+ LazyVGrid(columns: [GridItem(.flexible()), GridItem(.flexible())], spacing: 14) {
+ ForEach(features) { feature in
+ VStack(alignment: .leading, spacing: 10) {
+ Image(systemName: feature.symbol)
+ .font(.title2)
+ .foregroundStyle(feature.color)
+ Text(feature.title)
+ .font(.headline)
+ Text(feature.detail)
+ .font(.caption)
+ .foregroundStyle(.secondary)
+ }
+ .frame(maxWidth: .infinity, minHeight: 108, alignment: .leading)
+ .padding()
+ .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 18))
+ }
+ }
+
+ Label("Connected to XBridge", systemImage: "checkmark.circle.fill")
+ .font(.subheadline.weight(.semibold))
+ .foregroundStyle(.green)
+ .frame(maxWidth: .infinity)
+ .padding()
+ .background(.green.opacity(0.1), in: Capsule())
+ }
+ .padding(24)
+ }
+ .background(Color(.systemGroupedBackground))
+ .navigationTitle("XBridge Demo")
+ }
+ }
+}
+
+private struct Feature: Identifiable {
+ let title: String
+ let detail: String
+ let symbol: String
+ let color: Color
+
+ var id: String { title }
+}
+
+#Preview {
+ ContentView()
+}
diff --git a/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift
new file mode 100644
index 0000000..6595fd1
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemo/XBridgeDemoApp.swift
@@ -0,0 +1,10 @@
+import SwiftUI
+
+@main
+struct XBridgeDemoApp: App {
+ var body: some Scene {
+ WindowGroup {
+ ContentView()
+ }
+ }
+}
diff --git a/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift
new file mode 100644
index 0000000..8cd4cae
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemoTests/ModernTests.swift
@@ -0,0 +1,9 @@
+import Testing
+@testable import XBridgeDemo
+
+struct ModernTests {
+ @Test("XBridge discovers Swift Testing tests")
+ func xbridgeDiscoversModernTests() {
+ #expect("XBridge".hasPrefix("X"))
+ }
+}
diff --git a/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift
new file mode 100644
index 0000000..1ce42de
--- /dev/null
+++ b/examples/XBridgeDemo/XBridgeDemoTests/XBridgeDemoTests.swift
@@ -0,0 +1,8 @@
+import XCTest
+@testable import XBridgeDemo
+
+final class XBridgeDemoTests: XCTestCase {
+ func testDemoProjectLoads() {
+ XCTAssertEqual(2 + 2, 4)
+ }
+}
diff --git a/package-lock.json b/package-lock.json
index 181bb2e..0235ae5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "xbridge",
- "version": "1.4.1",
+ "version": "1.4.2",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "xbridge",
- "version": "1.4.1",
+ "version": "1.4.2",
"license": "MIT",
"devDependencies": {
"@types/node": "^20.14.0",
diff --git a/package.json b/package.json
index 8e76803..7d4bdd6 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "xbridge",
"displayName": "XBridge",
"description": "Build, test, and run Xcode projects from VS Code—with simulator, device, Test Explorer, and AI agent tools.",
- "version": "1.4.1",
+ "version": "1.4.2",
"publisher": "chaiwithcode",
"author": {
"name": "Deepak Sharma"