diff --git a/supacodeTests/AgentClassifierTests.swift b/supacodeTests/AgentClassifierTests.swift index b960b0e2..95028d8d 100644 --- a/supacodeTests/AgentClassifierTests.swift +++ b/supacodeTests/AgentClassifierTests.swift @@ -24,6 +24,7 @@ struct AgentClassifierTests { #expect(identifyAgent(processName: "amp") == .amp) #expect(identifyAgent(processName: "amp-local") == .amp) #expect(identifyAgent(processName: "qwen") == .qwen) + #expect(identifyAgent(processName: "qodercli")?.rawValue == "qodercli") #expect(identifyAgent(processName: "grok") == .grok) #expect(identifyAgent(processName: "grok-0.2.101-macos-aarch64") == .grok) // Model ids must not be treated as the install binary. @@ -228,6 +229,24 @@ struct AgentClassifierTests { #expect(result.name == "codex") } + @Test func identifiesQoderCLIWrappedByNode() throws { + let job = ForegroundJob( + processGroupID: 42, + processes: [ + ForegroundProcess( + pid: 100, + name: "node", + argv0: "node", + cmdline: "node /opt/homebrew/lib/node_modules/@qoder-ai/qodercli/bundle/qodercli.js" + ) + ] + ) + + let result = try #require(identifyAgentInJob(job)) + #expect(result.agent.rawValue == "qodercli") + #expect(result.name == "qodercli") + } + @Test func identifiesOmxAsCodexWrapper() throws { let job = ForegroundJob( processGroupID: 42, diff --git a/supacodeTests/CommandIconMapTests.swift b/supacodeTests/CommandIconMapTests.swift index 0ea6674f..821998b3 100644 --- a/supacodeTests/CommandIconMapTests.swift +++ b/supacodeTests/CommandIconMapTests.swift @@ -78,6 +78,7 @@ struct CommandIconMapTests { #expect(CommandIconMap.iconForFirstToken("cline")?.assetName == "Cline") #expect(CommandIconMap.iconForFirstToken("droid")?.assetName == "Droid") #expect(CommandIconMap.iconForFirstToken("qwen")?.assetName == "Qwen") + #expect(CommandIconMap.iconForFirstToken("qodercli")?.systemSymbol == "sparkle") #expect(CommandIconMap.iconForFirstToken("grok")?.assetName == "Grok") // Aider has no bundled brand asset — sparkle fallback only. #expect(CommandIconMap.iconForFirstToken("aider")?.systemSymbol == "sparkle") diff --git a/supacodeTests/ScreenHeuristicsTests.swift b/supacodeTests/ScreenHeuristicsTests.swift index f4a26630..eeb8a4fb 100644 --- a/supacodeTests/ScreenHeuristicsTests.swift +++ b/supacodeTests/ScreenHeuristicsTests.swift @@ -541,6 +541,61 @@ struct ScreenHeuristicsTests { #expect(DetectedAgent.qwen.detectState(in: "> Type your message") == .idle) } + @Test func qoderCLIDetection() throws { + let qoder = try #require(DetectedAgent(rawValue: "qodercli")) + + #expect( + qoder.detectState( + in: """ + Bash(git status) needs permission + > Allow once + Allow for this session + Reject and type something + No + """ + ) == .blocked + ) + #expect( + qoder.detectState( + in: "⠋ Thinking... (esc to cancel, 12s)" + ) == .working + ) + #expect(qoder.detectState(in: "Allow once\nNo") == .idle) + #expect( + qoder.detectState( + in: """ + > Allow once + Allow for this session + Reject and type something + No + Permission resolved. + Current prompt is ready. + Use /help for commands. + Workspace: Prowl + Mode: default + Model: Qoder + ? for shortcuts + ❯ + """ + ) == .idle + ) + #expect( + qoder.detectState( + in: """ + ⠋ Thinking... (esc to cancel, 12s) + Previous task completed. + Current prompt is ready. + Use /help for commands. + Workspace: Prowl + Mode: default + Model: Qoder + ? for shortcuts + ❯ + """ + ) == .idle + ) + } + @Test func grokDetection() { // Permission chrome (labels verified against Grok Build 0.2.101 binary). #expect(