Full Code of johnno1962/InjectionNext for AI

main 7bae3044bc64 cached
55 files
337.7 KB
85.3k tokens
7 symbols
1 requests
Download .txt
Showing preview only (356K chars total). Download the full file or copy to clipboard to get everything.
Repository: johnno1962/InjectionNext
Branch: main
Commit: 7bae3044bc64
Files: 55
Total size: 337.7 KB

Directory structure:
gitextract_vqy0p27r/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .gitmodules
├── .swiftpm/
│   └── xcode/
│       └── package.xcworkspace/
│           └── contents.xcworkspacedata
├── App/
│   ├── InjectionBundle/
│   │   ├── Info.plist
│   │   └── InjectionBundle-Bridging-Header.h
│   ├── InjectionNext/
│   │   ├── App.icns
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   └── MainMenu.xib
│   │   ├── ControlServer.swift
│   │   ├── Defaults.swift
│   │   ├── Experimental.swift
│   │   ├── FrontendServer.swift
│   │   ├── Info.plist
│   │   ├── InjectionBusy.tif
│   │   ├── InjectionError.tif
│   │   ├── InjectionHybrid.swift
│   │   ├── InjectionIdle.tif
│   │   ├── InjectionNext-Bridging-Header.h
│   │   ├── InjectionNext.entitlements
│   │   ├── InjectionOK.tif
│   │   ├── InjectionReady.tif
│   │   ├── InjectionServer.swift
│   │   ├── MonitorXcode.swift
│   │   ├── NextCompiler.swift
│   │   ├── build_bundle.sh
│   │   ├── build_bundles.sh
│   │   ├── copy_bundle.sh
│   │   ├── main.m
│   │   └── swift-frontend.sh
│   ├── InjectionNext.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       ├── IDEWorkspaceChecks.plist
│   │   │       └── WorkspaceSettings.xcsettings
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── InjectionNext.xcscheme
│   ├── LICENSE
│   ├── NIMBLE.md
│   ├── QUICK.md
│   └── feedcommands/
│       └── main.mm
├── BAZEL.md
├── INTRO.md
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   ├── InjectionNext/
│   │   └── InjectionNext.swift
│   └── InjectionNextC/
│       ├── ClientBoot.mm
│       ├── SimpleSocket.mm
│       └── include/
│           ├── InjectionClient.h
│           └── SimpleSocket.h
├── Tests/
│   └── InjectionNextTests/
│       └── InjectionNextTests.swift
└── mcp-server/
    ├── .gitignore
    ├── README.md
    ├── index.js
    └── package.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms

github: johnno1962
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']


================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

## User settings
xcuserdata/

## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
*.xcscmblueprint
*.xccheckout

## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
build/
DerivedData/
*.moved-aside
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3

## Obj-C/Swift specific
*.hmap

## App packaging
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
# Package.pins
Package.resolved
# *.xcodeproj
#
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
# hence it is not needed unless you have added a package configuration file to your project
# .swiftpm

.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
#
# Add this line if you want to avoid checking in source code from the Xcode workspace
# *.xcworkspace

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
# Carthage/Checkouts

Carthage/Build/

# Accio dependency management
Dependencies/
.accio/

# fastlane
#
# It is recommended to not store the screenshots in the git repo.
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/#source-control

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots/**/*.png
fastlane/test_output

# Code Injection
#
# After new code Injection tools there's a generated folder /iOSInjectionProject
# https://github.com/johnno1962/injectionforxcode

iOSInjectionProject/


================================================
FILE: .gitmodules
================================================
[submodule "fishhook"]
	path = fishhook
	url = https://github.com/johnno1962/fishhook
[submodule "DLKit"]
	path = DLKit
	url = https://github.com/johnno1962/DLKit
[submodule "InjectionLite"]
	path = InjectionLite
	url = https://github.com/johnno1962/InjectionLite
[submodule "SwiftRegex5"]
	path = SwiftRegex5
	url = https://github.com/johnno1962/SwiftRegex5
[submodule "SwiftTrace"]
	path = SwiftTrace
	url = https://github.com/johnno1962/SwiftTrace


================================================
FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: App/InjectionBundle/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>BNDL</string>
	<key>CFBundleShortVersionString</key>
	<string>1.0</string>
	<key>CFBundleVersion</key>
	<string>1</string>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2017 John Holdsworth. All rights reserved.</string>
	<key>NSPrincipalClass</key>
	<string></string>
</dict>
</plist>


================================================
FILE: App/InjectionBundle/InjectionBundle-Bridging-Header.h
================================================
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import <Foundation/Foundation.h> // Xcode 16.3??

#import "SimpleSocket.h"
#import "InjectionImplC.h"
#import "InjectionClient.h"

// InjectionBundle only
#import "fishhook.h"
#import "DLKitC.h"

#import "SwiftTrace.h"


================================================
FILE: App/InjectionNext/AppDelegate.swift
================================================
//
//  AppDelegate.swift
//  InjectionNext
//
//  Created by John Holdsworth on 06/11/2017.
//  Copyright © 2017 John Holdsworth. All rights reserved.
//
//  $Id: //depot/HotReloading/Sources/injectiond/AppDelegate.swift#76 $
//
//  Implementation Toolbar menu "UI".
//

import Cocoa
import Popen
import SwiftRegex

enum InjectionState: String {
    case ok = "OK" // Orange
    case idle = "Idle" // Blue
    case busy = "Busy" // Green
    case ready = "Ready" // Purple
    case error = "Error" // Yellow
}

@objc(AppDelegate)
class AppDelegate: NSObject, NSApplicationDelegate {

    static var ui: AppDelegate!

    // Status menu
    @IBOutlet weak var statusMenu: NSMenu!
    @IBOutlet var statusItem: NSStatusItem!
    // Codesigning identity
    @IBOutlet var identityField: NSTextField!
    // Enable injection on devices
    @IBOutlet var deviceTesting: NSButton!
    // Testing libraries to link with
    @IBOutlet var librariesField: NSTextField!
    // Place to display last error that occured
    @IBOutlet var lastErrorField: NSTextView!
    // Restart XCode if crashed.
    @IBOutlet weak var launchXcodeItem: NSMenuItem!
    @IBOutlet weak var selectXcodeItem: NSMenuItem!
    @IBOutlet weak var restartDeviceItem: NSMenuItem!
    @IBOutlet weak var patchCompilerItem: NSMenuItem!
    @IBOutlet weak var enableDevicesItem: NSMenuItem!
    @IBOutlet weak var watchDirectoryItem: NSMenuItem!

    // Interface to app's persistent state.
    @objc let defaults = Defaults.userDefaults

    @IBOutlet weak var codeSignBox: NSComboBox!

    /// Code signing ID as parsed from the code signing box. If the content of the box is not
    /// parsable as SHA1 code signing ID, an empty string.
    var codeSigningID: String { codeSignBox.stringValue.containedSHA1 ?? "" }

    let userIDComboBoxDataSaver = UserIDComboBoxDataSaver()

    @objc func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Insert code here to initialize your application
        Self.ui = self

        let appName = "InjectionNext"

        if Bundle.main.infoDictionary?["LSUIElement"] as? Bool != true {
            NSApp.mainMenu?.item(withTitle: "File")?.submenu = statusMenu
        } else {
            let statusBar = NSStatusBar.system
            statusItem = statusBar.statusItem(withLength: statusBar.thickness)
            statusItem.highlightMode = true
            statusItem.menu = statusMenu
            statusItem.isEnabled = true
            statusItem.title = appName
            setMenuIcon(.idle)
        }

        signal(SIGPIPE, { which in
            print(APP_PREFIX+"⚠️ SIGPIPE #\(which)\n" +
                  Thread.callStackSymbols.map { var frame = $0
                        frame[#"(?:\S+\s+){3}(\S+)"#, 1] = {
                            (groups: [String], stop) in
                            return groups[1].swiftDemangle ?? groups[1] }
                        return frame
                    }.joined(separator: "\n")) })

        if let quit = statusMenu.item(at: statusMenu.items.count-1) {
            quit.title = "Quit "+appName
            if let build = Bundle.main
                .infoDictionary?[kCFBundleVersionKey as String] {
                quit.toolTip = "Quit (build #\(build))"
            }
        }

        librariesField.stringValue = Defaults.deviceLibraries
        let enableDevicesSticky = false
        if !enableDevicesSticky || Defaults.codesigningIdentity == nil {
            enableDevicesItem.state = .on
        }
        deviceEnable(nil)
        if let xcodePath = NSRunningApplication
            .runningApplications(withBundleIdentifier: "com.apple.dt.Xcode")
            .first?.bundleURL?.path {
            if Defaults.xcodeDefault == nil {
                Defaults.xcodeDefault = xcodePath
            }
            selectXcodeItem.toolTip = Defaults.xcodePath
            if updatePatchUnpatch() == .unpatched &&
                getenv(INJECTION_HIDE_XCODE_ALERT) == nil {
                InjectionServer.alert("""
                    Please quit Xcode and
                    use this app to launch it
                    (unless you are using a file watcher).
                    """)
            }
        }
 
        setupCodeSigningComboBox()
        restartDeviceItem.state = Defaults.xcodeRestart ? .on : .off
        selectXcodeItem.toolTip = Defaults.xcodePath

//        #if DEBUG
//        if NSHomeDirectory() == "/Users/johnholdsworth",
//           let path = Bundle.main.path(forResource: "macOSInjection", ofType: "bundle"),
//           let bundle = Bundle(path: path),
//           bundle.load() {
//        }
//        #endif

        if let project = Defaults.projectPath {
            _ = MonitorXcode(args: " '\(project)'")
        }

        if Defaults.mcpServer {
            LogBuffer.shared = LogBuffer()
            ControlServer.start()
        }
    }

    func setMenuIcon(_ state: InjectionState) {
        DispatchQueue.main.async {
            let tiffName = "Injection"+state.rawValue
            if let path = Bundle.main.path(forResource: tiffName, ofType: "tif"),
                let image = NSImage(contentsOfFile: path) {
    //            image.template = TRUE;
                self.statusItem.image = image
                self.statusItem.alternateImage = image
            }
        }
    }

    @IBAction func runXcode(_ sender: Any) {
        if MonitorXcode.runningXcode == nil {
            _ = MonitorXcode()
        }
    }

    @IBAction func selectXcode(_ sender: NSMenuItem) {
        let open = NSOpenPanel()
        open.prompt = "Select Xcode"
        open.directoryURL = URL(fileURLWithPath: Defaults.xcodePath)
        open.canChooseDirectories = false
        open.canChooseFiles = true
        if open.runModal() == .OK, let path = open.url?.path {
            selectXcodeItem.toolTip = path
            Defaults.xcodeDefault = path
            updatePatchUnpatch()
            if Defaults.xcodeRestart {
                runXcode(sender)
            }
        }
    }

    lazy var startHostLocatingServerOnce: () = {
        InjectionServer.multicastServe(HOTRELOADING_MULTICAST,
                                       port: HOTRELOADING_PORT)
    }()

    @IBAction func deviceEnable(_ sender: NSMenuItem?) {
        var openPort = ""
        if enableDevicesItem.state.toggle() == .on {
            if sender != nil {
                NSApplication.shared.activate(ignoringOtherApps: true)
                codeSignBox.window?.makeKeyAndOrderFront(sender)
            }
            _ = startHostLocatingServerOnce
            openPort = "*"
        }
        if sender != nil { InjectionServer.stopLastServer() }
        InjectionServer.startServer(openPort+INJECTION_ADDRESS)
    }

    @IBAction func testingEnable(_ sender: NSButton) {
        if sender.state == .on, let script = Bundle.main
            .url(forResource: "copy_bundle", withExtension: "sh") {
            let buildPhase = """
                export RESOURCES="\(script.deletingLastPathComponent().path)"
                if [ -f "$RESOURCES/\(script.lastPathComponent)" ]; then
                    "$RESOURCES/\(script.lastPathComponent)"
                fi
                """
            let pasteBoard = NSPasteboard.general
            pasteBoard.declareTypes([.string], owner:nil)
            pasteBoard.setString(buildPhase, forType:.string)
            InjectionServer.error("Run Script, Build Phase to " +
                  "copy testing libraries added to clipboard.")
        }
    }

    @IBAction func updateLibraries(_ sender: NSTextField) {
        Defaults.deviceLibraries = librariesField.stringValue
    }


    @IBAction func updateXcodeRestart(_ sender: NSMenuItem) {
        Defaults.xcodeRestart = sender.state.toggle() == .on
    }

    @IBAction func unhideSymbols(_ sender: NSMenuItem) {
        Unhider.startUnhide()
    }

    @IBAction func resetUnhiding(_ sender: NSMenuItem) {
        Unhider.unhiddens.removeAll()
    }

    @IBAction func showlastError(_ sender: NSMenuItem) {
        lastErrorField.string = NextCompiler.lastError ?? "No error."
        lastErrorField.window?.makeKeyAndOrderFront(sender)
        NSApplication.shared.activate(ignoringOtherApps: true)
    }

    func setupCodeSigningComboBox() {
        codeSignBox.removeAllItems()

        codeSignBox.addItems(withObjectValues: userIDComboBoxDataSaver.validCodeSigningIDs)

        if let savedID = userIDComboBoxDataSaver.savedID {
            codeSignBox.stringValue = savedID
        } else if let firstIdentity = userIDComboBoxDataSaver.validCodeSigningIDs.first {
            codeSignBox.stringValue = firstIdentity
        } else {
            codeSignBox.stringValue = "No valid code signing IDs found"
        }

        codeSignBox.target = userIDComboBoxDataSaver
        codeSignBox.action = #selector(UserIDComboBoxDataSaver.comboBoxValueDidChange(_:))
    }
}

