Skip to content

Commit a8d03a7

Browse files
Rsdvops copilot (#2)
* Commit to rsdvops_copilot * Pre-release 0.32.110 * Pre-release 0.32.111 * Pre-release 0.32.112 * Release 0.33.0 --------- Co-authored-by: GitHub Actions <actions@github.com>
1 parent 5584e84 commit a8d03a7

84 files changed

Lines changed: 2877 additions & 537 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.github/actions/set-xcode-version/action.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ inputs:
66
Xcode version to use, in semver(ish)-style matching the format on the Actions runner image.
77
See available versions at https://github.com/actions/runner-images/blame/main/images/macos/macos-14-Readme.md#xcode
88
required: false
9-
default: '15.3'
9+
default: '16.2'
1010
outputs:
1111
xcode-path:
1212
description: "Path to current Xcode version"

‎CHANGELOG.md‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## 0.33.0 - April 17, 2025
9+
### Added
10+
- Added support for new models in Chat: Claude 3.7 Sonnet and GPT 4.5
11+
- Implemented @workspace context feature allowing questions about the entire codebase in Copilot Chat
12+
13+
### Changed
14+
- Simplified access to Copilot Chat from the Copilot for Xcode app with a single click
15+
- Enhanced instructions for granting background permissions
16+
17+
### Fixed
18+
- Resolved false alarms for sign-in and free plan limit notifications
19+
- Improved app launch performance
20+
- Fixed workspace and context update issues
21+
822
## 0.32.0 - March 11, 2025 (General Availability)
923
### Added
1024
- Implemented model picker for selecting LLM model in chat

‎CommunicationBridge/ServiceDelegate.swift‎

Lines changed: 93 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -136,28 +136,100 @@ actor ExtensionServiceLauncher {
136136
isLaunching = true
137137

138138
Logger.communicationBridge.info("Launching extension service app.")
139-
140-
NSWorkspace.shared.openApplication(
141-
at: appURL,
142-
configuration: {
143-
let configuration = NSWorkspace.OpenConfiguration()
144-
configuration.createsNewApplicationInstance = false
145-
configuration.addsToRecentItems = false
146-
configuration.activates = false
147-
return configuration
148-
}()
149-
) { app, error in
150-
if let error = error {
151-
Logger.communicationBridge.error(
152-
"Failed to launch extension service app: \(error)"
153-
)
154-
} else {
155-
Logger.communicationBridge.info(
156-
"Finished launching extension service app."
157-
)
139+
140+
// First check if the app is already running
141+
if let runningApp = NSWorkspace.shared.runningApplications.first(where: {
142+
$0.bundleIdentifier == appIdentifier
143+
}) {
144+
Logger.communicationBridge.info("Extension service app already running with PID: \(runningApp.processIdentifier)")
145+
self.application = runningApp
146+
self.isLaunching = false
147+
return
148+
}
149+
150+
// Implement a retry mechanism with exponential backoff
151+
Task {
152+
var retryCount = 0
153+
let maxRetries = 3
154+
var success = false
155+
156+
while !success && retryCount < maxRetries {
157+
do {
158+
// Add a delay between retries with exponential backoff
159+
if retryCount > 0 {
160+
let delaySeconds = pow(2.0, Double(retryCount - 1))
161+
Logger.communicationBridge.info("Retrying launch after \(delaySeconds) seconds (attempt \(retryCount + 1) of \(maxRetries))")
162+
try await Task.sleep(nanoseconds: UInt64(delaySeconds * 1_000_000_000))
163+
}
164+
165+
// Use a task-based approach for launching with timeout
166+
let launchTask = Task<NSRunningApplication?, Error> { () -> NSRunningApplication? in
167+
return await withCheckedContinuation { continuation in
168+
NSWorkspace.shared.openApplication(
169+
at: appURL,
170+
configuration: {
171+
let configuration = NSWorkspace.OpenConfiguration()
172+
configuration.createsNewApplicationInstance = false
173+
configuration.addsToRecentItems = false
174+
configuration.activates = false
175+
return configuration
176+
}()
177+
) { app, error in
178+
if let error = error {
179+
continuation.resume(returning: nil)
180+
} else {
181+
continuation.resume(returning: app)
182+
}
183+
}
184+
}
185+
}
186+
187+
// Set a timeout for the launch operation
188+
let timeoutTask = Task {
189+
try await Task.sleep(nanoseconds: 10_000_000_000) // 10 seconds
190+
return
191+
}
192+
193+
// Wait for either the launch or the timeout
194+
let app = try await withTaskCancellationHandler {
195+
try await launchTask.value ?? nil
196+
} onCancel: {
197+
launchTask.cancel()
198+
}
199+
200+
// Cancel the timeout task
201+
timeoutTask.cancel()
202+
203+
if let app = app {
204+
// Success!
205+
self.application = app
206+
success = true
207+
break
208+
} else {
209+
// App is nil, retry
210+
retryCount += 1
211+
Logger.communicationBridge.info("Launch attempt \(retryCount) failed, app is nil")
212+
}
213+
} catch {
214+
retryCount += 1
215+
Logger.communicationBridge.error("Error during launch attempt \(retryCount): \(error.localizedDescription)")
216+
}
158217
}
159-
160-
self.application = app
218+
219+
// Double-check we have a valid application
220+
if !success && self.application == nil {
221+
// After all retries, check once more if the app is running (it might have launched but we missed the callback)
222+
if let runningApp = NSWorkspace.shared.runningApplications.first(where: {
223+
$0.bundleIdentifier == appIdentifier
224+
}) {
225+
Logger.communicationBridge.info("Found running extension service after retries: \(runningApp.processIdentifier)")
226+
self.application = runningApp
227+
success = true
228+
} else {
229+
Logger.communicationBridge.info("Failed to launch extension service after \(maxRetries) attempts")
230+
}
231+
}
232+
161233
self.isLaunching = false
162234
}
163235
}

‎Copilot for Xcode/App.swift‎

Lines changed: 158 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,145 @@
1+
import SwiftUI
12
import Client
23
import HostApp
34
import LaunchAgentManager
45
import SharedUIComponents
5-
import SwiftUI
66
import UpdateChecker
77
import XPCShared
8+
import HostAppActivator
89

910
struct VisualEffect: NSViewRepresentable {
1011
func makeNSView(context: Self.Context) -> NSView { return NSVisualEffectView() }
1112
func updateNSView(_ nsView: NSView, context: Context) { }
1213
}
1314

1415
class AppDelegate: NSObject, NSApplicationDelegate {
15-
func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { true }
16+
private var permissionAlertShown = false
17+
18+
// Launch modes supported by the app
19+
enum LaunchMode {
20+
case chat
21+
case settings
22+
}
23+
24+
func applicationDidFinishLaunching(_ notification: Notification) {
25+
if #available(macOS 13.0, *) {
26+
checkBackgroundPermissions()
27+
}
28+
29+
let launchMode = determineLaunchMode()
30+
handleLaunchMode(launchMode)
31+
}
32+
33+
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
34+
if #available(macOS 13.0, *) {
35+
checkBackgroundPermissions()
36+
}
37+
38+
let launchMode = determineLaunchMode()
39+
handleLaunchMode(launchMode)
40+
return true
41+
}
42+
43+
// MARK: - Helper Methods
44+
45+
private func determineLaunchMode() -> LaunchMode {
46+
let launchArgs = CommandLine.arguments
47+
if launchArgs.contains("--settings") {
48+
return .settings
49+
} else {
50+
return .chat
51+
}
52+
}
53+
54+
private func handleLaunchMode(_ mode: LaunchMode) {
55+
switch mode {
56+
case .settings:
57+
openSettings()
58+
case .chat:
59+
openChat()
60+
}
61+
}
62+
63+
private func openSettings() {
64+
DispatchQueue.main.async {
65+
NSApp.activate(ignoringOtherApps: true)
66+
if #available(macOS 14.0, *) {
67+
let environment = SettingsEnvironment()
68+
environment.open()
69+
} else if #available(macOS 13.0, *) {
70+
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
71+
} else {
72+
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
73+
}
74+
}
75+
}
76+
77+
private func openChat() {
78+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
79+
Task {
80+
let service = try? getService()
81+
try? await service?.openChat()
82+
}
83+
}
84+
}
85+
86+
@available(macOS 13.0, *)
87+
private func checkBackgroundPermissions() {
88+
Task {
89+
// Direct check of permission status
90+
let launchAgentManager = LaunchAgentManager()
91+
let isPermissionGranted = await launchAgentManager.isBackgroundPermissionGranted()
92+
93+
if !isPermissionGranted {
94+
// Only show alert if permission isn't granted
95+
DispatchQueue.main.async {
96+
if !self.permissionAlertShown {
97+
showBackgroundPermissionAlert()
98+
self.permissionAlertShown = true
99+
}
100+
}
101+
} else {
102+
// Permission is granted, reset flag
103+
self.permissionAlertShown = false
104+
}
105+
}
106+
}
107+
108+
// MARK: - Application Termination
109+
110+
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
111+
// Immediately terminate extension service if it's running
112+
if let extensionService = NSWorkspace.shared.runningApplications.first(where: {
113+
$0.bundleIdentifier == "\(Bundle.main.bundleIdentifier!).ExtensionService"
114+
}) {
115+
extensionService.terminate()
116+
}
117+
118+
// Start cleanup in background without waiting
119+
Task {
120+
let quitTask = Task {
121+
let service = try? getService()
122+
try? await service?.quitService()
123+
}
124+
125+
// Wait just a tiny bit to allow cleanup to start
126+
try? await Task.sleep(nanoseconds: 100_000_000) // 100ms
127+
128+
DispatchQueue.main.async {
129+
NSApp.reply(toApplicationShouldTerminate: true)
130+
}
131+
}
132+
133+
return .terminateLater
134+
}
135+
136+
func applicationWillTerminate(_ notification: Notification) {
137+
if let extensionService = NSWorkspace.shared.runningApplications.first(where: {
138+
$0.bundleIdentifier == "\(Bundle.main.bundleIdentifier!).ExtensionService"
139+
}) {
140+
extensionService.terminate()
141+
}
142+
}
16143
}
17144

