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