magi-code 0.96.1

Repository-aware CLI coding agent for terminal work
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
mod theme_picker;
use theme_picker::classify_theme_picker_key;

use super::{
    PickerViewport, SessionPickerViewport, SystemPromptViewport, printable_char_for_text_input,
};
use crate::tui::state::{ConnectProviderStage, MissionControlState, ModalKind};
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::layout::Rect;

#[derive(Debug, Clone, Copy)]
pub(super) struct ModalKeyViewports {
    pub(super) system_prompt: SystemPromptViewport,
    pub(super) summary: Rect,
    pub(super) rewind_modal: SystemPromptViewport,
    pub(super) usage_modal: SystemPromptViewport,
    pub(super) connect_provider: Option<PickerViewport>,
    pub(super) connect_provider_modal_visible: bool,
    pub(super) connect_provider_custom_field_visible: bool,
    pub(super) connect_provider_oauth_controls_visible: bool,
    pub(super) connect_provider_oauth_fallback_visible: bool,
    pub(super) logout_picker: Option<PickerViewport>,
    pub(super) logout_confirmation_action_row: Option<Rect>,
    pub(super) model_picker: Option<PickerViewport>,
    pub(super) sessions_modal: Option<SessionPickerViewport>,
    pub(super) subagent_viewer: SystemPromptViewport,
    pub(super) skills_modal: Option<PickerViewport>,
    pub(super) skills_scope_visible: bool,
    pub(super) mcp_modal: Option<PickerViewport>,
    pub(super) tools_modal: Option<PickerViewport>,
    pub(super) tools_scope_visible: bool,
    pub(super) subagents_modal: Option<PickerViewport>,
    pub(super) subagents_scope_visible: bool,
    pub(super) theme_picker: Option<PickerViewport>,
    pub(super) theme_query_visible: bool,
}

pub(super) fn classify_modal_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewports: ModalKeyViewports,
) -> Option<super::InputCommand> {
    if state.header_modal_visible() && state.modals.focused_header_modal.is_none() {
        return None;
    }
    match state.top_modal()? {
        ModalKind::Summary | ModalKind::SessionFiles => Some(match (key.code, key.modifiers) {
            (KeyCode::Enter, KeyModifiers::NONE)
                if state.top_modal() == Some(ModalKind::Summary) =>
            {
                super::InputCommand::display(super::DisplayCommand::ToggleSummary)
            }
            (
                KeyCode::Up | KeyCode::Down | KeyCode::PageUp | KeyCode::PageDown,
                KeyModifiers::NONE,
            ) => super::InputCommand::display(super::DisplayCommand::ScrollSummary {
                files: state.top_modal() == Some(ModalKind::SessionFiles),
                down: matches!(key.code, KeyCode::Down | KeyCode::PageDown),
                rows: if matches!(key.code, KeyCode::PageUp | KeyCode::PageDown) {
                    viewports.summary.height.max(1)
                } else {
                    1
                },
                width: viewports.summary.width,
                height: viewports.summary.height,
            }),
            _ => super::InputCommand::None,
        }),
        ModalKind::ActivityTree | ModalKind::ActivityDetail => Some(super::InputCommand::None),
        ModalKind::Settings => Some(super::InputCommand::action(
            super::InputAction::SettingsEditorKey(key),
        )),
        ModalKind::SubagentViewer => {
            Some(classify_subagent_viewer_key(key, viewports.subagent_viewer))
        }
        ModalKind::SessionPicker => Some(classify_session_picker_key(
            key,
            state,
            viewports.sessions_modal,
        )),
        ModalKind::Skills => Some(classify_settings_modal_key(
            key,
            state,
            super::SettingsModalKind::Skills,
            viewports.skills_modal,
            viewports.skills_scope_visible,
        )),
        ModalKind::Mcp => Some(classify_settings_modal_key(
            key,
            state,
            super::SettingsModalKind::Mcp,
            viewports.mcp_modal,
            false,
        )),
        ModalKind::Tools => Some(classify_settings_modal_key(
            key,
            state,
            super::SettingsModalKind::Tools,
            viewports.tools_modal,
            viewports.tools_scope_visible,
        )),
        ModalKind::Subagents => Some(classify_settings_modal_key(
            key,
            state,
            super::SettingsModalKind::Subagents,
            viewports.subagents_modal,
            viewports.subagents_scope_visible,
        )),
        ModalKind::Usage => Some(classify_system_scroll_modal_key(
            key,
            super::ScrollTarget::Usage,
            viewports.usage_modal,
        )),
        ModalKind::SystemPrompt => Some(classify_system_scroll_modal_key(
            key,
            super::ScrollTarget::SystemPrompt,
            viewports.system_prompt,
        )),
        ModalKind::Rewind => Some(classify_rewind_modal_key(
            key,
            state,
            viewports.rewind_modal,
        )),
        ModalKind::ConnectProvider => Some(handle_connect_provider_key(
            key,
            state,
            viewports.connect_provider,
            viewports.connect_provider_modal_visible,
            viewports.connect_provider_custom_field_visible,
            viewports.connect_provider_oauth_controls_visible,
            viewports.connect_provider_oauth_fallback_visible,
        )),
        ModalKind::BashApproval => Some(match (key.code, key.modifiers) {
            (KeyCode::Enter | KeyCode::Char('a'), KeyModifiers::NONE) => {
                super::InputCommand::action(super::InputAction::ResolveBashApproval(true))
            }
            (KeyCode::Esc | KeyCode::Char('d'), KeyModifiers::NONE) => {
                super::InputCommand::action(super::InputAction::ResolveBashApproval(false))
            }
            _ => super::InputCommand::None,
        }),
        ModalKind::LogoutConfirmation => Some(classify_logout_confirmation_key(
            key,
            state,
            viewports.logout_confirmation_action_row,
        )),
        ModalKind::LogoutPicker => Some(classify_logout_picker_key(
            key,
            state,
            viewports.logout_picker,
        )),
        ModalKind::ModelPicker => Some(classify_model_picker_key(
            key,
            state,
            viewports.model_picker,
        )),
        ModalKind::ThemePicker => Some(classify_theme_picker_key(
            key,
            state,
            viewports.theme_picker,
            viewports.theme_query_visible,
        )),
    }
}

