From d01d6a38b5cac89e09bec5ef73971e8085953529 Mon Sep 17 00:00:00 2001 From: Mplan Date: Tue, 16 Jun 2026 14:49:00 +0800 Subject: [PATCH] fix(ui): skip "Press Enter" pause when user backs out of subcommand --- index.ts | 4 ++++ src/commands/commit.ts | 4 ++-- src/commands/config.ts | 3 ++- src/commands/explain.ts | 4 ++-- src/commands/pr.ts | 6 +++--- src/commands/review.ts | 4 ++-- src/commands/suggest.ts | 4 ++-- src/menu.ts | 4 ++++ 8 files changed, 21 insertions(+), 12 deletions(-) diff --git a/index.ts b/index.ts index 7c1488b..484cba3 100644 --- a/index.ts +++ b/index.ts @@ -15,6 +15,7 @@ import { setColorEnabled } from "./src/terminal"; import { BOLD, GREEN, CYAN, DIM, RESET } from "./src/terminal"; import { isStdinTTY, initTTY } from "./src/tty"; import { showBanner } from "./src/brand"; +import { SKIP_WAIT } from "./src/menu"; // ── Interactive Menu (mole-style) ───────────────────────────────────── @@ -153,6 +154,9 @@ async function dispatchAndWait(item: MenuItem, wasRaw: boolean): Promise process.stdin.pause(); process.stdout.write("\x1b[2J\x1b[H"); // clear screen const result = await dispatchMenuAction(item.key); + if (result === (SKIP_WAIT as unknown as number)) { + return 0; // user explicitly backed out — skip "Press Enter" and return directly + } await waitForEnter(); return result; } diff --git a/src/commands/commit.ts b/src/commands/commit.ts index c901ec7..544711a 100644 --- a/src/commands/commit.ts +++ b/src/commands/commit.ts @@ -10,7 +10,7 @@ import { commit, } from "../git"; import { selectFiles } from "../selector"; -import { BACK } from "../menu"; +import { BACK, SKIP_WAIT } from "../menu"; import { collectProjectContext } from "../context"; import { buildPrompt, SYSTEM_PROMPT } from "../prompt"; import { generateCommitMessage } from "../ai"; @@ -221,7 +221,7 @@ export async function handleCommit(args: ParsedArgs): Promise { console.log(` ${GREEN()}Auto-staged ${unstagedFiles.length} file(s).${RESET()}`); } else { const selected = await selectFiles(stagedFiles, unstagedFiles); - if (selected === BACK) return 0; + if (selected === BACK) return SKIP_WAIT as unknown as number; if (selected.length > 0) { await stageFiles(selected); console.log(` ${GREEN()}Staged ${selected.length} file(s).${RESET()}`); diff --git a/src/commands/config.ts b/src/commands/config.ts index 1822211..b692fab 100644 --- a/src/commands/config.ts +++ b/src/commands/config.ts @@ -1,6 +1,7 @@ import { loadConfig, saveConfig } from "../config"; import { BOLD, GREEN, YELLOW, CYAN, RED, DIM, RESET } from "../terminal"; import { isStdinTTY } from "../tty"; +import { SKIP_WAIT } from "../menu"; import type { Config } from "../types"; import type { ParsedArgs } from "../cli"; @@ -323,7 +324,7 @@ export async function handleConfig(args: ParsedArgs): Promise { // gai config (no args) → interactive if (positional.length === 0) { const result = await interactiveConfig(); - return result === "back" ? 0 : 0; + return result === "back" ? (SKIP_WAIT as unknown as number) : 0; } console.error(`\n ${RED()}Error: Unknown config subcommand: ${positional[0]}${RESET()}`); diff --git a/src/commands/explain.ts b/src/commands/explain.ts index 3ab11b2..e775306 100644 --- a/src/commands/explain.ts +++ b/src/commands/explain.ts @@ -1,7 +1,7 @@ import { loadConfig } from "../config"; import { isGitRepo, getStagedDiff, getUnstagedFiles, getRepoRoot, stageFiles } from "../git"; import { selectFiles } from "../selector"; -import { BACK } from "../menu"; +import { BACK, SKIP_WAIT } from "../menu"; import { collectProjectContext } from "../context"; import { EXPLAIN_SYSTEM_PROMPT, buildExplainPrompt } from "../prompt"; import { callAI } from "../ai"; @@ -52,7 +52,7 @@ export async function handleExplain(args: ParsedArgs): Promise { const unstagedFiles = await getUnstagedFiles(); if (unstagedFiles.length > 0) { const selected = await selectFiles([], unstagedFiles); - if (selected === BACK) return 0; + if (selected === BACK) return SKIP_WAIT as unknown as number; if (selected.length > 0) { await stageFiles(selected); console.log(` ${GREEN()}Staged ${selected.length} file(s).${RESET()}`); diff --git a/src/commands/pr.ts b/src/commands/pr.ts index f1bb019..304ac83 100644 --- a/src/commands/pr.ts +++ b/src/commands/pr.ts @@ -4,7 +4,7 @@ import { isGitRepo, getRepoRoot } from "../git"; import { collectProjectContext } from "../context"; import { PR_SYSTEM_PROMPT, buildPRPrompt } from "../prompt"; import { generatePRMessage } from "../ai"; -import { BACK, selectOne } from "../menu"; +import { BACK, SKIP_WAIT, selectOne } from "../menu"; import { getDefaultBranch, getBranchName, @@ -68,7 +68,7 @@ export async function handlePR(args: ParsedArgs): Promise { if (!platform) { const hostname = (await getRemoteHostname()) || "unknown"; const chosen = await selectPlatform(hostname); - if (chosen === BACK) return 0; + if (chosen === BACK) return SKIP_WAIT as unknown as number; if (!chosen) { console.log(" Aborted."); return 0; @@ -98,7 +98,7 @@ export async function handlePR(args: ParsedArgs): Promise { items: [{ label: "Back", value: "back" as const }], }); if (choice === null) process.exit(0); - return 0; + return SKIP_WAIT as unknown as number; } console.log(` ${commits.length} commit${commits.length > 1 ? "s" : ""} on this branch`); diff --git a/src/commands/review.ts b/src/commands/review.ts index 4664dc8..b18a301 100644 --- a/src/commands/review.ts +++ b/src/commands/review.ts @@ -1,7 +1,7 @@ import { loadConfig } from "../config"; import { isGitRepo, getStagedDiff, getUnstagedFiles, getRepoRoot, stageFiles } from "../git"; import { selectFiles } from "../selector"; -import { BACK } from "../menu"; +import { BACK, SKIP_WAIT } from "../menu"; import { collectProjectContext } from "../context"; import { REVIEW_SYSTEM_PROMPT, buildReviewPrompt } from "../prompt"; import { callAI } from "../ai"; @@ -61,7 +61,7 @@ export async function handleReview(args: ParsedArgs): Promise { const unstagedFiles = await getUnstagedFiles(); if (unstagedFiles.length > 0) { const selected = await selectFiles([], unstagedFiles); - if (selected === BACK) return 0; + if (selected === BACK) return SKIP_WAIT as unknown as number; if (selected.length > 0) { await stageFiles(selected); console.log(` ${GREEN()}Staged ${selected.length} file(s).${RESET()}`); diff --git a/src/commands/suggest.ts b/src/commands/suggest.ts index 970e88a..4d64141 100644 --- a/src/commands/suggest.ts +++ b/src/commands/suggest.ts @@ -1,7 +1,7 @@ import { loadConfig } from "../config"; import { isGitRepo, getStagedDiff, getUnstagedFiles, stageFiles } from "../git"; import { selectFiles } from "../selector"; -import { BACK } from "../menu"; +import { BACK, SKIP_WAIT } from "../menu"; import { SUGGEST_SYSTEM_PROMPT, buildSuggestBranchPrompt, @@ -58,7 +58,7 @@ export async function handleSuggest(args: ParsedArgs): Promise { const unstagedFiles = await getUnstagedFiles(); if (unstagedFiles.length > 0) { const selected = await selectFiles([], unstagedFiles); - if (selected === BACK) return 0; + if (selected === BACK) return SKIP_WAIT as unknown as number; if (selected.length > 0) { await stageFiles(selected); console.log(` ${GREEN()}Staged ${selected.length} file(s).${RESET()}`); diff --git a/src/menu.ts b/src/menu.ts index 63e1d79..8e679cb 100644 --- a/src/menu.ts +++ b/src/menu.ts @@ -15,6 +15,10 @@ const BACKSPACE = "\x7f"; export const BACK = Symbol("prompt-back"); export type PromptBack = typeof BACK; +// Sent by command handlers to skip the "Press Enter to return" wait in the +// interactive menu when the user explicitly backed out of a sub-menu. +export const SKIP_WAIT = Symbol("skip-wait"); + export interface Choice { label: string; value: T;