diff --git a/AGENTS.md b/AGENTS.md index ff921cd..884eaaa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -196,3 +196,6 @@ The older OpenClaw/beads/macOS-only prototype has been removed from the active s ## Imported Claude Cowork project instructions fix the ui theme + +## Activity — 2026-05-24 +- (no commits) diff --git a/AgentBoardTests/NativeSwiftUIInterfaceTests.swift b/AgentBoardTests/NativeSwiftUIInterfaceTests.swift index 4e93c02..c58404a 100644 --- a/AgentBoardTests/NativeSwiftUIInterfaceTests.swift +++ b/AgentBoardTests/NativeSwiftUIInterfaceTests.swift @@ -78,6 +78,33 @@ struct NativeSwiftUIInterfaceTests { #expect(!source.contains("loadItem(forTypeIdentifier:")) } + @Test func workBoardPersistsSelectedRepositoryAndSeedsCreateIssueSheet() throws { + let workSource = try Self.source("AgentBoardUI/Screens/WorkScreen.swift") + let createSource = try Self.source("AgentBoardUI/Screens/CreateIssueSheet.swift") + + #expect(workSource.contains(#"@SceneStorage("work.selectedRepository")"#)) + #expect(workSource.contains("CreateIssueSheet(initialRepository: selectedCreateRepository)")) + #expect(workSource.contains("private var selectedCreateRepository: ConfiguredRepository?")) + #expect(createSource.contains("init(initialRepository: ConfiguredRepository? = nil)")) + #expect(createSource.contains("_selectedRepository = State(initialValue: initialRepository)")) + } + + @Test func launchedSessionsOpenTerminalInDesktopShell() throws { + let source = try Self.source("AgentBoard/DesktopRootView.swift") + + #expect(source.contains("onChange(of: appModel.sessionLauncher.activeSessions.map(\\.id))")) + #expect(source.contains("activeSessionTerminal = session")) + } + + @Test func companionSessionDetailOffersTerminalWhenTmuxSessionExists() throws { + let source = try Self.source("AgentBoardUI/Screens/SessionDetailSheet.swift") + + #expect(source.contains("session.tmuxSession")) + #expect(source.contains(#"Text("Terminal").tag(2)"#)) + #expect(source.contains("SessionLauncher.attachCommand(for: tmuxSession)")) + #expect(source.contains("EmbeddedTerminalView(")) + } + @Test func nilIfEmptyHasSingleSharedDefinition() throws { let coreURL = Self.repositoryRoot.appending(path: "AgentBoardCore") let fileURLs = try FileManager.default.subpathsOfDirectory(atPath: coreURL.path) diff --git a/AgentBoardTests/SessionLauncherTests.swift b/AgentBoardTests/SessionLauncherTests.swift index 720c3e8..b551787 100644 --- a/AgentBoardTests/SessionLauncherTests.swift +++ b/AgentBoardTests/SessionLauncherTests.swift @@ -126,4 +126,68 @@ struct SessionLauncherTests { @Test @MainActor func tmuxSocketPathUsesExpectedSuffix() { #expect(SessionLauncher.tmuxSocketPath.hasSuffix("/.tmux/sock")) } + + @Test @MainActor func checkSessionReturnsCompletedWhenPaneShowsSuccessfulExit() async { + let launcher = SessionLauncher(tmux: FakeTmuxController( + hasSessionResult: true, + paneOutput: "work complete\nEXITED: 0" + )) + let session = makeActiveSession(name: "ab-agentboard-130") + + let status = await launcher.checkSession(session) + + #expect(status == .completed) + } + + @Test @MainActor func checkSessionReturnsFailedWhenPaneShowsNonzeroExit() async { + let launcher = SessionLauncher(tmux: FakeTmuxController( + hasSessionResult: true, + paneOutput: "Failed to authenticate\nEXITED: 1" + )) + let session = makeActiveSession(name: "ab-agentboard-132") + + let status = await launcher.checkSession(session) + + #expect(status == .failed) + } + + @MainActor + private func makeActiveSession(name: String) -> SessionLauncher.ActiveSession { + SessionLauncher.ActiveSession( + id: name, + sessionName: name, + issueNumber: 130, + preset: .ralphLoop, + agentType: .claude, + startTime: Date(), + status: .running + ) + } +} + +private actor FakeTmuxController: TmuxControlling { + private let hasSessionResult: Bool + private let paneOutput: String? + + init(hasSessionResult: Bool, paneOutput: String?) { + self.hasSessionResult = hasSessionResult + self.paneOutput = paneOutput + } + + func launchSession( + name _: String, + repoPath _: String, + agentLaunchFlag _: String, + prdPath _: String + ) async throws {} + + func hasSession(name _: String) async throws -> Bool { + hasSessionResult + } + + func capturePane(name _: String) async -> String? { + paneOutput + } + + nonisolated func openInTerminal(name _: String) {} } diff --git a/AgentBoardTests/WorkStoreCRUDTests.swift b/AgentBoardTests/WorkStoreCRUDTests.swift index 36f8274..bf8ec76 100644 --- a/AgentBoardTests/WorkStoreCRUDTests.swift +++ b/AgentBoardTests/WorkStoreCRUDTests.swift @@ -76,6 +76,26 @@ struct WorkStoreCRUDTests { #expect(store.errorMessage == nil) } + @Test func createIssueReturnsCreatedItemOnSuccess() async throws { + let createdJSON = issueJSON(number: 102, title: "Dismiss sheet", body: "Body here") + let (store, _) = try makeStore(mockHandler: { request in + let response = try Self.successResponse(for: request.url) + if request.httpMethod == "POST" { + return (response, Data(createdJSON.utf8)) + } + return (response, Data("[]".utf8)) + }) + + let created = await store.createIssue( + repository: ConfiguredRepository(owner: "org", name: "repo"), + title: "Dismiss sheet", + body: "Body here" + ) + + #expect(created?.issueNumber == 102) + #expect(created?.title == "Dismiss sheet") + } + @Test func createIssueSendsTitleBodyAndLabelsInRequestPayload() async throws { final class Capture: @unchecked Sendable { var payload: [String: Any] = [:]