fn handle_connect_provider_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewport: Option<PickerViewport>,
    modal_visible: bool,
    custom_field_visible: bool,
    oauth_controls_visible: bool,
    oauth_fallback_visible: bool,
) -> super::InputCommand {
    let Some(stage) = state
        .modals
        .connect_provider
        .as_ref()
        .map(|connect| connect.stage)
    else {
        return super::InputCommand::None;
    };
    let oauth_focus = state
        .modals
        .connect_provider
        .as_ref()
        .map(|connect| connect.oauth_focus)
        .unwrap_or_default();
    let oauth_url_ready = state
        .modals
        .connect_provider
        .as_ref()
        .is_some_and(|connect| !connect.url.is_empty());
    let oauth_controls_visible = oauth_controls_visible && oauth_url_ready;
    let oauth_fallback_focused =
        oauth_fallback_visible && oauth_focus == crate::tui::state::OAuthControl::Fallback;
    let action = match stage {
        ConnectProviderStage::ChooseProvider => match (key.code, key.modifiers) {
            (KeyCode::Up, KeyModifiers::NONE) => {
                viewport.map_or(super::InputAction::None, |viewport| {
                    super::InputAction::MoveConnectProviderUp(viewport.visible_rows as usize)
                })
            }
            (KeyCode::Down, KeyModifiers::NONE) => {
                viewport.map_or(super::InputAction::None, |viewport| {
                    super::InputAction::MoveConnectProviderDown(viewport.visible_rows as usize)
                })
            }
            (KeyCode::Enter, KeyModifiers::NONE) if viewport.is_some() => {
                state.selected_connect_provider_id().map_or(
                    super::InputAction::None,
                    super::InputAction::SelectConnectProvider,
                )
            }
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CancelConnectProvider,
            _ => super::InputAction::None,
        },
        ConnectProviderStage::ConfigureCustom => match (key.code, key.modifiers) {
            (KeyCode::Tab, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::FocusNextConnectProviderField
            }
            (KeyCode::BackTab, modifiers)
                if custom_field_visible
                    && (modifiers.is_empty() || modifiers == KeyModifiers::SHIFT) =>
            {
                super::InputAction::FocusPreviousConnectProviderField
            }
            (KeyCode::Enter, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::SubmitConnectProviderStep
            }
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CancelConnectProvider,
            (KeyCode::Backspace, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::BackspaceConnectProviderField
            }
            (KeyCode::Delete, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::DeleteConnectProviderField
            }
            (KeyCode::Left, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::MoveConnectProviderFieldLeft
            }
            (KeyCode::Right, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::MoveConnectProviderFieldRight
            }
            (KeyCode::Home, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::MoveConnectProviderFieldHome
            }
            (KeyCode::End, KeyModifiers::NONE) if custom_field_visible => {
                super::InputAction::MoveConnectProviderFieldEnd
            }
            (KeyCode::Char(_), _) if custom_field_visible => printable_char_for_text_input(key)
                .map_or(
                    super::InputAction::None,
                    super::InputAction::InsertConnectProviderChar,
                ),
            _ => super::InputAction::None,
        },
        ConnectProviderStage::AnthropicGuidance => match (key.code, key.modifiers) {
            (KeyCode::Enter, KeyModifiers::NONE) if modal_visible => {
                super::InputAction::BackConnectProvider
            }
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CancelConnectProvider,
            _ => super::InputAction::None,
        },
        ConnectProviderStage::OAuth => match (key.code, key.modifiers) {
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CancelConnectProvider,
            (KeyCode::PageDown, KeyModifiers::NONE) if oauth_controls_visible => {
                super::InputAction::ScrollOAuthUrl(1)
            }
            (KeyCode::PageUp, KeyModifiers::NONE) if oauth_controls_visible => {
                super::InputAction::ScrollOAuthUrl(-1)
            }
            (KeyCode::Tab, KeyModifiers::NONE) if oauth_fallback_visible => {
                super::InputAction::FocusNextConnectProviderField
            }
            (KeyCode::BackTab, modifiers)
                if oauth_fallback_visible
                    && (modifiers.is_empty() || modifiers == KeyModifiers::SHIFT) =>
            {
                super::InputAction::FocusPreviousConnectProviderField
            }
            (KeyCode::Enter, KeyModifiers::NONE)
                if oauth_controls_visible
                    && oauth_focus == crate::tui::state::OAuthControl::OpenBrowser =>
            {
                super::InputAction::OpenOAuthUrl
            }
            (KeyCode::Enter, KeyModifiers::NONE)
                if oauth_controls_visible
                    && oauth_focus == crate::tui::state::OAuthControl::CopyUrl =>
            {
                super::InputAction::CopyOAuthUrl
            }
            (KeyCode::Char('y'), KeyModifiers::CONTROL) if oauth_controls_visible => {
                super::InputAction::CopyOAuthUrl
            }
            (KeyCode::Char('o'), KeyModifiers::ALT) if oauth_controls_visible => {
                super::InputAction::OpenOAuthUrl
            }
            (KeyCode::Enter, KeyModifiers::NONE) if oauth_fallback_focused => state
                .modals
                .connect_provider
                .as_ref()
                .filter(|connect| !connect.fallback_input.is_empty())
                .map_or(super::InputAction::None, |_| {
                    super::InputAction::SubmitOAuthFallback
                }),
            (KeyCode::Backspace, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::BackspaceConnectProviderField
            }
            (KeyCode::Delete, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::DeleteConnectProviderField
            }
            (KeyCode::Left, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::MoveConnectProviderFieldLeft
            }
            (KeyCode::Right, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::MoveConnectProviderFieldRight
            }
            (KeyCode::Home, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::MoveConnectProviderFieldHome
            }
            (KeyCode::End, KeyModifiers::NONE) if oauth_fallback_focused => {
                super::InputAction::MoveConnectProviderFieldEnd
            }
            (KeyCode::Char(_), _) if oauth_fallback_focused => printable_char_for_text_input(key)
                .map_or(
                    super::InputAction::None,
                    super::InputAction::InsertConnectProviderChar,
                ),
            _ => super::InputAction::None,
        },
        ConnectProviderStage::ReplacementConfirmation => match (key.code, key.modifiers) {
            (KeyCode::Enter, KeyModifiers::NONE) if modal_visible => {
                super::InputAction::ConfirmConnectProviderReplacement
            }
            (KeyCode::Esc, KeyModifiers::NONE) => {
                super::InputAction::CancelConnectProviderReplacement
            }
            _ => super::InputAction::None,
        },
        ConnectProviderStage::Saving => super::InputAction::None,
        ConnectProviderStage::Success => match (key.code, key.modifiers) {
            (KeyCode::Enter, KeyModifiers::NONE) if modal_visible => {
                super::InputAction::CloseConnectProvider
            }
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CloseConnectProvider,
            _ => super::InputAction::None,
        },
        ConnectProviderStage::Error => match (key.code, key.modifiers) {
            (KeyCode::Enter, KeyModifiers::NONE) if modal_visible => {
                super::InputAction::RetryConnectProvider
            }
            (KeyCode::Backspace | KeyCode::Left, KeyModifiers::NONE) if modal_visible => {
                super::InputAction::BackConnectProvider
            }
            (KeyCode::Esc, KeyModifiers::NONE) => super::InputAction::CancelConnectProvider,
            _ => super::InputAction::None,
        },
    };
    super::InputCommand::action(action)
}

