fix: AI discussions going back instead of preparing
This commit is contained in:
23
ai_tui.go
23
ai_tui.go
@@ -27,6 +27,7 @@ const (
|
|||||||
|
|
||||||
type aiPreparedMsg struct {
|
type aiPreparedMsg struct {
|
||||||
preview AIPreview
|
preview AIPreview
|
||||||
|
threadID string
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,23 +82,24 @@ func (m *App) startAIDiscussion(threadID string) {
|
|||||||
func (m *App) beginAIPrepare(threadID, message string) tea.Cmd {
|
func (m *App) beginAIPrepare(threadID, message string) tea.Cmd {
|
||||||
if m.ai == nil || !m.ai.config.Enabled {
|
if m.ai == nil || !m.ai.config.Enabled {
|
||||||
m.err = errors.New("AI integration is disabled; set ai.enabled = true")
|
m.err = errors.New("AI integration is disabled; set ai.enabled = true")
|
||||||
m.aiMode = aiMenu
|
m.returnFromAIPrepareFailure(threadID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if m.loading {
|
if m.loading && threadID == "" {
|
||||||
m.err = errors.New("AI preparation is unavailable while PR data is refreshing")
|
m.err = errors.New("AI preparation is unavailable while PR data is refreshing")
|
||||||
m.aiMode = aiMenu
|
m.returnFromAIPrepareFailure(threadID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if m.details.FromCache {
|
if m.details.FromCache {
|
||||||
m.err = errors.New("AI preparation requires current live PR data, not a cached snapshot")
|
m.err = errors.New("AI preparation requires current live PR data, not a cached snapshot")
|
||||||
m.aiMode = aiMenu
|
m.returnFromAIPrepareFailure(threadID)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
m.aiCancel = cancel
|
m.aiCancel = cancel
|
||||||
controller, details := m.ai, m.details
|
controller, details := m.ai, m.details
|
||||||
m.aiMode = aiPreparing
|
m.aiMode = aiPreparing
|
||||||
|
m.err = nil
|
||||||
m.aiSpinner = 0
|
m.aiSpinner = 0
|
||||||
started := time.Now()
|
started := time.Now()
|
||||||
summary := "Checking the provider and loading the authenticated GitHub diff"
|
summary := "Checking the provider and loading the authenticated GitHub diff"
|
||||||
@@ -110,11 +112,19 @@ func (m *App) beginAIPrepare(threadID, message string) tea.Cmd {
|
|||||||
}
|
}
|
||||||
prepare := func() tea.Msg {
|
prepare := func() tea.Msg {
|
||||||
preview, err := controller.Prepare(ctx, details, threadID, message)
|
preview, err := controller.Prepare(ctx, details, threadID, message)
|
||||||
return aiPreparedMsg{preview: preview, err: err}
|
return aiPreparedMsg{preview: preview, threadID: threadID, err: err}
|
||||||
}
|
}
|
||||||
return tea.Batch(prepare, nextAIAnimationTick())
|
return tea.Batch(prepare, nextAIAnimationTick())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *App) returnFromAIPrepareFailure(threadID string) {
|
||||||
|
if threadID != "" {
|
||||||
|
m.aiMode = aiDiscussion
|
||||||
|
return
|
||||||
|
}
|
||||||
|
m.aiMode = aiMenu
|
||||||
|
}
|
||||||
|
|
||||||
func (m *App) beginAIRun() tea.Cmd {
|
func (m *App) beginAIRun() tea.Cmd {
|
||||||
if m.details.HeadOID != m.aiPreview.HeadOID {
|
if m.details.HeadOID != m.aiPreview.HeadOID {
|
||||||
m.err = errors.New("PR head changed after preparation; prepare the AI review again")
|
m.err = errors.New("PR head changed after preparation; prepare the AI review again")
|
||||||
@@ -210,7 +220,8 @@ func (m App) updateAI(msg tea.Msg) (tea.Model, tea.Cmd, bool) {
|
|||||||
}
|
}
|
||||||
m.aiCancel = nil
|
m.aiCancel = nil
|
||||||
if msg.err != nil {
|
if msg.err != nil {
|
||||||
m.err, m.aiMode = msg.err, aiMenu
|
m.err = msg.err
|
||||||
|
m.returnFromAIPrepareFailure(msg.threadID)
|
||||||
} else {
|
} else {
|
||||||
m.aiPreview, m.aiMode, m.aiPreviewScroll, m.err = msg.preview, aiConfirm, 0, nil
|
m.aiPreview, m.aiMode, m.aiPreviewScroll, m.err = msg.preview, aiConfirm, 0, nil
|
||||||
m.aiStatus = AIProviderStatus{
|
m.aiStatus = AIProviderStatus{
|
||||||
|
|||||||
55
tui_test.go
55
tui_test.go
@@ -856,6 +856,61 @@ func TestVimModeAppliesToEveryTextInput(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestVimAIDiscussionSubmitDuringRefreshPreservesDraft(t *testing.T) {
|
||||||
|
config := defaultAIConfig()
|
||||||
|
config.Enabled = true
|
||||||
|
settings := defaultAppSettings()
|
||||||
|
settings.AI = &AIController{config: config}
|
||||||
|
m := NewAppWithSettings(nil, "o", "r", false, 50, time.Second, settings)
|
||||||
|
m.screen, m.loading, m.width, m.height = threadScreen, true, 80, 24
|
||||||
|
m.details = PRDetails{
|
||||||
|
HeadOID: "head",
|
||||||
|
Threads: []ReviewThread{{ID: "thread", Path: "main.go"}},
|
||||||
|
}
|
||||||
|
m.aiMode, m.writeThreadID, m.aiInput = aiDiscussion, "thread", "Keep this question"
|
||||||
|
m.resetInputEditor(&m.aiInputEditor, m.aiInput)
|
||||||
|
|
||||||
|
updated, command, handled := m.updateAI(tea.KeyMsg{Type: tea.KeyCtrlS})
|
||||||
|
m = updated.(App)
|
||||||
|
if !handled || command == nil || m.aiMode != aiPreparing {
|
||||||
|
t.Fatalf("submit handled=%v command=%v mode=%v", handled, command, m.aiMode)
|
||||||
|
}
|
||||||
|
if m.aiInput != "Keep this question" || m.aiInputEditor.Text != m.aiInput {
|
||||||
|
t.Fatalf("AI discussion draft changed during submit: input=%q editor=%q",
|
||||||
|
m.aiInput, m.aiInputEditor.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAIDiscussionPreparationFailurePreservesDraft(t *testing.T) {
|
||||||
|
m := NewApp(nil, "o", "r", false, 50, time.Second)
|
||||||
|
m.aiMode, m.writeThreadID, m.aiInput = aiPreparing, "thread", "Keep this question"
|
||||||
|
m.resetInputEditor(&m.aiInputEditor, m.aiInput)
|
||||||
|
|
||||||
|
updated, command, handled := m.updateAI(aiPreparedMsg{
|
||||||
|
threadID: "thread", err: errors.New("prepare failed"),
|
||||||
|
})
|
||||||
|
m = updated.(App)
|
||||||
|
if !handled || command != nil || m.aiMode != aiDiscussion || m.err == nil {
|
||||||
|
t.Fatalf("failure handled=%v command=%v mode=%v err=%v",
|
||||||
|
handled, command, m.aiMode, m.err)
|
||||||
|
}
|
||||||
|
if m.aiInput != "Keep this question" || m.aiInputEditor.Text != m.aiInput {
|
||||||
|
t.Fatalf("AI discussion draft lost after failure: input=%q editor=%q",
|
||||||
|
m.aiInput, m.aiInputEditor.Text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPullRequestAIPreparationFailureIgnoresOldDiscussionThread(t *testing.T) {
|
||||||
|
m := NewApp(nil, "o", "r", false, 50, time.Second)
|
||||||
|
m.aiMode, m.writeThreadID = aiPreparing, "old-thread"
|
||||||
|
|
||||||
|
updated, _, _ := m.updateAI(aiPreparedMsg{err: errors.New("prepare failed")})
|
||||||
|
m = updated.(App)
|
||||||
|
if m.aiMode != aiMenu {
|
||||||
|
t.Fatalf("pull-request preparation failure returned to mode %v, want AI menu", m.aiMode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestVimModeUsesGlobalFooterBar(t *testing.T) {
|
func TestVimModeUsesGlobalFooterBar(t *testing.T) {
|
||||||
m := NewApp(nil, "o", "r", false, 50, time.Second)
|
m := NewApp(nil, "o", "r", false, 50, time.Second)
|
||||||
m.width, m.height = 80, 10
|
m.width, m.height = 80, 10
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
const dipleVersion = "0.3.2"
|
const dipleVersion = "0.3.3"
|
||||||
|
|||||||
Reference in New Issue
Block a user