18145
class AppUpdateCheckerDelegate: UpdateCheckerDelegate {
@@ -28,16 +155,40 @@ class AppUpdateCheckerDelegate: UpdateCheckerDelegate {
28155
@main
29156
struct CopilotForXcodeApp: App {
30157
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
158+
159+
init() {
160+
UserDefaults.setupDefaultSettings()
161+
162+
Task {
163+
await hostAppStore
164+
.send(.general(.setupLaunchAgentIfNeeded))
165+
.finish()
166+
}
167+
168+
DistributedNotificationCenter.default().addObserver(
169+
forName: .openSettingsWindowRequest,
170+
object: nil,
171+
queue: .main
172+
) { _ in
173+
DispatchQueue.main.async {
174+
NSApp.activate(ignoringOtherApps: true)
175+
if #available(macOS 14.0, *) {
176+
let environment = SettingsEnvironment()
177+
environment.open()
178+
} else if #available(macOS 13.0, *) {
179+
NSApp.sendAction(Selector(("showSettingsWindow:")), to: nil, from: nil)
180+
} else {
181+
NSApp.sendAction(Selector(("showPreferencesWindow:")), to: nil, from: nil)
182+
}
183+
}
184+
}
185+
}
31186

32187
var body: some Scene {
33-
WindowGroup {
188+
Settings {
34189
TabContainer()
35190
.frame(minWidth: 800, minHeight: 600)
36191
.background(VisualEffect().ignoresSafeArea())
37-
.onAppear {
38-
UserDefaults.setupDefaultSettings()
39-
}
40-
.copilotIntroSheet()
41192
.environment(\.updateChecker, UpdateChecker(
42193
hostBundle: Bundle.main,
43194
checkerDelegate: AppUpdateCheckerDelegate()

‎Copilot for Xcode/Assets.xcassets/ChatIcon.imageset/Chat.svg‎

Lines changed: 0 additions & 10 deletions
This file was deleted.
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
{
22
"images" : [
33
{
4-
"filename" : "Chat.svg",
4+
"filename" : "ChatIcon.svg",
55
"idiom" : "universal"
66
}
77
],
88
"info" : {
99
"author" : "xcode",
1010
"version" : 1
11+
},
12+
"properties" : {
13+
"preserves-vector-representation" : true
1114
}
1215
}

0 commit comments

Comments
 (0)