Fix command menu prompt residue
This commit is contained in:
parent
c903041474
commit
136a503846
@ -17,6 +17,7 @@ const { Spinner, truncateThinking } = require('../ui/spinner');
|
||||
const { renderBanner } = require('../ui/banner');
|
||||
const { buildStartLine, buildFinalLine, startToolDisplay, formatResultLines, printResultLines } = require('../ui/tool_display');
|
||||
const { createConversation, updateConversation } = require('../storage/conversation_store');
|
||||
const { applyUsage, normalizeTokenUsage } = require('../utils/token_usage');
|
||||
const { gray, cyan, green } = require('../utils/colors');
|
||||
const { createIndentedWriter } = require('../ui/indented_writer');
|
||||
|
||||
@ -24,6 +25,7 @@ const WORKSPACE = process.cwd();
|
||||
const WORKSPACE_NAME = path.basename(WORKSPACE);
|
||||
const USERNAME = os.userInfo().username || 'user';
|
||||
const PROMPT = `${USERNAME}@${WORKSPACE_NAME} % `;
|
||||
const MENU_PAGE_SIZE = 6;
|
||||
|
||||
const config = ensureConfig();
|
||||
const state = createState(config, WORKSPACE);
|
||||
@ -48,6 +50,7 @@ renderBanner({
|
||||
let rl = null;
|
||||
let commandMenuActive = false;
|
||||
let menuSearchTerm = '';
|
||||
let menuLastSearchTerm = '';
|
||||
let menuJustClosedAt = 0;
|
||||
let menuInjectedCommand = null;
|
||||
let menuAbortController = null;
|
||||
@ -68,12 +71,19 @@ process.stdin.on('keypress', (str, key) => {
|
||||
}
|
||||
if (str === '/' && (rl.line === '' || rl.line === '/')) {
|
||||
commandMenuActive = true;
|
||||
if (rl) {
|
||||
rl.pause();
|
||||
rl.line = '';
|
||||
rl.cursor = 0;
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
}
|
||||
menuSearchTerm = '';
|
||||
menuAbortController = new AbortController();
|
||||
void openCommandMenu({
|
||||
rl,
|
||||
prompt: PROMPT,
|
||||
pageSize: 6,
|
||||
pageSize: MENU_PAGE_SIZE,
|
||||
colorEnabled: process.stdout.isTTY,
|
||||
resetAnsi: '\x1b[0m',
|
||||
onInput: (input) => {
|
||||
@ -90,12 +100,25 @@ process.stdin.on('keypress', (str, key) => {
|
||||
commandMenuActive = false;
|
||||
menuAbortController = null;
|
||||
menuJustClosedAt = menuInjectedCommand ? Date.now() : 0;
|
||||
menuLastSearchTerm = menuSearchTerm;
|
||||
drainStdin();
|
||||
rl.line = '';
|
||||
rl.cursor = 0;
|
||||
menuSearchTerm = '';
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
if (process.stdout.isTTY) {
|
||||
readline.clearScreenDown(process.stdout);
|
||||
}
|
||||
// Clear possible echoes from the base readline line (current + previous line).
|
||||
if (process.stdout.isTTY) {
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
readline.moveCursor(process.stdout, 0, -1);
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
} else {
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
}
|
||||
rl.prompt(true);
|
||||
if (menuInjectedCommand) {
|
||||
const injected = menuInjectedCommand;
|
||||
@ -123,21 +146,31 @@ function initReadline() {
|
||||
rl.prompt();
|
||||
|
||||
rl.on('line', async (line) => {
|
||||
if (rl) {
|
||||
rl.line = '';
|
||||
rl.cursor = 0;
|
||||
}
|
||||
if (commandMenuActive) {
|
||||
rl.prompt();
|
||||
return;
|
||||
}
|
||||
const input = line.trim();
|
||||
if (menuJustClosedAt) {
|
||||
const tooOld = Date.now() - menuJustClosedAt > 800;
|
||||
const normalizedMenu = String(menuSearchTerm).trim().replace(/^\/+/, '');
|
||||
const normalizedMenu = String(menuLastSearchTerm).trim().replace(/^\/+/, '');
|
||||
const normalizedInput = input.replace(/^\/+/, '');
|
||||
if (!tooOld && (!normalizedMenu ? input === '' : normalizedInput === normalizedMenu)) {
|
||||
menuJustClosedAt = 0;
|
||||
menuLastSearchTerm = '';
|
||||
if (process.stdout.isTTY) {
|
||||
readline.moveCursor(process.stdout, 0, -1);
|
||||
readline.clearLine(process.stdout, 0);
|
||||
readline.cursorTo(process.stdout, 0);
|
||||
}
|
||||
rl.prompt();
|
||||
return;
|
||||
}
|
||||
menuJustClosedAt = 0;
|
||||
menuLastSearchTerm = '';
|
||||
}
|
||||
if (!input) {
|
||||
rl.prompt();
|
||||
@ -235,6 +268,8 @@ async function runAssistantLoop() {
|
||||
let assistantContent = '';
|
||||
let toolCalls = {};
|
||||
let usageTotal = null;
|
||||
let usagePrompt = null;
|
||||
let usageCompletion = null;
|
||||
let firstContent = true;
|
||||
let assistantWriter = null;
|
||||
|
||||
@ -253,7 +288,12 @@ async function runAssistantLoop() {
|
||||
for await (const chunk of streamChat({ config, messages, tools, thinkingMode: state.thinkingMode })) {
|
||||
const choice = chunk.choices && chunk.choices[0];
|
||||
if (!choice) continue;
|
||||
if (chunk.usage && chunk.usage.total_tokens) usageTotal = chunk.usage.total_tokens;
|
||||
const usage = (choice && (choice.usage || choice.delta?.usage)) || chunk.usage;
|
||||
if (usage) {
|
||||
if (Number.isFinite(usage.prompt_tokens)) usagePrompt = usage.prompt_tokens;
|
||||
if (Number.isFinite(usage.completion_tokens)) usageCompletion = usage.completion_tokens;
|
||||
if (Number.isFinite(usage.total_tokens)) usageTotal = usage.total_tokens;
|
||||
}
|
||||
const delta = choice.delta || {};
|
||||
|
||||
if (delta.reasoning_content || delta.reasoning_details) {
|
||||
@ -322,7 +362,13 @@ async function runAssistantLoop() {
|
||||
}
|
||||
showCursor();
|
||||
|
||||
if (usageTotal) state.tokenUsage = usageTotal;
|
||||
if (usageTotal !== null || usagePrompt !== null || usageCompletion !== null) {
|
||||
state.tokenUsage = applyUsage(normalizeTokenUsage(state.tokenUsage), {
|
||||
prompt_tokens: usagePrompt,
|
||||
completion_tokens: usageCompletion,
|
||||
total_tokens: usageTotal,
|
||||
});
|
||||
}
|
||||
|
||||
const toolCallList = Object.keys(toolCalls)
|
||||
.map((k) => Number(k))
|
||||
|
||||
Loading…
Reference in New Issue
Block a user