private extension String {
    /// Returns the sha1 string contained in this string, or `nil` if no such string is contained.
    var containedSHA1: String? { self[#"([0-9A-F]{40})"#] }
}

class UserIDComboBoxDataSaver {

    /// List of valid IDs.
    let validCodeSigningIDs: [String] = {
        var identities: [String] = []

        let security = Topen(exec: "/usr/bin/security",
                             arguments: ["find-identity", "-v", "-p", "codesigning"])

        while let line = security.readLine() {
            let components = line.split(separator: ")", maxSplits: 1)
            if components.count >= 2 {
                let identity = components[1]
                identities.append(String(identity))
            }
        }

        return identities
    }()

    /// Last savedID, if valid. `nil` otherwise.
    var savedID: String? {
        guard let savedValue = Defaults.codesigningIdentity else { return nil }

        return validCodeSigningIDs.first(where: { $0.containedSHA1 == savedValue.containedSHA1} )
    }

    @objc func comboBoxValueDidChange(_ sender: NSComboBox) {
        if let newValueSHA1 = sender.stringValue.containedSHA1,
           validCodeSigningIDs.contains(where: { $0.containedSHA1 == newValueSHA1 }) {
            Defaults.codesigningIdentity = newValueSHA1
        } else {
            NSLog("Selected value does not contain a valid ID")
            return
        }
    }
}

private extension NSControl.StateValue {
    @discardableResult
    mutating func toggle() -> Self {
        switch self {
        case .on:
            self = .off
        case .off:
            self = .on
        case .mixed:
            self = .mixed
        default:
            break
        }
        return self
    }
}


================================================
FILE: App/InjectionNext/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
  "images" : [
    {
      "idiom" : "mac",
      "size" : "16x16",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "16x16",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "32x32",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "32x32",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "128x128",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "128x128",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "256x256",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "256x256",
      "scale" : "2x"
    },
    {
      "idiom" : "mac",
      "size" : "512x512",
      "scale" : "1x"
    },
    {
      "idiom" : "mac",
      "size" : "512x512",
      "scale" : "2x"
    }
  ],
  "info" : {
    "version" : 1,
    "author" : "xcode"
  }
}

================================================
FILE: App/InjectionNext/Base.lproj/MainMenu.xib
================================================
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="23504" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
    <dependencies>
        <deployment identifier="macosx"/>
        <plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="23504"/>
        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
    </dependencies>
    <objects>
        <customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
            <connections>
                <outlet property="delegate" destination="Voe-Tx-rLC" id="GzC-gU-4Uq"/>
            </connections>
        </customObject>
        <customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
        <customObject id="-3" userLabel="Application" customClass="NSObject"/>
        <customObject id="Voe-Tx-rLC" customClass="AppDelegate">
            <connections>
                <outlet property="codeSignBox" destination="iKZ-P7-T7T" id="KiT-vS-8mL"/>
                <outlet property="deviceTesting" destination="SQv-Cq-gPC" id="wzU-n5-tvW"/>
                <outlet property="enableDevicesItem" destination="s0A-3D-ThA" id="xl4-eT-1mM"/>
                <outlet property="lastErrorField" destination="yfA-aU-ldx" id="oxQ-Xy-ugr"/>
                <outlet property="launchXcodeItem" destination="B7f-B2-5ZH" id="7Pb-Cs-Q9n"/>
                <outlet property="librariesField" destination="rw0-Sb-xW5" id="iLm-Qn-E1h"/>
                <outlet property="patchCompilerItem" destination="cw9-XM-GH8" id="uKH-nc-2sg"/>
                <outlet property="restartDeviceItem" destination="6cM-gd-Yoh" id="vaG-Ug-i8f"/>
                <outlet property="selectXcodeItem" destination="XlL-4Y-oKk" id="lAe-fa-JsE"/>
                <outlet property="statusMenu" destination="V8Q-mq-A2f" id="Epo-HD-J21"/>
                <outlet property="watchDirectoryItem" destination="fAp-yw-BqE" id="hwC-GA-RNY"/>
            </connections>
        </customObject>
        <customObject id="YLy-65-1bz" customClass="NSFontManager"/>
        <menu title="Main Menu" systemMenu="main" id="AYu-sK-qS6">
            <items>
                <menuItem title="InjectionIII" id="1Xt-HY-uBw">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="InjectionIII" systemMenu="apple" id="uQy-DD-JDr">
                        <items>
                            <menuItem title="About InjectionIII" id="5kV-Vb-QxS">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="orderFrontStandardAboutPanel:" target="-1" id="Exp-CZ-Vem"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="VOq-y0-SEH"/>
                            <menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW"/>
                            <menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
                            <menuItem title="Services" id="NMo-om-nkz">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Services" systemMenu="services" id="hz9-B4-Xy5"/>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="4je-JR-u6R"/>
                            <menuItem title="Hide InjectionIII" keyEquivalent="h" id="Olw-nP-bQN">
                                <connections>
                                    <action selector="hide:" target="-1" id="PnN-Uc-m68"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Hide Others" keyEquivalent="h" id="Vdr-fp-XzO">
                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                <connections>
                                    <action selector="hideOtherApplications:" target="-1" id="VT4-aY-XCT"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Show All" id="Kd2-mp-pUS">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="unhideAllApplications:" target="-1" id="Dhg-Le-xox"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="kCx-OE-vgT"/>
                            <menuItem title="Quit InjectionIII" keyEquivalent="q" id="4sb-4s-VLi">
                                <connections>
                                    <action selector="terminate:" target="-1" id="Te7-pn-YzF"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="File" id="dMs-cI-mzQ">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="File" id="bib-Uj-vzu">
                        <items>
                            <menuItem title="New" keyEquivalent="n" id="Was-JA-tGl">
                                <connections>
                                    <action selector="newDocument:" target="-1" id="4Si-XN-c54"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Open…" keyEquivalent="o" id="IAo-SY-fd9">
                                <connections>
                                    <action selector="openDocument:" target="-1" id="bVn-NM-KNZ"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="m54-Is-iLE"/>
                            <menuItem title="Close" keyEquivalent="w" id="DVo-aG-piG">
                                <connections>
                                    <action selector="performClose:" target="-1" id="HmO-Ls-i7Q"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Save…" keyEquivalent="s" id="pxx-59-PXV">
                                <connections>
                                    <action selector="saveDocument:" target="-1" id="teZ-XB-qJY"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Save As…" keyEquivalent="S" id="Bw7-FT-i3A">
                                <connections>
                                    <action selector="saveDocumentAs:" target="-1" id="mDf-zr-I0C"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Revert to Saved" keyEquivalent="r" id="KaW-ft-85H">
                                <connections>
                                    <action selector="revertDocumentToSaved:" target="-1" id="iJ3-Pv-kwq"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="aJh-i4-bef"/>
                            <menuItem title="Page Setup…" keyEquivalent="P" id="qIS-W8-SiK">
                                <modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
                                <connections>
                                    <action selector="runPageLayout:" target="-1" id="Din-rz-gC5"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Print…" keyEquivalent="p" id="aTl-1u-JFS">
                                <connections>
                                    <action selector="print:" target="-1" id="qaZ-4w-aoO"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Edit" id="5QF-Oa-p0T">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Edit" id="W48-6f-4Dl">
                        <items>
                            <menuItem title="Undo" keyEquivalent="z" id="dRJ-4n-Yzg">
                                <connections>
                                    <action selector="undo:" target="-1" id="M6e-cu-g7V"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Redo" keyEquivalent="Z" id="6dh-zS-Vam">
                                <connections>
                                    <action selector="redo:" target="-1" id="oIA-Rs-6OD"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="WRV-NI-Exz"/>
                            <menuItem title="Cut" keyEquivalent="x" id="uRl-iY-unG">
                                <connections>
                                    <action selector="cut:" target="-1" id="YJe-68-I9s"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Copy" keyEquivalent="c" id="x3v-GG-iWU">
                                <connections>
                                    <action selector="copy:" target="-1" id="G1f-GL-Joy"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Paste" keyEquivalent="v" id="gVA-U4-sdL">
                                <connections>
                                    <action selector="paste:" target="-1" id="UvS-8e-Qdg"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Paste and Match Style" keyEquivalent="V" id="WeT-3V-zwk">
                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                <connections>
                                    <action selector="pasteAsPlainText:" target="-1" id="cEh-KX-wJQ"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Delete" id="pa3-QI-u2k">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="delete:" target="-1" id="0Mk-Ml-PaM"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Select All" keyEquivalent="a" id="Ruw-6m-B2m">
                                <connections>
                                    <action selector="selectAll:" target="-1" id="VNm-Mi-diN"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="uyl-h8-XO2"/>
                            <menuItem title="Find" id="4EN-yA-p0u">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Find" id="1b7-l0-nxx">
                                    <items>
                                        <menuItem title="Find…" tag="1" keyEquivalent="f" id="Xz5-n4-O0W">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="cD7-Qs-BN4"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find and Replace…" tag="12" keyEquivalent="f" id="YEy-JH-Tfz">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="WD3-Gg-5AJ"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find Next" tag="2" keyEquivalent="g" id="q09-fT-Sye">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="NDo-RZ-v9R"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Find Previous" tag="3" keyEquivalent="G" id="OwM-mh-QMV">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="HOh-sY-3ay"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="buJ-ug-pKt">
                                            <connections>
                                                <action selector="performFindPanelAction:" target="-1" id="U76-nv-p5D"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Jump to Selection" keyEquivalent="j" id="S0p-oC-mLd">
                                            <connections>
                                                <action selector="centerSelectionInVisibleArea:" target="-1" id="IOG-6D-g5B"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Spelling and Grammar" id="Dv1-io-Yv7">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Spelling" id="3IN-sU-3Bg">
                                    <items>
                                        <menuItem title="Show Spelling and Grammar" keyEquivalent=":" id="HFo-cy-zxI">
                                            <connections>
                                                <action selector="showGuessPanel:" target="-1" id="vFj-Ks-hy3"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Check Document Now" keyEquivalent=";" id="hz2-CU-CR7">
                                            <connections>
                                                <action selector="checkSpelling:" target="-1" id="fz7-VC-reM"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="bNw-od-mp5"/>
                                        <menuItem title="Check Spelling While Typing" id="rbD-Rh-wIN">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleContinuousSpellChecking:" target="-1" id="7w6-Qz-0kB"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Check Grammar With Spelling" id="mK6-2p-4JG">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleGrammarChecking:" target="-1" id="muD-Qn-j4w"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Correct Spelling Automatically" id="78Y-hA-62v">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticSpellingCorrection:" target="-1" id="2lM-Qi-WAP"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Substitutions" id="9ic-FL-obx">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Substitutions" id="FeM-D8-WVr">
                                    <items>
                                        <menuItem title="Show Substitutions" id="z6F-FW-3nz">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="orderFrontSubstitutionsPanel:" target="-1" id="oku-mr-iSq"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="gPx-C9-uUO"/>
                                        <menuItem title="Smart Copy/Paste" id="9yt-4B-nSM">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleSmartInsertDelete:" target="-1" id="3IJ-Se-DZD"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Quotes" id="hQb-2v-fYv">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="ptq-xd-QOA"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Dashes" id="rgM-f4-ycn">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticDashSubstitution:" target="-1" id="oCt-pO-9gS"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smart Links" id="cwL-P1-jid">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticLinkDetection:" target="-1" id="Gip-E3-Fov"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Data Detectors" id="tRr-pd-1PS">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticDataDetection:" target="-1" id="R1I-Nq-Kbl"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Text Replacement" id="HFQ-gK-NFA">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleAutomaticTextReplacement:" target="-1" id="DvP-Fe-Py6"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Transformations" id="2oI-Rn-ZJC">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Transformations" id="c8a-y6-VQd">
                                    <items>
                                        <menuItem title="Make Upper Case" id="vmV-6d-7jI">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="uppercaseWord:" target="-1" id="sPh-Tk-edu"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Make Lower Case" id="d9M-CD-aMd">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="lowercaseWord:" target="-1" id="iUZ-b5-hil"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Capitalize" id="UEZ-Bs-lqG">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="capitalizeWord:" target="-1" id="26H-TL-nsh"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Speech" id="xrE-MZ-jX0">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Speech" id="3rS-ZA-NoH">
                                    <items>
                                        <menuItem title="Start Speaking" id="Ynk-f8-cLZ">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="startSpeaking:" target="-1" id="654-Ng-kyl"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Stop Speaking" id="Oyz-dy-DGm">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="stopSpeaking:" target="-1" id="dX8-6p-jy9"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Format" id="jxT-CU-nIS">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Format" id="GEO-Iw-cKr">
                        <items>
                            <menuItem title="Font" id="Gi5-1S-RQB">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Font" systemMenu="font" id="aXa-aM-Jaq">
                                    <items>
                                        <menuItem title="Show Fonts" keyEquivalent="t" id="Q5e-8K-NDq">
                                            <connections>
                                                <action selector="orderFrontFontPanel:" target="YLy-65-1bz" id="WHr-nq-2xA"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Bold" tag="2" keyEquivalent="b" id="GB9-OM-e27">
                                            <connections>
                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="hqk-hr-sYV"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Italic" tag="1" keyEquivalent="i" id="Vjx-xi-njq">
                                            <connections>
                                                <action selector="addFontTrait:" target="YLy-65-1bz" id="IHV-OB-c03"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Underline" keyEquivalent="u" id="WRG-CD-K1S">
                                            <connections>
                                                <action selector="underline:" target="-1" id="FYS-2b-JAY"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="5gT-KC-WSO"/>
                                        <menuItem title="Bigger" tag="3" keyEquivalent="+" id="Ptp-SP-VEL">
                                            <connections>
                                                <action selector="modifyFont:" target="YLy-65-1bz" id="Uc7-di-UnL"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Smaller" tag="4" keyEquivalent="-" id="i1d-Er-qST">
                                            <connections>
                                                <action selector="modifyFont:" target="YLy-65-1bz" id="HcX-Lf-eNd"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="kx3-Dk-x3B"/>
                                        <menuItem title="Kern" id="jBQ-r6-VK2">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Kern" id="tlD-Oa-oAM">
                                                <items>
                                                    <menuItem title="Use Default" id="GUa-eO-cwY">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="useStandardKerning:" target="-1" id="6dk-9l-Ckg"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Use None" id="cDB-IK-hbR">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="turnOffKerning:" target="-1" id="U8a-gz-Maa"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Tighten" id="46P-cB-AYj">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="tightenKerning:" target="-1" id="hr7-Nz-8ro"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Loosen" id="ogc-rX-tC1">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="loosenKerning:" target="-1" id="8i4-f9-FKE"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Ligatures" id="o6e-r0-MWq">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Ligatures" id="w0m-vy-SC9">
                                                <items>
                                                    <menuItem title="Use Default" id="agt-UL-0e3">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="useStandardLigatures:" target="-1" id="7uR-wd-Dx6"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Use None" id="J7y-lM-qPV">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="turnOffLigatures:" target="-1" id="iX2-gA-Ilz"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Use All" id="xQD-1f-W4t">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="useAllLigatures:" target="-1" id="KcB-kA-TuK"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem title="Baseline" id="OaQ-X3-Vso">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Baseline" id="ijk-EB-dga">
                                                <items>
                                                    <menuItem title="Use Default" id="3Om-Ey-2VK">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="unscript:" target="-1" id="0vZ-95-Ywn"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Superscript" id="Rqc-34-cIF">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="superscript:" target="-1" id="3qV-fo-wpU"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Subscript" id="I0S-gh-46l">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="subscript:" target="-1" id="Q6W-4W-IGz"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Raise" id="2h7-ER-AoG">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="raiseBaseline:" target="-1" id="4sk-31-7Q9"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem title="Lower" id="1tx-W0-xDw">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="lowerBaseline:" target="-1" id="OF1-bc-KW4"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="Ndw-q3-faq"/>
                                        <menuItem title="Show Colors" keyEquivalent="C" id="bgn-CT-cEk">
                                            <connections>
                                                <action selector="orderFrontColorPanel:" target="-1" id="mSX-Xz-DV3"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="iMs-zA-UFJ"/>
                                        <menuItem title="Copy Style" keyEquivalent="c" id="5Vv-lz-BsD">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="copyFont:" target="-1" id="GJO-xA-L4q"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste Style" keyEquivalent="v" id="vKC-jM-MkH">
                                            <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                            <connections>
                                                <action selector="pasteFont:" target="-1" id="JfD-CL-leO"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                            <menuItem title="Text" id="Fal-I4-PZk">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <menu key="submenu" title="Text" id="d9c-me-L2H">
                                    <items>
                                        <menuItem title="Align Left" keyEquivalent="{" id="ZM1-6Q-yy1">
                                            <connections>
                                                <action selector="alignLeft:" target="-1" id="zUv-R1-uAa"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Center" keyEquivalent="|" id="VIY-Ag-zcb">
                                            <connections>
                                                <action selector="alignCenter:" target="-1" id="spX-mk-kcS"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Justify" id="J5U-5w-g23">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="alignJustified:" target="-1" id="ljL-7U-jND"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Align Right" keyEquivalent="}" id="wb2-vD-lq4">
                                            <connections>
                                                <action selector="alignRight:" target="-1" id="r48-bG-YeY"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="4s2-GY-VfK"/>
                                        <menuItem title="Writing Direction" id="H1b-Si-o9J">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <menu key="submenu" title="Writing Direction" id="8mr-sm-Yjd">
                                                <items>
                                                    <menuItem title="Paragraph" enabled="NO" id="ZvO-Gk-QUH">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                    </menuItem>
                                                    <menuItem id="YGs-j5-SAR">
                                                        <string key="title">	Default</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeBaseWritingDirectionNatural:" target="-1" id="qtV-5e-UBP"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem id="Lbh-J2-qVU">
                                                        <string key="title">	Left to Right</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeBaseWritingDirectionLeftToRight:" target="-1" id="S0X-9S-QSf"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem id="jFq-tB-4Kx">
                                                        <string key="title">	Right to Left</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeBaseWritingDirectionRightToLeft:" target="-1" id="5fk-qB-AqJ"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem isSeparatorItem="YES" id="swp-gr-a21"/>
                                                    <menuItem title="Selection" enabled="NO" id="cqv-fj-IhA">
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                    </menuItem>
                                                    <menuItem id="Nop-cj-93Q">
                                                        <string key="title">	Default</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeTextWritingDirectionNatural:" target="-1" id="lPI-Se-ZHp"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem id="BgM-ve-c93">
                                                        <string key="title">	Left to Right</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeTextWritingDirectionLeftToRight:" target="-1" id="caW-Bv-w94"/>
                                                        </connections>
                                                    </menuItem>
                                                    <menuItem id="RB4-Sm-HuC">
                                                        <string key="title">	Right to Left</string>
                                                        <modifierMask key="keyEquivalentModifierMask"/>
                                                        <connections>
                                                            <action selector="makeTextWritingDirectionRightToLeft:" target="-1" id="EXD-6r-ZUu"/>
                                                        </connections>
                                                    </menuItem>
                                                </items>
                                            </menu>
                                        </menuItem>
                                        <menuItem isSeparatorItem="YES" id="fKy-g9-1gm"/>
                                        <menuItem title="Show Ruler" id="vLm-3I-IUL">
                                            <modifierMask key="keyEquivalentModifierMask"/>
                                            <connections>
                                                <action selector="toggleRuler:" target="-1" id="FOx-HJ-KwY"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Copy Ruler" keyEquivalent="c" id="MkV-Pr-PK5">
                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                            <connections>
                                                <action selector="copyRuler:" target="-1" id="71i-fW-3W2"/>
                                            </connections>
                                        </menuItem>
                                        <menuItem title="Paste Ruler" keyEquivalent="v" id="LVM-kO-fVI">
                                            <modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
                                            <connections>
                                                <action selector="pasteRuler:" target="-1" id="cSh-wd-qM2"/>
                                            </connections>
                                        </menuItem>
                                    </items>
                                </menu>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="View" id="H8h-7b-M4v">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="View" id="HyV-fh-RgO">
                        <items>
                            <menuItem title="Show Toolbar" keyEquivalent="t" id="snW-S8-Cw5">
                                <modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
                                <connections>
                                    <action selector="toggleToolbarShown:" target="-1" id="BXY-wc-z0C"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Customize Toolbar…" id="1UK-8n-QPP">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <connections>
                                    <action selector="runToolbarCustomizationPalette:" target="-1" id="pQI-g3-MTW"/>
                                </connections>
                            </menuItem>
                            <menuItem isSeparatorItem="YES" id="hB3-LF-h0Y"/>
                            <menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
                                <modifierMask key="keyEquivalentModifierMask" control="YES" option="YES" command="YES"/>
                                <connections>
                                    <action selector="toggleSourceList:" target="-1" id="iwa-gc-5KM"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Enter Full Screen" keyEquivalent="↩" id="4J7-dP-txa">
                                <modifierMask key="keyEquivalentModifierMask" control="YES" option="YES" command="YES"/>
                                <connections>
                                    <action selector="toggleFullScreen:" target="-1" id="dU3-MA-1Rq"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Help" id="wpr-3q-Mcd">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Help" systemMenu="help" id="F2S-fz-NVQ">
                        <items>
                            <menuItem title="InjectionIII Help" keyEquivalent="?" id="FKE-Sm-Kum">
                                <connections>
                                    <action selector="showHelp:" target="-1" id="y7X-2Q-9no"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
            </items>
            <point key="canvasLocation" x="141" y="154"/>
        </menu>
        <menu autoenablesItems="NO" id="V8Q-mq-A2f">
            <items>
                <menuItem title="Launch Xcode" toolTip="Luach supervised Xcode" id="B7f-B2-5ZH">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Launch Xcode"/>
                    <connections>
                        <action selector="runXcode:" target="Voe-Tx-rLC" id="HLd-Lq-tj9"/>
                    </connections>
                </menuItem>
                <menuItem title="Select Xcode" toolTip="Select Path to desired Xcode" id="XlL-4Y-oKk">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Select Xcode"/>
                    <connections>
                        <action selector="selectXcode:" target="Voe-Tx-rLC" id="rCK-bY-XeP"/>
                    </connections>
                </menuItem>
                <menuItem title="Auto Restart Xcode" toolTip="Restart Xcode on crash" id="6cM-gd-Yoh">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="updateXcodeRestart:" target="Voe-Tx-rLC" id="KgB-zd-aHd"/>
                    </connections>
                </menuItem>
                <menuItem title="...or Watch Project" toolTip="Start file watcher for project." id="fAp-yw-BqE">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="watchProject:" target="Voe-Tx-rLC" id="8cH-nD-fji"/>
                    </connections>
                </menuItem>
                <menuItem title="Intercept Compiler" toolTip="Enable(Disable) interception of swift-frontend commands by replacing it with a script." id="cw9-XM-GH8">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <connections>
                        <action selector="patchCompiler:" target="Voe-Tx-rLC" id="tSN-cc-ZSG"/>
                    </connections>
                </menuItem>
                <menuItem isSeparatorItem="YES" id="UYl-f8-xeq"/>
                <menuItem title="Enable Devices" toolTip="Enable injecting on a device" id="s0A-3D-ThA">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Evable Devices"/>
                    <connections>
                        <action selector="deviceEnable:" target="Voe-Tx-rLC" id="OAF-ab-cCp"/>
                    </connections>
                </menuItem>
                <menuItem title="Prepare SwiftUI" id="M7c-Z3-Oni">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <menu key="submenu" title="Prepare SwiftUI" id="G29-S5-KaB">
                        <items>
                            <menuItem title="Single Source" toolTip="Prepare a SwiftUI source for injection." id="92r-y9-TU2">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <accessibility description="Prepare Source"/>
                                <connections>
                                    <action selector="prepareSource:" target="Voe-Tx-rLC" id="FhK-O3-A0w"/>
                                </connections>
                            </menuItem>
                            <menuItem title="Entire Project" toolTip="Prepare a SwiftUI sources in a target for injection." id="E0g-Zl-9YZ">
                                <modifierMask key="keyEquivalentModifierMask"/>
                                <accessibility description="Prepare Project"/>
                                <connections>
                                    <action selector="prepareProject:" target="Voe-Tx-rLC" id="ork-tg-HB8"/>
                                </connections>
                            </menuItem>
                        </items>
                    </menu>
                </menuItem>
                <menuItem title="Unhide Symbols" toolTip="Make a pass over object files &quot;unhiding&quot; defualt argument generators" id="FEE-8j-48e">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Unhide symbols"/>
                    <connections>
                        <action selector="unhideSymbols:" target="Voe-Tx-rLC" id="6qL-3s-BHb"/>
                    </connections>
                </menuItem>
                <menuItem title="Show Last Error" toolTip="Show the last reported error." id="KKx-re-pmY">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Show Last Error"/>
                    <connections>
                        <action selector="showlastError:" target="Voe-Tx-rLC" id="B1J-ZX-nPf"/>
                    </connections>
                </menuItem>
                <menuItem title="Reset Unhiding" hidden="YES" toolTip="Reset internal storage" id="ysc-5y-buR">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Rest Unhiding"/>
                    <connections>
                        <action selector="resetUnhiding:" target="Voe-Tx-rLC" id="wee-I2-883"/>
                    </connections>
                </menuItem>
                <menuItem title="Quit InjectionNext" toolTip="Quit InjectionNext" id="LUw-n8-2Gj">
                    <modifierMask key="keyEquivalentModifierMask"/>
                    <accessibility description="Quit"/>
                    <connections>
                        <action selector="terminate:" target="-3" id="rW8-Je-8vQ"/>
                    </connections>
                </menuItem>
            </items>
            <point key="canvasLocation" x="3" y="613"/>
        </menu>
        <userDefaultsController representsSharedInstance="YES" id="mc2-qN-cFK"/>
        <window title="Codesigning Identity" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="p7X-fx-AEQ" customClass="NSPanel">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
            <rect key="contentRect" x="1355" y="786" width="737" height="204"/>
            <rect key="screenRect" x="0.0" y="0.0" width="2048" height="1127"/>
            <view key="contentView" id="Qow-ez-PwQ">
                <rect key="frame" x="0.0" y="0.0" width="737" height="204"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="rw0-Sb-xW5">
                        <rect key="frame" x="20" y="20" width="697" height="80"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" title="-framework XCTest -lXCTestSwiftSupport" drawsBackground="YES" id="XCE-C6-jqh">
                            <font key="font" metaFont="system"/>
                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                        </textFieldCell>
                        <connections>
                            <action selector="updateLibraries:" target="Voe-Tx-rLC" id="Rcu-9B-dga"/>
                        </connections>
                    </textField>
                    <comboBox focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="iKZ-P7-T7T">
                        <rect key="frame" x="19" y="138" width="701" height="23"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
                        <comboBoxCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" completes="NO" numberOfVisibleItems="5" id="A2I-vj-Z1G">
                            <font key="font" metaFont="system"/>
                            <color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                            <objectValues>
                                <string>Item 1</string>
                                <string>Item 2</string>
                                <string>Item 3</string>
                            </objectValues>
                        </comboBoxCell>
                    </comboBox>
                    <textField focusRingType="none" horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="haQ-Mn-2en">
                        <rect key="frame" x="18" y="168" width="169" height="16"/>
                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                        <textFieldCell key="cell" lineBreakMode="clipping" title="Select codesigning identity" id="9V5-ch-7QQ">
                            <font key="font" metaFont="system"/>
                            <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
                            <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                        </textFieldCell>
                    </textField>
                    <button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="SQv-Cq-gPC">
                        <rect key="frame" x="18" y="107" width="321" height="18"/>
                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
                        <buttonCell key="cell" type="check" title="Enable testing on device (paste into build phase)" bezelStyle="regularSquare" imagePosition="left" inset="2" id="WAY-D9-HRe">
                            <behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
                            <font key="font" metaFont="system"/>
                        </buttonCell>
                        <connections>
                            <action selector="testingEnable:" target="Voe-Tx-rLC" id="SgW-E7-bxy"/>
                        </connections>
                    </button>
                </subviews>
            </view>
            <point key="canvasLocation" x="-1244.5" y="757"/>
        </window>
        <window title="Last Compilation Error" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="HvJ-jZ-ITN" customClass="NSPanel">
            <windowStyleMask key="styleMask" titled="YES" closable="YES" resizable="YES"/>
            <windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
            <rect key="contentRect" x="1355" y="786" width="688" height="268"/>
            <rect key="screenRect" x="0.0" y="0.0" width="2048" height="1127"/>
            <view key="contentView" id="kR1-2l-DFV">
                <rect key="frame" x="0.0" y="0.0" width="688" height="268"/>
                <autoresizingMask key="autoresizingMask"/>
                <subviews>
                    <scrollView fixedFrame="YES" borderType="none" horizontalLineScroll="10" horizontalPageScroll="10" verticalLineScroll="10" verticalPageScroll="10" hasHorizontalScroller="NO" translatesAutoresizingMaskIntoConstraints="NO" id="959-C8-bvK">
                        <rect key="frame" x="8" y="10" width="670" height="251"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <clipView key="contentView" drawsBackground="NO" id="Jya-oY-Tsg">
                            <rect key="frame" x="0.0" y="0.0" width="670" height="251"/>
                            <autoresizingMask key="autoresizingMask"/>
                            <subviews>
                                <textView wantsLayer="YES" editable="NO" importsGraphics="NO" richText="NO" verticallyResizable="YES" spellingCorrection="YES" smartInsertDelete="YES" id="yfA-aU-ldx">
                                    <rect key="frame" x="0.0" y="0.0" width="670" height="251"/>
                                    <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                                    <color key="textColor" name="textColor" catalog="System" colorSpace="catalog"/>
                                    <color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
                                    <size key="minSize" width="670" height="251"/>
                                    <size key="maxSize" width="675" height="10000000"/>
                                </textView>
                            </subviews>
                        </clipView>
                        <scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="xKv-Ny-an6">
                            <rect key="frame" x="-100" y="-100" width="225" height="15"/>
                            <autoresizingMask key="autoresizingMask"/>
                        </scroller>
                        <scroller key="verticalScroller" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="cCD-KO-rn7">
                            <rect key="frame" x="655" y="0.0" width="15" height="251"/>
                            <autoresizingMask key="autoresizingMask"/>
                        </scroller>
                    </scrollView>
                </subviews>
            </view>
            <point key="canvasLocation" x="69" y="1096"/>
        </window>
    </objects>
</document>


================================================
FILE: App/InjectionNext/ControlServer.swift
================================================
//
//  ControlServer.swift
//  InjectionNext
//
//  Local TCP control server for MCP integration.
//  Listens on localhost:8919 for JSON commands and
//  maps them to existing AppDelegate actions.
//

import Cocoa

// MARK: - Log Buffer

class LogBuffer {

