     }
 
 }
-public typealias DefaultExtensionRunner = DefaultPluginScriptRunner
-
-/// An error in the default plugin runner.
-public enum DefaultPluginScriptRunnerError: Swift.Error {
-    case didNotReceiveJSONFromPlugin(_ message: String)
-    case pluginSubprocessFailed(_ message: String)
+
+
+/// An error encountered by the default plugin runner.
+public enum DefaultPluginScriptRunnerError: Error {
+    /// Failed to start running the compiled plugin script as a subprocess.  The message describes the error, and the
+    /// command is the full command line that the runner tried to launch.
+    case subprocessDidNotStart(_ message: String, command: [String])
+
+    /// Running the compiled plugin script as a subprocess failed.  The message describes the error, the command is
+    /// the full command line, and the output contains any emitted stdout and stderr.
+    case subprocessFailed(_ message: String, command: [String], output: String)
+    
+    /// The compiled plugin script completed successfully, but did not emit any JSON output that could be decoded to
+    /// transmit plugin information to SwiftPM.  The message describes the problem, the command is the full command
+    /// line, and the output contains any emitted stdout and stderr.
+    case missingPluginJSON(_ message: String, command: [String], output: String)
 }
+
+extension DefaultPluginScriptRunnerError: CustomStringConvertible {
+    public var description: String {
+        switch self {
+        case .subprocessDidNotStart(let message, _):
+            return "could not run plugin script: \(message)"
+        case .subprocessFailed(let message, _, let output):
+            return "running plugin script failed: \(message):\(output.isEmpty ? " (no output)" : "\n" + output)"
+        case .missingPluginJSON(let message, _, let output):
+            return "plugin script did not emit JSON output: \(message):\(output.isEmpty ? " (no output)" : "\n" + output)"
+        }
+    }
+}

        self.enableSandbox = enableSandbox
    }

    /// Public protocol function that compiles and runs the plugin as a subprocess.  The tools version controls the availability of APIs in PackagePlugin, and should be
    public func runPluginScript(sources: Sources, inputJSON: Data, toolsVersion: ToolsVersion, writableDirectories: [AbsolutePath], diagnostics: DiagnosticsEngine, fileSystem: FileSystem) throws -> (outputJSON: Data, stdoutText: Data) {
        let compiledExec = try self.compile(sources: sources, toolsVersion: toolsVersion, cacheDir: self.cacheDir)
        return try self.invoke(compiledExec: compiledExec, toolsVersion: toolsVersion, writableDirectories: writableDirectories, input: inputJSON)

        #endif

        // Invoke the plugin script as a subprocess.
        let result: ProcessResult
        do {
            result = try Process.popen(arguments: command)
        } catch {
            throw DefaultPluginScriptRunnerError.subprocessDidNotStart("\(error)", command: command)
        }

        // Collect the output. The `PackagePlugin` runtime library writes the output as a zero byte followed by
        // the JSON-serialized PluginEvaluationResult. Since this appears after any free-form output from the
        // script, it can be safely split out while maintaining the ability to see debug output without resorting
        // to side-channel communication that might be not be very cross-platform (e.g. pipes, file handles, etc).
        // We end up with an optional Data for the JSON, and two Datas for stdout and stderr respectively.
        var stdoutPieces = (try? result.output.get().split(separator: 0, omittingEmptySubsequences: false)) ?? []
        let jsonData = (stdoutPieces.count > 1) ? Data(stdoutPieces.removeLast()) : nil
        let stdoutData = Data(stdoutPieces.joined())
        let stderrData = (try? Data(result.stderrOutput.get())) ?? Data()
                
        // Throw an error if we the subprocess ended badly.
        if result.exitStatus != .terminated(code: 0) {
            let output = String(decoding: stdoutData + stderrData, as: UTF8.self)
            throw DefaultPluginScriptRunnerError.subprocessFailed("\(result.exitStatus)", command: command, output: output)
        }
        
        // Throw an error if we didn't get the JSON data.
        guard let json = jsonData else {
            let output = String(decoding: stdoutData + stderrData, as: UTF8.self)
            throw DefaultPluginScriptRunnerError.missingPluginJSON("didn't receive JSON output data", command: command, output: output)
        }

        // Otherwise return the JSON data and any output text.
        return (outputJSON: json, stdoutText: stdoutData + stderrData)
    }

    /// Constructs the sandbox profile to use for plugin scripts on macOS.  The tools version can affect what's allowed.
