feat(tui-go): interactive clarification question view

Render stage clarification questions as an AskUserQuestion-style form:
header chip, prompt, selectable option chips, and a free-text custom slot.
Arrow/hjkl nav, space to select, e for custom, enter to submit. Submit
guards on all-answered and sends ClarificationResponse; the parked stage
re-runs with the answers in context.
This commit is contained in:
2026-06-14 13:00:59 +04:00
parent 6242b590e4
commit 218a98034e
8 changed files with 584 additions and 3 deletions
+24
View File
@@ -132,11 +132,13 @@ func (m *Model) applyServer(msg protocol.ServerMessage) {
m.touch(msg.SessionID, "COMPLETED")
if s := m.session(msg.SessionID); s != nil {
s.Active = false
s.Clar = nil
}
case protocol.TypeSessionFailed:
m.touch(msg.SessionID, "FAILED")
if s := m.session(msg.SessionID); s != nil {
s.Active = false
s.Clar = nil
}
case protocol.TypeStageStarted:
if s := m.session(msg.SessionID); s != nil {
@@ -242,6 +244,8 @@ func (m *Model) applyServer(msg protocol.ServerMessage) {
}
case protocol.TypeApprovalRequired:
m.onApprovalRequired(msg)
case protocol.TypeClarification:
m.onClarificationRequired(msg)
case protocol.TypeApprovalResolved:
if s := m.session(msg.SessionID); s != nil {
s.Pending = nil
@@ -400,6 +404,26 @@ func (m *Model) onApprovalRequired(msg protocol.ServerMessage) {
}
}
func (m *Model) onClarificationRequired(msg protocol.ServerMessage) {
qs := make([]ClarQuestion, 0, len(msg.Questions))
for _, q := range msg.Questions {
qs = append(qs, ClarQuestion{
ID: q.ID, Prompt: q.Prompt, Header: q.Header,
Options: q.Options, MultiSelect: q.MultiSelect,
})
}
c := &Clarification{
RequestID: msg.RequestID, SessionID: msg.SessionID,
StageID: msg.StageID, Questions: qs,
}
if s := m.session(msg.SessionID); s != nil {
s.Clar = c
}
if msg.SessionID == m.selectedID {
m.clarInitState(len(qs))
}
}
func (m *Model) onSnapshot(msg protocol.ServerMessage) {
var pending *Approval
if len(msg.PendingAppr) > 0 {