diff --git a/cmd/cognitiveos-cli/main.go b/cmd/cognitiveos-cli/main.go index 3ee5d9b..b9f9a33 100644 --- a/cmd/cognitiveos-cli/main.go +++ b/cmd/cognitiveos-cli/main.go @@ -17,12 +17,26 @@ import ( var version = "dev" func main() { + // Ensure terminal is restored on panic/crash + defer func() { + if r := recover(); r != nil { + // Restore terminal on panic to prevent buffer issues + fmt.Fprintf(os.Stderr, "\n\nCognitiveOS CLI panicked: %v\n", r) + fmt.Fprintf(os.Stderr, "Restoring terminal...\n") + // Attempt to exit alt screen and restore normal terminal + fmt.Print("\033[?1049l") // Exit alternate screen + fmt.Print("\033[?25h") // Show cursor + fmt.Print("\033[0m") // Reset attributes + os.Exit(1) + } + }() + fs := flag.NewFlagSet("cognitiveos-cli", flag.ExitOnError) socketPath := fs.String("socket", "/cognitiveos/run/daemon.sock", "daemon socket path") tuiMode := fs.Bool("tui", false, "launch interactive TUI (default)") cmdText := fs.String("cmd", "", "send a command and print the response (non-interactive)") jsonOutput := fs.Bool("json", false, "print full JSON envelope (requires --cmd)") - showVersion := fs.Bool("version", false, "print version and exit") + showVersion := fs.Bool("version", false, "print version") fs.Usage = func() { fmt.Fprintf(os.Stderr, "cognitiveos-cli — CognitiveOS terminal interface\n\n") @@ -96,6 +110,10 @@ func main() { ) if _, err := p.Run(); err != nil { + // Restore terminal on program error + fmt.Print("\033[?1049l") // Exit alternate screen + fmt.Print("\033[?25h") // Show cursor + fmt.Print("\033[0m") // Reset attributes log.Fatal(fmt.Errorf("program error: %w", err)) } diff --git a/internal/tui/keybind.go b/internal/tui/keybind.go index 18bebd9..35d378f 100644 --- a/internal/tui/keybind.go +++ b/internal/tui/keybind.go @@ -64,6 +64,13 @@ func (m Model) handleIdleKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { m.state = StateVoiceRecording m.waveformIdx = 0 return m, spinnerTickCmd() + case " ": + if m.confirming { + return m, nil + } + m.input.WriteString(" ") + m.state = StateListening + return m, nil default: if m.confirming { return m, nil @@ -166,7 +173,13 @@ func (m Model) handleProcessingKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { return m, cancelProcessingCmd() case "ctrl+l": return m, tea.ClearScreen + case " ": + m.input.WriteString(" ") + return m, nil default: + if msg.Type == tea.KeyRunes { + m.input.WriteString(msg.String()) + } return m, nil } } @@ -282,6 +295,10 @@ func (m Model) handleRespondingKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { } return m, nil + case " ": + m.input.WriteString(" ") + return m, nil + case "up": if len(m.history) > 0 && m.historyIdx > 0 { m.historyIdx-- @@ -354,6 +371,10 @@ func (m Model) handleErrorKey(msg tea.KeyMsg) (tea.Model, tea.Cmd) { } return m, nil + case " ": + m.input.WriteString(" ") + return m, nil + default: if msg.Type == tea.KeyRunes { m.input.WriteString(msg.String())