import ComposableArchitecture import ChatService import Foundation import ConversationServiceProvider import GitHelper import LanguageServerProtocol import Terminal import Combine @MainActor public class CodeReviewStateService: ObservableObject { public static let shared = CodeReviewStateService() public let fileClickedEvent = PassthroughSubject() private init() { } func notifyFileClicked() { fileClickedEvent.send() } } @Reducer public struct ConversationCodeReviewFeature { @ObservableState public struct State: Equatable { public init() { } } public enum Action: Equatable { case request(GitDiffGroup) case accept(id: String, selectedFiles: [DocumentUri]) case cancel(id: String) case onFileClicked(URL, Int) } public let service: ChatService public var body: some ReducerOf { Reduce { state, action in switch action { case .request(let group): return .run { _ in try await service.requestCodeReview(group) } case let .accept(id, selectedFileUris): return .run { _ in await service.acceptCodeReview(id, selectedFileUris: selectedFileUris) } case .cancel(let id): return .run { _ in await service.cancelCodeReview(id) } // lineNumber: 0-based case .onFileClicked(let fileURL, let lineNumber): return .run { _ in if FileManager.default.fileExists(atPath: fileURL.path) { let terminal = Terminal() do { _ = try await terminal.runCommand( "/bin/bash", arguments: [ "-c", "xed -l \(lineNumber+1) \"${TARGET_REVIEW_FILE}\"" ], environment: [ "TARGET_REVIEW_FILE": fileURL.path ] ) } catch { print(error) } } Task { @MainActor in CodeReviewStateService.shared.notifyFileClicked() } } } } } }