Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions Sources/ClaudeCodeCore/Models/AskUserQuestionModels.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
//
// AskUserQuestionModels.swift
// ClaudeCodeUI
//
// Created for AskUserQuestion tool support
//

import Foundation

/// Represents a single option within a question
public struct QuestionOption: Codable, Equatable, Identifiable {
public let id: UUID

/// The display text for this option
public let label: String

/// Explanation of what this option means
public let description: String

public init(id: UUID = UUID(), label: String, description: String) {
self.id = id
self.label = label
self.description = description
}
}

/// Represents a single question with its options
public struct Question: Codable, Equatable, Identifiable {
public let id: UUID

/// The complete question text
public let question: String

/// Short label for display (max 12 chars)
public let header: String

/// Available options for this question
public let options: [QuestionOption]

/// Whether multiple selections are allowed
public let multiSelect: Bool

public init(
id: UUID = UUID(),
question: String,
header: String,
options: [QuestionOption],
multiSelect: Bool
) {
self.id = id
self.question = question
self.header = header
self.options = options
self.multiSelect = multiSelect
}
}

/// Represents a set of questions from an AskUserQuestion tool call
public struct QuestionSet: Codable, Equatable {
/// The questions to be answered
public let questions: [Question]

/// The tool use ID for sending back the response
public let toolUseId: String

public init(questions: [Question], toolUseId: String) {
self.questions = questions
self.toolUseId = toolUseId
}
}

/// Represents a user's answer to a question
public struct QuestionAnswer: Codable, Equatable {
/// Index of the question being answered
public let questionIndex: Int

/// Selected option labels (can be multiple for multiSelect)
public let selectedLabels: [String]

/// Custom text if "Other" was selected
public let otherText: String?

public init(questionIndex: Int, selectedLabels: [String], otherText: String? = nil) {
self.questionIndex = questionIndex
self.selectedLabels = selectedLabels
self.otherText = otherText
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
//
// AskUserQuestionFormatter.swift
// ClaudeCodeUI
//
// Created for AskUserQuestion tool support
//

import Foundation

/// Formatter for AskUserQuestion tool display
struct AskUserQuestionFormatter: ToolFormatterProtocol {

func formatOutput(_ output: String, tool: ToolType) -> (String, ToolDisplayFormatter.ToolContentFormatter.ContentType) {
// Output is typically the user's answers
return (output, .plainText)
}

func extractKeyParameters(_ arguments: String, tool: ToolType) -> String? {
// Extract number of questions for header display
if let jsonDict = arguments.toDictionary(),
let questions = jsonDict["questions"] as? [[String: Any]] {
let count = questions.count
return "\(count) question\(count == 1 ? "" : "s")"
}
return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ public struct ToolDisplayFormatter {
return TaskToolFormatter()
case .exitPlanMode:
return PlainTextToolFormatter()
case .askUserQuestion:
return AskUserQuestionFormatter()
default:
// Check format type as fallback
switch tool.formatType {
Expand Down
7 changes: 6 additions & 1 deletion Sources/ClaudeCodeCore/ToolFormatting/ToolType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ public enum ClaudeCodeTool: String, ToolType, CaseIterable {
case webFetch = "WebFetch"
case todoWrite = "TodoWrite"
case webSearch = "WebSearch"

case askUserQuestion = "AskUserQuestion"

public var identifier: String { rawValue }

public var friendlyName: String {
Expand All @@ -88,6 +89,7 @@ public enum ClaudeCodeTool: String, ToolType, CaseIterable {
case .webFetch: return "Fetch Web Content"
case .todoWrite: return "Todo List"
case .webSearch: return "Web Search"
case .askUserQuestion: return "Ask User Question"
}
}

Expand All @@ -108,6 +110,7 @@ public enum ClaudeCodeTool: String, ToolType, CaseIterable {
case .webFetch: return "globe"
case .todoWrite: return "checklist"
case .webSearch: return "magnifyingglass.circle"
case .askUserQuestion: return "questionmark.circle"
}
}

Expand Down Expand Up @@ -136,6 +139,7 @@ public enum ClaudeCodeTool: String, ToolType, CaseIterable {
case .todoWrite: return .todos
case .task: return .markdown
case .exitPlanMode: return .plainText
case .askUserQuestion: return .plainText
}
}

Expand Down Expand Up @@ -163,6 +167,7 @@ public enum ClaudeCodeTool: String, ToolType, CaseIterable {
case .webSearch: return ["query", "allowed_domains", "blocked_domains"]
case .task: return ["description", "prompt"]
case .exitPlanMode: return ["plan"]
case .askUserQuestion: return ["questions"]
}
}

Expand Down
4 changes: 3 additions & 1 deletion Sources/ClaudeCodeCore/UI/ChatMessageRow.swift
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ struct ChatMessageRow: View {
switch message.messageType {
case .toolUse, .toolResult, .toolError, .toolDenied, .thinking, .webSearch:
return true
case .text:
case .text, .askUserQuestion:
return false
}
}
Expand Down Expand Up @@ -406,6 +406,8 @@ struct ChatMessageRow: View {
return Color(red: 90/255, green: 200/255, blue: 250/255)
case .webSearch:
return Color(red: 0/255, green: 199/255, blue: 190/255)
case .askUserQuestion:
return Color(red: 147/255, green: 51/255, blue: 234/255)
}
}

Expand Down
Loading