Merge nucleic/olive-coral-civet-pvqv into dev
This commit is contained in:
@@ -170,13 +170,22 @@ struct ApprovalCardView: View {
|
||||
struct ExitPlanModeApprovalCardView: View {
|
||||
@EnvironmentObject var store: RemoteStore
|
||||
let approval: ApprovalRequest
|
||||
let plan: ExitPlanMode.Plan?
|
||||
let plan: PlanReview.Plan?
|
||||
var availableHeight: CGFloat = 0
|
||||
|
||||
@State private var revising = false
|
||||
@State private var feedback = ""
|
||||
|
||||
private var canRespond: Bool { store.connectivity.isLive }
|
||||
private var isLegacyExitPlanMode: Bool { approval.toolName == ExitPlanMode.toolName }
|
||||
private var rejectionReason: String {
|
||||
isLegacyExitPlanMode ? ExitPlanMode.rejectionReason : PlanReview.rejectionReason
|
||||
}
|
||||
private func revisionReason(_ feedback: String) -> String? {
|
||||
isLegacyExitPlanMode
|
||||
? ExitPlanMode.revisionReason(feedback: feedback)
|
||||
: PlanReview.revisionReason(feedback: feedback)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
@@ -193,13 +202,13 @@ struct ExitPlanModeApprovalCardView: View {
|
||||
// Claude versions that don't include the plan Markdown in the permission payload
|
||||
// still need the three-way review. State that plainly instead of degrading to the
|
||||
// generic permission card (which would incorrectly offer Allow Always).
|
||||
Text("Claude is ready to leave plan mode and begin implementation.")
|
||||
Text("The agent is ready to begin implementation and is waiting for plan review.")
|
||||
.font(.footnote).foregroundStyle(.secondary)
|
||||
}
|
||||
|
||||
if revising {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text("What should Claude change?")
|
||||
Text("What should the agent change?")
|
||||
.font(.footnote.weight(.semibold))
|
||||
TextField(
|
||||
"Describe the changes you want in the plan…",
|
||||
@@ -219,13 +228,13 @@ struct ExitPlanModeApprovalCardView: View {
|
||||
Button("Send Revision") { submitRevision() }
|
||||
.frame(maxWidth: .infinity)
|
||||
.buttonStyle(.borderedProminent)
|
||||
.disabled(!canRespond || ExitPlanMode.revisionReason(feedback: feedback) == nil)
|
||||
.disabled(!canRespond || revisionReason(feedback) == nil)
|
||||
.keyboardShortcut(.defaultAction)
|
||||
}
|
||||
} else {
|
||||
HStack(spacing: 8) {
|
||||
Button(role: .destructive) {
|
||||
store.respond(approval, .deny(reason: ExitPlanMode.rejectionReason))
|
||||
store.respond(approval, .deny(reason: rejectionReason))
|
||||
} label: {
|
||||
Text("Deny").frame(maxWidth: .infinity)
|
||||
}
|
||||
@@ -263,7 +272,7 @@ struct ExitPlanModeApprovalCardView: View {
|
||||
}
|
||||
|
||||
private func submitRevision() {
|
||||
guard let reason = ExitPlanMode.revisionReason(feedback: feedback) else { return }
|
||||
guard let reason = revisionReason(feedback) else { return }
|
||||
store.respond(approval, .deny(reason: reason))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -638,10 +638,10 @@ struct SessionDetailView: View {
|
||||
{
|
||||
AskUserQuestionCardView(
|
||||
approval: approval, questions: questions, availableHeight: availableHeight)
|
||||
} else if approval.toolName == ExitPlanMode.toolName {
|
||||
} else if PlanReview.isPlanTool(approval.toolName) {
|
||||
ExitPlanModeApprovalCardView(
|
||||
approval: approval,
|
||||
plan: ExitPlanMode.plan(from: approval.input),
|
||||
plan: PlanReview.plan(from: approval.input),
|
||||
availableHeight: availableHeight)
|
||||
} else {
|
||||
ApprovalCardView(approval: approval, availableHeight: availableHeight)
|
||||
@@ -860,7 +860,7 @@ struct SessionDetailView: View {
|
||||
if let approval = store.openApprovals.first {
|
||||
switch approval.toolName {
|
||||
case AskUserQuestion.toolName: return "Waiting for answers…"
|
||||
case ExitPlanMode.toolName: return "Waiting for plan review…"
|
||||
case ExitPlanMode.toolName, PlanReview.toolName: return "Waiting for plan review…"
|
||||
default: return "Waiting for approval…"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -582,7 +582,8 @@ enum ToolGlyph {
|
||||
case "Task", "Agent", ToolGroup.orchestraSubagentToolName: return "person.2"
|
||||
case "TodoWrite": return "checklist"
|
||||
case "AskUserQuestion": return "questionmark.bubble"
|
||||
case ExitPlanMode.toolName: return "list.bullet.clipboard"
|
||||
case ExitPlanMode.toolName, PlanReview.toolName, PlanReview.qualifiedToolName:
|
||||
return "list.bullet.clipboard"
|
||||
case HostCommandSummary.hostExecToolName: return "desktopcomputer"
|
||||
// The VM/container tools each get their own glyph — a screen for computer-use, a box for
|
||||
// a container — so they don't all collapse into the generic wrench.
|
||||
|
||||
@@ -121,7 +121,7 @@ struct ToolGroup: Equatable {
|
||||
name == "Task" || name == "Agent" || name == Self.orchestraSubagentToolName
|
||||
}
|
||||
var isAskUserQuestion: Bool { name == "AskUserQuestion" }
|
||||
var isExitPlanMode: Bool { name == ExitPlanMode.toolName }
|
||||
var isExitPlanMode: Bool { PlanReview.isPlanTool(name) }
|
||||
|
||||
/// The literal shell command a `Bash` call ran, if any (nil for every other tool). Used to
|
||||
/// detect a git-commit pipeline that should read as a structured commit card at the row level,
|
||||
|
||||
@@ -92,7 +92,7 @@ struct TranscriptRow: View {
|
||||
/// Nil for any other tool (which keeps the normal collapsible card) and for a call with no plan
|
||||
/// text. The plan is on the call input, so it shows whether or not the user has approved it yet.
|
||||
private func planCard(_ group: ToolGroup) -> ExitPlanModeCard? {
|
||||
guard group.isExitPlanMode, let plan = ExitPlanMode.plan(from: group.input) else { return nil }
|
||||
guard group.isExitPlanMode, let plan = PlanReview.plan(from: group.input) else { return nil }
|
||||
return ExitPlanModeCard(plan: plan)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user