fn classify_logout_picker_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewport: Option<PickerViewport>,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    match (key.code, key.modifiers) {
        (KeyCode::Up, KeyModifiers::NONE) => {
            viewport.map_or(super::InputCommand::None, |viewport| {
                display(super::ModalCommand::LogoutPickerMove {
                    down: false,
                    visible_rows: viewport.visible_rows as usize,
                })
            })
        }
        (KeyCode::Down, KeyModifiers::NONE) => {
            viewport.map_or(super::InputCommand::None, |viewport| {
                display(super::ModalCommand::LogoutPickerMove {
                    down: true,
                    visible_rows: viewport.visible_rows as usize,
                })
            })
        }
        (KeyCode::Enter, KeyModifiers::NONE) if viewport.is_some() => state
            .selected_logout_provider_id()
            .map_or(super::InputCommand::None, |provider_id| {
                super::InputCommand::action_with_display(
                    super::InputAction::SelectLogoutProvider(provider_id),
                    super::DisplayCommand::Modal(super::ModalCommand::CloseLogoutPicker),
                )
            }),
        (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseLogoutPicker),
        _ => super::InputCommand::None,
    }
}

fn classify_subagent_viewer_key(
    key: KeyEvent,
    viewport: SystemPromptViewport,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    let viewport = viewport.safe();
    let scroll = |down, rows| {
        super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
            target: super::ScrollTarget::SubagentViewer,
            down,
            rows,
            visible_rows: viewport.visible_rows,
            wrap_width: viewport.wrap_width,
        }))
    };
    match (key.code, key.modifiers) {
        (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseSubagentViewer),
        (KeyCode::Tab, KeyModifiers::NONE) | (KeyCode::Right, KeyModifiers::NONE) => {
            display(super::ModalCommand::SubagentViewerSelectNext)
        }
        (KeyCode::BackTab, modifiers)
            if modifiers.is_empty() || modifiers == KeyModifiers::SHIFT =>
        {
            display(super::ModalCommand::SubagentViewerSelectPrevious)
        }
        (KeyCode::Left, KeyModifiers::NONE) => {
            display(super::ModalCommand::SubagentViewerSelectPrevious)
        }
        (KeyCode::Up, KeyModifiers::NONE) => scroll(false, 1),
        (KeyCode::Down, KeyModifiers::NONE) => scroll(true, 1),
        (KeyCode::PageUp, KeyModifiers::NONE) => scroll(false, viewport.visible_rows.max(1)),
        (KeyCode::PageDown, KeyModifiers::NONE) => scroll(true, viewport.visible_rows.max(1)),
        (KeyCode::Home, KeyModifiers::NONE) => {
            display(super::ModalCommand::SubagentViewerScrollHome)
        }
        (KeyCode::End, KeyModifiers::NONE) => {
            display(super::ModalCommand::SubagentViewerScrollEnd { viewport })
        }
        _ => super::InputCommand::None,
    }
}

