Files
CxIDE/Agent/Tools/AutoPilotTools.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

253 lines
12 KiB
Swift

// AutoPilotTools.swift
// CxSwiftAgent AutoPilot MCP Tools
//
// 6 tools: autopilot_plan, autopilot_execute, autopilot_memory,
// autopilot_context, autopilot_diff, autopilot_status
import Foundation
enum AutoPilotTools {
static func register(on server: MCPServer, config: AgentConfig, memory: AgentMemory) {
// autopilot_plan
server.registerTool(
"autopilot_plan",
description: "Create a structured plan for a multi-step coding task. Returns steps with dependencies.",
inputSchema: [
"type": "object",
"required": ["goal"],
"properties": [
"goal": ["type": "string", "description": "What needs to be accomplished."],
"context": ["type": "string", "description": "Additional context about the codebase or constraints."],
] as [String: Any],
] as [String: Any],
annotations: ToolAnnotations(readOnlyHint: true)
) { args in
let goal = args["goal"] as? String ?? ""
let context = args["context"] as? String ?? ""
// Create structured plan
let planId = UUID().uuidString.prefix(8)
Task {
_ = await memory.startTask(goal)
}
var plan = "AutoPilot Plan [\(planId)]:\n"
plan += "Goal: \(goal)\n"
if !context.isEmpty { plan += "Context: \(context)\n" }
plan += "\nSteps:\n"
plan += " 1. Analyze - Understand current state and requirements\n"
plan += " 2. Design - Determine approach and identify affected files\n"
plan += " 3. Implement - Make necessary code changes\n"
plan += " 4. Verify - Run tests and validate changes\n"
plan += " 5. Review - Check for edge cases and quality\n"
plan += "\nUse autopilot_execute to begin step-by-step execution.\n"
plan += "Use autopilot_status to check progress."
return ok(plan)
}
// autopilot_execute
server.registerTool(
"autopilot_execute",
description: "Execute a step from an autopilot plan. Provides guidance on tools to use.",
inputSchema: [
"type": "object",
"required": ["step"],
"properties": [
"step": ["type": "string", "description": "Step description or number to execute."],
"plan_id": ["type": "string", "description": "Plan ID from autopilot_plan."],
"dry_run": ["type": "boolean", "description": "Show what would be done without executing (default: false)."],
] as [String: Any],
] as [String: Any]
) { args in
let step = args["step"] as? String ?? ""
let dryRun = args["dry_run"] as? Bool ?? false
let lowerStep = step.lowercased()
var recommendation = "Step: \(step)\n\n"
if lowerStep.contains("analyze") || lowerStep.contains("1") {
recommendation += "Recommended tools:\n"
recommendation += " - file_tree: Explore project structure\n"
recommendation += " - file_search: Find relevant code\n"
recommendation += " - code_symbols: Understand file structure\n"
recommendation += " - project_detect: Identify project type\n"
recommendation += " - git_status: Check current state\n"
} else if lowerStep.contains("design") || lowerStep.contains("2") {
recommendation += "Recommended tools:\n"
recommendation += " - file_read: Review related code\n"
recommendation += " - code_explain: Understand existing logic\n"
recommendation += " - project_deps: Check dependencies\n"
recommendation += " - autopilot_memory: Record design decisions\n"
} else if lowerStep.contains("implement") || lowerStep.contains("3") {
recommendation += "Recommended tools:\n"
recommendation += " - file_write: Create new files\n"
recommendation += " - file_patch: Modify existing files\n"
recommendation += " - code_complete: Generate code\n"
recommendation += " - code_fix: Fix issues\n"
} else if lowerStep.contains("verify") || lowerStep.contains("4") {
recommendation += "Recommended tools:\n"
recommendation += " - project_run: Run tests (action: test)\n"
recommendation += " - xcode_build: Build project\n"
recommendation += " - xcode_test: Run Xcode tests\n"
recommendation += " - terminal_exec: Run custom commands\n"
recommendation += " - git_diff: Review changes\n"
} else if lowerStep.contains("review") || lowerStep.contains("5") {
recommendation += "Recommended tools:\n"
recommendation += " - code_review: Review changed code\n"
recommendation += " - code_security: Security audit\n"
recommendation += " - diag_lint: Check style\n"
recommendation += " - diag_complexity: Check complexity\n"
recommendation += " - git_diff: Final diff review\n"
}
if dryRun {
recommendation += "\n[DRY RUN - no changes made]"
}
return ok(recommendation)
}
// autopilot_memory
server.registerTool(
"autopilot_memory",
description: "Store or retrieve agent memory notes for cross-turn context.",
inputSchema: [
"type": "object",
"required": ["action"],
"properties": [
"action": ["type": "string", "description": "Action: store, retrieve, list, clear."],
"key": ["type": "string", "description": "Memory key (for store/retrieve)."],
"value": ["type": "string", "description": "Value to store."],
"category": ["type": "string", "description": "Category: decision, finding, task, error."],
] as [String: Any],
] as [String: Any]
) { args in
let action = args["action"] as? String ?? "list"
switch action {
case "store":
let key = args["key"] as? String ?? ""
let value = args["value"] as? String ?? ""
let category = args["category"] as? String ?? "decision"
guard !key.isEmpty else { return err("Missing key") }
if category == "decision" {
Task { await memory.recordDecision(key, reason: value) }
}
return ok("Stored: \(key)")
case "retrieve":
let context = await memory.generateContext()
return ok(context)
case "list":
let dict = await memory.toDict()
return ok(JSON.serialize(dict, pretty: true))
case "clear":
return ok("Memory persists for session duration. Start a new session to clear.")
default:
return err("Unknown action: \(action). Use: store, retrieve, list, clear")
}
}
// autopilot_context
server.registerTool(
"autopilot_context",
description: "Generate contextual summary of current work state from agent memory.",
inputSchema: [
"type": "object",
"properties": [:] as [String: Any],
] as [String: Any],
annotations: ToolAnnotations(readOnlyHint: true)
) { _ in
let context = await memory.generateContext()
return ok(context)
}
// autopilot_diff
server.registerTool(
"autopilot_diff",
description: "Show all uncommitted changes as a summary with statistics.",
inputSchema: [
"type": "object",
"properties": [:] as [String: Any],
] as [String: Any],
annotations: ToolAnnotations(readOnlyHint: true)
) { _ in
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/git")
process.arguments = ["diff", "--stat", "--no-color"]
process.currentDirectoryURL = URL(fileURLWithPath: config.workspaceRoot)
let pipe = Pipe()
process.standardOutput = pipe
do {
try process.run()
process.waitUntilExit()
let output = String(data: pipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
// Also get staged changes
let staged = Process()
staged.executableURL = URL(fileURLWithPath: "/usr/bin/git")
staged.arguments = ["diff", "--cached", "--stat", "--no-color"]
staged.currentDirectoryURL = URL(fileURLWithPath: config.workspaceRoot)
let stagedPipe = Pipe()
staged.standardOutput = stagedPipe
try staged.run()
staged.waitUntilExit()
let stagedOutput = String(data: stagedPipe.fileHandleForReading.readDataToEndOfFile(), encoding: .utf8) ?? ""
var result = ""
if !stagedOutput.isEmpty {
result += "Staged changes:\n\(stagedOutput)\n"
}
if !output.isEmpty {
result += "Unstaged changes:\n\(output)"
}
return ok(result.isEmpty ? "No changes" : result)
} catch {
return err("Git not available: \(error.localizedDescription)")
}
}
// autopilot_status
server.registerTool(
"autopilot_status",
description: "Show current autopilot session status: tasks, tool usage, memory summary.",
inputSchema: [
"type": "object",
"properties": [:] as [String: Any],
] as [String: Any],
annotations: ToolAnnotations(readOnlyHint: true)
) { _ in
let dict = await memory.toDict()
let context = await memory.generateContext()
var status = "AutoPilot Status:\n\n"
status += context
status += "\n\nRaw State:\n"
status += JSON.serialize(dict, pretty: true)
return ok(status)
}
}
// MARK: - Helpers
private static func ok(_ text: String) -> [[String: Any]] {
[["type": "text", "text": text]]
}
private static func err(_ message: String) -> [[String: Any]] {
[["type": "text", "text": "Error: \(message)"]]
}
}