    static var shared: LogBuffer?

    struct Entry {
        let timestamp: TimeInterval
        let message: String
        let level: String
    }

    private let lock = NSLock()
    private var entries = [Entry]()
    private let maxEntries = 2000

    func append(_ message: String, level: String = "info") {
        lock.lock()
        defer { lock.unlock() }
        entries.append(Entry(
            timestamp: Date().timeIntervalSince1970,
            message: message,
            level: level
        ))
        if entries.count > maxEntries {
            entries.removeFirst(entries.count - maxEntries)
        }
    }

    func get(since: TimeInterval = 0, limit: Int = 200) -> [[String: Any]] {
        lock.lock()
        defer { lock.unlock() }
        let filtered = entries.filter { $0.timestamp > since }
        let sliced = filtered.suffix(limit)
        return sliced.map {
            ["timestamp": $0.timestamp, "message": $0.message, "level": $0.level]
        }
    }

    func clear() {
        lock.lock()
        defer { lock.unlock() }
        entries.removeAll()
    }

    var count: Int {
        lock.lock()
        defer { lock.unlock() }
        return entries.count
    }
}

// MARK: - Control Server

class ControlServer {

    static let port: UInt16 = 8919
    static var shared: ControlServer?

    private var serverSocket: Int32 = -1
    private let queue = DispatchQueue(label: "ControlServer", attributes: .concurrent)

    static func start() {
        guard shared == nil else { return }
        shared = ControlServer()
        shared?.listen()
    }

    private func listen() {
        queue.async { [weak self] in
            guard let self = self else { return }

            self.serverSocket = socket(AF_INET, SOCK_STREAM, 0)
            guard self.serverSocket >= 0 else {
                NSLog("\(APP_PREFIX)ControlServer: socket() failed")
                return
            }

            var reuse: Int32 = 1
            setsockopt(self.serverSocket, SOL_SOCKET, SO_REUSEADDR, &reuse, socklen_t(MemoryLayout<Int32>.size))

            var addr = sockaddr_in()
            addr.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
            addr.sin_family = sa_family_t(AF_INET)
            addr.sin_port = Self.port.bigEndian
            addr.sin_addr.s_addr = inet_addr("127.0.0.1")

            let bindResult = withUnsafePointer(to: &addr) {
                $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                    bind(self.serverSocket, $0, socklen_t(MemoryLayout<sockaddr_in>.size))
                }
            }

            guard bindResult == 0 else {
                NSLog("\(APP_PREFIX)ControlServer: bind() failed on port \(Self.port): \(String(cString: strerror(errno)))")
                close(self.serverSocket)
                return
            }

            guard Darwin.listen(self.serverSocket, 5) == 0 else {
                NSLog("\(APP_PREFIX)ControlServer: listen() failed")
                close(self.serverSocket)
                return
            }

            NSLog("\(APP_PREFIX)ControlServer: listening on localhost:\(Self.port)")

            while true {
                var clientAddr = sockaddr_in()
                var clientLen = socklen_t(MemoryLayout<sockaddr_in>.size)
                let clientSocket = withUnsafeMutablePointer(to: &clientAddr) {
                    $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {
                        accept(self.serverSocket, $0, &clientLen)
                    }
                }
                guard clientSocket >= 0 else { continue }
                self.queue.async {
                    self.handleClient(clientSocket)
                }
            }
        }
    }

    private func handleClient(_ sock: Int32) {
        defer { close(sock) }

        let maxRequestSize = 64 * 1024
        var data = Data()
        var buf = [UInt8](repeating: 0, count: 4096)
        while true {
            let n = recv(sock, &buf, buf.count, 0)
            guard n > 0 else { break }
            data.append(contentsOf: buf[0..<n])
            if data.contains(UInt8(ascii: "\n")) { break }
            if data.count > maxRequestSize {
                sendResponse(sock, success: false, error: "Request too large")
                return
            }
        }

        guard !data.isEmpty,
              let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
              let action = json["action"] as? String else {
            sendResponse(sock, success: false, error: "Invalid JSON or missing 'action'")
            return
        }

        let result = executeAction(action, params: json)
        sendResponse(sock, success: result.success, data: result.data, error: result.error)
    }

    private func sendResponse(_ sock: Int32, success: Bool, data: [String: Any]? = nil, error: String? = nil) {
        var response: [String: Any] = ["success": success]
        if let error = error { response["error"] = error }
        if let data = data { response["data"] = data }
        guard let jsonData = try? JSONSerialization.data(withJSONObject: response),
              let jsonStr = String(data: jsonData, encoding: .utf8) else { return }
        let line = jsonStr + "\n"
        _ = line.withCString { ptr in
            send(sock, ptr, strlen(ptr), 0)
        }
    }

    struct ActionResult {
        let success: Bool
        let data: [String: Any]?
        let error: String?

        static func ok(_ data: [String: Any]? = nil) -> ActionResult {
            ActionResult(success: true, data: data, error: nil)
        }
        static func fail(_ error: String) -> ActionResult {
            ActionResult(success: false, data: nil, error: error)
        }
    }

    private func executeAction(_ action: String, params: [String: Any]) -> ActionResult {
        switch action {

        case "status":
            return getStatus()

        case "watch_project":
            guard let path = params["path"] as? String else {
                return .fail("Missing 'path' parameter")
            }
            return watchProject(path: path)

        case "stop_watching":
            return stopWatching()

        case "launch_xcode":
            return launchXcode()

        case "intercept_compiler":
            return interceptCompiler()

        case "enable_devices":
            let enable = params["enable"] as? Bool ?? true
            return enableDevices(enable: enable)

        case "unhide_symbols":
            return unhideSymbols()

        case "get_last_error":
            return getLastError()

        case "prepare_swiftui_source":
            return prepareSwiftUISource()

        case "prepare_swiftui_project":
            return prepareSwiftUIProject()

        case "set_xcode_path":
            guard let path = params["path"] as? String else {
                return .fail("Missing 'path' parameter")
            }
            return setXcodePath(path: path)

        case "get_logs":
            let since = params["since"] as? TimeInterval ?? 0
            let limit = max(0, min(params["limit"] as? Int ?? 200, 500))
            return getLogs(since: since, limit: limit)

        case "clear_logs":
            return clearLogs()

        default:
            return .fail("Unknown action: \(action)")
        }
    }

    // MARK: - Actions

    private func getStatus() -> ActionResult {
        var result = [String: Any]()
        DispatchQueue.main.sync {
            let delegate = AppDelegate.ui!
            result["xcode_running"] = MonitorXcode.runningXcode != nil
            result["xcode_path"] = Defaults.xcodePath
            result["compiler_intercepted"] = delegate.updatePatchUnpatch() == .patched
            result["devices_enabled"] = delegate.enableDevicesItem.state == .on
            result["watching_directories"] = Array(AppDelegate.watchers.keys)
            result["has_connected_client"] = InjectionServer.currentClient != nil
            result["auto_restart_xcode"] = Defaults.xcodeRestart
            result["last_error"] = NextCompiler.lastError
        }
        return .ok(result)
    }

    private func watchProject(path: String) -> ActionResult {
        guard FileManager.default.fileExists(atPath: path) else {
            return .fail("Path does not exist: \(path)")
        }
        DispatchQueue.main.sync {
            Reloader.xcodeDev = Defaults.xcodePath + "/Contents/Developer"
            AppDelegate.ui.watch(path: path)
        }
        return .ok(["watching": path])
    }

    private func stopWatching() -> ActionResult {
        DispatchQueue.main.sync {
            AppDelegate.watchers.removeAll()
            AppDelegate.lastWatched = nil
            AppDelegate.ui.watchDirectoryItem.state = .off
        }
        return .ok()
    }

    private func launchXcode() -> ActionResult {
        DispatchQueue.main.sync {
            if MonitorXcode.runningXcode == nil {
                _ = MonitorXcode()
            }
        }
        return .ok(["xcode_path": Defaults.xcodePath])
    }

    private func interceptCompiler() -> ActionResult {
        var state = ""
        DispatchQueue.main.sync {
            let delegate = AppDelegate.ui!
            let currentState = delegate.updatePatchUnpatch()
            state = currentState == .patched ? "patched" : "unpatched"
        }
        return .ok(["compiler_state": state,
                     "note": "Use Xcode UI to toggle interception (requires user confirmation alert)"])
    }

    private func enableDevices(enable: Bool) -> ActionResult {
        DispatchQueue.main.sync {
            let delegate = AppDelegate.ui!
            let currentlyEnabled = delegate.enableDevicesItem.state == .on
            if enable != currentlyEnabled {
                delegate.deviceEnable(delegate.enableDevicesItem)
            }
        }
        return .ok(["devices_enabled": enable])
    }

    private func unhideSymbols() -> ActionResult {
        Unhider.startUnhide()
        return .ok()
    }

    private func getLastError() -> ActionResult {
        let error = NextCompiler.lastError ?? "No error."
        return .ok(["error": error])
    }

    private func prepareSwiftUISource() -> ActionResult {
        guard let lastSource = NextCompiler.lastSource else {
            return .fail("No source file currently being edited")
        }
        DispatchQueue.main.sync {
            AppDelegate.ui.prepareSwiftUI(source: lastSource)
        }
        return .ok(["source": lastSource])
    }

    private func prepareSwiftUIProject() -> ActionResult {
        DispatchQueue.main.sync {
            AppDelegate.ui.prepareProject(AppDelegate.ui.patchCompilerItem)
        }
        return .ok()
    }

    private func setXcodePath(path: String) -> ActionResult {
        guard FileManager.default.fileExists(atPath: path) else {
            return .fail("Xcode not found at: \(path)")
        }
        DispatchQueue.main.sync {
            Defaults.xcodeDefault = path
            AppDelegate.ui.selectXcodeItem.toolTip = path
            AppDelegate.ui.updatePatchUnpatch()
        }
        return .ok(["xcode_path": path])
    }

    private func getLogs(since: TimeInterval, limit: Int) -> ActionResult {
        let logs = LogBuffer.shared?.get(since: since, limit: limit)
        return .ok(["logs": logs ?? [], "count": LogBuffer.shared?.count ?? 0])
    }

    private func clearLogs() -> ActionResult {
        LogBuffer.shared?.clear()
        return .ok()
    }
}


================================================
FILE: App/InjectionNext/Defaults.swift
================================================
//
//  Defaults.swift
//  InjectionNext
//
//  Created by John Holdsworth on 24/07/2024.
//  Copyright © 2024 John Holdsworth. All rights reserved.
//

import Foundation

struct Defaults {
    /// App deauflts for persistent state
    static let userDefaults = UserDefaults.standard
    static let xcodePathDefault = "XcodePath"
    static let librariesDefault = "libraries"
    static let codesigningDefault = "codesigningIdentity"
    private static let xcodeRestartDefault = "xcodeRestartDefault"
    static var xcodePath: String {
        xcodeDefault ?? "/Applications/Xcode.app" }
    static var xcodeDefault: String? {
        get {
            userDefaults.string(forKey: xcodePathDefault)
        }
        set {
            userDefaults.setValue(newValue,
                                  forKey: xcodePathDefault)
        }
    }
    static var deviceLibraries: String {
        get {
            userDefaults.string(forKey: librariesDefault) ??
                "-framework XCTest -lXCTestSwiftSupport"
        }
        set {
            userDefaults.setValue(newValue,
                                  forKey: librariesDefault)
        }
    }
    static var codesigningIdentity: String? {
        get {
            userDefaults.string(forKey: codesigningDefault)
        }
        set {
            userDefaults.setValue(newValue,
                                  forKey: codesigningDefault)
        }
    }   

    static var xcodeRestart: Bool {
        get {
            if userDefaults.value(forKey: xcodeRestartDefault) == nil { return true }
            return userDefaults.bool(forKey: xcodeRestartDefault)
        }
        set {
            userDefaults.setValue(newValue,
                                  forKey: xcodeRestartDefault)
        }
    }
    static let projectPathDefault = "projectPath"
    static var projectPath: String? {
        get {
            userDefaults.string(forKey: projectPathDefault)
        }
    }
    static var mcpServer = userDefaults.bool(forKey: "mcpServer")
}


================================================
FILE: App/InjectionNext/Experimental.swift
================================================
//
//  Experimental.swift
//  InjectionIII
//
//  Created by User on 20/10/2020.
//  Copyright © 2020 John Holdsworth. All rights reserved.
//
//  $Id: //depot/HotReloading/Sources/injectiond/Experimental.swift#35 $
//
//  Some regular expressions to automatically prepare SwiftUI sources.
//
import Cocoa
import SwiftRegex

extension AppDelegate {

    /// Prepare the SwiftUI source file currently being edited for injection.
    @IBAction func prepareSource(_ sender: NSMenuItem) {
        if let lastSource = NextCompiler.lastSource {
            prepareSwiftUI(source: lastSource)
        }
    }

    /// Prepare all sources in the current target for injection.
    @IBAction func prepareProject(_ sender: NSMenuItem) {
        var changes = 0, edited = 0
        for source in ((MonitorXcode.runningXcode != nil ?
                        MonitorXcode.recompiler.lastCompilation : nil) ??
                       FrontendServer.frontendRecompiler().lastCompilation)?
            .swiftFiles.components(separatedBy: "\n").dropLast() ??
                       Array(Recompiler.workspaceCache.keys) {
            InjectionHybrid.lastInjected[source] = Date.timeIntervalSinceReferenceDate
            prepareSwiftUI(source: source, changes: &changes)
            edited += 1
        }
        let s = changes == 1 ? "" : "s"
        InjectionServer.error("\(changes) automatic edit\(s) made to \(edited) files")
    }
}


================================================
FILE: App/InjectionNext/FrontendServer.swift
================================================
//
//  FrontendServer.swift
//  InjectionNext
//
//  Created by John Holdsworth on 23/02/2025.
//  Copyright © 2025 John Holdsworth. All rights reserved.
//
//  Code related to "Intercepting" version where the binary
//  swift-frontend is replaced by a script which feeds all
//  compilation commands to the app where they can be reused
//  when a file is injected to recompile individual Swift files.
//
import Cocoa
import Popen
import Fortify

#if INJECTION_III_APP
struct Unhider { static var packageFrameworks: String? }
#endif

extension NextCompiler {
    func writeCache() {
        FrontendServer.writeCache(for: self.name, recompiler: self)
    }
}

class FrontendServer: SimpleSocket {

    enum State: String {
        case unpatched = "Intercept Compiler"
        case patched = "Unpatch Compiler"
    }

    /// Paths to unpatched/patched swift-frontend binary/script in toolchain.
    static let frontendQueue = DispatchQueue(label: "InjectionCapture")
    static var binURL: URL { URL(fileURLWithPath: Defaults.xcodePath +
        "/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin") }
    static var unpatchedURL: URL { binURL.appendingPathComponent("swift-frontend") }
    static var patched: String { unpatchedURL.path + ".save" }
    static var patchedURL: URL { URL(fileURLWithPath: patched) }
    /// Path to swift-frontend  last logged.
    static var loggedFrontend: String?
    /// Start server for command logging.
    static var startOnce: Void = {
        FrontendServer.startServer(COMMANDS_PORT)
    }()

    static var clientPlatform: String {
        InjectionServer.currentClient?.platform ?? "iPhoneSimulator" }
    static func cacheURL(platform: String) -> URL {
        return URL(fileURLWithPath: Reloader.tmpbase+"_\(platform)_builds.json")
    }
    static private var recompilersLock = os_unfair_lock()
    static private var recompilers = [String: NextCompiler]()
    static func frontendRecompiler(for platform: String = clientPlatform) -> NextCompiler {
        os_unfair_lock_lock(&recompilersLock)
        defer { os_unfair_lock_unlock(&recompilersLock) }
        if let recompiler = recompilers[platform] {
            return recompiler
        }
        let recompiler = NextCompiler(name: platform)
        do {
            let compressed = cacheURL(platform: platform).path+".gz"
            if Fstat(path: compressed)?.st_size ?? 0 != 0,
               let stream = Popen(cmd: "gunzip <"+compressed),
               let cached = stream.readAll().data(using: .utf8) {
                let stored = try JSONDecoder().decode(
                    [String: NextCompiler.Compilation].self, from: cached)
                for source in stored.keys.sorted() {
                    guard let compile = stored[source] else { continue }
                    recompiler.store(compilation: compile, for: source)
                }
                recompiler.modified = false
                print("Loaded \(recompiler.compilations.count) \(platform) commands.")
            }
        } catch {
            InjectionServer.error("Unable to read commands cache: \(error).")
        }
        recompilers[platform] = recompiler
        return recompiler
    }
    static func writeCache(for platform: String, recompiler: NextCompiler? = nil) {
        let recompiler = recompiler ?? frontendRecompiler(for: platform)
        do {
            let encoder = JSONEncoder()
            encoder.outputFormatting = .prettyPrinted
            let cache = cacheURL(platform: platform)
            let commands = recompiler.compilations
            try encoder.encode(commands).write(to: cache, options: .atomic)
            if let error = Popen.system("gzip -f "+cache.path, errors: true) {
                InjectionServer.error("Unable to zip commands cache: \(error)")
            } else {
                print("Cached \(commands.count) \(platform) commands")
            }
            recompiler.modified = false
        } catch {
            InjectionServer.error("Unable to write commands cache: \(error)")
        }
    }

    func validateConnection() -> Bool {
        return readInt() == COMMANDS_VERSION && readString() == NSHomeDirectory()
    }

    override func run() {
        Self.frontendQueue.async {
            do {
                try Fortify.protect { () -> () in
                    guard self.validateConnection(),
                          let vers = self.readString(), vers == "1.0" || vers == "2.0" else {
                        return Self.frontendRecompiler()
                            .error("Unpatch then repatch compiler to update script version")
                    }
                    try Self.processFrontendCommandFrom(feed: self)
                }
            } catch {
                Self.error("Feed error: \(error)")
            }
        }
    }

    class func processFrontendCommandFrom(feed: SimpleSocket) throws {
        guard var projectRoot = feed.readString(),
              let frontendPath = feed.readString(),
              frontendPath.hasSuffix(".save"),
              feed.readString() == "-frontend" &&
                feed.readString() == "-c" else { return }
        