fn classify_session_picker_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewport: Option<SessionPickerViewport>,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    let Some(viewport) = viewport else {
        return match (key.code, key.modifiers) {
            (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseSessionPicker),
            _ => super::InputCommand::None,
        };
    };
    let focus = state
        .modals
        .session_picker
        .as_ref()
        .map(|picker| picker.focus)
        .unwrap_or(crate::tui::state::SessionPickerFocus::List);
    let focus = if viewport.preview_visible {
        focus
    } else {
        crate::tui::state::SessionPickerFocus::List
    };

    match (key.code, key.modifiers) {
        (KeyCode::Tab, KeyModifiers::NONE) if viewport.preview_visible => {
            display(super::ModalCommand::SessionPickerFocusNext)
        }
        (KeyCode::BackTab, modifiers)
            if viewport.preview_visible
                && (modifiers.is_empty() || modifiers == KeyModifiers::SHIFT) =>
        {
            display(super::ModalCommand::SessionPickerFocusPrevious)
        }
        (KeyCode::Up, KeyModifiers::NONE) => match focus {
            crate::tui::state::SessionPickerFocus::List => {
                display(super::ModalCommand::SessionPickerMove {
                    focus,
                    down: false,
                    amount: 1,
                    visible_rows: viewport.list_rows as usize,
                    preview_rows: viewport.preview_rows,
                    preview_width: viewport.preview_width,
                })
            }
            crate::tui::state::SessionPickerFocus::Preview => {
                super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
                    target: super::ScrollTarget::SessionPreview,
                    down: false,
                    rows: 1,
                    visible_rows: viewport.preview_rows,
                    wrap_width: viewport.preview_width,
                }))
            }
        },
        (KeyCode::Down, KeyModifiers::NONE) => match focus {
            crate::tui::state::SessionPickerFocus::List => {
                display(super::ModalCommand::SessionPickerMove {
                    focus,
                    down: true,
                    amount: 1,
                    visible_rows: viewport.list_rows as usize,
                    preview_rows: viewport.preview_rows,
                    preview_width: viewport.preview_width,
                })
            }
            crate::tui::state::SessionPickerFocus::Preview => {
                super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
                    target: super::ScrollTarget::SessionPreview,
                    down: true,
                    rows: 1,
                    visible_rows: viewport.preview_rows,
                    wrap_width: viewport.preview_width,
                }))
            }
        },
        (KeyCode::PageUp, KeyModifiers::NONE) => match focus {
            crate::tui::state::SessionPickerFocus::List => {
                display(super::ModalCommand::SessionPickerMove {
                    focus,
                    down: false,
                    amount: viewport.list_rows.max(1) as usize,
                    visible_rows: viewport.list_rows as usize,
                    preview_rows: viewport.preview_rows,
                    preview_width: viewport.preview_width,
                })
            }
            crate::tui::state::SessionPickerFocus::Preview => {
                super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
                    target: super::ScrollTarget::SessionPreview,
                    down: false,
                    rows: viewport.preview_rows,
                    visible_rows: viewport.preview_rows,
                    wrap_width: viewport.preview_width,
                }))
            }
        },
        (KeyCode::PageDown, KeyModifiers::NONE) => match focus {
            crate::tui::state::SessionPickerFocus::List => {
                display(super::ModalCommand::SessionPickerMove {
                    focus,
                    down: true,
                    amount: viewport.list_rows.max(1) as usize,
                    visible_rows: viewport.list_rows as usize,
                    preview_rows: viewport.preview_rows,
                    preview_width: viewport.preview_width,
                })
            }
            crate::tui::state::SessionPickerFocus::Preview => {
                super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
                    target: super::ScrollTarget::SessionPreview,
                    down: true,
                    rows: viewport.preview_rows,
                    visible_rows: viewport.preview_rows,
                    wrap_width: viewport.preview_width,
                }))
            }
        },
        (KeyCode::Enter, KeyModifiers::NONE) => {
            state
                .selected_session_id()
                .map_or(super::InputCommand::None, |session_id| {
                    super::InputCommand::action_with_display(
                        super::InputAction::SelectSession(session_id),
                        super::DisplayCommand::Modal(super::ModalCommand::CloseSessionPicker),
                    )
                })
        }
        (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseSessionPicker),
        _ => super::InputCommand::None,
    }
}

