spb/zyquo-agent Public MIT
The autonomous agent that actually operates your Mac — plans, runs real commands, verifies its own work.
Swift 94.7%
Shell 4.1%
Python 0.7%
Makefile 0.5%
1//2// ZyquoAgentApp.swift3// Zyquo Agent4//5// Author: Simon-Pierre Boucher6// Mail: contact@spboucher.ai7//8// The SwiftUI app shell: builds the shared environment (task store, run9// hub, model catalog, key vault, appearance, agent settings, templates,10// personas, UI signals), presents the command-center window (1320×86011// default, 1040×680 min), the Settings scene (760×560, 7 tabs), the12// toggleable menu bar extra with running-task status, the ⌥Space Quick Task13// panel, and the full Phase 6 command set (⌘N, ⌘K, ⌘⇧A, ⌘⇧E, ⌥Space;14// ⌘⏎/⌘./⌘F live in the detail/sidebar views).15//1617import SwiftUI1819/// Shared object graph for the app session.20@MainActor21final class AppEnvironment: ObservableObject {22 let tasks: TaskStore23 let hub: RunHub24 let catalog: ModelCatalog25 let vault: KeyVaultStore26 let appearance: AppearanceStore27 let settings: AgentSettingsStore28 let templates: TemplateStore29 let personas: PersonaStore30 let uiState: AppUIState3132 init() {33 let tasks = TaskStore()34 let settings = AgentSettingsStore()35 self.tasks = tasks36 self.settings = settings37 self.hub = RunHub(store: tasks, settings: settings)38 self.catalog = ModelCatalog()39 self.vault = KeyVaultStore()40 self.appearance = AppearanceStore()41 self.templates = TemplateStore()42 self.personas = PersonaStore()43 self.uiState = AppUIState()44 }4546 /// The model new tasks start with (persisted default, catalog fallback).47 var newTaskModel: AIModel? {48 settings.defaultAgentModel(in: catalog)49 }50}5152struct ZyquoAgentApp: App {53 @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate54 @StateObject private var environment = AppEnvironment()55 @State private var quickTask: QuickTaskController?56 @AppStorage("menuBarExtraEnabled") private var menuBarExtraEnabled = true5758 var body: some Scene {59 WindowGroup("Zyquo Agent") {60 MainWindowView()61 .modifier(EnvironmentInjector(environment: environment))62 .frame(63 minWidth: ZyquoMetrics.windowMinWidth,64 minHeight: ZyquoMetrics.windowMinHeight65 )66 .onAppear {67 if quickTask == nil {68 quickTask = QuickTaskController(environment: environment)69 }70 }71 }72 .defaultSize(73 width: ZyquoMetrics.windowDefaultWidth,74 height: ZyquoMetrics.windowDefaultHeight75 )76 .commands {77 AppCommands(environment: environment, quickTask: { quickTask })78 }7980 Settings {81 SettingsView()82 .modifier(EnvironmentInjector(environment: environment))83 }8485 MenuBarExtra(isInserted: $menuBarExtraEnabled) {86 MenuBarContent(store: environment.tasks, environment: environment, quickTask: { quickTask })87 } label: {88 MenuBarIconView()89 }90 }91}9293/// Injects the full shared object graph (all scenes get the same set).94private struct EnvironmentInjector: ViewModifier {95 let environment: AppEnvironment9697 func body(content: Content) -> some View {98 content99 .environmentObject(environment.tasks)100 .environmentObject(environment.hub)101 .environmentObject(environment.catalog)102 .environmentObject(environment.vault)103 .environmentObject(environment.appearance)104 .environmentObject(environment.settings)105 .environmentObject(environment.templates)106 .environmentObject(environment.personas)107 .environmentObject(environment.uiState)108 }109}110111// MARK: - Commands112113/// App-level menu commands and shortcuts (⌘N, ⌘K, ⌘⇧A, ⌘⇧E, Quick Task).114struct AppCommands: Commands {115 let environment: AppEnvironment116 var quickTask: () -> QuickTaskController?117118 var body: some Commands {119 CommandGroup(replacing: .newItem) {120 Button("New Task") {121 environment.tasks.newTask(122 model: environment.newTaskModel,123 safetyMode: environment.settings.settings.defaultSafetyMode124 )125 }126 .keyboardShortcut("n", modifiers: .command)127 }128 CommandMenu("Task") {129 Button("Command Palette") {130 environment.uiState.showCommandPalette.toggle()131 }132 .keyboardShortcut("k", modifiers: .command)133 Button("Quick Task") {134 quickTask()?.show()135 }136 Button("Browse Templates…") {137 environment.uiState.showTemplateBrowser = true138 }139 Divider()140 Button("Open Audit Log") {141 environment.uiState.requestAuditLog()142 }143 .keyboardShortcut("a", modifiers: [.command, .shift])144 Divider()145 Button("Export Transcript as Markdown…") {146 if let id = environment.tasks.selectedID, let task = environment.tasks.task(id: id) {147 TaskTranscriptExporter.presentSavePanel(for: task, format: .markdown)148 }149 }150 .keyboardShortcut("e", modifiers: [.command, .shift])151 .disabled(environment.tasks.selectedID == nil)152 Button("Export Transcript as PDF…") {153 if let id = environment.tasks.selectedID, let task = environment.tasks.task(id: id) {154 TaskTranscriptExporter.presentSavePanel(for: task, format: .pdf)155 }156 }157 .disabled(environment.tasks.selectedID == nil)158 }159 }160}161162// MARK: - Menu bar extra163164/// Menu bar glyph: the shipped template PNG when bundled, SF Symbol fallback165/// during `swift run` (no bundle resources).166struct MenuBarIconView: View {167 var body: some View {168 if let image = Self.templateImage() {169 Image(nsImage: image)170 } else {171 Image(systemName: "bolt.circle")172 }173 }174175 private static func templateImage() -> NSImage? {176 guard let path = Bundle.main.path(forResource: "MenuBarIcon", ofType: "png"),177 let image = NSImage(contentsOfFile: path) else { return nil }178 image.isTemplate = true179 image.size = NSSize(width: 18, height: 18)180 return image181 }182}183184/// Menu content: running-task status, New Task, Quick Task, open, quit.185struct MenuBarContent: View {186 @ObservedObject var store: TaskStore187 let environment: AppEnvironment188 var quickTask: () -> QuickTaskController?189190 var body: some View {191 let running = store.tasks.filter { $0.status.isActive }192 if running.isEmpty {193 Text("No running tasks")194 } else {195 Text("\(running.count) running task\(running.count == 1 ? "" : "s")")196 ForEach(running) { task in197 Button("\(task.title) — \(task.status.displayName)") {198 store.selectedID = task.id199 NSApp.activate(ignoringOtherApps: true)200 }201 }202 }203 Divider()204 Button("New Task") {205 store.newTask(206 model: environment.newTaskModel,207 safetyMode: environment.settings.settings.defaultSafetyMode208 )209 NSApp.activate(ignoringOtherApps: true)210 }211 Button("Quick Task ⌥Space") {212 quickTask()?.show()213 }214 Divider()215 Button("Open Zyquo Agent") {216 NSApp.activate(ignoringOtherApps: true)217 }218 Button("Quit") {219 NSApp.terminate(nil)220 }221 }222}223224final class AppDelegate: NSObject, NSApplicationDelegate {225 func applicationDidFinishLaunching(_ notification: Notification) {226 // Ensure the app fronts correctly when launched outside Finder227 // (e.g. `swift run` or `open` from a terminal during development).228 NSApp.setActivationPolicy(.regular)229 NSApp.activate(ignoringOtherApps: true)230 }231}232