        // swift-frontend.sh 2.0+ capture environment
        var env: String?
        if let pwd: String = projectRoot["PWD=(.*)\n"] ??
                             projectRoot["HOME=(.*)\n"] {
            env = projectRoot
            projectRoot = pwd
        }

        var parser = CompilationArgParser()
        while let arg = feed.readString() {
            if arg.hasPrefix("llvmcas://") { return }
            parser.process(arg: arg, next: feed.readString)
        }

        let update = NextCompiler.Compilation(arguments: parser.args,
            swiftFiles: parser.swiftFiles, workingDir: projectRoot, env: env)

        DispatchQueue.main.async {
            if !projectRoot.hasSuffix(".xcodeproj") && projectRoot != "/" &&
//                MonitorXcode.runningXcode == nil &&
                AppDelegate.alreadyWatching(projectRoot) == nil {
                let open = NSOpenPanel()
//                open.titleVisibility = .visible
//                open.title = "InjectionNext: add directory"
                open.prompt = "InjectionNext - Watch Directory?"
                open.directoryURL = URL(fileURLWithPath: projectRoot)
                open.canChooseDirectories = true
                open.canChooseFiles = false
                if open.runModal() == .OK, let url = open.url {
                    AppDelegate.ui.watch(path: url.path)
                }
            }
        }

        NextCompiler.compileQueue.async {
            let recompiler = Self.frontendRecompiler(for: parser.platform)
            loggedFrontend = frontendPath

            for source in parser.primaries {
                #if !INJECTION_III_APP
                // Don't update compilations while connected
                if InjectionServer.currentClient != nil &&
                    recompiler.canCompile(source: source, for: parser.platform) {
                    continue
                }
                #endif

                print("Updating \(parser.args.count) args for \(parser.platform)/" +
                      URL(fileURLWithPath: source).lastPathComponent)
                recompiler.store(compilation: update, for: source)
            }
        }
    }

    /// Argument parser shared by FrontendServer and MonitorXcode to accumulate
    /// compiler arguments into a NextCompiler.Compilation.
    struct CompilationArgParser {
        var swiftFiles = ""
        var args = [String]()
        var primaries = [String]()
        var platform = "iPhoneSimulator"
        var workingDir = "/tmp"
        var swiftFileCount = 0
        lazy var productDirSuffix = "-"+clientPlatform.lowercased()

        /// Process one argument, calling `next()` to consume the following
        /// token whenever the argument takes a value.
        mutating func process(arg: String, next: () -> String?) {
            switch arg {
            case "-filelist":
                if let filelist = next(), let files =
                    try? String(contentsOfFile: filelist, encoding: .utf8) {
                    swiftFiles += files
                }
            case "-primary-file":
                if let source = next(), !source.isEmpty {
                    primaries.append(source)
                    if strstr(swiftFiles, source) == nil {
                        swiftFiles += source+"\n"
                    }
                }
            case "-o":
                if let object = next(), !object.isEmpty &&
                    Unhider.packageFrameworks == nil {
                    var url = URL(fileURLWithPath: object)
                    for _ in 1...4 { url.deleteLastPathComponent() }
                    Unhider.packageFrameworks = url.path
                }
            default:
                if let sdkPlatform: String = arg[#"/([A-Za-z]+)[\d\.]+\.sdk$"#] {
                    platform = sdkPlatform
                } else if args.last == "-F" {
                    if arg.hasSuffix("/PackageFrameworks") {
                        Unhider.packageFrameworks = arg
                    } else if Unhider.packageFrameworks == nil,
                              arg.hasSuffix(productDirSuffix) {
                        Unhider.packageFrameworks = arg+"/PackageFrameworks"
                    }
                }
                let pathArgFlags: Set<String> = ["-F", "-I", "-iquote", "-isystem"]
                let isPathArgValue = pathArgFlags.contains(args.last ?? "")
                    || arg.hasPrefix("-I") || arg.hasPrefix("-F")
                if arg.hasSuffix(".swift") && !isPathArgValue {
                    swiftFiles += arg+"\n"
                    swiftFileCount += 1
                } else if arg[Reloader.optionsToRemove] {
                    _ = next()
                } else if !arg[
                    "-validate-clang-modules-once|-frontend-parseable-output"] {
                    args.append(arg)
                }
            }
        }
    }
}

extension AppDelegate {

    @IBAction func patchCompiler(_ sender: NSMenuItem) {
        let fm = FileManager.default
        do {
            let linksToMove = ["swift", "swiftc", "swift-symbolgraph-extract",
                               "swift-api-digester", "swift-cache-tool"]
            if updatePatchUnpatch() == .unpatched {
                if !fm.fileExists(atPath: FrontendServer.patched),
                   let feeder = Bundle.main
                    .url(forResource: "swift-frontend", withExtension: "sh") {
                    let alert: NSAlert = NSAlert()
                    alert.alertStyle = .warning
                    alert.messageText = APP_NAME
                    alert.informativeText = """
                        The Swift compiler of your current toolchain \
                        \(FrontendServer.unpatchedURL.path) will be \
                        replaced by a script that calls the compiler \
                        and captures all compilation commands. Use menu \
                        item "Unpatch Compiler" to revert this change.
                        """
                    alert.addButton(withTitle: "OK")
                    alert.addButton(withTitle: "Cancel")
                    if alert.runModal() != .alertFirstButtonReturn {
                        return
                    }
                    try fm.moveItem(at: FrontendServer.unpatchedURL,
                                    to: FrontendServer.patchedURL)
                    try fm.copyItem(at: feeder,
                                    to: FrontendServer.unpatchedURL)
                    for binary in linksToMove {
                        let link = FrontendServer.binURL
                            .appendingPathComponent(binary)
                        try fm.removeItem(at: link)
                        symlink("swift-frontend.save", link.path)
                    }
                }
            } else if fm.fileExists(atPath: FrontendServer.patched) {
                try? fm.removeItem(at: FrontendServer.unpatchedURL)
                try fm.moveItem(at: FrontendServer.patchedURL,
                                to: FrontendServer.unpatchedURL)
                for binary in linksToMove {
                    let link = FrontendServer.binURL
                        .appendingPathComponent(binary)
                    try fm.removeItem(at: link)
                    symlink("swift-frontend", link.path)
                }
                FrontendServer.loggedFrontend = nil
            }
        } catch {
            let chmod = FrontendServer.unpatchedURL.deletingLastPathComponent()
            InjectionServer.error("Patching error: \(error). " +
                                  "Is the directory \(chmod.path) writable?")
        }
        updatePatchUnpatch()
    }

    @discardableResult
    func updatePatchUnpatch() -> FrontendServer.State {
        let state = FileManager.default
            .fileExists(atPath: FrontendServer.patched) ?
            FrontendServer.State.patched : .unpatched
        DispatchQueue.main.async {
            self.patchCompilerItem?.title = state.rawValue
            if state == .patched {
                _ = FrontendServer.startOnce
            }
        }
        return state
    }