fn classify_settings_modal_key(
    key: KeyEvent,
    state: &MissionControlState,
    kind: super::SettingsModalKind,
    viewport: Option<PickerViewport>,
    scope_visible: bool,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    match (key.code, key.modifiers) {
        (KeyCode::Tab, KeyModifiers::NONE) if scope_visible => {
            super::InputCommand::action(match kind {
                super::SettingsModalKind::Skills => super::InputAction::ToggleSkillsSettingsScope,
                super::SettingsModalKind::Mcp => super::InputAction::None,
                super::SettingsModalKind::Tools => super::InputAction::ToggleToolsSettingsScope,
                super::SettingsModalKind::Subagents => {
                    super::InputAction::ToggleSubagentsSettingsScope
                }
            })
        }
        (KeyCode::Up, KeyModifiers::NONE) => viewport
            .map_or(super::InputCommand::None, |viewport| {
                display(settings_move_command(kind, false, viewport))
            }),
        (KeyCode::Down, KeyModifiers::NONE) => viewport
            .map_or(super::InputCommand::None, |viewport| {
                display(settings_move_command(kind, true, viewport))
            }),
        (KeyCode::Enter, KeyModifiers::NONE) => {
            if viewport.is_none() {
                return super::InputCommand::None;
            }
            let toggle = match kind {
                super::SettingsModalKind::Skills => state
                    .modals
                    .skills_modal
                    .as_ref()
                    .and_then(|modal| modal.rows.get(modal.selected))
                    .map(|row| (row.name.clone(), !row.enabled))
                    .map(|(key, enabled)| {
                        (
                            super::InputAction::SetSkillEnabled {
                                name: key.clone(),
                                enabled,
                            },
                            super::ModalCommand::SetSettingRow { kind, key, enabled },
                        )
                    }),
                super::SettingsModalKind::Mcp => state
                    .modals
                    .mcp_modal
                    .as_ref()
                    .and_then(|modal| modal.rows.get(modal.selected))
                    .map(|row| (row.name.clone(), !row.enabled))
                    .map(|(key, enabled)| {
                        (
                            super::InputAction::SetMcpServerEnabled {
                                name: key.clone(),
                                enabled,
                            },
                            super::ModalCommand::SetSettingRow { kind, key, enabled },
                        )
                    }),
                super::SettingsModalKind::Tools => state
                    .modals
                    .tools_modal
                    .as_ref()
                    .and_then(|modal| modal.rows.get(modal.selected))
                    .map(|row| (row.name.clone(), !row.enabled))
                    .map(|(key, enabled)| {
                        (
                            super::InputAction::SetToolEnabled {
                                name: key.clone(),
                                enabled,
                            },
                            super::ModalCommand::SetSettingRow { kind, key, enabled },
                        )
                    }),
                super::SettingsModalKind::Subagents => state
                    .modals
                    .subagents_modal
                    .as_ref()
                    .and_then(|modal| modal.rows.get(modal.selected))
                    .map(|row| (row.id.clone(), !row.enabled))
                    .map(|(key, enabled)| {
                        (
                            super::InputAction::SetSubagentProfileEnabled {
                                id: key.clone(),
                                enabled,
                            },
                            super::ModalCommand::SetSettingRow { kind, key, enabled },
                        )
                    }),
            };
            toggle.map_or(super::InputCommand::None, |(action, command)| {
                super::InputCommand::action_with_display(
                    action,
                    super::DisplayCommand::Modal(command),
                )
            })
        }
        (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseSettings(kind)),
        _ => super::InputCommand::None,
    }
}

