Initial Push

This commit is contained in:
2026-01-31 23:01:54 +00:00
parent 44d036c8d6
commit 5874b31464
32 changed files with 7171 additions and 0 deletions

344
EnhancedTerminal.swift Normal file
View File

@@ -0,0 +1,344 @@
import Cocoa
import Foundation
// MARK: - Configuration & Settings
class TerminalSettings {
static let shared = TerminalSettings()
var theme: Theme = .dracula
var fontSize: CGFloat = 14
var fontName: String = "Menlo"
var opacity: CGFloat = 1.0
var aiProvider: AIProvider = .none
var openAIKey: String = ""
var ollamaURL: String = "http://localhost:11434"
var ollamaModel: String = "llama2"
enum AIProvider: String {
case none = "None"
case openai = "OpenAI"
case ollama = "Ollama"
}
struct Theme {
let name: String
let background: NSColor
let foreground: NSColor
let cursor: NSColor
let selection: NSColor
static let dracula = Theme(
name: "Dracula",
background: NSColor(red: 0.16, green: 0.16, blue: 0.21, alpha: 1.0),
foreground: NSColor(red: 0.95, green: 0.95, blue: 0.95, alpha: 1.0),
cursor: NSColor(red: 0.97, green: 0.97, blue: 0.95, alpha: 1.0),
selection: NSColor(red: 0.27, green: 0.28, blue: 0.35, alpha: 1.0)
)
static let solarizedDark = Theme(
name: "Solarized Dark",
background: NSColor(red: 0.0, green: 0.17, blue: 0.21, alpha: 1.0),
foreground: NSColor(red: 0.51, green: 0.58, blue: 0.59, alpha: 1.0),
cursor: NSColor(red: 0.51, green: 0.58, blue: 0.59, alpha: 1.0),
selection: NSColor(red: 0.03, green: 0.21, blue: 0.26, alpha: 1.0)
)
static let nord = Theme(
name: "Nord",
background: NSColor(red: 0.18, green: 0.20, blue: 0.25, alpha: 1.0),
foreground: NSColor(red: 0.85, green: 0.87, blue: 0.91, alpha: 1.0),
cursor: NSColor(red: 0.85, green: 0.87, blue: 0.91, alpha: 1.0),
selection: NSColor(red: 0.26, green: 0.30, blue: 0.37, alpha: 1.0)
)
static let classic = Theme(
name: "Classic",
background: .black,
foreground: NSColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0),
cursor: NSColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0),
selection: NSColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 1.0)
)
static let allThemes: [Theme] = [.classic, .dracula, .solarizedDark, .nord]
}
}
// MARK: - Command Snippet Manager
class SnippetManager {
static let shared = SnippetManager()
private var snippets: [Snippet] = []
struct Snippet: Codable {
let id: UUID
var name: String
var command: String
var description: String
var tags: [String]
var category: String
init(name: String, command: String, description: String = "", tags: [String] = [], category: String = "General") {
self.id = UUID()
self.name = name
self.command = command
self.description = description
self.tags = tags
self.category = category
}
}
init() {
loadSnippets()
if snippets.isEmpty {
addDefaultSnippets()
}
}
func addSnippet(_ snippet: Snippet) {
snippets.append(snippet)
saveSnippets()
}
func removeSnippet(_ id: UUID) {
snippets.removeAll { $0.id == id }
saveSnippets()
}
func searchSnippets(_ query: String) -> [Snippet] {
if query.isEmpty { return snippets }
return snippets.filter {
$0.name.localizedCaseInsensitiveContains(query) ||
$0.command.localizedCaseInsensitiveContains(query) ||
$0.tags.contains { $0.localizedCaseInsensitiveContains(query) }
}
}
private func addDefaultSnippets() {
snippets = [
Snippet(name: "List files", command: "ls -lah", description: "List all files with details", tags: ["files", "list"], category: "Files"),
Snippet(name: "Find large files", command: "find . -type f -size +100M", description: "Find files larger than 100MB", tags: ["find", "files"], category: "Files"),
Snippet(name: "Git status", command: "git status", description: "Show git repository status", tags: ["git"], category: "Git"),
Snippet(name: "Git commit", command: "git add . && git commit -m \"\"", description: "Stage and commit changes", tags: ["git"], category: "Git"),
Snippet(name: "Docker ps", command: "docker ps -a", description: "List all containers", tags: ["docker"], category: "Docker"),
Snippet(name: "Kill port", command: "lsof -ti:8080 | xargs kill -9", description: "Kill process on port 8080", tags: ["network", "kill"], category: "Network")
]
saveSnippets()
}
private func saveSnippets() {
if let data = try? JSONEncoder().encode(snippets) {
UserDefaults.standard.set(data, forKey: "terminal_snippets")
}
}
private func loadSnippets() {
if let data = UserDefaults.standard.data(forKey: "terminal_snippets"),
let loaded = try? JSONDecoder().decode([Snippet].self, from: data) {
snippets = loaded
}
}
func getAllSnippets() -> [Snippet] {
return snippets
}
}
// MARK: - Command History Manager
class HistoryManager {
static let shared = HistoryManager()
private var history: [String] = []
private let maxHistory = 1000
init() {
loadHistory()
}
func addCommand(_ command: String) {
guard !command.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty else { return }
history.append(command)
if history.count > maxHistory {
history.removeFirst()
}
saveHistory()
}
func searchHistory(_ query: String) -> [String] {
if query.isEmpty { return Array(history.reversed().prefix(50)) }
return history.reversed().filter { $0.localizedCaseInsensitiveContains(query) }
}
func getRecentCommands(_ count: Int = 10) -> [String] {
return Array(history.reversed().prefix(count))
}
private func saveHistory() {
UserDefaults.standard.set(history, forKey: "terminal_history")
}
private func loadHistory() {
history = UserDefaults.standard.stringArray(forKey: "terminal_history") ?? []
}
}
// MARK: - AI Service
protocol AIService {
func generateCommand(from prompt: String, completion: @escaping (Result<String, Error>) -> Void)
func explainError(_ error: String, completion: @escaping (Result<String, Error>) -> Void)
func suggestNextCommand(history: [String], completion: @escaping (Result<String, Error>) -> Void)
}
class OpenAIService: AIService {
private let apiKey: String
init(apiKey: String) {
self.apiKey = apiKey
}
func generateCommand(from prompt: String, completion: @escaping (Result<String, Error>) -> Void) {
let systemPrompt = "You are a terminal command expert. Generate only the command, no explanations. Respond with just the command."
makeRequest(system: systemPrompt, user: prompt, completion: completion)
}
func explainError(_ error: String, completion: @escaping (Result<String, Error>) -> Void) {
let systemPrompt = "You are a terminal expert. Explain this error briefly and suggest a fix."
makeRequest(system: systemPrompt, user: error, completion: completion)
}
func suggestNextCommand(history: [String], completion: @escaping (Result<String, Error>) -> Void) {
let systemPrompt = "Based on command history, suggest the next logical command. Respond with just the command."
let historyText = history.suffix(5).joined(separator: "\n")
makeRequest(system: systemPrompt, user: "Recent commands:\n\(historyText)", completion: completion)
}
private func makeRequest(system: String, user: String, completion: @escaping (Result<String, Error>) -> Void) {
guard let url = URL(string: "https://api.openai.com/v1/chat/completions") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"model": "gpt-3.5-turbo",
"messages": [
["role": "system", "content": system],
["role": "user", "content": user]
],
"max_tokens": 150,
"temperature": 0.7
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let choices = json["choices"] as? [[String: Any]],
let firstChoice = choices.first,
let message = firstChoice["message"] as? [String: Any],
let content = message["content"] as? String else {
completion(.failure(NSError(domain: "OpenAI", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
return
}
completion(.success(content.trimmingCharacters(in: .whitespacesAndNewlines)))
}.resume()
}
}
class OllamaService: AIService {
private let baseURL: String
private let model: String
init(baseURL: String, model: String) {
self.baseURL = baseURL
self.model = model
}
func generateCommand(from prompt: String, completion: @escaping (Result<String, Error>) -> Void) {
let fullPrompt = "Generate only the terminal command for: \(prompt). Respond with just the command, no explanation."
makeRequest(prompt: fullPrompt, completion: completion)
}
func explainError(_ error: String, completion: @escaping (Result<String, Error>) -> Void) {
let fullPrompt = "Explain this terminal error briefly and suggest a fix:\n\(error)"
makeRequest(prompt: fullPrompt, completion: completion)
}
func suggestNextCommand(history: [String], completion: @escaping (Result<String, Error>) -> Void) {
let historyText = history.suffix(5).joined(separator: "\n")
let fullPrompt = "Based on these recent commands, suggest the next logical command:\n\(historyText)\nRespond with just the command."
makeRequest(prompt: fullPrompt, completion: completion)
}
private func makeRequest(prompt: String, completion: @escaping (Result<String, Error>) -> Void) {
guard let url = URL(string: "\(baseURL)/api/generate") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: Any] = [
"model": model,
"prompt": prompt,
"stream": false
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body)
URLSession.shared.dataTask(with: request) { data, response, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data,
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
let response = json["response"] as? String else {
completion(.failure(NSError(domain: "Ollama", code: -1, userInfo: [NSLocalizedDescriptionKey: "Invalid response"])))
return
}
completion(.success(response.trimmingCharacters(in: .whitespacesAndNewlines)))
}.resume()
}
}
class AIManager {
static let shared = AIManager()
private var service: AIService?
func updateService() {
let settings = TerminalSettings.shared
switch settings.aiProvider {
case .openai:
if !settings.openAIKey.isEmpty {
service = OpenAIService(apiKey: settings.openAIKey)
}
case .ollama:
service = OllamaService(baseURL: settings.ollamaURL, model: settings.ollamaModel)
case .none:
service = nil
}
}
func isAvailable() -> Bool {
return service != nil
}
func generateCommand(from prompt: String, completion: @escaping (Result<String, Error>) -> Void) {
service?.generateCommand(from: prompt, completion: completion)
}
func explainError(_ error: String, completion: @escaping (Result<String, Error>) -> Void) {
service?.explainError(error, completion: completion)
}
func suggestNextCommand(history: [String], completion: @escaping (Result<String, Error>) -> Void) {
service?.suggestNextCommand(history: history, completion: completion)
}
}