    /// Shared regular expresssions to patch .enableInjection() and @ObserveInject into a source
    func prepareSwiftUI(source: String, changes: UnsafeMutablePointer<Int>? = nil) {
        let fileURL = URL(fileURLWithPath: source)
        guard let original = try? String(contentsOf: fileURL) else {
            return
        }

        var patched = original, before = changes?.pointee
        patched[#"""
            ^((\s+)(public )?(var body:|func body\([^)]*\) -\>) some View \{\n\#
            (\2(?!    (if|switch|ForEach) )\s+(?!\.enableInjection)\S.*\n|(\s*|#.+)\n)+)(?<!#endif\n)\2\}\n
            """#.anchorsMatchLines, count: changes] = """
            $2#if DEBUG
            $2@ObserveInjection var forceRedraw
            $2#endif

            $1$2    .enableInjection()
            $2}

            """
        if changes?.pointee != before {
            print("Patched", source)
        }

        if (patched.contains("class AppDelegate") ||
            patched.contains("@main\n")) &&
            !patched.contains("InjectionObserver") {
            if !patched.contains("import SwiftUI") {
                patched += "\nimport SwiftUI\n"
            }

            patched += """

                #if canImport(HotSwiftUI)
                @_exported import HotSwiftUI
                #elseif canImport(Inject)
                @_exported import Inject
                #else
                // This code can be found in the Swift package:
                // https://github.com/johnno1962/HotSwiftUI or
                // https://github.com/krzysztofzablocki/Inject

                #if DEBUG
                import Combine

                public class InjectionObserver: ObservableObject {
                    public static let shared = InjectionObserver()
                    @Published var injectionNumber = 0
                    var cancellable: AnyCancellable? = nil
                    let publisher = PassthroughSubject<Void, Never>()
                    init() {
                        cancellable = NotificationCenter.default.publisher(for:
                            Notification.Name("INJECTION_BUNDLE_NOTIFICATION"))
                            .sink { [weak self] change in
                            self?.injectionNumber += 1
                            self?.publisher.send()
                        }
                    }
                }

                extension SwiftUI.View {
                    public func eraseToAnyView() -> some SwiftUI.View {
                        return AnyView(self)
                    }
                    public func enableInjection() -> some SwiftUI.View {
                        return eraseToAnyView()
                    }
                    public func onInjection(bumpState: @escaping () -> ()) -> some SwiftUI.View {
                        return self
                            .onReceive(InjectionObserver.shared.publisher, perform: bumpState)
                            .eraseToAnyView()
                    }
                }

                @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
                @propertyWrapper
                public struct ObserveInjection: DynamicProperty {
                    @ObservedObject private var iO = InjectionObserver.shared
                    public init() {}
                    public private(set) var wrappedValue: Int {
                        get {0} set {}
                    }
                }
                #else
                extension SwiftUI.View {
                    @inline(__always)
                    public func eraseToAnyView() -> some SwiftUI.View { return self }
                    @inline(__always)
                    public func enableInjection() -> some SwiftUI.View { return self }
                    @inline(__always)
                    public func onInjection(bumpState: @escaping () -> ()) -> some SwiftUI.View {
                        return self
                    }
                }

                @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
                @propertyWrapper
                public struct ObserveInjection {
                    public init() {}
                    public private(set) var wrappedValue: Int {
                        get {0} set {}
                    }
                }
                #endif
                #endif

                """
        }

        if patched != original {
            do {
                try patched.write(to: fileURL,
                                  atomically: true, encoding: .utf8)
            } catch {
                InjectionServer.error("Could not save \(source): \(error)")
            }
        }
    }
}


================================================
FILE: App/InjectionNext/Info.plist
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>CFBundleDevelopmentRegion</key>
	<string>$(DEVELOPMENT_LANGUAGE)</string>
	<key>CFBundleExecutable</key>
	<string>$(EXECUTABLE_NAME)</string>
	<key>CFBundleIconFile</key>
	<string>App.icns</string>
	<key>CFBundleIdentifier</key>
	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
	<key>CFBundleInfoDictionaryVersion</key>
	<string>6.0</string>
	<key>CFBundleName</key>
	<string>$(PRODUCT_NAME)</string>
	<key>CFBundlePackageType</key>
	<string>APPL</string>
	<key>CFBundleShortVersionString</key>
	<string>$(MARKETING_VERSION)</string>
	<key>CFBundleVersion</key>
	<string>14094</string>
	<key>LSApplicationCategoryType</key>
	<string>public.app-category.developer-tools</string>
	<key>LSMinimumSystemVersion</key>
	<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
	<key>LSUIElement</key>
	<true/>
	<key>NSHumanReadableCopyright</key>
	<string>Copyright © 2017-20 John Holdsworth. All rights reserved.</string>
	<key>NSMainNibFile</key>
	<string>MainMenu</string>
	<key>NSPrincipalClass</key>
	<string>NSApplication</string>
</dict>
</plist>


================================================
FILE: App/InjectionNext/InjectionHybrid.swift
================================================
//
//  InjectionHybrid.swift
//  InjectionNext
//
//  Created by John Holdsworth on 09/11/2024.
//  Copyright © 2024 John Holdsworth. All rights reserved.
//
//  Provide file watcher/log parser fallback
//  for use outside Xcode (e.g. Cursor/VSCode)
//  Also uses FileWatcher for operation when
//  swift-frontend has been replaced by a
//  script to capture compiler invocations.
//
import Cocoa

extension AppDelegate {
    static var watchers = [String: InjectionHybrid]()
    static var lastWatched: String?

    @IBAction func watchProject(_ sender: NSMenuItem) {
        let open = NSOpenPanel()
        open.prompt = "Select Project Directory"
        open.canChooseDirectories = true
        open.canChooseFiles = false
        // open.showsHiddenFiles = TRUE;
        if open.runModal() == .OK, let url = open.url {
            Reloader.xcodeDev = Defaults.xcodePath+"/Contents/Developer"
            watch(path: url.path)
        } else {
            Self.watchers.removeAll()
            Self.lastWatched = nil
        }
    }

    func watch(path: String) {
        guard Self.alreadyWatching(path) == nil else { return }
        GitIgnoreParser.monitor(directory: path)
        Self.watchers[path] = InjectionHybrid(watching: path)
        Self.lastWatched = path
        watchDirectoryItem.state = Self.watchers.isEmpty ? .off : .on
    }
    static func alreadyWatching(_ projectRoot: String) -> String? {
        return Self.watchers[projectRoot] != nil ? projectRoot :
            watchers.keys.first { projectRoot.hasPrefix($0+"/") }
    }
    static func restartLastWatcher() {
        DispatchQueue.main.async {
            lastWatched.flatMap { watchers[$0]?.watcher?.restart() }
        }
    }
}

class InjectionHybrid: InjectionBase {
    /// Last Injected for deduplication
    static var lastInjected = [String: TimeInterval]()
    /// Last queue of file changes
    static var pendingFilesChanged = [String]()
    /// Repository locked state - stops processing until app reconnects
    static var isRepositoryLocked = false
    /// Path to detected git lock file - used to check if git operation still active
    static var gitLockPath: String?
    /// InjectionNext compiler that uses InjectionLite log parser
    var logParsingCompiler: NextCompiler = HybridCompiler(name: "BuildLogs")
    /// Minimum seconds between injections
    let minInterval = 1.0

    init(watching path: String) { // FileWatcher compatibility
        let watchPaths = (getenv(INJECTION_DIRECTORIES) == nil ?
            NSHomeDirectory()+"/Library/Developer," : "") + path
        setenv(INJECTION_DIRECTORIES, watchPaths, 1)
        Reloader.injectionQueue = .main
        super.init()
        // Extend FileWatcher pattern to detect git lock files
        FileWatcher.INJECTABLE_PATTERN = try! NSRegularExpression(
            pattern: #"[^~]\.(mm?|cpp|cc|swift|lock|o)$"#)
    }

    /// Called from file watcher when file is edited.
    override func inject(source: String) {
        guard MonitorXcode.runningXcode == nil else { return }
        // Detect git lock files - record path for later checking
        if source.hasSuffix(".lock") &&
           source.contains("/.git/") {
            Self.gitLockPath = source
            return
        }

        // Skip processing if repository is already locked
        if Self.isRepositoryLocked {
            log("""
                File processing stopped due to git lock. \
                Please relaunch your app to resume injection.
                """)
            return
        }

        // Check if source file is changing while git lock still exists
        if let lockPath = Self.gitLockPath {
            if FileManager.default.fileExists(atPath: lockPath) {
                // Source files changing while git lock exists = branch switch/merge/rebase
                Self.isRepositoryLocked = true
                Self.pendingFilesChanged.removeAll()
                Self.gitLockPath = nil
                log("""
                    Git operation in progress (branch switch/merge/rebase detected). \
                    File processing stopped. Please relaunch your app to resume injection.
                    """)
                return
            } else {
                // Lock file is gone - was probably just a commit
                Self.gitLockPath = nil
            }
        }

        let now = Date.timeIntervalSinceReferenceDate
        guard !AppDelegate.watchers.isEmpty, now - (
                Self.lastInjected[source] ?? 0.0) > minInterval else {
            return
        }
        Self.lastInjected[source] = now

        Self.pendingFilesChanged.append(source)
        NextCompiler.compileQueue.async {
            self.injectNext()
        }
    }

    func injectNext() {
        guard let source = (DispatchQueue.main.sync { () -> String? in
            guard let source = Self.pendingFilesChanged.first else { return nil }
            Self.pendingFilesChanged.removeAll(where: { $0 == source })
            if !Self.pendingFilesChanged.isEmpty {
                NextCompiler.compileQueue.async { self.injectNext() }
            }
            return source
        }) else { return }

        autoreleasepool {
        var recompiler = MonitorXcode.recompiler
        let platform = FrontendServer.clientPlatform
        if recompiler.canCompile(source: source, for: platform),
           recompiler.inject(source: source) { return }

        recompiler = logParsingCompiler
        if source.hasSuffix(".swift") &&
            AppDelegate.ui.updatePatchUnpatch() == .patched {
            let proxyCompiler = FrontendServer.frontendRecompiler(for: platform)
            if proxyCompiler.canCompile(source: source) {
                recompiler = proxyCompiler
            }
        }

        if let why = GitIgnoreParser.shouldExclude(file: source) {
            log("Excluded \(source) as \(why)")
        } else if !recompiler.inject(source: source) {
            recompiler.pendingSource = source
        }
        }
    }
}

class HybridCompiler: NextCompiler {
    /// Legacy log parsing version of recomilation
    static var liteRecompiler = Recompiler()

    override func recompile(source: String, platform: String) ->  String? {
        let oldCache = Reloader.cacheFile
        Reloader.sdk = platform // Select commands cache file.
        if oldCache != Reloader.cacheFile { Self.liteRecompiler = Recompiler() }
        return Self.liteRecompiler.recompile(source: source, platformFilter:
                                            "SDKs/"+platform, dylink: false)
    }

    override func link(object: String, dylib: String, arch: String) -> (String, Double)? {
        return super.link(object: object, dylib: dylib, arch: arch) ??
                                   Self.liteRecompiler.linkingFailed()
    }
}


================================================
FILE: App/InjectionNext/InjectionNext-Bridging-Header.h
================================================
//
//  Use this file to import your target's public headers that you would like to expose to Swift.
//

#import "SimpleSocket.h"
#import "InjectionImplC.h"
#import "InjectionClient.h"
#import "/tmp/InjectionNextSalt.h"


================================================
FILE: App/InjectionNext/InjectionNext.entitlements
================================================
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>com.apple.security.app-sandbox</key>
	<false/>
	<key>com.apple.security.files.user-selected.read-only</key>
	<true/>
	<key>com.apple.security.network.server</key>
	<true/>
	<key>com.apple.security.print</key>
	<false/>
	<key>com.apple.security.files.bookmarks.app-scope</key>
	<false/>
</dict>
</plist>


================================================
FILE: App/InjectionNext/InjectionServer.swift
================================================
//
//  InjectionServer.swift
//  InjectionNext
//
//  Created by John H on 30/05/2024.
//  Copyright © 2024 John Holdsworth. All rights reserved.
//
//  Subclass of SimpleSocket to receive connection from
//  user apps using the InjectionNext Swift Package. An
//  incoming connection will enter runInBackground() on
//  a background thread. Validiates the connection and
//  forwards "commands" to the client app to load dynamic
//  libraries and inject them etc. Also receives feeds
//  of compilation commands from swift-frontend.sh.
//
import Cocoa
import Fortify
import Popen

class InjectionServer: SimpleSocket {

    /// So commands from differnt threads don't get mixed up
    static let clientQueue = DispatchQueue(label: "InjectionCommand")
    static private var connected = [InjectionServer]()
    /// All access to `connected` serialised through clientQueue.
    static var currentClients: [InjectionServer?] {
        return clientQueue.sync { connected.isEmpty ? [nil] : connected }
    }
    /// Current connection to client app. There can be only one.
    static var currentClient: InjectionServer? { currentClients.last ?? nil }
    static var lastAlert: NSAlert?

    /// Sorted last symbols exported by source.
    var exports = [String: [String]]()
    /// Keeps dynamic library file names unique.
    var injectionNumber = 0
    /// Some defaults
    var platform = "iPhoneSimulator"
    var arch = "arm64"
    var tmpPath = "/unset"

    class func alert(_ msg: String) {
        NSLog("\(APP_PREFIX)\(APP_NAME) \(msg)")
        LogBuffer.shared?.append("\(APP_NAME) \(msg)", level: "alert")
        lastAlert = NSAlert()
        lastAlert?.messageText = "\(self)"
        lastAlert?.informativeText = msg
        lastAlert?.alertStyle = .warning
        lastAlert?.addButton(withTitle: "OK")
        _ = lastAlert?.runModal()
    }

    /// Pops up an alert panel for networking
    @discardableResult
    override public class func error(_ message: String) -> Int32 {
        let msg = String(format:message, strerror(errno))
        LogBuffer.shared?.append(msg, level: "error")
        DispatchQueue.main.async { alert(msg) }
        return -1
    }

    // Send command to client app
    func sendCommand(_ command: InjectionCommand, with string: String?) {
        Self.clientQueue.async {
            _ = self.writeCommand(command.rawValue, with: string)
        }
    }

    // Write message into Xcode console of client app.
    open func log(_ msg: String) {
        NSLog("\(APP_PREFIX)\(APP_NAME) \(msg)")
        LogBuffer.shared?.append(msg, level: "info")
        sendCommand(.log, with: APP_PREFIX+msg)
    }
    open func error(_ msg: String) {
        log("⚠️ "+msg)
    }

    lazy var copyPlugIns: () = {
        let pattern = "/tmp/InjectionNext.PlugIns/*.xctest"
        if platform == "iPhoneOS" && isLocalClient {
            if let errors = Popen.system("""
                rm -rf "\(tmpPath)"/*.xctest; \
                rsync -a \(pattern) "\(tmpPath)"
                """, errors: true) {
                error("Copy *.xctest failed: "+errors)
            }
            return
        }
        guard let plugins = Glob(pattern: pattern) else { return }
        for plugin in plugins {
            writeCommand(InjectionCommand.log.rawValue, with: APP_PREFIX+"Sending "+plugin)
            let url = URL(fileURLWithPath: plugin)
            let dest = tmpPath+"/"+url.lastPathComponent
            writeCommand(InjectionCommand.sendFile.rawValue, with: dest+"/")
            writeCommand(InjectionCommand.sendFile.rawValue, with: dest+"/_CodeSignature/")
            for file in [url.deletingPathExtension().lastPathComponent,
                         "Info.plist", "/_CodeSignature/CodeResources"] {
                writeCommand(InjectionCommand.sendFile.rawValue, with: dest+"/"+file)
                sendFile(url.appendingPathComponent(file).path)
            }
        }
    }()

    // Simple validation to weed out invalid connections
    func validateConnection() -> Bool {
        guard readInt() == INJECTION_VERSION,
              let injectionKey = readString() else { return false }
        guard injectionKey.hasPrefix(NSHomeDirectory()) else {
            error("Invalid INJECTION_KEY: "+injectionKey)
            return false
        }
        return true
    }

    // On a new connection starts executing here
    override func runInBackground() {
        do {
            try Fortify.protect {
                guard validateConnection() else {
                    sendCommand(.invalid, with: nil)
                    error("Connection did not validate.")
                    return
                }
                DispatchQueue.main.async {
                    InjectionHybrid.pendingFilesChanged.removeAll()
                    // Reset repository locked state on app reconnect (relaunch)
                    if InjectionHybrid.isRepositoryLocked {
                        InjectionHybrid.isRepositoryLocked = false
                        InjectionHybrid.gitLockPath = nil
                        self.log("Repository lock cleared - injection resumed")
                    }
                }
                AppDelegate.ui.setMenuIcon(.ok)
                processResponses()
                AppDelegate.ui.setMenuIcon(MonitorXcode
                    .runningXcode != nil ? .ready : .idle)
            }
        } catch {
            self.error("\(self) error \(error)")
        }
        Self.clientQueue.sync {
            Self.connected.removeAll { $0 === self }
        } // flush messages and de-register
    }

    func processResponses() {
        sendCommand(.xcodePath, with: Defaults.xcodePath)
        AppDelegate.restartLastWatcher()

        while true {
            let responseInt = readInt()
            guard let response = InjectionResponse(rawValue: responseInt) else {
                error("Invalid responseInt: \(responseInt)")
                break
            }
            switch response {
            case .platform:
                if let platform = readString(), let arch = readString() {
                    log("Platform connected: "+platform)
                    self.platform = platform
                    Reloader.arch = arch
                    self.arch = arch
                } else {
                    error("**** Bad platform ****")
                    return
                }
            case .tmpPath:
                if let tmpPath = readString() {
                    print("Tmp path: "+tmpPath)
                    if tmpPath.contains("/Xcode/UserData/Previews/") {
                        return
                    }
                    self.tmpPath = tmpPath
                    self.tmpPath[#"/$"#] = "" // strip trailing slash
                    Self.clientQueue.async {
                        Self.connected.append(self)
                    }
                } else {
                    error("**** Bad tmp ****")
                }
                if MonitorXcode.runningXcode == nil &&
                    AppDelegate.watchers.isEmpty &&
                    AppDelegate.ui.updatePatchUnpatch() == .unpatched {
                    error("""
                        Xcode not launched via app. Injection will not be possible \ 
                        unless you file-watch a project and Xcode logs are available. \
                        You can add an env var INJECTION_PROJECT_ROOT to your scheme \
                        with value $(SRCROOT) to auto file-watch this project.
                        """)
                }
                if !AppDelegate.watchers.isEmpty {
                    log("Watching directory: " +
                        AppDelegate.watchers.keys.joined(separator: ", "))
                }
            case .projectRoot:
                if let projectRoot = readString() {
                    log("Auto-watching project: \(projectRoot)")
                    DispatchQueue.main.sync {
                        AppDelegate.ui.watch(path: projectRoot)
                        Self.lastAlert?.buttons.last?.performClick(self)
                        Self.lastAlert = nil
                    }
                } else {
                    error("**** Bad root ****")
                }
            case .executable:
                if let executable = readString() {
                    Reloader.appName = URL(fileURLWithPath:
                                            executable).lastPathComponent
                }
            case .detail:
                if let detail = readString() {
                    setenv(INJECTION_DETAIL, detail, 1)
                }
            case .bazelTarget:
                if let target = readString() {
                    log("Received Bazel target: \(target)")
                    BazelActionQueryHandler.cachedAppTarget = target
                    // Set environment variable so Bazel parsers can use this target
                    setenv(INJECTION_BAZEL_TARGET, target, 1)
                } else {
                    error("**** Bad Bazel target ****")
                }
            case .injected:
                AppDelegate.ui.setMenuIcon(.ok)
            case .failed:
                AppDelegate.ui.setMenuIcon(.error)
            case .unhide:
                log("Injection could not load. If this was due to a default " +
                    "argument. Select the app's menu item \"Unhide Symbols\".")
            case .exit:
                log("**** client disconnected ****")
                return
            @unknown default:
                Self.error("**** @unknown response case \(responseInt) ****")
                return
            }
        }
    }
}


================================================
FILE: App/InjectionNext/MonitorXcode.swift
================================================
//
//  RunXcode.swift
//  InjectionNext
//
//  Created by John H on 30/05/2024.
//  Copyright © 2024 John Holdsworth. All rights reserved.
//
//  Launches Xcode and monitors console output for SourceKit
//  logging messages which reveal the compiler arguments to
//  use for the file currently being edited (Used for real
//  time syntax and error checking by the SourceKit daemon).
//  Captures this information and passes it onto a Recompiler
//  instance to process and inject when edited file is saved.
//
import Foundation
import SwiftRegex
import Fortify
import Popen

class MonitorXcode {

    // Currently running Xcode process
    static weak var runningXcode: MonitorXcode?
    // The service to recompile and inject a source file.
    static var recompiler = FrontendServer.frontendRecompiler(for: "Xcode")

    func debug(_ what: Any..., separator: String = " ") {
        #if DEBUG
        print(what, separator: separator)
        #endif
    }

    init(args: String = "") {
        var args = args
        #if DEBUG
        args += " | tee \(Reloader.tmpbase).log"
        #endif
        if !FileManager.default.fileExists(atPath: Defaults.xcodePath) {
            InjectionServer.error("""
                No valid Xcode at path:
                \(Defaults.xcodePath)
                Use menu item "Select Xcode"
                to select a valid path.
                """)
        }
        else if let xcodeStdout = Popen(cmd: """
            export SOURCEKIT_LOGGING=1
            export RUNNING_VIA_INJECTION_NEXT=1
            '\(Defaults.xcodePath)/Contents/MacOS/Xcode' 2>&1 \(args)
            """) {
            Self.runningXcode = self
            AppDelegate.ui.launchXcodeItem.state = .on
            DispatchQueue.global().async {
                while true {
                    do {
                        try Fortify.protect {
                            AppDelegate.ui.setMenuIcon(.ready)
                            self.processSourceKitOutput(from: xcodeStdout)
                            AppDelegate.ui.setMenuIcon(.idle)
                        }
                        Self.runningXcode = nil
                        AppDelegate.ui.launchXcodeItem.state = .off
                        if !xcodeStdout.terminatedOK() && Defaults.xcodeRestart == true {
                            AppDelegate.ui.runXcode(self)
                        }
                        Self.recompiler.writeCache()
                        break // break on clean exit and EOF.
                    } catch {
                        // Continue processing on error
                        Self.recompiler.error(error)
                    }
                }
            }
        }
    }

    func processSourceKitOutput(from xcodeStdout: Popen) {
        var buffer = [CChar](repeating: 0, count: Popen.initialLineBufferSize)
        func readQuotedString() -> String? {
            var offset = 0
            let doubleQuote = Int32(UInt8(ascii: "\"")), escaped = #"\""#
            while let line = fgets(&buffer[offset], CInt(buffer.count-offset),
                                   xcodeStdout.fileStream) {
                offset += strlen(line)
                if offset > 0 && buffer[offset-1] == UInt8(ascii: "\n") {
                    if let start = strchr(buffer, doubleQuote),
                       let end = strrchr(start+1, doubleQuote) {
                        end[0] = 0
                        var out = String(cString: start+1)
                        // Xcode used NSLog to log internal UTF8 strings
                        // using %s which uses the macOS system encoding.
                        // https://en.wikipedia.org/wiki/Mac_OS_Roman
                        // For now we need to do the following dance
                        // to revert scrambled non-ASCII file paths.
                        if out.hasPrefix("/") &&
                            !FileManager.default.fileExists(atPath: out),
                           let data = out.data(using: .macOSRoman),
                           let recovered = String(data: data, encoding: .utf8),
                           FileManager.default.fileExists(atPath: recovered) {
                            out = recovered
                        }
                        if strstr(start+1, escaped) != nil {
                            out = out.replacingOccurrences(of: escaped, with: "\"")
                        }
                        return out
                    }

                    return nil
                }

                var grown = [CChar](repeating: 0, count: buffer.count*2)
                strcpy(&grown, buffer)
                buffer = grown
            }

            return nil
        }

        let indexBuild = "/Index.noindex/Build/"
        while let line = xcodeStdout.readLine() {
//            debug(">>"+line+"<<")
            autoreleasepool {
            if line.hasPrefix("  key.request: source.request.") &&
                (line == "  key.request: source.request.editor.open," ||
                 line == "  key.request: source.request.diagnostics," ||
                 line == "  key.request: source.request.activeregions," ||
                 line == "  key.request: source.request.relatedidents,") &&
                xcodeStdout.readLine() == "  key.compilerargs: [" ||
                line == "  key.compilerargs: [" {
                var parser = FrontendServer.CompilationArgParser()

                while var arg = readQuotedString() {
                    /// Used if injecting the Swift compiler.
                    let llvmIncs = "/llvm-macosx-arm64/lib"
                    if arg.hasPrefix("-I"), arg.contains(llvmIncs) {
                        arg = arg.replacingOccurrences(of: llvmIncs,
                            with: "/../buildbot_osx"+llvmIncs)
                    }

                    /// Arguments received from SourceKit while syntax highlighting the editor
                    /// have their own "Intermediates" directory. Map it back to the main one.
                    let alt = arg[indexBuild, "/Build/"]
                    if !arg.hasSuffix(".yaml"), alt != arg,
                       !arg.contains("/Intermediates.noindex/"),
                       let path: String = alt[#"[^/]*([^#]+)"#],
                       FileManager.default.fileExists(atPath: path) {
                        arg = alt
                    }

                    // SourceKit-specific args handled before the shared parser.
                    if arg == "-fsyntax-only" || arg == "-o" {
                        _ = xcodeStdout.readLine()
                    } else if var work: String = arg[#"-working-directory(?:=(.*))?"#] {
                        if work == RegexOptioned.unmatchedGroup,
                           let swork = readQuotedString() {
                            work = swork
                        }
                        parser.workingDir = work
                    } else if parser.args.last == "-vfsoverlay",
                              arg.contains(indexBuild) {
                        // injecting tests without having run tests
                        parser.args.removeLast()
                    } else if arg == "-Xfrontend" || arg.hasPrefix("-driver-") {
                        // drop silently
                    } else {
                        parser.process(arg: arg, next: readQuotedString)
                    }
                }

                guard !parser.args.isEmpty, let source =
                        readQuotedString() ?? readQuotedString(),
                      !source.contains("\\n") else {
                    return
                }

                print("Updating \(parser.args.count) args with \(parser.swiftFileCount) swift files "+source+" "+line)
                let update = NextCompiler.Compilation(arguments: parser.args,
                    swiftFiles: parser.swiftFiles, workingDir: parser.workingDir)

                NextCompiler.compileQueue.async {
                    Self.recompiler.store(compilation: update, for: source)
                }
            } else if line ==
                "  key.request: source.request.indexer.editor-did-save-file,",
                let _ = xcodeStdout.readLine(), let source = readQuotedString() {
                print("Injecting saved file "+source)
                NextCompiler.compileQueue.async {
                    _ = Self.recompiler.inject(source: source)
                }
            }
        }
        }
    }
}


================================================
FILE: App/InjectionNext/NextCompiler.swift
================================================
//
//  Recompiler.swift
//  InjectionNext
//
//  Created by John Holdsworth on 21/06/2024.
//  Copyright © 2024 John Holdsworth. All rights reserved.
//
//  Server side implementation of injection.
//  Recompile, link, codesign, send to client.
//
import Foundation
import Fortify
import Popen
import DLKit

/// bring in injectingXCTest()
struct Reloader {}

@discardableResult
public func log(_ what: Any..., prefix: String = APP_PREFIX, separator: String = " ") -> Bool {
    var msg = what.map {"\($0)"}.joined(separator: separator)
    #if INJECTION_III_APP
    msg = "⏳ "+msg
    #else
    msg = prefix+msg
    LogBuffer.shared?.append(msg, level: "info")
    #endif
    print(msg)
    for client in InjectionServer.currentClients {
        client?.sendCommand(.log, with: msg)
    }
    return true
}

class NextCompiler {

    /// Information required to call the compiler for a file.
    struct Compilation: Codable, Hashable {
        /// Sundry arguments to the compiler
        let arguments: [String]
        /// Swift files in the target ready to be written as a -filelist
        let swiftFiles: String
        /// Directory to run compiler in (not important for Swift)
        let workingDir: String
        /// captured environment
        var env: String?
    }

    /// Queue for one compilation at a time.
    static let compileQueue = DispatchQueue(label: "InjectionCompile")
    /// Last build error.
    static var lastError: String?, lastSource: String?

    let name: String
    /// Base for temporary files
    let tmpbase = "/tmp/injectionNext"
    /// Injection pending if information was not available
    var pendingSource: String?
    /// Information for compiling a file per source file.
    var compilations = [String: Compilation]()
    /// Trying to avoid fragmenting memory
    var lastCompilation: Compilation?
    /// Previous dynamic libraries prepared by source file
    var prepared = [String: String]()
    /// Invalidate on three failures
    var strikes = [String: Int]()
    /// Default counter for Compilertron
    var compileNumber = 0
    
    init(name: String) {
        self.name = name
    }

    func error(_ msg: String) {
        let msg = "⚠️ "+msg
        NSLog(msg)
        log(msg)
    }
    func error(_ err: Error) {
        error("Internal app error: \(err)")
    }
    
    var modified = false

    func store(compilation: Compilation, for source: String) {
        Self.lastSource = source
        if lastCompilation != compilation {
            lastCompilation = compilation
        } //else { print("reusing") }
        if compilations[source] != lastCompilation {
            compilations[source] = lastCompilation
            modified = true
        }
        if source == pendingSource {
            print("Delayed injection of "+source)
            if inject(source: source) {
                pendingSource = nil
            }
        }
    }
    
    func canCompile(source: String, for platform: String? = nil) -> Bool {
        if let compilation = compilations[source],
           platform == nil || ("SDKs/"+platform!).withCString({ sdk in
               compilation.arguments.first { strstr($0, sdk) != nil }}) != nil {
            return true
        } else { return false }
    }

    /// Main entry point called by MonitorXcode
    func inject(source: String) -> Bool {
        // Start tracking metrics
        currentMetrics = InjectionMetricsTracker(sourcePath: source)

        do {
            let result = try Fortify.protect { () -> Bool in
                for client in InjectionServer.currentClients.reversed() {
                guard let (dylib, dylibName, platform, useFilesystem)
                        = try prepare(source: source, connected: client),
                   let data = codesign(dylib: dylib, platform: platform) else {
                    error("Injection failed. Was your app connected?")
                    AppDelegate.ui.setMenuIcon(.error)
                    return false
                }

                InjectionServer.clientQueue.sync {
                    guard let client = client else {
                        AppDelegate.ui.setMenuIcon(.ready)
                        return
                    }
//                    if Reloader.injectingXCTest(in: dylib) {
//                        _ = client.copyPlugIns
//                    }
                    if useFilesystem {
                        client.writeCommand(InjectionCommand
                            .load.rawValue, with: dylib)
                    } else {
                        client.writeCommand(InjectionCommand
                            .inject.rawValue, with: dylibName)
                        client.write(data)
                    }
                    unsupported(source: source, dylib: dylib, client: client)
                }
                }
                Self.lastSource = source
                if modified {
                    writeCache()
                }
                return true
            }

            // Calculate total time and send metrics
            if let metrics = currentMetrics {
                metrics.success = result
                sendMetrics(metrics)
            }

            return result
        } catch {
            // Send failure metrics
            if let metrics = currentMetrics {
                metrics.success = false
                sendMetrics(metrics)
            }
            self.error(error)
            return false
        }
    }

    /// Seek to highlight potentially unsupported injections.
    func unsupported(source: String, dylib: String, client: InjectionServer) {
        #if !INJECTION_III_APP
        if let symbols = FileSymbols(path: dylib)?.trieSymbols()?
            .filter({ entry in
                lazy var symbol: String = String(cString: entry.name)
                return strncmp(entry.name, "_$s", 3) == 0 &&
                strstr(entry.name, "fU") == nil && // closures
                !symbol.hasSuffix("MD") && !symbol.hasSuffix("Oh") &&
                !symbol.hasSuffix("Wl") && !symbol.hasSuffix("WL") })
            .map({ String(cString: $0.name) }).sorted() {
//            print(symbols)
            if let previous = client.exports[source],
               previous.count != symbols.count {
                log("ℹ️ Symbols altered, this may not be supported." +
                      " \(symbols.count) c.f. \(previous.count)")
                if #available(macOS 15.0, *) {
                    print(symbols.difference(from: previous))
                }
            }
            client.exports[source] = symbols
        }
        #endif
    }

    func prepare(source: String, connected: InjectionServer?) throws
        -> (dylib: String, dylibName: String, platform: String, Bool)? {
        AppDelegate.ui.setMenuIcon(.busy)
        connected?.injectionNumber += 1
        compileNumber += 1
        Self.lastError = nil

        // Support for https://github.com/johnno1962/Compilertron
        let isCompilertron = connected == nil && source.hasSuffix(".cpp")
        let compilerTmp = "/tmp/compilertron_patches"
        let compilerPlatform = "MacOSX"
        let compilerArch = "arm64"

        let tmpPath = connected?.tmpPath ?? compilerTmp
        let platform = connected?.platform ?? compilerPlatform
        let sourceName = URL(fileURLWithPath: source)
            .deletingPathExtension().lastPathComponent
        if isCompilertron, let previous = prepared[sourceName] {
            unlink(previous)
        }

        let dylibName = DYLIB_PREFIX + sourceName +
            "_\(connected?.injectionNumber ?? compileNumber).dylib"
        let useFilesystem = connected?.isLocalClient != false
        #if INJECTION_III_APP
        let dylibPath = (true ? tmpPath : "/tmp") + dylibName
        #else
        let dylibPath = (useFilesystem ? tmpPath : "/tmp") + dylibName
        #endif
        guard let object = try recompile(source: source, platform: platform), {
                // Track compilation time
                if let startTime = currentMetrics?.startTime {
                    currentMetrics?.compilationTimeMs =
                    (Date.timeIntervalSinceReferenceDate - startTime) * 1000
                }
                return true
            }(),
           tmpPath != compilerTmp || mkdir(compilerTmp, 0o777) != -999,
           let (dylib, linkingTimeMs) = link(object: object, dylib: dylibPath,
                    arch: connected?.arch ?? compilerArch) else {
                        let strike = (strikes[source] ?? 0)+1
                        strikes[source] = strike
                        if strike >= 3 {
                            compilations.removeValue(forKey: source)
                            writeCache()
                        }
                        return nil
                    }

        currentMetrics?.linkingTimeMs = linkingTimeMs

        strikes[source] = 0
        prepared[sourceName] = dylib
        print("Prepared dylib: "+dylib)
        return (dylib, dylibName, platform, useFilesystem)
    }

    /// Compile a source file using inforation provided by MonitorXcode
    /// task and return the full path to the resulting object file.
    func recompile(source: String, platform: String) throws ->  String? {
        guard let stored = compilations[source] else {
            error("Postponing: \(source) Have you viewed it in Xcode?")
            pendingSource = source
            return nil
        }

        let uniqueObject = InjectionServer.currentClient?.injectionNumber ?? 0
        let object = tmpbase+"_\(uniqueObject).o"
        let isSwift = source.hasSuffix(".swift")
        let filesfile = tmpbase+".filelist"

        unlink(object)
        unlink(filesfile)
        try stored.swiftFiles.write(toFile: filesfile,
                                    atomically: false, encoding: .utf8)

        log("Recompiling: "+source)
        let toolchain = Defaults.xcodePath +
            "/Contents/Developer/Toolchains/XcodeDefault.xctoolchain"
        let compiler = (isSwift ? FrontendServer.loggedFrontend : nil) ??
            toolchain + "/usr/bin/" + (isSwift ? "swift-frontend" : "clang")
        let platformUsr = Defaults.xcodePath + "/Contents/Developer/Platforms/" +
            platform.replacingOccurrences(of: "Simulator", with: "OS") +
            ".platform/Developer/usr/"
        let baseOptionsToAdd = ["-o", object, "-DDEBUG", "-DINJECTING"]
        let languageSpecific = (isSwift ?
            ["-c", "-filelist", filesfile, "-primary-file", source,
             Reloader.typeCheckLimit,
             "-external-plugin-path",
             platformUsr+"lib/swift/host/plugins#" +
             platformUsr+"bin/swift-plugin-server",
             "-external-plugin-path",
             platformUsr+"local/lib/swift/host/plugins#" +
             platformUsr+"bin/swift-plugin-server",
             "-plugin-path", toolchain+"/usr/lib/swift/host/plugins",
             "-plugin-path", toolchain+"/usr/local/lib/swift/host/plugins"] :
            ["-c", source, "-Xclang", "-fno-validate-pch"]) + baseOptionsToAdd
        let wmoFlags: Set<String> = [
            "-whole-module-optimization",
            "-internalize-at-link",
            "-no-serialize-debugging-options"
        ]
        var arguments = [String]()
        var skipNext = false
        for arg in stored.arguments where !wmoFlags.contains(arg) {
            if skipNext { skipNext = false; continue }
            if arg == "-o" { skipNext = true; continue }
            arguments.append(arg)
        }
        if let target = InjectionServer.currentClient?.arch, target != "arm64" {
            // Simulator running in Rosetta.
            for i in 0..<arguments.count {
                arguments[i][#"^(\w+)-apple-ios"#] = target
            }
        }
        // Call compiler process with timing
        let compilationStartTime = Date.timeIntervalSinceReferenceDate
        var env: [String: String]?
        if let environment = stored.env {
            env = [String: String]()
            for (key, value): (String, String) in environment[
                #"^(\w+)=(.*)"#.anchorsMatchLines] {
                env?[key] = value
            }
        }
        let compile = Topen(exec: compiler,
                            arguments: arguments + languageSpecific,
                            cd: stored.workingDir, env: env)

        var errors = ""
        while let line = compile.readLine() {
            if let slow: String = line[Reloader.typeCheckRegex] {
                log(slow)
            }
            errors += line+"\n"
        }
        if errors.contains(" error: ") {
            error("Failed compilation: "+([compiler] + arguments +
                        languageSpecific).joined(separator: " "))
            error("Recompile failed for: \(source)\n"+errors)
            Self.lastError = errors
            return nil
        }

        // Log successful compilation with timing
        let now = Date.timeIntervalSinceReferenceDate
        let compilationTimeMs = (now - compilationStartTime) * 1000
        detail(String(format: "⚡ Compiled for \(name) in %.0fms",
                      compilationTimeMs))

        let compilationCommand = (arguments + languageSpecific)
            .map { $0[#"([ $()])"#, "\\\\$1"] }.joined(separator:  " ")
        Reloader.extractLinkCommand(from: compilationCommand)
        return object
    }

    /// Link and object file to create a dynamic library
    func link(object: String, dylib: String, arch: String) -> (String, Double)? {
        let linkingStartTime = Date.timeIntervalSinceReferenceDate
        var linkCommand = Reloader.linkCommand + " \(object) -o \"\(dylib)\" "
        if DispatchQueue.main.sync(execute: {
            AppDelegate.ui.deviceTesting?.state == .on }) {
            let otherOptions = DispatchQueue.main.sync { () -> String in
                AppDelegate.ui.librariesField.stringValue = Defaults.deviceLibraries
                return Defaults.deviceLibraries }
            let platformDev = "\(Reloader.xcodeDev)/Platforms/\(Reloader.platform).platform/Developer"
            linkCommand += """
                -F /tmp/InjectionNext.Products \
                -F "\(platformDev)/Library/Frameworks" \
                -L "\(platformDev)/usr/lib" \(otherOptions)
                """.replacingOccurrences(of: "__PLATFORM__", with: Reloader.sysroot)
        }

        if let errors = Popen.system(linkCommand, errors: true) {
            error("Linking failed:\n\(linkCommand)\nerrors:\n"+errors)
            Self.lastError = errors
            return nil
        }

        let linkingTimeMs = (Date.timeIntervalSinceReferenceDate - linkingStartTime) * 1000
        return (dylib, linkingTimeMs)
    }

    /// Codesign a dynamic library
    func codesign(dylib: String, platform: String) -> Data? {
        if platform != "iPhoneSimulator" {
        var identity = "-"
        if !platform.hasSuffix("Simulator") && platform != "MacOSX" {
            identity = DispatchQueue.main.sync { AppDelegate.ui.codeSigningID }
            log("Codesigning dylib with identity "+identity)
        }
        let codesign = """
            (export CODESIGN_ALLOCATE="\(Defaults.xcodePath+"/Contents/Developer"
             )/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate"; \
            if /usr/bin/file \"\(dylib)\" | /usr/bin/grep ' shared library ' >/dev/null; \
            then /usr/bin/codesign --force -s "\(identity)" \"\(dylib)\";\
            else exit 1; fi)
            """
        if let errors = Popen.system(codesign, errors: true) {
            error("Codesign failed \(codesign) errors:\n"+errors)
            Self.lastError = errors
        }
        }
        return try? Data(contentsOf: URL(fileURLWithPath: dylib))
    }

    /// Tracks timing metrics for injection process
    final class InjectionMetricsTracker: Codable {
        var compilationTimeMs: Double = 0
        var linkingTimeMs: Double = 0
        var totalTimeMs: Double = 0
        var sourcePath: String
        var bazelTarget: String?
        var success: Bool = false
        var notificationName: String = INJECTION_METRICS_NOTIFICATION
        let startTime: Double

        init(sourcePath: String) {
            self.sourcePath = sourcePath
            self.startTime = Date.timeIntervalSinceReferenceDate
        }
    }

    /// Current metrics being tracked
    var currentMetrics: InjectionMetricsTracker?

    /// Enrich metrics with Bazel target and normalize source path
    #if !INJECTION_III_APP
    func enrichMetrics(_ metrics: inout InjectionMetricsTracker) {
        let sourcePath = metrics.sourcePath

        // Find workspace root to normalize the path
        if let workspaceRoot = BazelInterface.findWorkspaceRoot(containing: sourcePath) {
            let workspaceRootPath = (workspaceRoot as NSString).standardizingPath
            let fullPath = (sourcePath as NSString).standardizingPath

            // Normalize sourcePath to workspace-relative (in place)
            if fullPath.hasPrefix(workspaceRootPath + "/") {
                metrics.sourcePath = String(fullPath.dropFirst(workspaceRootPath.count + 1))
            } else {
                metrics.sourcePath = URL(fileURLWithPath: sourcePath).lastPathComponent
            }

            // Try to discover the Bazel app target
            do {
                let queryHandler = try BazelAQueryParser(workspaceRoot: workspaceRoot)
                queryHandler.autoDiscoverAppTarget(for: sourcePath)
                metrics.bazelTarget = queryHandler.getAppTarget()
            } catch {
                // Bazel discovery failed, metrics will have nil bazelTarget
                print("⚠️ Could not discover Bazel target for \(sourcePath): \(error)")
            }
        }
    }
    #endif

    /// Send metrics to all connected clients
    func sendMetrics(_ metrics: InjectionMetricsTracker) {
        #if !INJECTION_III_APP
        if AppDelegate.watchers.isEmpty &&
            BazelActionQueryHandler.cachedAppTarget == nil { return }
        metrics.totalTimeMs = (Date.timeIntervalSinceReferenceDate
                               - metrics.startTime) * 1000
        var enrichedMetrics = metrics
        enrichMetrics(&enrichedMetrics)

        let encoder = JSONEncoder()
        encoder.keyEncodingStrategy = .convertToSnakeCase
        guard let jsonData = try? encoder.encode(enrichedMetrics),
              let jsonString = String(data: jsonData, encoding: .utf8) else {
            return
        }
        for client in InjectionServer.currentClients {
            client?.sendCommand(.metrics, with: jsonString)
        }
        #endif
    }
}


================================================
FILE: App/InjectionNext/build_bundle.sh
================================================
#!/bin/sh -x

#  build_bundle.sh
#  InjectionNext
#
#  Created by John Holdsworth on 22/07/2024.
#  Copyright © 2024 John Holdsworth. All rights reserved.

FAMILY=$1
PLATFORM=$2
SDK="$(echo $PLATFORM | tr "[A-Z]" "[a-z]")"
sleep $3

FIXED_XCODE_DEVELOPER_PATH=/Applications/Xcode.app/Contents/Developer
export SWIFT_ACTIVE_COMPILATION_CONDITIONS=""

    SWIFT_DYLIBS_PATH="$FIXED_XCODE_DEVELOPER_PATH/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$SDK"
    CONCURRENCY_DYLIBS="$FIXED_XCODE_DEVELOPER_PATH/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/$SDK"
    XCODE_PLATFORM_PATH="$FIXED_XCODE_DEVELOPER_PATH/Platforms/$PLATFORM.platform"
    XCTEST_FRAMEWORK_PATH="$XCODE_PLATFORM_PATH/Developer/Library/Frameworks"
    XCTEST_SUPPORT_PATH="$XCODE_PLATFORM_PATH/Developer/usr/lib"
    BUNDLE_CONFIG=Debug

    if [ ! -d "$SWIFT_DYLIBS_PATH" -o ! -d "${XCTEST_FRAMEWORK_PATH}/XCTest.framework" ]; then
        echo "Missing RPATH $SWIFT_DYLIBS_PATH $XCTEST_FRAMEWORK_PATH"
        exit 1
    fi
    
    ADD_INSTALL_NAME=""
    if [[ ${FAMILY} =~ Dev ]]; then
        # real devices require a copy_bundle.sh build phase
        ADD_INSTALL_NAME="LD_DYLIB_INSTALL_NAME=@rpath/lib${SDK}Injection.dylib"
    fi
    for i in 1 2 3; do if
    "$DEVELOPER_BIN_DIR"/xcodebuild SYMROOT=$SYMROOT ARCHS="$ARCHS" $APP_SANDBOXED PRODUCT_NAME="${FAMILY}Injection" LD_RUNPATH_SEARCH_PATHS="@loader_path/Frameworks @loader_path/${FAMILY}Injection.bundle/Frameworks $SWIFT_DYLIBS_PATH $CONCURRENCY_DYLIBS $XCTEST_FRAMEWORK_PATH $XCTEST_SUPPORT_PATH" $ADD_INSTALL_NAME PLATFORM_DIR="$DEVELOPER_DIR/Platforms/$PLATFORM.platform" -sdk $SDK -config $BUNDLE_CONFIG  -target InjectionBundle; then
#-archivePath "/tmp/Archive.$SDK" -derivedDataPath "/tmp/Derived.$SDK"
            break
        elif [ "$i" = "3" ]; then
            exit 1
        fi
        echo "Retrying...";
    done &&
    
    rsync -au $SYMROOT/$BUNDLE_CONFIG-$SDK/*.bundle "$CODESIGNING_FOLDER_PATH/Contents/Resources" &&
    ln -sf "${FAMILY}Injection.bundle/${FAMILY}Injection" "$CODESIGNING_FOLDER_PATH/Contents/Resources/lib${SDK}Injection.dylib"


================================================
FILE: App/InjectionNext/build_bundles.sh
================================================
#!/bin/sh

#  build_bundles.sh
#  InjectionNext
#
#  Created by John Holdsworth on 22/07/2024.
#  Copyright © 2024 John Holdsworth. All rights reserved.

FIXED_XCODE_DEVELOPER_PATH=/Applications/Xcode.app/Contents/Developer
export SWIFT_ACTIVE_COMPILATION_CONDITIONS=""
BUILD=`/usr/libexec/PlistBuddy -c "Print :CFBundleVersion" $CODESIGNING_FOLDER_PATH/Contents/Info.plist`

function build_bundle () {
    FAMILY=$1
    PLATFORM=$2
    SDK=$3
    SWIFT_DYLIBS_PATH="$FIXED_XCODE_DEVELOPER_PATH/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/$SDK"
    CONCURRENCY_DYLIBS="$FIXED_XCODE_DEVELOPER_PATH/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift-5.5/$SDK"
    XCODE_PLATFORM_PATH="$FIXED_XCODE_DEVELOPER_PATH/Platforms/$PLATFORM.platform"
    XCCORE_FRAMEWORK_PATH="$XCODE_PLATFORM_PATH/Developer/Library/PrivateFrameworks"
    XCTEST_FRAMEWORK_PATH="$XCODE_PLATFORM_PATH/Developer/Library/Frameworks"
    XCTEST_SUPPORT_PATH="$XCODE_PLATFORM_PATH/Developer/usr/lib"
    BUNDLE_CONFIG=Debug

    if [ ! -d "$SWIFT_DYLIBS_PATH" -o ! -d "${XCTEST_FRAMEWORK_PATH}/XCTest.framework" ]; then
        echo "Missing RPATH $SWIFT_DYLIBS_PATH $XCTEST_FRAMEWORK_PATH"
        exit 1
    fi

    ADD_INSTALL_NAME=""
    if [[ ${FAMILY} =~ Dev ]]; then
        # real devices require a copy_bundle.sh build phase
        ADD_INSTALL_NAME="LD_DYLIB_INSTALL_NAME=@rpath/lib${SDK}Injection.dylib"
    fi
    "$DEVELOPER_BIN_DIR"/xcodebuild SYMROOT=$SYMROOT ARCHS="$ARCHS" $APP_SANDBOXED PRODUCT_NAME="${FAMILY}Injection" LD_RUNPATH_SEARCH_PATHS="@executable_path/Frameworks @loader_path/Frameworks @loader_path/${FAMILY}Injection.bundle/Frameworks $SWIFT_DYLIBS_PATH $CONCURRENCY_DYLIBS $XCTEST_FRAMEWORK_PATH $XCTEST_SUPPORT_PATH $XCCORE_FRAMEWORK_PATH" $ADD_INSTALL_NAME PLATFORM_DIR="$DEVELOPER_DIR/Platforms/$PLATFORM.platform" -sdk $SDK -config $BUNDLE_CONFIG -target InjectionBundle &&

    rsync -au $SYMROOT/$BUNDLE_CONFIG-$SDK/*.bundle "$CODESIGNING_FOLDER_PATH/Contents/Resources" &&
    PLIST="$CODESIGNING_FOLDER_PATH/Contents/Resources/${FAMILY}Injection.bundle/Info.plist" &&
    (/usr/libexec/PlistBuddy -c "Delete :CFBundleVersion" "$PLIST" || echo -n) &&
    /usr/libexec/PlistBuddy -c "Add :CFBundleVersion string $BUILD" "$PLIST" &&
    ln -sf "${FAMILY}Injection.bundle/${FAMILY}Injection" "$CODESIGNING_FOLDER_PATH/Contents/Resources/lib${SDK}Injection.dylib"
}

ln -sf "macOSInjection.bundle/Contents/MacOS/macOSInjection" "$CODESIGNING_FOLDER_PATH/Contents/Resources/libmacosxInjection.dylib" &&

build_bundle iOS iPhoneSimulator iphonesimulator &&
if [[ "$ACTION" = "install" ]]; then
    build_bundle tvOS AppleTVSimulator appletvsimulator &&
    build_bundle xrOS XRSimulator xrsimulator &&
    build_bundle iOSDev iPhoneOS iphoneos &&
    build_bundle tvOSDev AppleTVOS appletvos &&
    build_bundle xrOSDev XROS xros
fi &&
exit 0


================================================
FILE: App/InjectionNext/copy_bundle.sh
================================================
#!/bin/bash -x
#
#  copy_bundle.sh
#  InjectionIII
#
#  Copies injection bundle for on-device injection.
#  Thanks @oryonatan
#
#  $Id: //depot/HotReloading/copy_bundle.sh#14 $
#

if [[ "$CONFIGURATION" =~ Debug ]]; then
    if [ ! -w "$CODESIGNING_FOLDER_PATH" ]; then
        echo '*** copy_bundle.sh unable to write to file system. ***' \
            'Change build setting "User Script Sandboxing" to NO'
        exit 1;
    fi

    # determine which prebuilt bundle to copy
    RESOURCES=${RESOURCES:-"$(dirname "$0")"}
    # If there are frameworks used only by tests
    TESTING_FRAMEWORKS="$2"
    COPY="$CODESIGNING_FOLDER_PATH/iOSInjection.bundle"
    PLIST="$COPY/Info.plist"
    if [ "$PLATFORM_NAME" == "macosx" ]; then
     BUNDLE=${1:-macOSInjection}
     COPY="$CODESIGNING_FOLDER_PATH/Contents/Resources/macOSInjection.bundle"
     PLIST="$COPY/Contents/Info.plist"
    elif [ "$PLATFORM_NAME" == "appletvsimulator" ]; then
     BUNDLE=${1:-tvOSInjection}
    elif [ "$PLATFORM_NAME" == "appletvos" ]; then
     BUNDLE=${1:-tvOSDevInjection}
    elif [ "$PLATFORM_NAME" == "xrsimulator" ]; then
     BUNDLE=${1:-xrOSInjection}
    elif [ "$PLATFORM_NAME" == "xros" ]; then
     BUNDLE=${1:-xrOSDevInjection}
    elif [ "$PLATFORM_NAME" == "iphoneos" ]; then
     BUNDLE=${1:-iOSDevInjection}
    else
     BUNDLE=${1:-iOSInjection}
    fi

    mkdir -p "$CODESIGNING_FOLDER_PATH/Frameworks" &&

    # copy frameworks used for testing into app's bundle/Frameworks
    ln -sf "../iOSInjection.bundle/$BUNDLE" "$CODESIGNING_FOLDER_PATH/Frameworks/lib${PLATFORM_NAME}Injection.dylib" &&
    if [[ "$BUNDLE" =~ Dev ]]; then
    rsync -a "$PLATFORM_DEVELOPER_LIBRARY_DIR"/*Frameworks/{XC,StoreKit}* "$PLATFORM_DEVELOPER_USR_DIR/lib"/*.dylib "$CODESIGNING_FOLDER_PATH/Frameworks/" &&
    codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/Frameworks"/{XC*,StoreKit*,*.dylib} ||
    echo "*** You should be able to ignore the above errors ***"
    else
    rsync -a "$PLATFORM_DEVELOPER_LIBRARY_DIR"/*Frameworks/XC* "$PLATFORM_DEVELOPER_USR_DIR/lib"/*.dylib "$CODESIGNING_FOLDER_PATH/Frameworks/" &&
    codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/Frameworks"/{XC*,*.dylib}
    fi &&

    # Copy frameworks only used in test target
    PRODUCTS_DIR="$(dirname "$CODESIGNING_FOLDER_PATH")"
    rm -f /tmp/InjectionNext.Products
    ln -s "$PRODUCTS_DIR" /tmp/InjectionNext.Products
    (cd "$PRODUCTS_DIR" && for fwork in $TESTING_FRAMEWORKS; do
        if [ -f "$fwork/Info.plist" -a \
            ! -d "$CODESIGNING_FOLDER_PATH/Frameworks/$fwork" ]; then
            rsync -a "$fwork" "$CODESIGNING_FOLDER_PATH/Frameworks" &&
            codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/Frameworks/$fwork"
        fi
    done)

    # Xcode 16's new SwiftTesting framework
    TESTING="$PLATFORM_DEVELOPER_LIBRARY_DIR/Frameworks/Testing.Framework"
    if [ -d "$TESTING" ]; then
      rsync -a "$TESTING"/* "$CODESIGNING_FOLDER_PATH/Frameworks/Testing.framework/"
      codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/Frameworks/Testing.framework";
    fi

    # Make copy of "PlugIns" directory when testing
    export PLUGINS="/tmp/PlugIns.$PRODUCT_NAME.$PLATFORM_NAME"
    export LAST_PLUGINS="/tmp/InjectionNext.PlugIns"
    rm -f $LAST_PLUGINS
    if [ -d "$CODESIGNING_FOLDER_PATH/PlugIns" ]; then
     (sleep 5; while
      rsync -va "$CODESIGNING_FOLDER_PATH/PlugIns"/* "$PLUGINS/" |
      grep -v /sec | grep /; do sleep 15; done) 1>/dev/null 2>&1 &
    else bash -x <<'CAN_FAIL' 2>/dev/null
      # Xcode 16 deletes PlugIns directory. copy or create link
      rsync -a "$PLUGINS"/* "$CODESIGNING_FOLDER_PATH/PlugIns/" &&
      codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$CODESIGNING_FOLDER_PATH/PlugIns/*.xctest" ||
      ln -s $PLUGINS $LAST_PLUGINS
CAN_FAIL
    fi

    # copy prebuilt bundle into app package and codesign
    rsync -a "$RESOURCES/$BUNDLE.bundle"/* "$COPY/" &&
    # See +[SimpleSocket initialize] for pre-built bundles/dylibs
    /usr/libexec/PlistBuddy -c "Add :UserHome string $HOME" "$PLIST" &&
    (/usr/libexec/PlistBuddy -c "Delete :InjectionUserHome" "$CODESIGNING_FOLDER_PATH/Info.plist" || echo -n) &&
    /usr/libexec/PlistBuddy -c "Add :InjectionUserHome string $HOME" "$CODESIGNING_FOLDER_PATH/Info.plist" &&
    codesign -f --sign "$EXPANDED_CODE_SIGN_IDENTITY" --timestamp\=none --preserve-metadata\=identifier,entitlements,flags --generate-entitlement-der "$COPY" &&
    defaults write com.johnholdsworth.InjectionNext codesigningIdentity "$EXPANDED_CODE_SIGN_IDENTITY"
fi


================================================
FILE: App/InjectionNext/main.m
================================================
//
//  main.m
//  InjectionIII
//
//  Created by John Holdsworth on 06/11/2017.
//  Copyright © 2017 John Holdsworth. All rights reserved.
//

#import <Cocoa/Cocoa.h>

int main(int argc, const char * argv[]) {
    return NSApplicationMain(argc, argv);
}


================================================
FILE: App/InjectionNext/swift-frontend.sh
================================================
#!/bin/bash

#  swift-frontend.sh
#  InjectionNext
#
#  Created by John Holdsworth on 23/02/2025.
#  Copyright © 2025 John Holdsworth. All rights reserved.

FRONTEND="$0"
"$FRONTEND.save" "$@" &&
if [ "$2" = "-c" ]; then "/Applications/InjectionNext.app/Contents/Resources/feedcommands" \
    "2.0" "$(/usr/bin/env)" "$FRONTEND.save" "$@" >>/tmp/feedcommands.log 2>&1 & fi


================================================
FILE: App/InjectionNext.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
	archiveVersion = 1;
	classes = {
	};
	objectVersion = 70;
	objects = {

/* Begin PBXBuildFile section */
		2217BE812C0A65C20032A832 /* Fortify in Frameworks */ = {isa = PBXBuildFile; productRef = 2217BE802C0A65C20032A832 /* Fortify */; };
		222BC9452C157C3200780A41 /* InjectionReady.tif in Resources */ = {isa = PBXBuildFile; fileRef = 222BC9442C157C3200780A41 /* InjectionReady.tif */; };
		224E57FB2C08978F00B71C79 /* SwiftRegex in Frameworks */ = {isa = PBXBuildFile; productRef = 224E57FA2C08978F00B71C79 /* SwiftRegex */; };
		224E57FE2C08BBE300B71C79 /* InjectionServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224E57FD2C08BBE300B71C79 /* InjectionServer.swift */; };
		224E58002C08BC0E00B71C79 /* MonitorXcode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 224E57FF2C08BC0E00B71C79 /* MonitorXcode.swift */; };
		229127F12C1EEF69005D7625 /* Experimental.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC17029253ED117002E823F /* Experimental.swift */; };
		22B645A02C18DD9D00F99B61 /* Unhider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B6459F2C18DD9D00F99B61 /* Unhider.swift */; };
		22E3D98B2C19009900BB234E /* DLKit in Frameworks */ = {isa = PBXBuildFile; productRef = 22E3D98A2C19009900BB234E /* DLKit */; };
		22E3D98D2C19009900BB234E /* DLKitC in Frameworks */ = {isa = PBXBuildFile; productRef = 22E3D98C2C19009900BB234E /* DLKitC */; };
		BB0CECB02C73C1610000A4E6 /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = BB0CECAF2C73C1610000A4E6 /* Quick */; };
		BB0CECB32C73C1890000A4E6 /* Nimble in Frameworks */ = {isa = PBXBuildFile; productRef = BB0CECB22C73C1890000A4E6 /* Nimble */; };
		BB16653A25E9A5F2001407AE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = BB16653925E9A5F0001407AE /* main.m */; };
		BB2A66362E3E753B001EDD38 /* BAZEL.md in Resources */ = {isa = PBXBuildFile; fileRef = BB2A66352E3E753B001EDD38 /* BAZEL.md */; };
		BB2D41AC2DAD14EA0073C203 /* Popen in Frameworks */ = {isa = PBXBuildFile; productRef = BB2D41AB2DAD14EA0073C203 /* Popen */; };
		BB42F8E12EB626F100FDBBCC /* SwiftAspects.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D62EB626F100FDBBCC /* SwiftAspects.swift */; };
		BB42F8E22EB626F100FDBBCC /* SwiftInterpose.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D72EB626F100FDBBCC /* SwiftInterpose.swift */; };
		BB42F8E32EB626F100FDBBCC /* SwiftTrace.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8DF2EB626F100FDBBCC /* SwiftTrace.swift */; };
		BB42F8E42EB626F100FDBBCC /* SwiftSwizzle.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8DD2EB626F100FDBBCC /* SwiftSwizzle.swift */; };
		BB42F8E52EB626F100FDBBCC /* SwiftArgs.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D52EB626F100FDBBCC /* SwiftArgs.swift */; };
		BB42F8E62EB626F100FDBBCC /* SwiftInvoke.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D82EB626F100FDBBCC /* SwiftInvoke.swift */; };
		BB42F8E72EB626F100FDBBCC /* EasyPointer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D22EB626F100FDBBCC /* EasyPointer.swift */; };
		BB42F8E82EB626F100FDBBCC /* SwiftStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8DB2EB626F100FDBBCC /* SwiftStack.swift */; };
		BB42F8E92EB626F100FDBBCC /* SwiftStats.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8DC2EB626F100FDBBCC /* SwiftStats.swift */; };
		BB42F8EA2EB626F100FDBBCC /* StringIndex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D42EB626F100FDBBCC /* StringIndex.swift */; };
		BB42F8EB2EB626F100FDBBCC /* SwiftLifetime.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8D92EB626F100FDBBCC /* SwiftLifetime.swift */; };
		BB42F8EC2EB626F100FDBBCC /* SwiftMeta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F8DA2EB626F100FDBBCC /* SwiftMeta.swift */; };
		BB42F9212EB639D100FDBBCC /* DLKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91B2EB639D100FDBBCC /* DLKit.swift */; };
		BB42F9222EB639D100FDBBCC /* Iterators.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91F2EB639D100FDBBCC /* Iterators.swift */; };
		BB42F9232EB639D100FDBBCC /* Interposing.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91E2EB639D100FDBBCC /* Interposing.swift */; };
		BB42F9242EB639D100FDBBCC /* ImageSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91D2EB639D100FDBBCC /* ImageSymbols.swift */; };
		BB42F9252EB639D100FDBBCC /* Demangling.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91A2EB639D100FDBBCC /* Demangling.swift */; };
		BB42F9262EB639D100FDBBCC /* FileSymbols.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F91C2EB639D100FDBBCC /* FileSymbols.swift */; };
		BB42F92D2EB639E500FDBBCC /* DLKitC.c in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9292EB639E500FDBBCC /* DLKitC.c */; };
		BB42F92E2EB639E500FDBBCC /* trie_dladdr.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB42F92A2EB639E500FDBBCC /* trie_dladdr.mm */; };
		BB42F92F2EB639E500FDBBCC /* trie_dlops.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB42F92B2EB639E500FDBBCC /* trie_dlops.mm */; };
		BB42F9382EB63C2900FDBBCC /* KeyPaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9332EB63C2900FDBBCC /* KeyPaths.swift */; };
		BB42F9392EB63C2900FDBBCC /* Reloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9352EB63C2900FDBBCC /* Reloader.swift */; };
		BB42F93A2EB63C2900FDBBCC /* Generics.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9322EB63C2900FDBBCC /* Generics.swift */; };
		BB42F93B2EB63C2900FDBBCC /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9342EB63C2900FDBBCC /* Metadata.swift */; };
		BB42F93C2EB63C2900FDBBCC /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9312EB63C2900FDBBCC /* Common.swift */; };
		BB42F93D2EB63C2900FDBBCC /* Sweeper.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9362EB63C2900FDBBCC /* Sweeper.swift */; };
		BB42F93F2EB63CD800FDBBCC /* Common.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9312EB63C2900FDBBCC /* Common.swift */; };
		BB42F9462EB63E3400FDBBCC /* InjectionBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9412EB63E3400FDBBCC /* InjectionBase.swift */; };
		BB42F9472EB63E3400FDBBCC /* Recompiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9442EB63E3400FDBBCC /* Recompiler.swift */; };
		BB42F9482EB63E3400FDBBCC /* InjectionLite.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9422EB63E3400FDBBCC /* InjectionLite.swift */; };
		BB42F9492EB63E3400FDBBCC /* LogParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9432EB63E3400FDBBCC /* LogParser.swift */; };
		BB42F94A2EB63E3400FDBBCC /* FileWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9402EB63E3400FDBBCC /* FileWatcher.swift */; };
		BB42F94B2EB63E3F00FDBBCC /* FileWatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9402EB63E3400FDBBCC /* FileWatcher.swift */; };
		BB42F94C2EB63E4F00FDBBCC /* LogParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9432EB63E3400FDBBCC /* LogParser.swift */; };
		BB42F94D2EB63E5600FDBBCC /* Recompiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9442EB63E3400FDBBCC /* Recompiler.swift */; };
		BB42F94E2EB63E7700FDBBCC /* InjectionBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9412EB63E3400FDBBCC /* InjectionBase.swift */; };
		BB42F9552EB63EFF00FDBBCC /* BazelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9512EB63EFF00FDBBCC /* BazelInterface.swift */; };
		BB42F9562EB63EFF00FDBBCC /* GitIgnoreParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9532EB63EFF00FDBBCC /* GitIgnoreParser.swift */; };
		BB42F9572EB63EFF00FDBBCC /* BazelActionQueryHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F94F2EB63EFF00FDBBCC /* BazelActionQueryHandler.swift */; };
		BB42F9582EB63EFF00FDBBCC /* BazelAQueryParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9502EB63EFF00FDBBCC /* BazelAQueryParser.swift */; };
		BB42F9592EB63EFF00FDBBCC /* FilenameMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9522EB63EFF00FDBBCC /* FilenameMatcher.swift */; };
		BB42F95A2EB63EFF00FDBBCC /* BazelInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9512EB63EFF00FDBBCC /* BazelInterface.swift */; };
		BB42F95B2EB63EFF00FDBBCC /* GitIgnoreParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9532EB63EFF00FDBBCC /* GitIgnoreParser.swift */; };
		BB42F95C2EB63EFF00FDBBCC /* BazelActionQueryHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F94F2EB63EFF00FDBBCC /* BazelActionQueryHandler.swift */; };
		BB42F95D2EB63EFF00FDBBCC /* BazelAQueryParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9502EB63EFF00FDBBCC /* BazelAQueryParser.swift */; };
		BB42F95E2EB63EFF00FDBBCC /* FilenameMatcher.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB42F9522EB63EFF00FDBBCC /* FilenameMatcher.swift */; };
		BB4654812C257F110080EC40 /* NextCompiler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4654802C257F110080EC40 /* NextCompiler.swift */; };
		BB4788802EB6B6E700464AB4 /* InjectionBoot.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB47887E2EB6B6E700464AB4 /* InjectionBoot.mm */; };
		BB4788812EB6B71600464AB4 /* InjectionImplC.h in Resources */ = {isa = PBXBuildFile; fileRef = BB42F9302EB63AAF00FDBBCC /* InjectionImplC.h */; };
		BB4788902EB6B81A00464AB4 /* fast_dladdr.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB4788852EB6B81A00464AB4 /* fast_dladdr.mm */; };
		BB4788912EB6B81A00464AB4 /* xt_forwarding_trampoline_arm64.s in Sources */ = {isa = PBXBuildFile; fileRef = BB47888C2EB6B81A00464AB4 /* xt_forwarding_trampoline_arm64.s */; };
		BB4788922EB6B81A00464AB4 /* xt_forwarding_trampoline_x64.s in Sources */ = {isa = PBXBuildFile; fileRef = BB47888D2EB6B81A00464AB4 /* xt_forwarding_trampoline_x64.s */; };
		BB4788932EB6B81A00464AB4 /* ObjCBridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB4788872EB6B81A00464AB4 /* ObjCBridge.mm */; };
		BB4788942EB6B81A00464AB4 /* Trampolines.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB47888A2EB6B81A00464AB4 /* Trampolines.mm */; };
		BB4788952EB6B81A00464AB4 /* xt_forwarding_trampoline_arm7.s in Sources */ = {isa = PBXBuildFile; fileRef = BB47888B2EB6B81A00464AB4 /* xt_forwarding_trampoline_arm7.s */; };
		BB4788962EB6B81A00464AB4 /* SwiftTrace.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB4788882EB6B81A00464AB4 /* SwiftTrace.mm */; };
		BB4788972EB6B81A00464AB4 /* xt_forwarding_trampoline_x86.s in Sources */ = {isa = PBXBuildFile; fileRef = BB47888E2EB6B81A00464AB4 /* xt_forwarding_trampoline_x86.s */; };
		BB4788982EB6B81A00464AB4 /* fishhook.c in Sources */ = {isa = PBXBuildFile; fileRef = BB4788862EB6B81A00464AB4 /* fishhook.c */; };
		BB5155DA2CDED44F00704C7A /* InjectionHybrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB5155D92CDED44400704C7A /* InjectionHybrid.swift */; };
		AA0000012F08000000000001 /* ControlServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA0000002F08000000000001 /* ControlServer.swift */; };
		BB52AD552F1501F500297CD9 /* Unhider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 22B6459F2C18DD9D00F99B61 /* Unhider.swift */; };
		BB6A56F02C50E73600C92112 /* Defaults.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6A56EF2C50E73600C92112 /* Defaults.swift */; };
		BB6A56F12C50E79800C92112 /* copy_bundle.sh in Resources */ = {isa = PBXBuildFile; fileRef = BBDD84582C4FF0B9000F3124 /* copy_bundle.sh */; };
		BB85A42B2D6B9C7B00FA29D0 /* feedcommands in Resources */ = {isa = PBXBuildFile; fileRef = BB85A4242D6B9C5300FA29D0 /* feedcommands */; };
		BB85A42E2D6BA13B00FA29D0 /* SimpleSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB67DBB31FB0CDA8000EAC8A /* SimpleSocket.mm */; };
		BBA082362D6BC33500FFFB2F /* FrontendServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBA082352D6BC32D00FFFB2F /* FrontendServer.swift */; };
		BBA082382D6BD6CB00FFFB2F /* swift-frontend.sh in Resources */ = {isa = PBXBuildFile; fileRef = BBA082372D6BD6CB00FFFB2F /* swift-frontend.sh */; };
		BBB040641FB17A6C007DDD0A /* ScriptingBridge.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBB040631FB1798A007DDD0A /* ScriptingBridge.framework */; };
		BBB64DC51FD450AF0020BE47 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = BB037DF31FAD808B004B267C /* README.md */; };
		BBB64DC61FD450B40020BE47 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = BB037DF41FAD80D0004B267C /* LICENSE */; };
		BBB64DE51FD571310020BE47 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = BBB64DE41FD571300020BE47 /* libz.tbd */; };
		BBB64FEC1FD585D50020BE47 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBB64FEB1FD585D50020BE47 /* WebKit.framework */; };
		BBCA02021FB0F10300E45F0F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBCA02011FB0F10300E45F0F /* AppDelegate.swift */; };
		BBCA02041FB0F10300E45F0F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BBCA02031FB0F10300E45F0F /* Assets.xcassets */; };
		BBCA02071FB0F10300E45F0F /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = BBCA02051FB0F10300E45F0F /* MainMenu.xib */; };
		BBCA022A1FB0F64800E45F0F /* SimpleSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB67DBB31FB0CDA8000EAC8A /* SimpleSocket.mm */; };
		BBCA02591FB112F500E45F0F /* App.icns in Resources */ = {isa = PBXBuildFile; fileRef = BBCA02581FB112F500E45F0F /* App.icns */; };
		BBCA02601FB122C300E45F0F /* InjectionIdle.tif in Resources */ = {isa = PBXBuildFile; fileRef = BBCA025E1FB122C300E45F0F /* InjectionIdle.tif */; };
		BBCA02611FB122C300E45F0F /* InjectionOK.tif in Resources */ = {isa = PBXBuildFile; fileRef = BBCA025F1FB122C300E45F0F /* InjectionOK.tif */; };
		BBCCC3B72C73B6FD00E71F81 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BBBEC42D1FC56522004188B3 /* XCTest.framework */; };
		BBDD843F2C4FE8D5000F3124 /* macOSInjection.bundle in Resources */ = {isa = PBXBuildFile; fileRef = BBDD84132C4FE749000F3124 /* macOSInjection.bundle */; };
		BBDD84422C4FEA4E000F3124 /* InjectionNext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDD84182C4FE7E6000F3124 /* InjectionNext.swift */; };
		BBDD84542C4FEB16000F3124 /* TupleRegex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBDD84532C4FEB16000F3124 /* TupleRegex.swift */; };
		BBDD84552C4FEC98000F3124 /* SimpleSocket.mm in Sources */ = {isa = PBXBuildFile; fileRef = BB67DBB31FB0CDA8000EAC8A /* SimpleSocket.mm */; };
		BBDD845A2C4FF646000F3124 /* ClientBoot.mm in Sources */ = {isa = PBXBuildFile; fileRef = BBDD84592C4FF645000F3124 /* ClientBoot.mm */; };
		BBE490DB1FB2C643003D41BB /* InjectionBusy.tif in Resources */ = {isa = PBXBuildFile; fileRef = BBE490D91FB2C643003D41BB /* InjectionBusy.tif */; };
		BBE490DC1FB2C643003D41BB /* InjectionError.tif in Resources */ = {isa = PBXBuildFile; fileRef = BBE490DA1FB2C643003D41BB /* InjectionError.tif */; };
		BBEB87122C74FD930044578A /* Quick in Frameworks */ = {isa = PBXBuildFile; productRef = BBEB87112C74FD930044578A /* Quick */; };
		BBF6572E2DAFF00200638170 /* NIMBLE.md in Resources */ = {isa = PBXBuildFile; fileRef = BBF6572C2DAFF00200638170 /* NIMBLE.md */; };
		BBF6572F2DAFF00200638170 /* QUICK.md in Resources */ = {isa = PBXBuildFile; fileRef = BBF6572D2DAFF00200638170 /* QUICK.md */; };
		BBFE1B422ED7C116004E14EE /* SwiftRefs.swift in Sources */ = {isa = PBXBuildFile; fileRef = BBFE1B412ED7C116004E14EE /* SwiftRefs.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
		BB85A42C2D6B9CA200FA29D0 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = BB439B5D1FABA64300B4F50B /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = BB85A4232D6B9C5300FA29D0;
			remoteInfo = feedcommands;
		};
		BBDD843D2C4FE8C5000F3124 /* PBXContainerItemProxy */ = {
			isa = PBXContainerItemProxy;
			containerPortal = BB439B5D1FABA64300B4F50B /* Project object */;
			proxyType = 1;
			remoteGlobalIDString = BBDD84122C4FE749000F3124;
			remoteInfo = InjectionBundle;
		};
/* End PBXContainerItemProxy section */

/* Begin PBXCopyFilesBuildPhase section */
		BB6224721FC5BA6F00AD7A3A /* CopyFiles */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = Contents/Library/LaunchServices;
			dstSubfolderSpec = 1;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 0;
		};
		BB6C87DB2520D0D3005AFCFC /* Embed Frameworks */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = "";
			dstSubfolderSpec = 10;
			files = (
			);
			name = "Embed Frameworks";
			runOnlyForDeploymentPostprocessing = 0;
		};
		BB85A4222D6B9C5300FA29D0 /* CopyFiles */ = {
			isa = PBXCopyFilesBuildPhase;
			buildActionMask = 2147483647;
			dstPath = /usr/share/man/man1/;
			dstSubfolderSpec = 0;
			files = (
			);
			runOnlyForDeploymentPostprocessing = 1;
		};
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
		222BC9442C157C3200780A41 /* InjectionReady.tif */ = {isa = PBXFileReference; lastKnownFileType = image.tiff; path = InjectionReady.tif; sourceTree = "<group>"; };
		224E57FC2C08BAE200B71C79 /* InjectionClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = InjectionClient.h; path = ../../Sources/InjectionNextC/include/InjectionClient.h; sourceTree = "<group>"; };
		224E57FD2C08BBE300B71C79 /* InjectionServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectionServer.swift; sourceTree = "<group>"; };
		224E57FF2C08BC0E00B71C79 /* MonitorXcode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonitorXcode.swift; sourceTree = "<group>"; };
		22B6459F2C18DD9D00F99B61 /* Unhider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Unhider.swift; path = ../../InjectionLite/Sources/InjectionImpl/Unhider.swift; sourceTree = "<group>"; };
		BB037DF31FAD808B004B267C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
		BB037DF41FAD80D0004B267C /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
		BB16653925E9A5F0001407AE /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
		BB2A66352E3E753B001EDD38 /* BAZEL.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = BAZEL.md; path = ../BAZEL.md; sourceTree = SOURCE_ROOT; };
		BB42F8D22EB626F100FDBBCC /* EasyPointer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EasyPointer.swift; sourceTree = "<group>"; };
		BB42F8D32EB626F100FDBBCC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
		BB42F8D42EB626F100FDBBCC /* StringIndex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringIndex.swift; sourceTree = "<group>"; };
		BB42F8D52EB626F100FDBBCC /* SwiftArgs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftArgs.swift; sourceTree = "<group>"; };
		BB42F8D62EB626F100FDBBCC /* SwiftAspects.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftAspects.swift; sourceTree = "<group>"; };
		BB42F8D72EB626F100FDBBCC /* SwiftInterpose.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftInterpose.swift; sourceTree = "<group>"; };
		BB42F8D82EB626F100FDBBCC /* SwiftInvoke.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftInvoke.swift; sourceTree = "<group>"; };
		BB42F8D92EB626F100FDBBCC /* SwiftLifetime.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftLifetime.swift; sourceTree = "<group>"; };
		BB42F8DA2EB626F100FDBBCC /* SwiftMeta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftMeta.swift; sourceTree = "<group>"; };
		BB42F8DB2EB626F100FDBBCC /* SwiftStack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStack.swift; sourceTree = "<group>"; };
		BB42F8DC2EB626F100FDBBCC /* SwiftStats.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftStats.swift; sourceTree = "<group>"; };
		BB42F8DD2EB626F100FDBBCC /* SwiftSwizzle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSwizzle.swift; sourceTree = "<group>"; };
		BB42F8DE2EB626F100FDBBCC /* SwiftTrace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftTrace.h; sourceTree = "<group>"; };
		BB42F8DF2EB626F100FDBBCC /* SwiftTrace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTrace.swift; sourceTree = "<group>"; };
		BB42F91A2EB639D100FDBBCC /* Demangling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Demangling.swift; sourceTree = "<group>"; };
		BB42F91B2EB639D100FDBBCC /* DLKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DLKit.swift; sourceTree = "<group>"; };
		BB42F91C2EB639D100FDBBCC /* FileSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileSymbols.swift; sourceTree = "<group>"; };
		BB42F91D2EB639D100FDBBCC /* ImageSymbols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageSymbols.swift; sourceTree = "<group>"; };
		BB42F91E2EB639D100FDBBCC /* Interposing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Interposing.swift; sourceTree = "<group>"; };
		BB42F91F2EB639D100FDBBCC /* Iterators.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Iterators.swift; sourceTree = "<group>"; };
		BB42F9272EB639E500FDBBCC /* DLKitC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = DLKitC.h; sourceTree = "<group>"; };
		BB42F9292EB639E500FDBBCC /* DLKitC.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = DLKitC.c; sourceTree = "<group>"; };
		BB42F92A2EB639E500FDBBCC /* trie_dladdr.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = trie_dladdr.mm; sourceTree = "<group>"; };
		BB42F92B2EB639E500FDBBCC /* trie_dlops.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = trie_dlops.mm; sourceTree = "<group>"; };
		BB42F9302EB63AAF00FDBBCC /* InjectionImplC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = InjectionImplC.h; path = ../InjectionLite/Sources/InjectionImplC/include/InjectionImplC.h; sourceTree = SOURCE_ROOT; };
		BB42F9312EB63C2900FDBBCC /* Common.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Common.swift; sourceTree = "<group>"; };
		BB42F9322EB63C2900FDBBCC /* Generics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Generics.swift; sourceTree = "<group>"; };
		BB42F9332EB63C2900FDBBCC /* KeyPaths.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyPaths.swift; sourceTree = "<group>"; };
		BB42F9342EB63C2900FDBBCC /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = "<group>"; };
		BB42F9352EB63C2900FDBBCC /* Reloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Reloader.swift; sourceTree = "<group>"; };
		BB42F9362EB63C2900FDBBCC /* Sweeper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Sweeper.swift; sourceTree = "<group>"; };
		BB42F9402EB63E3400FDBBCC /* FileWatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileWatcher.swift; sourceTree = "<group>"; };
		BB42F9412EB63E3400FDBBCC /* InjectionBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectionBase.swift; sourceTree = "<group>"; };
		BB42F9422EB63E3400FDBBCC /* InjectionLite.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InjectionLite.swift; sourceTree = "<group>"; };
		BB42F9432EB63E3400FDBBCC /* LogParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LogParser.swift; sourceTree = "<group>"; };
		BB42F9442EB63E3400FDBBCC /* Recompiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Recompiler.swift; sourceTree = "<group>"; };
		BB42F94F2EB63EFF00FDBBCC /* BazelActionQueryHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BazelActionQueryHandler.swift; sourceTree = "<group>"; };
		BB42F9502EB63EFF00FDBBCC /* BazelAQueryParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BazelAQueryParser.swift; sourceTree = "<group>"; };
		BB42F9512EB63EFF00FDBBCC /* BazelInterface.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BazelInterface.swift; sourceTree = "<group>"; };
		BB42F9522EB63EFF00FDBBCC /* FilenameMatcher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilenameMatcher.swift; sourceTree = "<group>"; };
		BB42F9532EB63EFF00FDBBCC /* GitIgnoreParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GitIgnoreParser.swift; sourceTree = "<group>"; };
		BB4654802C257F110080EC40 /* NextCompiler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NextCompiler.swift; sourceTree = "<group>"; };
		BB47887C2EB6B6E700464AB4 /* InjectionImplC.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InjectionImplC.h; sourceTree = "<group>"; };
		BB47887E2EB6B6E700464AB4 /* InjectionBoot.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = InjectionBoot.mm; sourceTree = "<group>"; };
		BB4788822EB6B81A00464AB4 /* fishhook.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = fishhook.h; sourceTree = "<group>"; };
		BB4788832EB6B81A00464AB4 /* SwiftTrace.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftTrace.h; sourceTree = "<group>"; };
		BB4788852EB6B81A00464AB4 /* fast_dladdr.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = fast_dladdr.mm; sourceTree = "<group>"; };
		BB4788862EB6B81A00464AB4 /* fishhook.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fishhook.c; sourceTree = "<group>"; };
		BB4788872EB6B81A00464AB4 /* ObjCBridge.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ObjCBridge.mm; sourceTree = "<group>"; };
		BB4788882EB6B81A00464AB4 /* SwiftTrace.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = SwiftTrace.mm; sourceTree = "<group>"; };
		BB4788892EB6B81A00464AB4 /* SwiftTrace-Swift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SwiftTrace-Swift.h"; sourceTree = "<group>"; };
		BB47888A2EB6B81A00464AB4 /* Trampolines.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = Trampolines.mm; 
Download .txt
gitextract_vqy0p27r/

├── .github/
│   └── FUNDING.yml
├── .gitignore
├── .gitmodules
├── .swiftpm/
│   └── xcode/
│       └── package.xcworkspace/
│           └── contents.xcworkspacedata
├── App/
│   ├── InjectionBundle/
│   │   ├── Info.plist
│   │   └── InjectionBundle-Bridging-Header.h
│   ├── InjectionNext/
│   │   ├── App.icns
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   └── AppIcon.appiconset/
│   │   │       └── Contents.json
│   │   ├── Base.lproj/
│   │   │   └── MainMenu.xib
│   │   ├── ControlServer.swift
│   │   ├── Defaults.swift
│   │   ├── Experimental.swift
│   │   ├── FrontendServer.swift
│   │   ├── Info.plist
│   │   ├── InjectionBusy.tif
│   │   ├── InjectionError.tif
│   │   ├── InjectionHybrid.swift
│   │   ├── InjectionIdle.tif
│   │   ├── InjectionNext-Bridging-Header.h
│   │   ├── InjectionNext.entitlements
│   │   ├── InjectionOK.tif
│   │   ├── InjectionReady.tif
│   │   ├── InjectionServer.swift
│   │   ├── MonitorXcode.swift
│   │   ├── NextCompiler.swift
│   │   ├── build_bundle.sh
│   │   ├── build_bundles.sh
│   │   ├── copy_bundle.sh
│   │   ├── main.m
│   │   └── swift-frontend.sh
│   ├── InjectionNext.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       ├── IDEWorkspaceChecks.plist
│   │   │       └── WorkspaceSettings.xcsettings
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           └── InjectionNext.xcscheme
│   ├── LICENSE
│   ├── NIMBLE.md
│   ├── QUICK.md
│   └── feedcommands/
│       └── main.mm
├── BAZEL.md
├── INTRO.md
├── LICENSE
├── Package.swift
├── README.md
├── Sources/
│   ├── InjectionNext/
│   │   └── InjectionNext.swift
│   └── InjectionNextC/
│       ├── ClientBoot.mm
│       ├── SimpleSocket.mm
│       └── include/
│           ├── InjectionClient.h
│           └── SimpleSocket.h
├── Tests/
│   └── InjectionNextTests/
│       └── InjectionNextTests.swift
└── mcp-server/
    ├── .gitignore
    ├── README.md
    ├── index.js
    └── package.json
Download .txt
SYMBOL INDEX (7 symbols across 3 files)

FILE: Sources/InjectionNextC/include/InjectionClient.h
  function end (line 33) | end

FILE: Sources/InjectionNextC/include/SimpleSocket.h
  function interface (line 14) | interface SimpleSocket : NSObject {
  type sockaddr_storage (line 27) | struct sockaddr_storage

FILE: mcp-server/index.js
  constant CONTROL_PORT (line 8) | const CONTROL_PORT = 8919;
  constant CONTROL_HOST (line 9) | const CONTROL_HOST = "127.0.0.1";
  function sendCommand (line 11) | function sendCommand(action, params = {}) {
  function formatResponse (line 52) | function formatResponse(result) {
Condensed preview — 55 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (367K chars).
[
  {
    "path": ".github/FUNDING.yml",
    "chars": 644,
    "preview": "# These are supported funding model platforms\n\ngithub: johnno1962\npatreon: # Replace with a single Patreon username\nopen"
  },
  {
    "path": ".gitignore",
    "chars": 2169,
    "preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
  },
  {
    "path": ".gitmodules",
    "chars": 451,
    "preview": "[submodule \"fishhook\"]\n\tpath = fishhook\n\turl = https://github.com/johnno1962/fishhook\n[submodule \"DLKit\"]\n\tpath = DLKit\n"
  },
  {
    "path": ".swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "App/InjectionBundle/Info.plist",
    "chars": 859,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "App/InjectionBundle/InjectionBundle-Bridging-Header.h",
    "chars": 324,
    "preview": "//\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import <Foundat"
  },
  {
    "path": "App/InjectionNext/AppDelegate.swift",
    "chars": 10662,
    "preview": "//\n//  AppDelegate.swift\n//  InjectionNext\n//\n//  Created by John Holdsworth on 06/11/2017.\n//  Copyright © 2017 John Ho"
  },
  {
    "path": "App/InjectionNext/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 903,
    "preview": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"mac\",\n      \"size\" : \"16x16\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : "
  },
  {
    "path": "App/InjectionNext/Base.lproj/MainMenu.xib",
    "chars": 62964,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion"
  },
  {
    "path": "App/InjectionNext/ControlServer.swift",
    "chars": 11803,
    "preview": "//\n//  ControlServer.swift\n//  InjectionNext\n//\n//  Local TCP control server for MCP integration.\n//  Listens on localho"
  },
  {
    "path": "App/InjectionNext/Defaults.swift",
    "chars": 2024,
    "preview": "//\n//  Defaults.swift\n//  InjectionNext\n//\n//  Created by John Holdsworth on 24/07/2024.\n//  Copyright © 2024 John Holds"
  },
  {
    "path": "App/InjectionNext/Experimental.swift",
    "chars": 1421,
    "preview": "//\n//  Experimental.swift\n//  InjectionIII\n//\n//  Created by User on 20/10/2020.\n//  Copyright © 2020 John Holdsworth. A"
  },
  {
    "path": "App/InjectionNext/FrontendServer.swift",
    "chars": 18031,
    "preview": "//\n//  FrontendServer.swift\n//  InjectionNext\n//\n//  Created by John Holdsworth on 23/02/2025.\n//  Copyright © 2025 John"
  },
  {
    "path": "App/InjectionNext/Info.plist",
    "chars": 1211,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "App/InjectionNext/InjectionHybrid.swift",
    "chars": 6830,
    "preview": "//\n//  InjectionHybrid.swift\n//  InjectionNext\n//\n//  Created by John Holdsworth on 09/11/2024.\n//  Copyright © 2024 Joh"
  },
  {
    "path": "App/InjectionNext/InjectionNext-Bridging-Header.h",
    "chars": 219,
    "preview": "//\n//  Use this file to import your target's public headers that you would like to expose to Swift.\n//\n\n#import \"SimpleS"
  },
  {
    "path": "App/InjectionNext/InjectionNext.entitlements",
    "chars": 480,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "App/InjectionNext/InjectionServer.swift",
    "chars": 9677,
    "preview": "//\n//  InjectionServer.swift\n//  InjectionNext\n//\n//  Created by John H on 30/05/2024.\n//  Copyright © 2024 John Holdswo"
  },
  {
    "path": "App/InjectionNext/MonitorXcode.swift",
    "chars": 8479,
    "preview": "//\n//  RunXcode.swift\n//  InjectionNext\n//\n//  Created by John H on 30/05/2024.\n//  Copyright © 2024 John Holdsworth. Al"
  },
  {
    "path": "App/InjectionNext/NextCompiler.swift",
    "chars": 18723,
    "preview": "//\n//  Recompiler.swift\n//  InjectionNext\n//\n//  Created by John Holdsworth on 21/06/2024.\n//  Copyright © 2024 John Hol"
  },
  {
    "path": "App/InjectionNext/build_bundle.sh",
    "chars": 2116,
    "preview": "#!/bin/sh -x\n\n#  build_bundle.sh\n#  InjectionNext\n#\n#  Created by John Holdsworth on 22/07/2024.\n#  Copyright © 2024 Joh"
  },
  {
    "path": "App/InjectionNext/build_bundles.sh",
    "chars": 2850,
    "preview": "#!/bin/sh\n\n#  build_bundles.sh\n#  InjectionNext\n#\n#  Created by John Holdsworth on 22/07/2024.\n#  Copyright © 2024 John "
  },
  {
    "path": "App/InjectionNext/copy_bundle.sh",
    "chars": 5127,
    "preview": "#!/bin/bash -x\n#\n#  copy_bundle.sh\n#  InjectionIII\n#\n#  Copies injection bundle for on-device injection.\n#  Thanks @oryo"
  },
  {
    "path": "App/InjectionNext/main.m",
    "chars": 254,
    "preview": "//\n//  main.m\n//  InjectionIII\n//\n//  Created by John Holdsworth on 06/11/2017.\n//  Copyright © 2017 John Holdsworth. Al"
  },
  {
    "path": "App/InjectionNext/swift-frontend.sh",
    "chars": 373,
    "preview": "#!/bin/bash\n\n#  swift-frontend.sh\n#  InjectionNext\n#\n#  Created by John Holdsworth on 23/02/2025.\n#  Copyright © 2025 Jo"
  },
  {
    "path": "App/InjectionNext.xcodeproj/project.pbxproj",
    "chars": 78993,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 70;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "App/InjectionNext.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "App/InjectionNext.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "App/InjectionNext.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "chars": 181,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "App/InjectionNext.xcodeproj/xcshareddata/xcschemes/InjectionNext.xcscheme",
    "chars": 3248,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1520\"\n   version = \"1.7\">\n   <BuildAction\n      "
  },
  {
    "path": "App/LICENSE",
    "chars": 1073,
    "preview": "MIT License\n\nCopyright (c) 2024 John Holdsworth \n\nPermission is hereby granted, free of charge, to any person obtaining "
  },
  {
    "path": "App/NIMBLE.md",
    "chars": 11307,
    "preview": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licens"
  },
  {
    "path": "App/QUICK.md",
    "chars": 11308,
    "preview": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licens"
  },
  {
    "path": "App/feedcommands/main.mm",
    "chars": 1325,
    "preview": "//\n//  main.mm\n//  feedcommands\n//\n//  Created by John Holdsworth on 23/02/2025.\n//  Copyright © 2025 John Holdsworth. A"
  },
  {
    "path": "BAZEL.md",
    "chars": 1871,
    "preview": "## Bazel Support\n\nThis version includes enhanced Bazel build system support with automatic target discovery and optimize"
  },
  {
    "path": "INTRO.md",
    "chars": 3990,
    "preview": "\n## A lightning introduction to Injection\n\nAs it says in the README.md, using a feature of Apple's linker Code Injection"
  },
  {
    "path": "LICENSE",
    "chars": 1072,
    "preview": "MIT License\n\nCopyright (c) 2024 John Holdsworth\n\nPermission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "Package.swift",
    "chars": 1296,
    "preview": "// swift-tools-version: 5.4\n// The swift-tools-version declares the minimum version of Swift required to build this pack"
  },
  {
    "path": "README.md",
    "chars": 9749,
    "preview": "# InjectionNext\n\n### The fourth evolution of Code Injection for Xcode\n\nUsing a feature of Apple's linker this implementa"
  },
  {
    "path": "Sources/InjectionNext/InjectionNext.swift",
    "chars": 12342,
    "preview": "//\n//  InjectionNext.swift\n//  InjectionNext Package\n//\n//  Created by John Holdsworth on 30/05/2024.\n//\n//  Client app "
  },
  {
    "path": "Sources/InjectionNextC/ClientBoot.mm",
    "chars": 2589,
    "preview": "//\n//  ClientBoot.m\n//  \n//\n//  Created by John H on 31/05/2024.\n//\n\n#if DEBUG || !SWIFT_PACKAGE\n#import <Foundation/Fou"
  },
  {
    "path": "Sources/InjectionNextC/SimpleSocket.mm",
    "chars": 20041,
    "preview": "//\n//  SimpleSocket.mm\n//  InjectionIII\n//\n//  Created by John Holdsworth on 06/11/2017.\n//  Copyright © 2017 John Holds"
  },
  {
    "path": "Sources/InjectionNextC/include/InjectionClient.h",
    "chars": 1625,
    "preview": "//\n//  InjectionClient.h\n//  InjectionBundle\n//\n//  Created by John Holdsworth on 06/11/2017.\n//  Copyright © 2017 John "
  },
  {
    "path": "Sources/InjectionNextC/include/SimpleSocket.h",
    "chars": 2053,
    "preview": "//\n//  SimpleSocket.h\n//  InjectionIII\n//\n//  Created by John Holdsworth on 06/11/2017.\n//  Copyright © 2017 John Holdsw"
  },
  {
    "path": "Tests/InjectionNextTests/InjectionNextTests.swift",
    "chars": 371,
    "preview": "import XCTest\n@testable import InjectionBundle\n\nfinal class InjectionNextTests: XCTestCase {\n    func testExample() thro"
  },
  {
    "path": "mcp-server/.gitignore",
    "chars": 14,
    "preview": "node_modules/\n"
  },
  {
    "path": "mcp-server/README.md",
    "chars": 5320,
    "preview": "# InjectionNext MCP Server\n\nMCP (Model Context Protocol) server that lets AI agents control InjectionNext — start file w"
  },
  {
    "path": "mcp-server/index.js",
    "chars": 6521,
    "preview": "#!/usr/bin/env node\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport "
  },
  {
    "path": "mcp-server/package.json",
    "chars": 416,
    "preview": "{\n  \"name\": \"injection-next-mcp\",\n  \"version\": \"1.0.0\",\n  \"description\": \"MCP server for controlling InjectionNext hot-r"
  }
]

// ... and 6 more files (download for full content)

About this extraction

This page contains the full source code of the johnno1962/InjectionNext GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 55 files (337.7 KB), approximately 85.3k tokens, and a symbol index with 7 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!