fn settings_move_command(
    kind: super::SettingsModalKind,
    down: bool,
    viewport: PickerViewport,
) -> super::ModalCommand {
    super::ModalCommand::MoveSettings {
        kind,
        down,
        visible_rows: viewport.visible_rows as usize,
    }
}

fn classify_system_scroll_modal_key(
    key: KeyEvent,
    target: super::ScrollTarget,
    viewport: SystemPromptViewport,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    let scroll = |down, rows| {
        super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
            target,
            down,
            rows,
            visible_rows: viewport.visible_rows,
            wrap_width: viewport.wrap_width,
        }))
    };
    match (key.code, key.modifiers) {
        (KeyCode::Esc | KeyCode::Char('q'), KeyModifiers::NONE) => match target {
            super::ScrollTarget::SystemPrompt => display(super::ModalCommand::CloseSystemPrompt),
            super::ScrollTarget::Usage => display(super::ModalCommand::CloseUsage),
            _ => super::InputCommand::None,
        },
        (KeyCode::Up, KeyModifiers::NONE | KeyModifiers::ALT) => scroll(false, 1),
        (KeyCode::Down, KeyModifiers::NONE | KeyModifiers::ALT) => scroll(true, 1),
        (KeyCode::PageUp, KeyModifiers::NONE) => scroll(false, viewport.visible_rows),
        (KeyCode::PageDown, KeyModifiers::NONE) => scroll(true, viewport.visible_rows),
        (KeyCode::Home, KeyModifiers::NONE) => scroll(false, u16::MAX),
        (KeyCode::End, KeyModifiers::NONE) => scroll(true, u16::MAX),
        _ => super::InputCommand::None,
    }
}

