Files
CxIDE/Core/CodeEngine.swift
T
cx-git-agent c118996746 feat: CxIDE v1 — native macOS SwiftUI IDE with agentic AI assistant
- SwiftUI macOS app with C++17 code analysis engine (ObjC++ bridge)
- Agentic AI loop: LLM plans → tool calls → execution → feedback loop
- 15 agent tools: file ops, terminal, git, xcode build, code intel
- 7 persistent terminal tools with background session management
- Chat sidebar with agent step rendering and auto-apply
- NVIDIA NIM API integration (Llama 3.3 70B default)
- OpenAI tool_calls format with prompt-based fallback
- Code editor with syntax highlighting and multi-tab support
- File tree, console view, terminal view
- Git integration and workspace management
2026-04-21 16:05:52 -05:00

195 lines
7.8 KiB
Swift

import Foundation
// Errors
enum IDEError: Error, LocalizedError {
case compilationFailed(String)
case runtimeError(String)
case permissionDenied
case timeout
case fileWriteError(String)
case cancelled
var errorDescription: String? {
switch self {
case .compilationFailed(let msg): return "Compilation failed: \(msg)"
case .runtimeError(let msg): return "Runtime error: \(msg)"
case .permissionDenied: return "Permission denied."
case .timeout: return "Execution timed out."
case .fileWriteError(let msg): return "File write error: \(msg)"
case .cancelled: return "Execution cancelled."
}
}
}
// C++ Engine Swift Facade
struct CppAnalysisConfig {
var maxLineLength: Int = 120
var maxNestingDepth: Int = 5
var maxFunctionLength: Int = 50
var minCommentRatio: Double = 0.05
var detectForceUnwraps: Bool = true
var detectForceTry: Bool = true
var detectForceCast: Bool = true
var detectDuplicateLines: Bool = true
var detectTodoFixme: Bool = true
var detectRetainCycles: Bool = true
}
final class CppEngineController {
private let wrapper: SwiftEngineWrapper
init() {
wrapper = SwiftEngineWrapper()
}
// Configuration
func applyConfig(_ config: CppAnalysisConfig) {
wrapper.setMaxLineLength(config.maxLineLength)
wrapper.setMaxNestingDepth(config.maxNestingDepth)
wrapper.setMaxFunctionLength(config.maxFunctionLength)
wrapper.setMinCommentRatio(config.minCommentRatio)
wrapper.setDetectForceUnwraps(config.detectForceUnwraps)
wrapper.setDetectForceTry(config.detectForceTry)
wrapper.setDetectForceCast(config.detectForceCast)
wrapper.setDetectDuplicateLines(config.detectDuplicateLines)
wrapper.setDetectTodoFixme(config.detectTodoFixme)
wrapper.setDetectRetainCycles(config.detectRetainCycles)
}
// Analysis
func runAnalysis(_ code: String) -> String {
wrapper.runAnalysis(code)
}
func runStructuredAnalysis(_ code: String) -> [String: Any] {
(wrapper.runStructuredAnalysis(code) as [String: Any]?) ?? [:]
}
// Metrics
func checksum(_ code: String) -> Int { wrapper.getChecksum(code) }
func lineCount(_ code: String) -> Int { wrapper.countLines(code) }
func blankLineCount(_ code: String) -> Int { wrapper.countBlankLines(code) }
func codeLineCount(_ code: String) -> Int { wrapper.countCodeLines(code) }
func functionCount(_ code: String) -> Int { wrapper.countFunctions(code) }
func classCount(_ code: String) -> Int { wrapper.countClasses(code) }
func structCount(_ code: String) -> Int { wrapper.countStructs(code) }
func enumCount(_ code: String) -> Int { wrapper.countEnums(code) }
func protocolCount(_ code: String) -> Int { wrapper.countProtocols(code) }
// Complexity
func complexity(_ code: String) -> String { wrapper.estimateComplexity(code) }
func cyclomaticComplexity(_ code: String) -> Int { wrapper.cyclomaticComplexity(code) }
func maintainabilityIndex(_ code: String) -> Double { wrapper.maintainabilityIndex(code) }
func bracesBalanced(_ code: String) -> Bool { wrapper.checkBalancedBraces(code) }
func commentRatio(_ code: String) -> Double { wrapper.commentRatio(code) }
// Extraction
func keywords(_ code: String) -> [String] { wrapper.extractKeywords(code) }
func issues(_ code: String) -> [String] { wrapper.findIssues(code) }
func imports(_ code: String) -> [String] { wrapper.extractImports(code) }
func symbols(_ code: String) -> [[String: Any]] {
(wrapper.extractSymbols(code) as [[String: Any]]?) ?? []
}
func duplicateLines(_ code: String) -> [[String: Any]] {
(wrapper.detectDuplicateLines(code) as [[String: Any]]?) ?? []
}
func todoComments(_ code: String) -> [[String: Any]] {
(wrapper.extractTodoComments(code) as [[String: Any]]?) ?? []
}
// Indentation
func suggestIndentation(codeBefore: String, tabWidth: Int = 4) -> Int {
wrapper.suggestIndentation(codeBefore, tabWidth: tabWidth)
}
}
// Runtime Engine
final class SwiftRuntimeEngine: CodeEngineProtocol {
private let projectDirectory: URL
private let timeoutSeconds: TimeInterval
private var currentProcess: Process?
init(projectDirectory: URL, timeoutSeconds: TimeInterval = 30) {
self.projectDirectory = projectDirectory
self.timeoutSeconds = timeoutSeconds
}
func execute(script: String) async throws -> String {
let fileID = UUID().uuidString
let tempFile = projectDirectory.appendingPathComponent("cxide_run_\(fileID).swift")
do {
try script.write(to: tempFile, atomically: true, encoding: .utf8)
} catch {
throw IDEError.fileWriteError(error.localizedDescription)
}
defer { try? FileManager.default.removeItem(at: tempFile) }
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/swift")
process.arguments = [tempFile.path]
process.currentDirectoryURL = projectDirectory
let stdoutPipe = Pipe()
let stderrPipe = Pipe()
process.standardOutput = stdoutPipe
process.standardError = stderrPipe
currentProcess = process
do {
try process.run()
} catch {
currentProcess = nil
throw IDEError.compilationFailed(error.localizedDescription)
}
// Timeout handling
let timer = DispatchSource.makeTimerSource(queue: .global())
timer.schedule(deadline: .now() + timeoutSeconds)
timer.setEventHandler { [weak process] in
process?.terminate()
}
timer.resume()
process.waitUntilExit()
timer.cancel()
currentProcess = nil
let outData = stdoutPipe.fileHandleForReading.readDataToEndOfFile()
let errData = stderrPipe.fileHandleForReading.readDataToEndOfFile()
let stdout = String(data: outData, encoding: .utf8) ?? ""
let stderr = String(data: errData, encoding: .utf8) ?? ""
guard process.terminationStatus == 0 else {
let detail = stderr.isEmpty ? "Exit code \(process.terminationStatus)" : stderr
throw IDEError.compilationFailed(detail)
}
return stdout
}
func cancel() {
currentProcess?.terminate()
currentProcess = nil
}
func analyze(script: String) async throws -> [Diagnostic] {
return []
}
}