fn classify_rewind_modal_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewport: SystemPromptViewport,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    let scroll = |down, rows| {
        super::InputCommand::display(super::DisplayCommand::Scroll(super::ScrollCommand {
            target: super::ScrollTarget::Rewind,
            down,
            rows,
            visible_rows: viewport.visible_rows,
            wrap_width: viewport.wrap_width,
        }))
    };
    match (key.code, key.modifiers) {
        (super::keybindings::REWIND_MODE_KEY, KeyModifiers::NONE) => {
            display(super::ModalCommand::CycleRewindMode)
        }
        (KeyCode::Enter, KeyModifiers::NONE) => {
            if state.modals.rewind_modal.as_ref().is_some_and(|modal| {
                (modal.plan.is_some() && !modal.dry_run) || !modal.plans.is_empty()
            }) {
                super::InputCommand::action(super::InputAction::ConfirmRewind)
            } else {
                super::InputCommand::None
            }
        }
        (KeyCode::Esc, KeyModifiers::NONE) => super::InputCommand::action_with_display(
            super::InputAction::CancelRewind,
            super::DisplayCommand::Modal(super::ModalCommand::CloseRewind),
        ),
        (KeyCode::Up, KeyModifiers::NONE | KeyModifiers::ALT) => {
            display(super::ModalCommand::RewindPickerMoveOrScroll {
                down: false,
                viewport,
            })
        }
        (KeyCode::Down, KeyModifiers::NONE | KeyModifiers::ALT) => {
            display(super::ModalCommand::RewindPickerMoveOrScroll {
                down: true,
                viewport,
            })
        }
        (KeyCode::PageUp, KeyModifiers::NONE) => scroll(false, viewport.visible_rows),
        (KeyCode::PageDown, KeyModifiers::NONE) => scroll(true, viewport.visible_rows),
        _ => super::InputCommand::None,
    }
}

fn classify_logout_confirmation_key(
    key: KeyEvent,
    state: &MissionControlState,
    action_row: Option<Rect>,
) -> super::InputCommand {
    match (key.code, key.modifiers) {
        (KeyCode::Enter, KeyModifiers::NONE) if action_row.is_some() => state
            .modals
            .logout_confirmation
            .as_ref()
            .map(|confirmation| {
                super::InputCommand::action_with_display(
                    super::InputAction::ConfirmLogout(confirmation.provider_id.clone()),
                    super::DisplayCommand::Modal(super::ModalCommand::CloseLogoutConfirmation),
                )
            })
            .unwrap_or(super::InputCommand::None),
        (KeyCode::Esc, KeyModifiers::NONE) => super::InputCommand::action_with_display(
            super::InputAction::CancelLogout,
            super::DisplayCommand::Modal(super::ModalCommand::CloseLogoutConfirmation),
        ),
        _ => super::InputCommand::None,
    }
}

fn classify_model_picker_key(
    key: KeyEvent,
    state: &MissionControlState,
    viewport: Option<PickerViewport>,
) -> super::InputCommand {
    let display = |command| super::InputCommand::display(super::DisplayCommand::Modal(command));
    match (key.code, key.modifiers) {
        (KeyCode::Up, KeyModifiers::NONE) => {
            viewport.map_or(super::InputCommand::None, |viewport| {
                display(super::ModalCommand::ModelPickerMove {
                    down: false,
                    visible_rows: viewport.visible_rows as usize,
                })
            })
        }
        (KeyCode::Down, KeyModifiers::NONE) => {
            viewport.map_or(super::InputCommand::None, |viewport| {
                display(super::ModalCommand::ModelPickerMove {
                    down: true,
                    visible_rows: viewport.visible_rows as usize,
                })
            })
        }
        (KeyCode::Enter, KeyModifiers::NONE) if viewport.is_some() => state
            .selected_model_picker_id()
            .map_or(super::InputCommand::None, |model_id| {
                super::InputCommand::action_with_display(
                    super::InputAction::SelectModel(model_id),
                    super::DisplayCommand::Modal(super::ModalCommand::CloseModelPicker),
                )
            }),
        (KeyCode::Esc, KeyModifiers::NONE) => display(super::ModalCommand::CloseModelPicker),
        _ => super::InputCommand::None,
    }
}