| Server IP : 85.155.190.233 / Your IP : 216.73.216.103 Web Server : nginx/1.24.0 System : Linux antigravity-cli 6.8.0-31-generic #31-Ubuntu SMP PREEMPT_DYNAMIC Sat Apr 20 00:40:06 UTC 2024 x86_64 User : wp-moonbloom ( 1001) PHP Version : 8.3.6 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /lib/node_modules/@google/gemini-cli/bundle/ |
Upload File : |
const require = (await import('node:module')).createRequire(import.meta.url); const __chunk_filename = (await import('node:url')).fileURLToPath(import.meta.url); const __chunk_dirname = (await import('node:path')).dirname(__chunk_filename);
import {
AT_COMMAND_PATH_REGEX_SOURCE,
AppContext_default,
BaseSelectionList,
Box_default,
BuiltinCommandLoader,
CliSpinner,
Command,
CommandService,
ConfigContext,
ConsolePatcher,
ContextUsageDisplay,
DEFAULT_THEME,
DialogFooter,
DiffRenderer,
ExpandableText,
FileCommandLoader,
Footer,
GIT_COMMIT_INFO,
InputContext,
KeyBinding,
KeyMatchersProvider,
KeypressProvider,
LARGE_PASTE_CHAR_THRESHOLD,
LARGE_PASTE_LINE_THRESHOLD,
MAX_WIDTH,
MaxSizedBox,
McpPromptLoader,
MouseProvider,
MultiFolderTrustDialog,
Newline,
OverflowProvider,
PASTED_TEXT_PLACEHOLDER_REGEX,
QUOTA_USED_CRITICAL_THRESHOLD,
QUOTA_USED_WARNING_THRESHOLD,
QuotaContext,
RadioButtonSelect,
ResizeObserver,
SCROLL_TO_ITEM_END,
ScrollProvider,
ScrollableListWithForwardRef,
SettingsContext,
ShowMoreLines,
Static,
StreamingContext,
StyledLine,
TOOL_SUCCESS_RATE_HIGH,
TOOL_SUCCESS_RATE_MEDIUM,
TerminalCapabilityManager,
Text,
TextInput,
Transform,
UIStateContext,
USER_AGREEMENT_RATE_HIGH,
USER_AGREEMENT_RATE_MEDIUM,
VimModeProvider,
batchAddDirectories,
build_default,
calculateTransformationsForLine,
calculateTransformedLine,
checkPermissions,
cleanupOldClipboardImages,
cleanupTerminalOnExit,
clipboardHasImage,
clipboardy_default,
colorizeCode,
escapeAtSymbols,
expandHomeDir,
expandPastePlaceholders,
formatBytes,
formatCommand,
formatDuration,
formatResetTime,
getDialogRestartRequiredSettings,
getDialogSettingKeys,
getDisplayValue,
getEditValue,
getEffectiveValue,
getFileExtension,
getFormattedSettingValue,
getNestedValue,
getSettingDefinition,
getStatusColor,
getTransformUnderCursor,
getUsedStatusColor,
handleAtCommand,
isAlternateBufferEnabled,
isAtCommand,
isAutoExecutableCommand,
isBrowser,
isContextUsageHigh,
isInSettingsScope,
isNarrowWidth,
isNewFile,
isRecord,
isSlashCommand,
isUltraTier,
loadKeyMatchers,
logicalPosToOffset,
measure_element_default,
openFileInEditor,
parseColor,
parseDiffWithLineNumbers,
parseEditedValue,
parseSlashCommand,
persistentState,
profiler,
renderDiffLines,
render_default,
requestConsentInteractive,
require_jsx_runtime,
require_semver,
saveClipboardImage,
shouldSwitchTheme,
startAutoMemoryIfEnabled,
styledCharsWidth,
styledLineToString,
terminalCapabilityManager,
theme,
themeManager,
toStyledCharacters,
useAlternateBuffer,
useAnimatedScrollbar,
useBatchedScroll,
useConfig,
useConfirmUpdateRequests,
useExtensionUpdates,
useFocus,
useHistory,
useIdeTrustListener,
useInputState,
useKeyMatchers,
useKeypress,
useMouse,
useMouseClick,
useOverflowActions,
useOverflowState,
useQuotaState,
useScrollable,
useSearchBuffer,
useSessionBrowser,
useSettings,
useSettingsStore,
useStreamingContext,
useTerminalSetupPrompt,
useTextBuffer,
useUIState,
useVimMode,
use_app_default,
use_is_screen_reader_enabled_default,
use_stdin_default,
use_stdout_default,
validateAuthMethod,
widestLineFromStyledChars,
wordBreakStyledChars,
wrapStyledChars
} from "./chunk-4N6JQGX6.js";
import {
appEvents
} from "./chunk-5PS3AYFU.js";
import {
require_source
} from "./chunk-TK52UAKX.js";
import {
ACTIVE_SHELL_MAX_LINES,
COMPACT_TOOL_SUBVIEW_MAX_LINES,
COMPLETED_SHELL_MAX_LINES,
DefaultLight,
EXPAND_HINT_DURATION_MS,
INK_NAME_TO_HEX_MAP,
INK_SUPPORTED_NAMES,
KEYBOARD_SHORTCUTS_URL,
LRU_BUFFER_PERF_CACHE_LIMIT,
MAX_GEMINI_MESSAGE_LINES,
MAX_MCP_RESOURCES_TO_SHOW,
MAX_SHELL_OUTPUT_SIZE,
QUEUE_ERROR_DISPLAY_DURATION_MS,
SHELL_ACTION_REQUIRED_TITLE_DELAY_MS,
SHELL_COMMAND_NAME,
SHELL_FOCUS_HINT_DELAY_MS,
SHELL_NAME,
SHELL_OUTPUT_TRUNCATION_BUFFER,
SHELL_SILENT_WORKING_TITLE_DELAY_MS,
SKILLS_DOCS_URL,
SUBAGENT_MAX_LINES,
SettingScope,
TOGGLE_TYPES,
TOOL_STATUS,
WARNING_PROMPT_DURATION_MS,
cleanMessage,
convertSessionToHistoryFormats,
cpIndexToOffset,
cpLen,
cpSlice,
escapeAnsiCtrlCodes,
formatRelativeTime,
getAsciiArtWidth,
getCachedStringWidth,
getLuminance,
getSessionFiles,
interpolateColor,
isLoadableSettingScope,
isWorkspaceTrusted,
loadTrustedFolders,
normalizeEscapedNewlines,
pickDefaultThemeName,
require_tinygradient,
resolveColor,
sanitizeForDisplay,
stringWidth,
stripUnsafeCharacters,
toCodePoints
} from "./chunk-4IXP3BQP.js";
import {
handleAutoUpdate,
isDevelopment,
relaunchApp,
setUpdateHandler
} from "./chunk-DB4PQOJV.js";
import {
isTodoList,
mapCoreStatusToDisplayStatus,
require_react
} from "./chunk-VNPDB5AB.js";
import {
registerCleanup,
removeCleanup,
runExitCleanup,
setupTtyCheck
} from "./chunk-5LLZICYH.js";
import {
ApiKeyUpdatedEvent,
AsyncFzf,
AudioRecorder,
AuthType,
CompressionStatus,
ConversationFinishedEvent,
CoreToolCallStatus,
CreditPurchaseClickEvent,
EDITOR_DISPLAY_NAMES,
EmptyWalletMenuShownEvent,
ExecutionLifecycleService,
ExitCodes,
FileDiscoveryService,
FileSearchFactory,
FolderTrustDiscoveryService,
G1_UTM_CAMPAIGNS,
GeminiCliOperation,
GeminiEventType,
GitService,
IdeClient,
LegacyAgentProtocol,
LlmRole,
Logger,
MCPDiscoveryState,
MCPServerStatus,
MessageSenderType,
ModelNotFoundError,
ModelSlashCommandEvent,
OverageMenuShownEvent,
OverageOptionSelectedEvent,
PolicyIntegrityManager,
ProjectIdRequiredError,
RELEASE_CHANNEL_STABILITY,
ROOT_SCHEDULER_ID,
Scheduler,
SessionEndReason,
SessionStartSource,
ShellExecutionService,
SlashCommandStatus,
SubagentState,
TerminalQuotaError,
TranscriptionFactory,
TrustLevel,
UserAccountManager,
UserPromptEvent,
UserTierId,
ValidationRequiredError,
WarningPriority,
WhisperModelManager,
addMCPStatusChangeListener,
allowEditorTypeInSandbox,
belongsInConfirmationQueue,
buildG1Url,
buildToolVisibilityContext,
buildUserSteeringHintPrompt,
checkExhaustive,
clearApiKey,
clearCachedCredentialFile,
convertSessionToClientHistory,
createWorkingStdio,
disableLineWrapping,
disableMouseEvents,
displayContentToString,
enableLineWrapping,
enableMouseEvents,
enterAlternateScreen,
escapeShellArg,
exitAlternateScreen,
flattenMemory,
geminiPartsToContentParts,
generateSummary,
getAbsoluteGitDir,
getAdminErrorMessage,
getChannelFromVersion,
getCodeAssistServer,
getEditorDisplayName,
getG1CreditBalance,
getMCPServerStatus,
getPackageJson,
getPlanModeExitMessage,
getResponseText,
getShellConfiguration,
getVersion,
hasRedirection,
hasValidEditorCommand,
ideContextStore,
import_lru_cache,
isAppleTerminal,
isBinary,
isBinaryAvailable,
isCompletedAskUserTool,
isEditorAvailable,
isHeadlessMode,
isOverageEligibleModel,
isRenderedInHistory,
isSubagentProgress,
isUserVisibleHook,
isVisibleInToolGroup,
isWindows,
loadApiKey,
logBillingEvent,
logConversationFinishedEvent,
logModelSlashCommand,
logSlashCommand,
logUserPrompt,
makeSlashCommandEvent,
openBrowserSecurely,
parseAndFormatApiError,
parseThought,
processRestorableToolCalls,
processSingleFileContent,
promptIdContext,
recordCreditPurchaseClick,
recordExitFail,
recordFlickerFrame,
recordOverageOptionSelected,
recordSlowRender,
recordToolCallInteractions,
removeMCPStatusChangeListener,
runInDevTraceSpan,
saveApiKey,
shouldAutoUseCredits,
shouldEnterAlternateScreen,
shouldLaunchBrowser,
shouldShowEmptyWalletMenu,
shouldShowOverageMenu,
spawnAsync,
startupProfiler,
stripAnsi,
supportsTrueColor,
tokenLimit,
uiTelemetryService,
validatePlanContent,
validatePlanPath,
writeToStdout
} from "./chunk-U6X4OPT5.js";
import {
ACTIVATE_SKILL_TOOL_NAME,
AGENT_TOOL_NAME,
ApprovalMode,
ChangeAuthRequestedError,
CoreEvent,
DEFAULT_GEMINI_FLASH_LITE_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
DEFAULT_GEMINI_MODEL,
DEFAULT_GEMINI_MODEL_AUTO,
EDIT_DISPLAY_NAME,
EDIT_TOOL_NAMES,
FinishReason,
GEMINI_DIR,
GEMMA_4_26B_A4B_IT_MODEL,
GEMMA_4_31B_IT_MODEL,
GLOB_DISPLAY_NAME,
GREP_DISPLAY_NAME,
Kind,
LS_DISPLAY_NAME,
MessageBusType,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
PREVIEW_GEMINI_3_1_MODEL,
PREVIEW_GEMINI_FLASH_MODEL,
PREVIEW_GEMINI_MODEL,
PREVIEW_GEMINI_MODEL_AUTO,
QuestionType,
READ_FILE_DISPLAY_NAME,
READ_MANY_FILES_DISPLAY_NAME,
SHELL_TOOL_NAME,
Storage,
TOPIC_PARAM_STRATEGIC_INTENT,
TOPIC_PARAM_SUMMARY,
TOPIC_PARAM_TITLE,
ToolConfirmationOutcome,
ToolErrorType,
UPDATE_TOPIC_DISPLAY_NAME,
UPDATE_TOPIC_TOOL_NAME,
UnauthorizedError,
VALID_GEMINI_MODELS,
WEB_FETCH_DISPLAY_NAME,
WEB_SEARCH_DISPLAY_NAME,
WRITE_FILE_DISPLAY_NAME,
coreEvents,
debugLogger,
escapePath,
getAllGeminiMdFilenames,
getDisplayString,
getErrorMessage,
hasSummary,
homedir,
isAccountSuspendedError,
isAutoModel,
isBackgroundExecutionData,
isFileDiff,
isGrepResult,
isListResult,
isNodeError,
isProModel,
isReadManyFilesResult,
isStructuredToolResult,
refreshServerHierarchicalMemory,
safeJsonToMarkdown,
shortenPath,
tildeifyPath
} from "./chunk-UJ26GAE5.js";
import "./chunk-664ZODQF.js";
import "./chunk-RJTRUG2J.js";
import "./chunk-IUUIT4SU.js";
import {
__commonJS,
__export,
__require,
__toESM
} from "./chunk-34MYV7JD.js";
// node_modules/prop-types/lib/ReactPropTypesSecret.js
var require_ReactPropTypesSecret = __commonJS({
"node_modules/prop-types/lib/ReactPropTypesSecret.js"(exports, module) {
"use strict";
var ReactPropTypesSecret = "SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED";
module.exports = ReactPropTypesSecret;
}
});
// node_modules/prop-types/factoryWithThrowingShims.js
var require_factoryWithThrowingShims = __commonJS({
"node_modules/prop-types/factoryWithThrowingShims.js"(exports, module) {
"use strict";
var ReactPropTypesSecret = require_ReactPropTypesSecret();
function emptyFunction() {
}
function emptyFunctionWithReset() {
}
emptyFunctionWithReset.resetWarningCache = emptyFunction;
module.exports = function() {
function shim(props, propName, componentName, location, propFullName, secret) {
if (secret === ReactPropTypesSecret) {
return;
}
var err = new Error(
"Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types"
);
err.name = "Invariant Violation";
throw err;
}
;
shim.isRequired = shim;
function getShim() {
return shim;
}
;
var ReactPropTypes = {
array: shim,
bigint: shim,
bool: shim,
func: shim,
number: shim,
object: shim,
string: shim,
symbol: shim,
any: shim,
arrayOf: getShim,
element: shim,
elementType: shim,
instanceOf: getShim,
node: shim,
objectOf: getShim,
oneOf: getShim,
oneOfType: getShim,
shape: getShim,
exact: getShim,
checkPropTypes: emptyFunctionWithReset,
resetWarningCache: emptyFunction
};
ReactPropTypes.PropTypes = ReactPropTypes;
return ReactPropTypes;
};
}
});
// node_modules/prop-types/index.js
var require_prop_types = __commonJS({
"node_modules/prop-types/index.js"(exports, module) {
if (false) {
ReactIs = null;
throwOnDirectAccess = true;
module.exports = null(ReactIs.isElement, throwOnDirectAccess);
} else {
module.exports = require_factoryWithThrowingShims()();
}
var ReactIs;
var throwOnDirectAccess;
}
});
// node_modules/gradient-string/index.js
var require_gradient_string = __commonJS({
"node_modules/gradient-string/index.js"(exports, module) {
"use strict";
var chalk4 = require_source();
var tinygradient2 = require_tinygradient();
var forbiddenChars = /\s/g;
function InitGradient(...args) {
const grad = tinygradient2.apply(this, args);
const ret = (str, opts) => applyGradient(str ? str.toString() : "", grad, opts);
ret.multiline = (str, opts) => multilineGradient(str ? str.toString() : "", grad, opts);
return ret;
}
var getColors = (gradient, options, count) => options.interpolation.toLowerCase() === "hsv" ? gradient.hsv(count, options.hsvSpin.toLowerCase()) : gradient.rgb(count);
function applyGradient(str, gradient, opts) {
const options = validateOptions(opts);
const colorsCount = Math.max(str.replace(forbiddenChars, "").length, gradient.stops.length);
const colors = getColors(gradient, options, colorsCount);
let result = "";
for (const s of str) {
result += s.match(forbiddenChars) ? s : chalk4.hex(colors.shift().toHex())(s);
}
return result;
}
function multilineGradient(str, gradient, opts) {
const options = validateOptions(opts);
const lines = str.split("\n");
const maxLength = Math.max.apply(null, lines.map((l) => l.length).concat([gradient.stops.length]));
const colors = getColors(gradient, options, maxLength);
const results = [];
for (const line of lines) {
const lineColors = colors.slice(0);
let lineResult = "";
for (const l of line) {
lineResult += chalk4.hex(lineColors.shift().toHex())(l);
}
results.push(lineResult);
}
return results.join("\n");
}
function validateOptions(opts) {
const options = { interpolation: "rgb", hsvSpin: "short", ...opts };
if (opts !== void 0 && typeof opts !== "object") {
throw new TypeError(`Expected \`options\` to be an \`object\`, got \`${typeof opts}\``);
}
if (typeof options.interpolation !== "string") {
throw new TypeError(`Expected \`options.interpolation\` to be a \`string\`, got \`${typeof options.interpolation}\``);
}
if (options.interpolation.toLowerCase() === "hsv" && typeof options.hsvSpin !== "string") {
throw new TypeError(`Expected \`options.hsvSpin\` to be a \`string\`, got \`${typeof options.hsvSpin}\``);
}
return options;
}
var aliases = {
atlas: { colors: ["#feac5e", "#c779d0", "#4bc0c8"], options: {} },
cristal: { colors: ["#bdfff3", "#4ac29a"], options: {} },
teen: { colors: ["#77a1d3", "#79cbca", "#e684ae"], options: {} },
mind: { colors: ["#473b7b", "#3584a7", "#30d2be"], options: {} },
morning: { colors: ["#ff5f6d", "#ffc371"], options: { interpolation: "hsv" } },
vice: { colors: ["#5ee7df", "#b490ca"], options: { interpolation: "hsv" } },
passion: { colors: ["#f43b47", "#453a94"], options: {} },
fruit: { colors: ["#ff4e50", "#f9d423"], options: {} },
instagram: { colors: ["#833ab4", "#fd1d1d", "#fcb045"], options: {} },
retro: { colors: ["#3f51b1", "#5a55ae", "#7b5fac", "#8f6aae", "#a86aa4", "#cc6b8e", "#f18271", "#f3a469", "#f7c978"], options: {} },
summer: { colors: ["#fdbb2d", "#22c1c3"], options: {} },
rainbow: { colors: ["#ff0000", "#ff0100"], options: { interpolation: "hsv", hsvSpin: "long" } },
pastel: { colors: ["#74ebd5", "#74ecd5"], options: { interpolation: "hsv", hsvSpin: "long" } }
};
module.exports = InitGradient;
for (const a in aliases) {
module.exports[a] = (str) => new InitGradient(aliases[a].colors)(str, aliases[a].options);
module.exports[a].multiline = (str) => new InitGradient(aliases[a].colors).multiline(str, aliases[a].options);
}
}
});
// node_modules/ini/ini.js
var require_ini = __commonJS({
"node_modules/ini/ini.js"(exports) {
exports.parse = exports.decode = decode;
exports.stringify = exports.encode = encode;
exports.safe = safe;
exports.unsafe = unsafe;
var eol = typeof process !== "undefined" && process.platform === "win32" ? "\r\n" : "\n";
function encode(obj, opt) {
var children = [];
var out = "";
if (typeof opt === "string") {
opt = {
section: opt,
whitespace: false
};
} else {
opt = opt || {};
opt.whitespace = opt.whitespace === true;
}
var separator = opt.whitespace ? " = " : "=";
Object.keys(obj).forEach(function(k, _, __) {
var val = obj[k];
if (val && Array.isArray(val)) {
val.forEach(function(item) {
out += safe(k + "[]") + separator + safe(item) + "\n";
});
} else if (val && typeof val === "object")
children.push(k);
else
out += safe(k) + separator + safe(val) + eol;
});
if (opt.section && out.length)
out = "[" + safe(opt.section) + "]" + eol + out;
children.forEach(function(k, _, __) {
var nk = dotSplit(k).join("\\.");
var section = (opt.section ? opt.section + "." : "") + nk;
var child = encode(obj[k], {
section,
whitespace: opt.whitespace
});
if (out.length && child.length)
out += eol;
out += child;
});
return out;
}
function dotSplit(str) {
return str.replace(/\1/g, "LITERAL\\1LITERAL").replace(/\\\./g, "").split(/\./).map(function(part) {
return part.replace(/\1/g, "\\.").replace(/\2LITERAL\\1LITERAL\2/g, "");
});
}
function decode(str) {
var out = {};
var p = out;
var section = null;
var re = /^\[([^\]]*)\]$|^([^=]+)(=(.*))?$/i;
var lines = str.split(/[\r\n]+/g);
lines.forEach(function(line, _, __) {
if (!line || line.match(/^\s*[;#]/))
return;
var match = line.match(re);
if (!match)
return;
if (match[1] !== void 0) {
section = unsafe(match[1]);
if (section === "__proto__") {
p = {};
return;
}
p = out[section] = out[section] || {};
return;
}
var key = unsafe(match[2]);
if (key === "__proto__")
return;
var value = match[3] ? unsafe(match[4]) : true;
switch (value) {
case "true":
case "false":
case "null":
value = JSON.parse(value);
}
if (key.length > 2 && key.slice(-2) === "[]") {
key = key.substring(0, key.length - 2);
if (key === "__proto__")
return;
if (!p[key])
p[key] = [];
else if (!Array.isArray(p[key]))
p[key] = [p[key]];
}
if (Array.isArray(p[key]))
p[key].push(value);
else
p[key] = value;
});
Object.keys(out).filter(function(k, _, __) {
if (!out[k] || typeof out[k] !== "object" || Array.isArray(out[k]))
return false;
var parts = dotSplit(k);
var p2 = out;
var l = parts.pop();
var nl = l.replace(/\\\./g, ".");
parts.forEach(function(part, _2, __2) {
if (part === "__proto__")
return;
if (!p2[part] || typeof p2[part] !== "object")
p2[part] = {};
p2 = p2[part];
});
if (p2 === out && nl === l)
return false;
p2[nl] = out[k];
return true;
}).forEach(function(del, _, __) {
delete out[del];
});
return out;
}
function isQuoted(val) {
return val.charAt(0) === '"' && val.slice(-1) === '"' || val.charAt(0) === "'" && val.slice(-1) === "'";
}
function safe(val) {
return typeof val !== "string" || val.match(/[=\r\n]/) || val.match(/^\[/) || val.length > 1 && isQuoted(val) || val !== val.trim() ? JSON.stringify(val) : val.replace(/;/g, "\\;").replace(/#/g, "\\#");
}
function unsafe(val, doUnesc) {
val = (val || "").trim();
if (isQuoted(val)) {
if (val.charAt(0) === "'")
val = val.substr(1, val.length - 2);
try {
val = JSON.parse(val);
} catch (_) {
}
} else {
var esc = false;
var unesc = "";
for (var i = 0, l = val.length; i < l; i++) {
var c = val.charAt(i);
if (esc) {
if ("\\;#".indexOf(c) !== -1)
unesc += c;
else
unesc += "\\" + c;
esc = false;
} else if (";#".indexOf(c) !== -1)
break;
else if (c === "\\")
esc = true;
else
unesc += c;
}
if (esc)
unesc += "\\";
return unesc.trim();
}
return val;
}
}
});
// node_modules/rc/node_modules/strip-json-comments/index.js
var require_strip_json_comments = __commonJS({
"node_modules/rc/node_modules/strip-json-comments/index.js"(exports, module) {
"use strict";
var singleComment = 1;
var multiComment = 2;
function stripWithoutWhitespace() {
return "";
}
function stripWithWhitespace(str, start, end) {
return str.slice(start, end).replace(/\S/g, " ");
}
module.exports = function(str, opts) {
opts = opts || {};
var currentChar;
var nextChar;
var insideString = false;
var insideComment = false;
var offset = 0;
var ret = "";
var strip = opts.whitespace === false ? stripWithoutWhitespace : stripWithWhitespace;
for (var i = 0; i < str.length; i++) {
currentChar = str[i];
nextChar = str[i + 1];
if (!insideComment && currentChar === '"') {
var escaped = str[i - 1] === "\\" && str[i - 2] !== "\\";
if (!escaped) {
insideString = !insideString;
}
}
if (insideString) {
continue;
}
if (!insideComment && currentChar + nextChar === "//") {
ret += str.slice(offset, i);
offset = i;
insideComment = singleComment;
i++;
} else if (insideComment === singleComment && currentChar + nextChar === "\r\n") {
i++;
insideComment = false;
ret += strip(str, offset, i);
offset = i;
continue;
} else if (insideComment === singleComment && currentChar === "\n") {
insideComment = false;
ret += strip(str, offset, i);
offset = i;
} else if (!insideComment && currentChar + nextChar === "/*") {
ret += str.slice(offset, i);
offset = i;
insideComment = multiComment;
i++;
continue;
} else if (insideComment === multiComment && currentChar + nextChar === "*/") {
i++;
insideComment = false;
ret += strip(str, offset, i + 1);
offset = i + 1;
continue;
}
}
return ret + (insideComment ? strip(str.substr(offset)) : str.substr(offset));
};
}
});
// node_modules/rc/lib/utils.js
var require_utils = __commonJS({
"node_modules/rc/lib/utils.js"(exports) {
"use strict";
var fs8 = __require("fs");
var ini = require_ini();
var path14 = __require("path");
var stripJsonComments = require_strip_json_comments();
var parse = exports.parse = function(content) {
if (/^\s*{/.test(content))
return JSON.parse(stripJsonComments(content));
return ini.parse(content);
};
var file = exports.file = function() {
var args = [].slice.call(arguments).filter(function(arg) {
return arg != null;
});
for (var i in args)
if ("string" !== typeof args[i])
return;
var file2 = path14.join.apply(null, args);
var content;
try {
return fs8.readFileSync(file2, "utf-8");
} catch (err) {
return;
}
};
var json = exports.json = function() {
var content = file.apply(null, arguments);
return content ? parse(content) : null;
};
var env = exports.env = function(prefix, env2) {
env2 = env2 || process.env;
var obj = {};
var l = prefix.length;
for (var k in env2) {
if (k.toLowerCase().indexOf(prefix.toLowerCase()) === 0) {
var keypath = k.substring(l).split("__");
var _emptyStringIndex;
while ((_emptyStringIndex = keypath.indexOf("")) > -1) {
keypath.splice(_emptyStringIndex, 1);
}
var cursor = obj;
keypath.forEach(function _buildSubObj(_subkey, i) {
if (!_subkey || typeof cursor !== "object")
return;
if (i === keypath.length - 1)
cursor[_subkey] = env2[k];
if (cursor[_subkey] === void 0)
cursor[_subkey] = {};
cursor = cursor[_subkey];
});
}
}
return obj;
};
var find = exports.find = function() {
var rel = path14.join.apply(null, [].slice.call(arguments));
function find2(start, rel2) {
var file2 = path14.join(start, rel2);
try {
fs8.statSync(file2);
return file2;
} catch (err) {
if (path14.dirname(start) !== start)
return find2(path14.dirname(start), rel2);
}
}
return find2(process.cwd(), rel);
};
}
});
// node_modules/deep-extend/lib/deep-extend.js
var require_deep_extend = __commonJS({
"node_modules/deep-extend/lib/deep-extend.js"(exports, module) {
"use strict";
function isSpecificValue(val) {
return val instanceof Buffer || val instanceof Date || val instanceof RegExp ? true : false;
}
function cloneSpecificValue(val) {
if (val instanceof Buffer) {
var x = Buffer.alloc ? Buffer.alloc(val.length) : new Buffer(val.length);
val.copy(x);
return x;
} else if (val instanceof Date) {
return new Date(val.getTime());
} else if (val instanceof RegExp) {
return new RegExp(val);
} else {
throw new Error("Unexpected situation");
}
}
function deepCloneArray(arr) {
var clone = [];
arr.forEach(function(item, index) {
if (typeof item === "object" && item !== null) {
if (Array.isArray(item)) {
clone[index] = deepCloneArray(item);
} else if (isSpecificValue(item)) {
clone[index] = cloneSpecificValue(item);
} else {
clone[index] = deepExtend({}, item);
}
} else {
clone[index] = item;
}
});
return clone;
}
function safeGetProperty(object, property) {
return property === "__proto__" ? void 0 : object[property];
}
var deepExtend = module.exports = function() {
if (arguments.length < 1 || typeof arguments[0] !== "object") {
return false;
}
if (arguments.length < 2) {
return arguments[0];
}
var target = arguments[0];
var args = Array.prototype.slice.call(arguments, 1);
var val, src, clone;
args.forEach(function(obj) {
if (typeof obj !== "object" || obj === null || Array.isArray(obj)) {
return;
}
Object.keys(obj).forEach(function(key) {
src = safeGetProperty(target, key);
val = safeGetProperty(obj, key);
if (val === target) {
return;
} else if (typeof val !== "object" || val === null) {
target[key] = val;
return;
} else if (Array.isArray(val)) {
target[key] = deepCloneArray(val);
return;
} else if (isSpecificValue(val)) {
target[key] = cloneSpecificValue(val);
return;
} else if (typeof src !== "object" || src === null || Array.isArray(src)) {
target[key] = deepExtend({}, val);
return;
} else {
target[key] = deepExtend(src, val);
return;
}
});
});
return target;
};
}
});
// node_modules/minimist/index.js
var require_minimist = __commonJS({
"node_modules/minimist/index.js"(exports, module) {
"use strict";
function hasKey(obj, keys) {
var o = obj;
keys.slice(0, -1).forEach(function(key2) {
o = o[key2] || {};
});
var key = keys[keys.length - 1];
return key in o;
}
function isNumber(x) {
if (typeof x === "number") {
return true;
}
if (/^0x[0-9a-f]+$/i.test(x)) {
return true;
}
return /^[-+]?(?:\d+(?:\.\d*)?|\.\d+)(e[-+]?\d+)?$/.test(x);
}
function isConstructorOrProto(obj, key) {
return key === "constructor" && typeof obj[key] === "function" || key === "__proto__";
}
module.exports = function(args, opts) {
if (!opts) {
opts = {};
}
var flags = {
bools: {},
strings: {},
unknownFn: null
};
if (typeof opts.unknown === "function") {
flags.unknownFn = opts.unknown;
}
if (typeof opts.boolean === "boolean" && opts.boolean) {
flags.allBools = true;
} else {
[].concat(opts.boolean).filter(Boolean).forEach(function(key2) {
flags.bools[key2] = true;
});
}
var aliases = {};
function aliasIsBoolean(key2) {
return aliases[key2].some(function(x) {
return flags.bools[x];
});
}
Object.keys(opts.alias || {}).forEach(function(key2) {
aliases[key2] = [].concat(opts.alias[key2]);
aliases[key2].forEach(function(x) {
aliases[x] = [key2].concat(aliases[key2].filter(function(y) {
return x !== y;
}));
});
});
[].concat(opts.string).filter(Boolean).forEach(function(key2) {
flags.strings[key2] = true;
if (aliases[key2]) {
[].concat(aliases[key2]).forEach(function(k) {
flags.strings[k] = true;
});
}
});
var defaults = opts.default || {};
var argv = { _: [] };
function argDefined(key2, arg2) {
return flags.allBools && /^--[^=]+$/.test(arg2) || flags.strings[key2] || flags.bools[key2] || aliases[key2];
}
function setKey(obj, keys, value2) {
var o = obj;
for (var i2 = 0; i2 < keys.length - 1; i2++) {
var key2 = keys[i2];
if (isConstructorOrProto(o, key2)) {
return;
}
if (o[key2] === void 0) {
o[key2] = {};
}
if (o[key2] === Object.prototype || o[key2] === Number.prototype || o[key2] === String.prototype) {
o[key2] = {};
}
if (o[key2] === Array.prototype) {
o[key2] = [];
}
o = o[key2];
}
var lastKey = keys[keys.length - 1];
if (isConstructorOrProto(o, lastKey)) {
return;
}
if (o === Object.prototype || o === Number.prototype || o === String.prototype) {
o = {};
}
if (o === Array.prototype) {
o = [];
}
if (o[lastKey] === void 0 || flags.bools[lastKey] || typeof o[lastKey] === "boolean") {
o[lastKey] = value2;
} else if (Array.isArray(o[lastKey])) {
o[lastKey].push(value2);
} else {
o[lastKey] = [o[lastKey], value2];
}
}
function setArg(key2, val, arg2) {
if (arg2 && flags.unknownFn && !argDefined(key2, arg2)) {
if (flags.unknownFn(arg2) === false) {
return;
}
}
var value2 = !flags.strings[key2] && isNumber(val) ? Number(val) : val;
setKey(argv, key2.split("."), value2);
(aliases[key2] || []).forEach(function(x) {
setKey(argv, x.split("."), value2);
});
}
Object.keys(flags.bools).forEach(function(key2) {
setArg(key2, defaults[key2] === void 0 ? false : defaults[key2]);
});
var notFlags = [];
if (args.indexOf("--") !== -1) {
notFlags = args.slice(args.indexOf("--") + 1);
args = args.slice(0, args.indexOf("--"));
}
for (var i = 0; i < args.length; i++) {
var arg = args[i];
var key;
var next;
if (/^--.+=/.test(arg)) {
var m = arg.match(/^--([^=]+)=([\s\S]*)$/);
key = m[1];
var value = m[2];
if (flags.bools[key]) {
value = value !== "false";
}
setArg(key, value, arg);
} else if (/^--no-.+/.test(arg)) {
key = arg.match(/^--no-(.+)/)[1];
setArg(key, false, arg);
} else if (/^--.+/.test(arg)) {
key = arg.match(/^--(.+)/)[1];
next = args[i + 1];
if (next !== void 0 && !/^(-|--)[^-]/.test(next) && !flags.bools[key] && !flags.allBools && (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, next, arg);
i += 1;
} else if (/^(true|false)$/.test(next)) {
setArg(key, next === "true", arg);
i += 1;
} else {
setArg(key, flags.strings[key] ? "" : true, arg);
}
} else if (/^-[^-]+/.test(arg)) {
var letters = arg.slice(1, -1).split("");
var broken = false;
for (var j = 0; j < letters.length; j++) {
next = arg.slice(j + 2);
if (next === "-") {
setArg(letters[j], next, arg);
continue;
}
if (/[A-Za-z]/.test(letters[j]) && next[0] === "=") {
setArg(letters[j], next.slice(1), arg);
broken = true;
break;
}
if (/[A-Za-z]/.test(letters[j]) && /-?\d+(\.\d*)?(e-?\d+)?$/.test(next)) {
setArg(letters[j], next, arg);
broken = true;
break;
}
if (letters[j + 1] && letters[j + 1].match(/\W/)) {
setArg(letters[j], arg.slice(j + 2), arg);
broken = true;
break;
} else {
setArg(letters[j], flags.strings[letters[j]] ? "" : true, arg);
}
}
key = arg.slice(-1)[0];
if (!broken && key !== "-") {
if (args[i + 1] && !/^(-|--)[^-]/.test(args[i + 1]) && !flags.bools[key] && (aliases[key] ? !aliasIsBoolean(key) : true)) {
setArg(key, args[i + 1], arg);
i += 1;
} else if (args[i + 1] && /^(true|false)$/.test(args[i + 1])) {
setArg(key, args[i + 1] === "true", arg);
i += 1;
} else {
setArg(key, flags.strings[key] ? "" : true, arg);
}
}
} else {
if (!flags.unknownFn || flags.unknownFn(arg) !== false) {
argv._.push(flags.strings._ || !isNumber(arg) ? arg : Number(arg));
}
if (opts.stopEarly) {
argv._.push.apply(argv._, args.slice(i + 1));
break;
}
}
}
Object.keys(defaults).forEach(function(k) {
if (!hasKey(argv, k.split("."))) {
setKey(argv, k.split("."), defaults[k]);
(aliases[k] || []).forEach(function(x) {
setKey(argv, x.split("."), defaults[k]);
});
}
});
if (opts["--"]) {
argv["--"] = notFlags.slice();
} else {
notFlags.forEach(function(k) {
argv._.push(k);
});
}
return argv;
};
}
});
// node_modules/rc/index.js
var require_rc = __commonJS({
"node_modules/rc/index.js"(exports, module) {
var cc = require_utils();
var join4 = __require("path").join;
var deepExtend = require_deep_extend();
var etc = "/etc";
var win = process.platform === "win32";
var home = win ? process.env.USERPROFILE : process.env.HOME;
module.exports = function(name, defaults, argv, parse) {
if ("string" !== typeof name)
throw new Error("rc(name): name *must* be string");
if (!argv)
argv = require_minimist()(process.argv.slice(2));
defaults = ("string" === typeof defaults ? cc.json(defaults) : defaults) || {};
parse = parse || cc.parse;
var env = cc.env(name + "_");
var configs = [defaults];
var configFiles = [];
function addConfigFile(file) {
if (configFiles.indexOf(file) >= 0) return;
var fileConfig = cc.file(file);
if (fileConfig) {
configs.push(parse(fileConfig));
configFiles.push(file);
}
}
if (!win)
[
join4(etc, name, "config"),
join4(etc, name + "rc")
].forEach(addConfigFile);
if (home)
[
join4(home, ".config", name, "config"),
join4(home, ".config", name),
join4(home, "." + name, "config"),
join4(home, "." + name + "rc")
].forEach(addConfigFile);
addConfigFile(cc.find("." + name + "rc"));
if (env.config) addConfigFile(env.config);
if (argv.config) addConfigFile(argv.config);
return deepExtend.apply(null, configs.concat([
env,
argv,
configFiles.length ? { configs: configFiles, config: configFiles[configFiles.length - 1] } : void 0
]));
};
}
});
// node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/polyfills.js
var require_polyfills = __commonJS({
"node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/polyfills.js"(exports, module) {
var constants2 = __require("constants");
var origCwd = process.cwd;
var cwd5 = null;
var platform = process.env.GRACEFUL_FS_PLATFORM || process.platform;
process.cwd = function() {
if (!cwd5)
cwd5 = origCwd.call(process);
return cwd5;
};
try {
process.cwd();
} catch (er) {
}
if (typeof process.chdir === "function") {
chdir = process.chdir;
process.chdir = function(d) {
cwd5 = null;
chdir.call(process, d);
};
if (Object.setPrototypeOf) Object.setPrototypeOf(process.chdir, chdir);
}
var chdir;
module.exports = patch;
function patch(fs8) {
if (constants2.hasOwnProperty("O_SYMLINK") && process.version.match(/^v0\.6\.[0-2]|^v0\.5\./)) {
patchLchmod(fs8);
}
if (!fs8.lutimes) {
patchLutimes(fs8);
}
fs8.chown = chownFix(fs8.chown);
fs8.fchown = chownFix(fs8.fchown);
fs8.lchown = chownFix(fs8.lchown);
fs8.chmod = chmodFix(fs8.chmod);
fs8.fchmod = chmodFix(fs8.fchmod);
fs8.lchmod = chmodFix(fs8.lchmod);
fs8.chownSync = chownFixSync(fs8.chownSync);
fs8.fchownSync = chownFixSync(fs8.fchownSync);
fs8.lchownSync = chownFixSync(fs8.lchownSync);
fs8.chmodSync = chmodFixSync(fs8.chmodSync);
fs8.fchmodSync = chmodFixSync(fs8.fchmodSync);
fs8.lchmodSync = chmodFixSync(fs8.lchmodSync);
fs8.stat = statFix(fs8.stat);
fs8.fstat = statFix(fs8.fstat);
fs8.lstat = statFix(fs8.lstat);
fs8.statSync = statFixSync(fs8.statSync);
fs8.fstatSync = statFixSync(fs8.fstatSync);
fs8.lstatSync = statFixSync(fs8.lstatSync);
if (fs8.chmod && !fs8.lchmod) {
fs8.lchmod = function(path14, mode, cb) {
if (cb) process.nextTick(cb);
};
fs8.lchmodSync = function() {
};
}
if (fs8.chown && !fs8.lchown) {
fs8.lchown = function(path14, uid, gid, cb) {
if (cb) process.nextTick(cb);
};
fs8.lchownSync = function() {
};
}
if (platform === "win32") {
fs8.rename = typeof fs8.rename !== "function" ? fs8.rename : function(fs$rename) {
function rename(from, to, cb) {
var start = Date.now();
var backoff = 0;
fs$rename(from, to, function CB(er) {
if (er && (er.code === "EACCES" || er.code === "EPERM") && Date.now() - start < 6e4) {
setTimeout(function() {
fs8.stat(to, function(stater, st) {
if (stater && stater.code === "ENOENT")
fs$rename(from, to, CB);
else
cb(er);
});
}, backoff);
if (backoff < 100)
backoff += 10;
return;
}
if (cb) cb(er);
});
}
if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename);
return rename;
}(fs8.rename);
}
fs8.read = typeof fs8.read !== "function" ? fs8.read : function(fs$read) {
function read(fd, buffer, offset, length, position, callback_) {
var callback;
if (callback_ && typeof callback_ === "function") {
var eagCounter = 0;
callback = function(er, _, __) {
if (er && er.code === "EAGAIN" && eagCounter < 10) {
eagCounter++;
return fs$read.call(fs8, fd, buffer, offset, length, position, callback);
}
callback_.apply(this, arguments);
};
}
return fs$read.call(fs8, fd, buffer, offset, length, position, callback);
}
if (Object.setPrototypeOf) Object.setPrototypeOf(read, fs$read);
return read;
}(fs8.read);
fs8.readSync = typeof fs8.readSync !== "function" ? fs8.readSync : /* @__PURE__ */ function(fs$readSync) {
return function(fd, buffer, offset, length, position) {
var eagCounter = 0;
while (true) {
try {
return fs$readSync.call(fs8, fd, buffer, offset, length, position);
} catch (er) {
if (er.code === "EAGAIN" && eagCounter < 10) {
eagCounter++;
continue;
}
throw er;
}
}
};
}(fs8.readSync);
function patchLchmod(fs9) {
fs9.lchmod = function(path14, mode, callback) {
fs9.open(
path14,
constants2.O_WRONLY | constants2.O_SYMLINK,
mode,
function(err, fd) {
if (err) {
if (callback) callback(err);
return;
}
fs9.fchmod(fd, mode, function(err2) {
fs9.close(fd, function(err22) {
if (callback) callback(err2 || err22);
});
});
}
);
};
fs9.lchmodSync = function(path14, mode) {
var fd = fs9.openSync(path14, constants2.O_WRONLY | constants2.O_SYMLINK, mode);
var threw = true;
var ret;
try {
ret = fs9.fchmodSync(fd, mode);
threw = false;
} finally {
if (threw) {
try {
fs9.closeSync(fd);
} catch (er) {
}
} else {
fs9.closeSync(fd);
}
}
return ret;
};
}
function patchLutimes(fs9) {
if (constants2.hasOwnProperty("O_SYMLINK") && fs9.futimes) {
fs9.lutimes = function(path14, at, mt, cb) {
fs9.open(path14, constants2.O_SYMLINK, function(er, fd) {
if (er) {
if (cb) cb(er);
return;
}
fs9.futimes(fd, at, mt, function(er2) {
fs9.close(fd, function(er22) {
if (cb) cb(er2 || er22);
});
});
});
};
fs9.lutimesSync = function(path14, at, mt) {
var fd = fs9.openSync(path14, constants2.O_SYMLINK);
var ret;
var threw = true;
try {
ret = fs9.futimesSync(fd, at, mt);
threw = false;
} finally {
if (threw) {
try {
fs9.closeSync(fd);
} catch (er) {
}
} else {
fs9.closeSync(fd);
}
}
return ret;
};
} else if (fs9.futimes) {
fs9.lutimes = function(_a, _b, _c, cb) {
if (cb) process.nextTick(cb);
};
fs9.lutimesSync = function() {
};
}
}
function chmodFix(orig) {
if (!orig) return orig;
return function(target, mode, cb) {
return orig.call(fs8, target, mode, function(er) {
if (chownErOk(er)) er = null;
if (cb) cb.apply(this, arguments);
});
};
}
function chmodFixSync(orig) {
if (!orig) return orig;
return function(target, mode) {
try {
return orig.call(fs8, target, mode);
} catch (er) {
if (!chownErOk(er)) throw er;
}
};
}
function chownFix(orig) {
if (!orig) return orig;
return function(target, uid, gid, cb) {
return orig.call(fs8, target, uid, gid, function(er) {
if (chownErOk(er)) er = null;
if (cb) cb.apply(this, arguments);
});
};
}
function chownFixSync(orig) {
if (!orig) return orig;
return function(target, uid, gid) {
try {
return orig.call(fs8, target, uid, gid);
} catch (er) {
if (!chownErOk(er)) throw er;
}
};
}
function statFix(orig) {
if (!orig) return orig;
return function(target, options, cb) {
if (typeof options === "function") {
cb = options;
options = null;
}
function callback(er, stats) {
if (stats) {
if (stats.uid < 0) stats.uid += 4294967296;
if (stats.gid < 0) stats.gid += 4294967296;
}
if (cb) cb.apply(this, arguments);
}
return options ? orig.call(fs8, target, options, callback) : orig.call(fs8, target, callback);
};
}
function statFixSync(orig) {
if (!orig) return orig;
return function(target, options) {
var stats = options ? orig.call(fs8, target, options) : orig.call(fs8, target);
if (stats) {
if (stats.uid < 0) stats.uid += 4294967296;
if (stats.gid < 0) stats.gid += 4294967296;
}
return stats;
};
}
function chownErOk(er) {
if (!er)
return true;
if (er.code === "ENOSYS")
return true;
var nonroot = !process.getuid || process.getuid() !== 0;
if (nonroot) {
if (er.code === "EINVAL" || er.code === "EPERM")
return true;
}
return false;
}
}
}
});
// node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/legacy-streams.js
var require_legacy_streams = __commonJS({
"node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/legacy-streams.js"(exports, module) {
var Stream = __require("stream").Stream;
module.exports = legacy;
function legacy(fs8) {
return {
ReadStream,
WriteStream
};
function ReadStream(path14, options) {
if (!(this instanceof ReadStream)) return new ReadStream(path14, options);
Stream.call(this);
var self = this;
this.path = path14;
this.fd = null;
this.readable = true;
this.paused = false;
this.flags = "r";
this.mode = 438;
this.bufferSize = 64 * 1024;
options = options || {};
var keys = Object.keys(options);
for (var index = 0, length = keys.length; index < length; index++) {
var key = keys[index];
this[key] = options[key];
}
if (this.encoding) this.setEncoding(this.encoding);
if (this.start !== void 0) {
if ("number" !== typeof this.start) {
throw TypeError("start must be a Number");
}
if (this.end === void 0) {
this.end = Infinity;
} else if ("number" !== typeof this.end) {
throw TypeError("end must be a Number");
}
if (this.start > this.end) {
throw new Error("start must be <= end");
}
this.pos = this.start;
}
if (this.fd !== null) {
process.nextTick(function() {
self._read();
});
return;
}
fs8.open(this.path, this.flags, this.mode, function(err, fd) {
if (err) {
self.emit("error", err);
self.readable = false;
return;
}
self.fd = fd;
self.emit("open", fd);
self._read();
});
}
function WriteStream(path14, options) {
if (!(this instanceof WriteStream)) return new WriteStream(path14, options);
Stream.call(this);
this.path = path14;
this.fd = null;
this.writable = true;
this.flags = "w";
this.encoding = "binary";
this.mode = 438;
this.bytesWritten = 0;
options = options || {};
var keys = Object.keys(options);
for (var index = 0, length = keys.length; index < length; index++) {
var key = keys[index];
this[key] = options[key];
}
if (this.start !== void 0) {
if ("number" !== typeof this.start) {
throw TypeError("start must be a Number");
}
if (this.start < 0) {
throw new Error("start must be >= zero");
}
this.pos = this.start;
}
this.busy = false;
this._queue = [];
if (this.fd === null) {
this._open = fs8.open;
this._queue.push([this._open, this.path, this.flags, this.mode, void 0]);
this.flush();
}
}
}
}
});
// node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/clone.js
var require_clone = __commonJS({
"node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/clone.js"(exports, module) {
"use strict";
module.exports = clone;
var getPrototypeOf = Object.getPrototypeOf || function(obj) {
return obj.__proto__;
};
function clone(obj) {
if (obj === null || typeof obj !== "object")
return obj;
if (obj instanceof Object)
var copy = { __proto__: getPrototypeOf(obj) };
else
var copy = /* @__PURE__ */ Object.create(null);
Object.getOwnPropertyNames(obj).forEach(function(key) {
Object.defineProperty(copy, key, Object.getOwnPropertyDescriptor(obj, key));
});
return copy;
}
}
});
// node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/graceful-fs.js
var require_graceful_fs = __commonJS({
"node_modules/@pnpm/network.ca-file/node_modules/graceful-fs/graceful-fs.js"(exports, module) {
var fs8 = __require("fs");
var polyfills = require_polyfills();
var legacy = require_legacy_streams();
var clone = require_clone();
var util = __require("util");
var gracefulQueue;
var previousSymbol;
if (typeof Symbol === "function" && typeof Symbol.for === "function") {
gracefulQueue = Symbol.for("graceful-fs.queue");
previousSymbol = Symbol.for("graceful-fs.previous");
} else {
gracefulQueue = "___graceful-fs.queue";
previousSymbol = "___graceful-fs.previous";
}
function noop() {
}
function publishQueue(context, queue2) {
Object.defineProperty(context, gracefulQueue, {
get: function() {
return queue2;
}
});
}
var debug = noop;
if (util.debuglog)
debug = util.debuglog("gfs4");
else if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || ""))
debug = function() {
var m = util.format.apply(util, arguments);
m = "GFS4: " + m.split(/\n/).join("\nGFS4: ");
console.error(m);
};
if (!fs8[gracefulQueue]) {
queue = global[gracefulQueue] || [];
publishQueue(fs8, queue);
fs8.close = function(fs$close) {
function close(fd, cb) {
return fs$close.call(fs8, fd, function(err) {
if (!err) {
resetQueue();
}
if (typeof cb === "function")
cb.apply(this, arguments);
});
}
Object.defineProperty(close, previousSymbol, {
value: fs$close
});
return close;
}(fs8.close);
fs8.closeSync = function(fs$closeSync) {
function closeSync(fd) {
fs$closeSync.apply(fs8, arguments);
resetQueue();
}
Object.defineProperty(closeSync, previousSymbol, {
value: fs$closeSync
});
return closeSync;
}(fs8.closeSync);
if (/\bgfs4\b/i.test(process.env.NODE_DEBUG || "")) {
process.on("exit", function() {
debug(fs8[gracefulQueue]);
__require("assert").equal(fs8[gracefulQueue].length, 0);
});
}
}
var queue;
if (!global[gracefulQueue]) {
publishQueue(global, fs8[gracefulQueue]);
}
module.exports = patch(clone(fs8));
if (process.env.TEST_GRACEFUL_FS_GLOBAL_PATCH && !fs8.__patched) {
module.exports = patch(fs8);
fs8.__patched = true;
}
function patch(fs9) {
polyfills(fs9);
fs9.gracefulify = patch;
fs9.createReadStream = createReadStream;
fs9.createWriteStream = createWriteStream;
var fs$readFile = fs9.readFile;
fs9.readFile = readFile3;
function readFile3(path14, options, cb) {
if (typeof options === "function")
cb = options, options = null;
return go$readFile(path14, options, cb);
function go$readFile(path15, options2, cb2, startTime) {
return fs$readFile(path15, options2, function(err) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([go$readFile, [path15, options2, cb2], err, startTime || Date.now(), Date.now()]);
else {
if (typeof cb2 === "function")
cb2.apply(this, arguments);
}
});
}
}
var fs$writeFile = fs9.writeFile;
fs9.writeFile = writeFile2;
function writeFile2(path14, data, options, cb) {
if (typeof options === "function")
cb = options, options = null;
return go$writeFile(path14, data, options, cb);
function go$writeFile(path15, data2, options2, cb2, startTime) {
return fs$writeFile(path15, data2, options2, function(err) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([go$writeFile, [path15, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
else {
if (typeof cb2 === "function")
cb2.apply(this, arguments);
}
});
}
}
var fs$appendFile = fs9.appendFile;
if (fs$appendFile)
fs9.appendFile = appendFile;
function appendFile(path14, data, options, cb) {
if (typeof options === "function")
cb = options, options = null;
return go$appendFile(path14, data, options, cb);
function go$appendFile(path15, data2, options2, cb2, startTime) {
return fs$appendFile(path15, data2, options2, function(err) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([go$appendFile, [path15, data2, options2, cb2], err, startTime || Date.now(), Date.now()]);
else {
if (typeof cb2 === "function")
cb2.apply(this, arguments);
}
});
}
}
var fs$copyFile = fs9.copyFile;
if (fs$copyFile)
fs9.copyFile = copyFile;
function copyFile(src, dest, flags, cb) {
if (typeof flags === "function") {
cb = flags;
flags = 0;
}
return go$copyFile(src, dest, flags, cb);
function go$copyFile(src2, dest2, flags2, cb2, startTime) {
return fs$copyFile(src2, dest2, flags2, function(err) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([go$copyFile, [src2, dest2, flags2, cb2], err, startTime || Date.now(), Date.now()]);
else {
if (typeof cb2 === "function")
cb2.apply(this, arguments);
}
});
}
}
var fs$readdir = fs9.readdir;
fs9.readdir = readdir2;
var noReaddirOptionVersions = /^v[0-5]\./;
function readdir2(path14, options, cb) {
if (typeof options === "function")
cb = options, options = null;
var go$readdir = noReaddirOptionVersions.test(process.version) ? function go$readdir2(path15, options2, cb2, startTime) {
return fs$readdir(path15, fs$readdirCallback(
path15,
options2,
cb2,
startTime
));
} : function go$readdir2(path15, options2, cb2, startTime) {
return fs$readdir(path15, options2, fs$readdirCallback(
path15,
options2,
cb2,
startTime
));
};
return go$readdir(path14, options, cb);
function fs$readdirCallback(path15, options2, cb2, startTime) {
return function(err, files) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([
go$readdir,
[path15, options2, cb2],
err,
startTime || Date.now(),
Date.now()
]);
else {
if (files && files.sort)
files.sort();
if (typeof cb2 === "function")
cb2.call(this, err, files);
}
};
}
}
if (process.version.substr(0, 4) === "v0.8") {
var legStreams = legacy(fs9);
ReadStream = legStreams.ReadStream;
WriteStream = legStreams.WriteStream;
}
var fs$ReadStream = fs9.ReadStream;
if (fs$ReadStream) {
ReadStream.prototype = Object.create(fs$ReadStream.prototype);
ReadStream.prototype.open = ReadStream$open;
}
var fs$WriteStream = fs9.WriteStream;
if (fs$WriteStream) {
WriteStream.prototype = Object.create(fs$WriteStream.prototype);
WriteStream.prototype.open = WriteStream$open;
}
Object.defineProperty(fs9, "ReadStream", {
get: function() {
return ReadStream;
},
set: function(val) {
ReadStream = val;
},
enumerable: true,
configurable: true
});
Object.defineProperty(fs9, "WriteStream", {
get: function() {
return WriteStream;
},
set: function(val) {
WriteStream = val;
},
enumerable: true,
configurable: true
});
var FileReadStream = ReadStream;
Object.defineProperty(fs9, "FileReadStream", {
get: function() {
return FileReadStream;
},
set: function(val) {
FileReadStream = val;
},
enumerable: true,
configurable: true
});
var FileWriteStream = WriteStream;
Object.defineProperty(fs9, "FileWriteStream", {
get: function() {
return FileWriteStream;
},
set: function(val) {
FileWriteStream = val;
},
enumerable: true,
configurable: true
});
function ReadStream(path14, options) {
if (this instanceof ReadStream)
return fs$ReadStream.apply(this, arguments), this;
else
return ReadStream.apply(Object.create(ReadStream.prototype), arguments);
}
function ReadStream$open() {
var that = this;
open(that.path, that.flags, that.mode, function(err, fd) {
if (err) {
if (that.autoClose)
that.destroy();
that.emit("error", err);
} else {
that.fd = fd;
that.emit("open", fd);
that.read();
}
});
}
function WriteStream(path14, options) {
if (this instanceof WriteStream)
return fs$WriteStream.apply(this, arguments), this;
else
return WriteStream.apply(Object.create(WriteStream.prototype), arguments);
}
function WriteStream$open() {
var that = this;
open(that.path, that.flags, that.mode, function(err, fd) {
if (err) {
that.destroy();
that.emit("error", err);
} else {
that.fd = fd;
that.emit("open", fd);
}
});
}
function createReadStream(path14, options) {
return new fs9.ReadStream(path14, options);
}
function createWriteStream(path14, options) {
return new fs9.WriteStream(path14, options);
}
var fs$open = fs9.open;
fs9.open = open;
function open(path14, flags, mode, cb) {
if (typeof mode === "function")
cb = mode, mode = null;
return go$open(path14, flags, mode, cb);
function go$open(path15, flags2, mode2, cb2, startTime) {
return fs$open(path15, flags2, mode2, function(err, fd) {
if (err && (err.code === "EMFILE" || err.code === "ENFILE"))
enqueue([go$open, [path15, flags2, mode2, cb2], err, startTime || Date.now(), Date.now()]);
else {
if (typeof cb2 === "function")
cb2.apply(this, arguments);
}
});
}
}
return fs9;
}
function enqueue(elem) {
debug("ENQUEUE", elem[0].name, elem[1]);
fs8[gracefulQueue].push(elem);
retry();
}
var retryTimer;
function resetQueue() {
var now = Date.now();
for (var i = 0; i < fs8[gracefulQueue].length; ++i) {
if (fs8[gracefulQueue][i].length > 2) {
fs8[gracefulQueue][i][3] = now;
fs8[gracefulQueue][i][4] = now;
}
}
retry();
}
function retry() {
clearTimeout(retryTimer);
retryTimer = void 0;
if (fs8[gracefulQueue].length === 0)
return;
var elem = fs8[gracefulQueue].shift();
var fn = elem[0];
var args = elem[1];
var err = elem[2];
var startTime = elem[3];
var lastTime = elem[4];
if (startTime === void 0) {
debug("RETRY", fn.name, args);
fn.apply(null, args);
} else if (Date.now() - startTime >= 6e4) {
debug("TIMEOUT", fn.name, args);
var cb = args.pop();
if (typeof cb === "function")
cb.call(null, err);
} else {
var sinceAttempt = Date.now() - lastTime;
var sinceStart = Math.max(lastTime - startTime, 1);
var desiredDelay = Math.min(sinceStart * 1.2, 100);
if (sinceAttempt >= desiredDelay) {
debug("RETRY", fn.name, args);
fn.apply(null, args.concat([startTime]));
} else {
fs8[gracefulQueue].push(elem);
}
}
if (retryTimer === void 0) {
retryTimer = setTimeout(retry, 0);
}
}
}
});
// node_modules/@pnpm/network.ca-file/dist/ca-file.js
var require_ca_file = __commonJS({
"node_modules/@pnpm/network.ca-file/dist/ca-file.js"(exports) {
"use strict";
var __importDefault = exports && exports.__importDefault || function(mod) {
return mod && mod.__esModule ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.readCAFileSync = void 0;
var graceful_fs_1 = __importDefault(require_graceful_fs());
function readCAFileSync(filePath) {
try {
const contents = graceful_fs_1.default.readFileSync(filePath, "utf8");
const delim = "-----END CERTIFICATE-----";
const output = contents.split(delim).filter((ca) => Boolean(ca.trim())).map((ca) => `${ca.trimLeft()}${delim}`);
return output;
} catch (err) {
if (err.code === "ENOENT")
return void 0;
throw err;
}
}
exports.readCAFileSync = readCAFileSync;
}
});
// node_modules/@pnpm/network.ca-file/dist/index.js
var require_dist = __commonJS({
"node_modules/@pnpm/network.ca-file/dist/index.js"(exports) {
"use strict";
var __createBinding = exports && exports.__createBinding || (Object.create ? function(o, m, k, k2) {
if (k2 === void 0) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() {
return m[k];
} };
}
Object.defineProperty(o, k2, desc);
} : function(o, m, k, k2) {
if (k2 === void 0) k2 = k;
o[k2] = m[k];
});
var __exportStar = exports && exports.__exportStar || function(m, exports2) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports2, p)) __createBinding(exports2, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require_ca_file(), exports);
}
});
// node_modules/proto-list/proto-list.js
var require_proto_list = __commonJS({
"node_modules/proto-list/proto-list.js"(exports, module) {
module.exports = ProtoList;
function setProto(obj, proto) {
if (typeof Object.setPrototypeOf === "function")
return Object.setPrototypeOf(obj, proto);
else
obj.__proto__ = proto;
}
function ProtoList() {
this.list = [];
var root = null;
Object.defineProperty(this, "root", {
get: function() {
return root;
},
set: function(r) {
root = r;
if (this.list.length) {
setProto(this.list[this.list.length - 1], r);
}
},
enumerable: true,
configurable: true
});
}
ProtoList.prototype = {
get length() {
return this.list.length;
},
get keys() {
var k = [];
for (var i in this.list[0]) k.push(i);
return k;
},
get snapshot() {
var o = {};
this.keys.forEach(function(k) {
o[k] = this.get(k);
}, this);
return o;
},
get store() {
return this.list[0];
},
push: function(obj) {
if (typeof obj !== "object") obj = { valueOf: obj };
if (this.list.length >= 1) {
setProto(this.list[this.list.length - 1], obj);
}
setProto(obj, this.root);
return this.list.push(obj);
},
pop: function() {
if (this.list.length >= 2) {
setProto(this.list[this.list.length - 2], this.root);
}
return this.list.pop();
},
unshift: function(obj) {
setProto(obj, this.list[0] || this.root);
return this.list.unshift(obj);
},
shift: function() {
if (this.list.length === 1) {
setProto(this.list[0], this.root);
}
return this.list.shift();
},
get: function(key) {
return this.list[0][key];
},
set: function(key, val, save) {
if (!this.length) this.push({});
if (save && this.list[0].hasOwnProperty(key)) this.push({});
return this.list[0][key] = val;
},
forEach: function(fn, thisp) {
for (var key in this.list[0]) fn.call(thisp, key, this.list[0][key]);
},
slice: function() {
return this.list.slice.apply(this.list, arguments);
},
splice: function() {
var ret = this.list.splice.apply(this.list, arguments);
for (var i = 0, l = this.list.length; i < l; i++) {
setProto(this.list[i], this.list[i + 1] || this.root);
}
return ret;
}
};
}
});
// node_modules/config-chain/index.js
var require_config_chain = __commonJS({
"node_modules/config-chain/index.js"(exports, module) {
var ProtoList = require_proto_list();
var path14 = __require("path");
var fs8 = __require("fs");
var ini = require_ini();
var EE = __require("events").EventEmitter;
var url2 = __require("url");
var http = __require("http");
var exports = module.exports = function() {
var args = [].slice.call(arguments), conf = new ConfigChain();
while (args.length) {
var a = args.shift();
if (a) conf.push("string" === typeof a ? json(a) : a);
}
return conf;
};
var find = exports.find = function() {
var rel = path14.join.apply(null, [].slice.call(arguments));
function find2(start, rel2) {
var file = path14.join(start, rel2);
try {
fs8.statSync(file);
return file;
} catch (err) {
if (path14.dirname(start) !== start)
return find2(path14.dirname(start), rel2);
}
}
return find2(__chunk_dirname, rel);
};
var parse = exports.parse = function(content, file, type) {
content = "" + content;
if (!type) {
try {
return JSON.parse(content);
} catch (er) {
return ini.parse(content);
}
} else if (type === "json") {
if (this.emit) {
try {
return JSON.parse(content);
} catch (er) {
this.emit("error", er);
}
} else {
return JSON.parse(content);
}
} else {
return ini.parse(content);
}
};
var json = exports.json = function() {
var args = [].slice.call(arguments).filter(function(arg) {
return arg != null;
});
var file = path14.join.apply(null, args);
var content;
try {
content = fs8.readFileSync(file, "utf-8");
} catch (err) {
return;
}
return parse(content, file, "json");
};
var env = exports.env = function(prefix, env2) {
env2 = env2 || process.env;
var obj = {};
var l = prefix.length;
for (var k in env2) {
if (k.indexOf(prefix) === 0)
obj[k.substring(l)] = env2[k];
}
return obj;
};
exports.ConfigChain = ConfigChain;
function ConfigChain() {
EE.apply(this);
ProtoList.apply(this, arguments);
this._awaiting = 0;
this._saving = 0;
this.sources = {};
}
var extras = {
constructor: { value: ConfigChain }
};
Object.keys(EE.prototype).forEach(function(k) {
extras[k] = Object.getOwnPropertyDescriptor(EE.prototype, k);
});
ConfigChain.prototype = Object.create(ProtoList.prototype, extras);
ConfigChain.prototype.del = function(key, where) {
if (where) {
var target = this.sources[where];
target = target && target.data;
if (!target) {
return this.emit("error", new Error("not found " + where));
}
delete target[key];
} else {
for (var i = 0, l = this.list.length; i < l; i++) {
delete this.list[i][key];
}
}
return this;
};
ConfigChain.prototype.set = function(key, value, where) {
var target;
if (where) {
target = this.sources[where];
target = target && target.data;
if (!target) {
return this.emit("error", new Error("not found " + where));
}
} else {
target = this.list[0];
if (!target) {
return this.emit("error", new Error("cannot set, no confs!"));
}
}
target[key] = value;
return this;
};
ConfigChain.prototype.get = function(key, where) {
if (where) {
where = this.sources[where];
if (where) where = where.data;
if (where && Object.hasOwnProperty.call(where, key)) return where[key];
return void 0;
}
return this.list[0][key];
};
ConfigChain.prototype.save = function(where, type, cb) {
if (typeof type === "function") cb = type, type = null;
var target = this.sources[where];
if (!target || !(target.path || target.source) || !target.data) {
return this.emit("error", new Error("bad save target: " + where));
}
if (target.source) {
var pref = target.prefix || "";
Object.keys(target.data).forEach(function(k) {
target.source[pref + k] = target.data[k];
});
return this;
}
var type = type || target.type;
var data = target.data;
if (target.type === "json") {
data = JSON.stringify(data);
} else {
data = ini.stringify(data);
}
this._saving++;
fs8.writeFile(target.path, data, "utf8", function(er) {
this._saving--;
if (er) {
if (cb) return cb(er);
else return this.emit("error", er);
}
if (this._saving === 0) {
if (cb) cb();
this.emit("save");
}
}.bind(this));
return this;
};
ConfigChain.prototype.addFile = function(file, type, name) {
name = name || file;
var marker = { __source__: name };
this.sources[name] = { path: file, type };
this.push(marker);
this._await();
fs8.readFile(file, "utf8", function(er, data) {
if (er) this.emit("error", er);
this.addString(data, file, type, marker);
}.bind(this));
return this;
};
ConfigChain.prototype.addEnv = function(prefix, env2, name) {
name = name || "env";
var data = exports.env(prefix, env2);
this.sources[name] = { data, source: env2, prefix };
return this.add(data, name);
};
ConfigChain.prototype.addUrl = function(req, type, name) {
this._await();
var href = url2.format(req);
name = name || href;
var marker = { __source__: name };
this.sources[name] = { href, type };
this.push(marker);
http.request(req, function(res) {
var c = [];
var ct = res.headers["content-type"];
if (!type) {
type = ct.indexOf("json") !== -1 ? "json" : ct.indexOf("ini") !== -1 ? "ini" : href.match(/\.json$/) ? "json" : href.match(/\.ini$/) ? "ini" : null;
marker.type = type;
}
res.on("data", c.push.bind(c)).on("end", function() {
this.addString(Buffer.concat(c), href, type, marker);
}.bind(this)).on("error", this.emit.bind(this, "error"));
}.bind(this)).on("error", this.emit.bind(this, "error")).end();
return this;
};
ConfigChain.prototype.addString = function(data, file, type, marker) {
data = this.parse(data, file, type);
this.add(data, marker);
return this;
};
ConfigChain.prototype.add = function(data, marker) {
if (marker && typeof marker === "object") {
var i = this.list.indexOf(marker);
if (i === -1) {
return this.emit("error", new Error("bad marker"));
}
this.splice(i, 1, data);
marker = marker.__source__;
this.sources[marker] = this.sources[marker] || {};
this.sources[marker].data = data;
this._resolve();
} else {
if (typeof marker === "string") {
this.sources[marker] = this.sources[marker] || {};
this.sources[marker].data = data;
}
this._await();
this.push(data);
process.nextTick(this._resolve.bind(this));
}
return this;
};
ConfigChain.prototype.parse = exports.parse;
ConfigChain.prototype._await = function() {
this._awaiting++;
};
ConfigChain.prototype._resolve = function() {
this._awaiting--;
if (this._awaiting === 0) this.emit("load", this);
};
}
});
// node_modules/@pnpm/npm-conf/lib/envKeyToSetting.js
var require_envKeyToSetting = __commonJS({
"node_modules/@pnpm/npm-conf/lib/envKeyToSetting.js"(exports, module) {
module.exports = function(x) {
const colonIndex = x.indexOf(":");
if (colonIndex === -1) {
return normalize(x);
}
const firstPart = x.substr(0, colonIndex);
const secondPart = x.substr(colonIndex + 1);
return `${normalize(firstPart)}:${normalize(secondPart)}`;
};
function normalize(s) {
s = s.toLowerCase();
if (s === "_authtoken") return "_authToken";
let r = s[0];
for (let i = 1; i < s.length; i++) {
r += s[i] === "_" ? "-" : s[i];
}
return r;
}
}
});
// node_modules/@pnpm/config.env-replace/dist/env-replace.js
var require_env_replace = __commonJS({
"node_modules/@pnpm/config.env-replace/dist/env-replace.js"(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envReplace = void 0;
var ENV_EXPR = /(?<!\\)(\\*)\$\{([^${}]+)\}/g;
function envReplace(settingValue, env) {
return settingValue.replace(ENV_EXPR, replaceEnvMatch.bind(null, env));
}
exports.envReplace = envReplace;
function replaceEnvMatch(env, orig, escape, name) {
if (escape.length % 2) {
return orig.slice((escape.length + 1) / 2);
}
const envValue = getEnvValue(env, name);
if (envValue === void 0) {
throw new Error(`Failed to replace env in config: ${orig}`);
}
return `${escape.slice(escape.length / 2)}${envValue}`;
}
var ENV_VALUE = /([^:-]+)(:?)-(.+)/;
function getEnvValue(env, name) {
const matched = name.match(ENV_VALUE);
if (!matched)
return env[name];
const [, variableName, colon, fallback] = matched;
if (Object.prototype.hasOwnProperty.call(env, variableName)) {
return !env[variableName] && colon ? fallback : env[variableName];
}
return fallback;
}
}
});
// node_modules/@pnpm/config.env-replace/dist/index.js
var require_dist2 = __commonJS({
"node_modules/@pnpm/config.env-replace/dist/index.js"(exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.envReplace = void 0;
var env_replace_1 = require_env_replace();
Object.defineProperty(exports, "envReplace", { enumerable: true, get: function() {
return env_replace_1.envReplace;
} });
}
});
// node_modules/@pnpm/npm-conf/lib/util.js
var require_util = __commonJS({
"node_modules/@pnpm/npm-conf/lib/util.js"(exports) {
"use strict";
var fs8 = __require("fs");
var path14 = __require("path");
var { envReplace } = require_dist2();
var parseKey = (key) => {
if (typeof key !== "string") {
return key;
}
return envReplace(key, process.env);
};
var parseField = (types, field, key) => {
if (typeof field !== "string") {
return field;
}
const typeList = [].concat(types[key]);
const isPath = typeList.indexOf(path14) !== -1;
const isBool = typeList.indexOf(Boolean) !== -1;
const isString = typeList.indexOf(String) !== -1;
const isNumber = typeList.indexOf(Number) !== -1;
field = `${field}`.trim();
if (/^".*"$/.test(field)) {
try {
field = JSON.parse(field);
} catch (error) {
throw new Error(`Failed parsing JSON config key ${key}: ${field}`);
}
}
if (isBool && !isString && field === "") {
return true;
}
switch (field) {
// eslint-disable-line default-case
case "true": {
return true;
}
case "false": {
return false;
}
case "null": {
return null;
}
case "undefined": {
return void 0;
}
}
field = envReplace(field, process.env);
if (isPath) {
const regex = process.platform === "win32" ? /^~(\/|\\)/ : /^~\//;
if (regex.test(field) && process.env.HOME) {
field = path14.resolve(process.env.HOME, field.substr(2));
}
field = path14.resolve(field);
}
if (isNumber && !isNaN(field)) {
field = Number(field);
}
return field;
};
var findPrefix = (name) => {
name = path14.resolve(name);
let walkedUp = false;
while (path14.basename(name) === "node_modules") {
name = path14.dirname(name);
walkedUp = true;
}
if (walkedUp) {
return name;
}
const find = (name2, original) => {
const regex = /^[a-zA-Z]:(\\|\/)?$/;
if (name2 === "/" || process.platform === "win32" && regex.test(name2)) {
return original;
}
try {
const files = fs8.readdirSync(name2);
if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
return name2;
}
const dirname5 = path14.dirname(name2);
if (dirname5 === name2) {
return original;
}
return find(dirname5, original);
} catch (error) {
if (name2 === original) {
if (error.code === "ENOENT") {
return original;
}
throw error;
}
return original;
}
};
return find(name, name);
};
exports.envReplace = envReplace;
exports.findPrefix = findPrefix;
exports.parseField = parseField;
exports.parseKey = parseKey;
}
});
// node_modules/@pnpm/npm-conf/lib/types.js
var require_types = __commonJS({
"node_modules/@pnpm/npm-conf/lib/types.js"(exports) {
"use strict";
var path14 = __require("path");
var Stream = __require("stream").Stream;
var url2 = __require("url");
var Umask = () => {
};
var getLocalAddresses = () => [];
var semver3 = () => {
};
exports.types = {
access: [null, "restricted", "public"],
"allow-same-version": Boolean,
"always-auth": Boolean,
also: [null, "dev", "development"],
audit: Boolean,
"auth-type": ["legacy", "sso", "saml", "oauth"],
"bin-links": Boolean,
browser: [null, String],
ca: [null, String, Array],
cafile: path14,
cache: path14,
"cache-lock-stale": Number,
"cache-lock-retries": Number,
"cache-lock-wait": Number,
"cache-max": Number,
"cache-min": Number,
cert: [null, String],
cidr: [null, String, Array],
color: ["always", Boolean],
depth: Number,
description: Boolean,
dev: Boolean,
"dry-run": Boolean,
editor: String,
"engine-strict": Boolean,
force: Boolean,
"fetch-retries": Number,
"fetch-retry-factor": Number,
"fetch-retry-mintimeout": Number,
"fetch-retry-maxtimeout": Number,
git: String,
"git-tag-version": Boolean,
"commit-hooks": Boolean,
global: Boolean,
globalconfig: path14,
"global-style": Boolean,
group: [Number, String],
"https-proxy": [null, url2],
"user-agent": String,
"ham-it-up": Boolean,
"heading": String,
"if-present": Boolean,
"ignore-prepublish": Boolean,
"ignore-scripts": Boolean,
"init-module": path14,
"init-author-name": String,
"init-author-email": String,
"init-author-url": ["", url2],
"init-license": String,
"init-version": semver3,
json: Boolean,
key: [null, String],
"legacy-bundling": Boolean,
link: Boolean,
// local-address must be listed as an IP for a local network interface
// must be IPv4 due to node bug
"local-address": getLocalAddresses(),
loglevel: ["silent", "error", "warn", "notice", "http", "timing", "info", "verbose", "silly"],
logstream: Stream,
"logs-max": Number,
long: Boolean,
maxsockets: Number,
message: String,
"metrics-registry": [null, String],
"node-options": [null, String],
"node-version": [null, semver3],
"no-proxy": [null, String, Array],
offline: Boolean,
"onload-script": [null, String],
only: [null, "dev", "development", "prod", "production"],
optional: Boolean,
"package-lock": Boolean,
otp: [null, String],
"package-lock-only": Boolean,
parseable: Boolean,
"prefer-offline": Boolean,
"prefer-online": Boolean,
prefix: path14,
production: Boolean,
progress: Boolean,
proxy: [null, false, url2],
provenance: Boolean,
// allow proxy to be disabled explicitly
"read-only": Boolean,
"rebuild-bundle": Boolean,
registry: [null, url2],
rollback: Boolean,
save: Boolean,
"save-bundle": Boolean,
"save-dev": Boolean,
"save-exact": Boolean,
"save-optional": Boolean,
"save-prefix": String,
"save-prod": Boolean,
scope: String,
"script-shell": [null, String],
"scripts-prepend-node-path": [false, true, "auto", "warn-only"],
searchopts: String,
searchexclude: [null, String],
searchlimit: Number,
searchstaleness: Number,
"send-metrics": Boolean,
shell: String,
shrinkwrap: Boolean,
"sign-git-tag": Boolean,
"sso-poll-frequency": Number,
"sso-type": [null, "oauth", "saml"],
"strict-ssl": Boolean,
tag: String,
timing: Boolean,
tmp: path14,
unicode: Boolean,
"unsafe-perm": Boolean,
usage: Boolean,
user: [Number, String],
userconfig: path14,
umask: Umask,
version: Boolean,
"tag-version-prefix": String,
versions: Boolean,
viewer: String,
_exit: Boolean
};
}
});
// node_modules/@pnpm/npm-conf/lib/conf.js
var require_conf = __commonJS({
"node_modules/@pnpm/npm-conf/lib/conf.js"(exports, module) {
"use strict";
var { readCAFileSync } = require_dist();
var fs8 = __require("fs");
var path14 = __require("path");
var { ConfigChain } = require_config_chain();
var envKeyToSetting = require_envKeyToSetting();
var util = require_util();
var Conf = class extends ConfigChain {
// https://github.com/npm/cli/blob/latest/lib/config/core.js#L203-L217
constructor(base, types) {
super(base);
this.root = base;
this._parseField = util.parseField.bind(null, types || require_types());
}
// https://github.com/npm/cli/blob/latest/lib/config/core.js#L326-L338
add(data, marker) {
try {
for (const [key, value] of Object.entries(data)) {
const substKey = util.parseKey(key);
if (substKey !== key) {
delete data[key];
}
data[substKey] = this._parseField(value, substKey);
}
} catch (error) {
throw error;
}
return super.add(data, marker);
}
// https://github.com/npm/cli/blob/latest/lib/config/core.js#L306-L319
addFile(file, name) {
name = name || file;
const marker = { __source__: name };
this.sources[name] = { path: file, type: "ini" };
this.push(marker);
this._await();
try {
const contents = fs8.readFileSync(file, "utf8");
this.addString(contents, file, "ini", marker);
} catch (error) {
if (error.code === "ENOENT") {
this.add({}, marker);
} else if (error.code !== "EISDIR") {
return `Issue while reading "${file}". ${error.message}`;
}
}
}
// https://github.com/npm/cli/blob/latest/lib/config/core.js#L341-L357
addEnv(env) {
env = env || process.env;
const conf = {};
Object.keys(env).filter((x) => /^npm_config_/i.test(x)).forEach((x) => {
if (!env[x]) {
return;
}
const key = envKeyToSetting(x.substr(11));
const rawVal = env[x];
conf[key] = deserializeEnvVal(key, rawVal);
});
return super.addEnv("", conf, "env");
}
// https://github.com/npm/cli/blob/latest/lib/config/load-prefix.js
loadPrefix() {
const cli = this.list[0];
Object.defineProperty(this, "prefix", {
enumerable: true,
set: (prefix) => {
const g = this.get("global");
this[g ? "globalPrefix" : "localPrefix"] = prefix;
},
get: () => {
const g = this.get("global");
return g ? this.globalPrefix : this.localPrefix;
}
});
Object.defineProperty(this, "globalPrefix", {
enumerable: true,
set: (prefix) => {
this.set("prefix", prefix);
},
get: () => {
return path14.resolve(this.get("prefix"));
}
});
let p;
Object.defineProperty(this, "localPrefix", {
enumerable: true,
set: (prefix) => {
p = prefix;
},
get: () => {
return p;
}
});
if (Object.prototype.hasOwnProperty.call(cli, "prefix")) {
p = path14.resolve(cli.prefix);
} else {
try {
const prefix = util.findPrefix(process.cwd());
p = prefix;
} catch (error) {
throw error;
}
}
return p;
}
// https://github.com/npm/cli/blob/latest/lib/config/load-cafile.js
loadCAFile(file) {
if (!file) {
return;
}
const ca = readCAFileSync(file);
if (ca) {
this.set("ca", ca);
}
}
// https://github.com/npm/cli/blob/latest/lib/config/set-user.js
loadUser() {
const defConf = this.root;
if (this.get("global")) {
return;
}
if (process.env.SUDO_UID) {
defConf.user = Number(process.env.SUDO_UID);
return;
}
const prefix = path14.resolve(this.get("prefix"));
try {
const stats = fs8.statSync(prefix);
defConf.user = stats.uid;
} catch (error) {
if (error.code === "ENOENT") {
return;
}
throw error;
}
}
};
function deserializeEnvVal(envKey, envValue) {
function deserializeList(envValue2) {
const npmConfigSep = "\n\n";
if (envValue2.indexOf(npmConfigSep)) {
return envValue2.split(npmConfigSep);
}
return envValue2.split(",");
}
switch (envKey) {
case "hoist-pattern":
case "public-hoist-pattern":
return deserializeList(envValue);
}
return envValue;
}
module.exports = Conf;
}
});
// node_modules/@pnpm/npm-conf/lib/defaults.js
var require_defaults = __commonJS({
"node_modules/@pnpm/npm-conf/lib/defaults.js"(exports) {
"use strict";
var os4 = __require("os");
var path14 = __require("path");
var temp = os4.tmpdir();
var uidOrPid = process.getuid ? process.getuid() : process.pid;
var hasUnicode = () => true;
var isWindows3 = process.platform === "win32";
var osenv = {
editor: () => process.env.EDITOR || process.env.VISUAL || (isWindows3 ? "notepad.exe" : "vi"),
shell: () => isWindows3 ? process.env.COMSPEC || "cmd.exe" : process.env.SHELL || "/bin/bash"
};
var umask = {
fromString: () => process.umask()
};
var home = os4.homedir();
if (home) {
process.env.HOME = home;
} else {
home = path14.resolve(temp, "npm-" + uidOrPid);
}
var cacheExtra = process.platform === "win32" ? "npm-cache" : ".npm";
var cacheRoot = process.platform === "win32" && process.env.APPDATA || home;
var cache = path14.resolve(cacheRoot, cacheExtra);
var defaults;
var globalPrefix;
Object.defineProperty(exports, "defaults", {
get: function() {
if (defaults) return defaults;
if (process.env.PREFIX) {
globalPrefix = process.env.PREFIX;
} else if (process.platform === "win32") {
globalPrefix = path14.dirname(process.execPath);
} else {
globalPrefix = path14.dirname(path14.dirname(process.execPath));
if (process.env.DESTDIR) {
globalPrefix = path14.join(process.env.DESTDIR, globalPrefix);
}
}
defaults = {
access: null,
"allow-same-version": false,
"always-auth": false,
also: null,
audit: true,
"auth-type": "legacy",
"bin-links": true,
browser: null,
ca: null,
cafile: null,
cache,
"cache-lock-stale": 6e4,
"cache-lock-retries": 10,
"cache-lock-wait": 1e4,
"cache-max": Infinity,
"cache-min": 10,
cert: null,
cidr: null,
color: process.env.NO_COLOR == null,
depth: Infinity,
description: true,
dev: false,
"dry-run": false,
editor: osenv.editor(),
"engine-strict": false,
force: false,
"fetch-retries": 2,
"fetch-retry-factor": 10,
"fetch-retry-mintimeout": 1e4,
"fetch-retry-maxtimeout": 6e4,
git: "git",
"git-tag-version": true,
"commit-hooks": true,
global: false,
globalconfig: path14.resolve(globalPrefix, "etc", "npmrc"),
"global-style": false,
group: process.platform === "win32" ? 0 : process.env.SUDO_GID || process.getgid && process.getgid(),
"ham-it-up": false,
heading: "npm",
"if-present": false,
"ignore-prepublish": false,
"ignore-scripts": false,
"init-module": path14.resolve(home, ".npm-init.js"),
"init-author-name": "",
"init-author-email": "",
"init-author-url": "",
"init-version": "1.0.0",
"init-license": "ISC",
json: false,
key: null,
"legacy-bundling": false,
link: false,
"local-address": void 0,
loglevel: "notice",
logstream: process.stderr,
"logs-max": 10,
long: false,
maxsockets: 50,
message: "%s",
"metrics-registry": null,
"node-options": null,
// We remove node-version to fix the issue described here: https://github.com/pnpm/pnpm/issues/4203#issuecomment-1133872769
"offline": false,
"onload-script": false,
only: null,
optional: true,
otp: null,
"package-lock": true,
"package-lock-only": false,
parseable: false,
"prefer-offline": false,
"prefer-online": false,
prefix: globalPrefix,
production: true,
"progress": !process.env.TRAVIS && !process.env.CI,
provenance: false,
proxy: null,
"https-proxy": null,
"no-proxy": null,
"user-agent": "npm/{npm-version} node/{node-version} {platform} {arch}",
"read-only": false,
"rebuild-bundle": true,
registry: "https://registry.npmjs.org/",
rollback: true,
save: true,
"save-bundle": false,
"save-dev": false,
"save-exact": false,
"save-optional": false,
"save-prefix": "^",
"save-prod": false,
scope: "",
"script-shell": null,
"scripts-prepend-node-path": "warn-only",
searchopts: "",
searchexclude: null,
searchlimit: 20,
searchstaleness: 15 * 60,
"send-metrics": false,
shell: osenv.shell(),
shrinkwrap: true,
"sign-git-tag": false,
"sso-poll-frequency": 500,
"sso-type": "oauth",
"strict-ssl": true,
tag: "latest",
"tag-version-prefix": "v",
timing: false,
tmp: temp,
unicode: hasUnicode(),
"unsafe-perm": process.platform === "win32" || process.platform === "cygwin" || !(process.getuid && process.setuid && process.getgid && process.setgid) || process.getuid() !== 0,
usage: false,
user: process.platform === "win32" ? 0 : "nobody",
userconfig: path14.resolve(home, ".npmrc"),
umask: process.umask ? process.umask() : umask.fromString("022"),
version: false,
versions: false,
viewer: process.platform === "win32" ? "browser" : "man",
_exit: true
};
return defaults;
}
});
}
});
// node_modules/@pnpm/npm-conf/index.js
var require_npm_conf = __commonJS({
"node_modules/@pnpm/npm-conf/index.js"(exports, module) {
"use strict";
var path14 = __require("path");
var Conf = require_conf();
var _defaults = require_defaults();
module.exports = (opts, types, defaults) => {
const conf = new Conf(Object.assign({}, _defaults.defaults, defaults), types);
conf.add(Object.assign({}, opts), "cli");
const warnings = [];
let failedToLoadBuiltInConfig = false;
if (__require.resolve.paths) {
const paths = __require.resolve.paths("npm");
let npmPath;
try {
npmPath = __require.resolve("npm", { paths: paths.slice(-1) });
} catch (error) {
failedToLoadBuiltInConfig = true;
}
if (npmPath) {
warnings.push(conf.addFile(path14.resolve(path14.dirname(npmPath), "..", "npmrc"), "builtin"));
}
}
conf.addEnv();
conf.loadPrefix();
const projectConf = path14.resolve(conf.localPrefix, ".npmrc");
const userConf = conf.get("userconfig");
if (!conf.get("global") && projectConf !== userConf) {
warnings.push(conf.addFile(projectConf, "project"));
} else {
conf.add({}, "project");
}
if (conf.get("workspace-prefix") && conf.get("workspace-prefix") !== projectConf) {
const workspaceConf = path14.resolve(conf.get("workspace-prefix"), ".npmrc");
warnings.push(conf.addFile(workspaceConf, "workspace"));
}
warnings.push(conf.addFile(conf.get("userconfig"), "user"));
if (conf.get("prefix")) {
const etc = path14.resolve(conf.get("prefix"), "etc");
conf.root.globalconfig = path14.resolve(etc, "npmrc");
conf.root.globalignorefile = path14.resolve(etc, "npmignore");
}
warnings.push(conf.addFile(conf.get("globalconfig"), "global"));
conf.loadUser();
const caFile = conf.get("cafile");
if (caFile) {
conf.loadCAFile(caFile);
}
return {
config: conf,
warnings: warnings.filter(Boolean),
failedToLoadBuiltInConfig
};
};
Object.defineProperty(module.exports, "defaults", {
get() {
return _defaults.defaults;
},
enumerable: true
});
}
});
// node_modules/registry-auth-token/index.js
var require_registry_auth_token = __commonJS({
"node_modules/registry-auth-token/index.js"(exports, module) {
var npmConf = require_npm_conf();
var tokenKey = ":_authToken";
var legacyTokenKey = ":_auth";
var userKey = ":username";
var passwordKey = ":_password";
module.exports = function getRegistryAuthToken() {
let checkUrl;
let options;
if (arguments.length >= 2) {
checkUrl = arguments[0];
options = Object.assign({}, arguments[1]);
} else if (typeof arguments[0] === "string") {
checkUrl = arguments[0];
} else {
options = Object.assign({}, arguments[0]);
}
options = options || {};
const providedNpmrc = options.npmrc;
options.npmrc = (options.npmrc ? {
config: {
get: (key) => providedNpmrc[key]
}
} : npmConf()).config;
checkUrl = checkUrl || options.npmrc.get("registry") || npmConf.defaults.registry;
return getRegistryAuthInfo(checkUrl, options) || getLegacyAuthInfo(options.npmrc);
};
function urlResolve(from, to) {
const resolvedUrl = new URL(to, new URL(from.startsWith("//") ? `./${from}` : from, "resolve://"));
if (resolvedUrl.protocol === "resolve:") {
const { pathname, search, hash } = resolvedUrl;
return pathname + search + hash;
}
return resolvedUrl.toString();
}
function getRegistryAuthInfo(checkUrl, options) {
let parsed = checkUrl instanceof URL ? checkUrl : new URL(checkUrl.startsWith("//") ? `http:${checkUrl}` : checkUrl);
let pathname;
while (pathname !== "/" && parsed.pathname !== pathname) {
pathname = parsed.pathname || "/";
const regUrl = "//" + parsed.host + pathname.replace(/\/$/, "");
const authInfo = getAuthInfoForUrl(regUrl, options.npmrc);
if (authInfo) {
return authInfo;
}
if (!options.recursive) {
return /\/$/.test(checkUrl) ? void 0 : getRegistryAuthInfo(new URL("./", parsed), options);
}
parsed.pathname = urlResolve(normalizePath(pathname), "..") || "/";
}
return void 0;
}
function getLegacyAuthInfo(npmrc) {
if (!npmrc.get("_auth")) {
return void 0;
}
const token = replaceEnvironmentVariable(npmrc.get("_auth"));
return { token, type: "Basic" };
}
function normalizePath(path14) {
return path14[path14.length - 1] === "/" ? path14 : path14 + "/";
}
function getAuthInfoForUrl(regUrl, npmrc) {
const bearerAuth = getBearerToken(npmrc.get(regUrl + tokenKey) || npmrc.get(regUrl + "/" + tokenKey));
if (bearerAuth) {
return bearerAuth;
}
const username = npmrc.get(regUrl + userKey) || npmrc.get(regUrl + "/" + userKey);
const password = npmrc.get(regUrl + passwordKey) || npmrc.get(regUrl + "/" + passwordKey);
const basicAuth = getTokenForUsernameAndPassword(username, password);
if (basicAuth) {
return basicAuth;
}
const basicAuthWithToken = getLegacyAuthToken(npmrc.get(regUrl + legacyTokenKey) || npmrc.get(regUrl + "/" + legacyTokenKey));
if (basicAuthWithToken) {
return basicAuthWithToken;
}
return void 0;
}
function replaceEnvironmentVariable(token) {
return token.replace(/^\$\{?([^}]*)\}?$/, function(fullMatch, envVar) {
return process.env[envVar];
});
}
function getBearerToken(tok) {
if (!tok) {
return void 0;
}
const token = replaceEnvironmentVariable(tok);
return { token, type: "Bearer" };
}
function getTokenForUsernameAndPassword(username, password) {
if (!username || !password) {
return void 0;
}
const pass = Buffer.from(replaceEnvironmentVariable(password), "base64").toString("utf8");
const token = Buffer.from(username + ":" + pass, "utf8").toString("base64");
return {
token,
type: "Basic",
password: pass,
username
};
}
function getLegacyAuthToken(tok) {
if (!tok) {
return void 0;
}
const token = replaceEnvironmentVariable(tok);
return { token, type: "Basic" };
}
}
});
// packages/cli/src/interactiveCli.tsx
var import_react122 = __toESM(require_react(), 1);
import { basename as basename5 } from "node:path";
// packages/cli/src/ui/AppContainer.tsx
var import_react121 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/HistoryItemDisplay.tsx
var import_react25 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/messages/UserMessage.tsx
var import_react2 = __toESM(require_react(), 1);
// packages/cli/src/ui/textConstants.ts
var SCREEN_READER_USER_PREFIX = "User: ";
var SCREEN_READER_MODEL_PREFIX = "Model: ";
var SCREEN_READER_LOADING = "loading";
var SCREEN_READER_RESPONDING = "responding";
var GENERIC_WORKING_LABEL = "Working...";
// packages/cli/src/ui/components/shared/HalfLinePaddedBox.tsx
var import_react = __toESM(require_react(), 1);
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
var HalfLinePaddedBox = (props) => {
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
if (props.useBackgroundColor === false || isScreenReaderEnabled) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: props.children });
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HalfLinePaddedBoxInternal, { ...props });
};
var HalfLinePaddedBoxInternal = ({
backgroundBaseColor,
backgroundOpacity,
children
}) => {
const { terminalWidth } = useUIState();
const terminalBg = theme.background.primary || "black";
const backgroundColor = (0, import_react.useMemo)(() => {
const resolvedBase = resolveColor(backgroundBaseColor) || backgroundBaseColor;
const resolvedTerminalBg = resolveColor(terminalBg) || terminalBg;
return interpolateColor(
resolvedTerminalBg,
resolvedBase,
backgroundOpacity
);
}, [backgroundBaseColor, backgroundOpacity, terminalBg]);
if (!backgroundColor) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children });
}
const noTrueColor = !supportsTrueColor();
if (noTrueColor) {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: terminalWidth, backgroundColor, paddingY: 1, children });
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
Box_default,
{
width: terminalWidth,
flexDirection: "column",
alignItems: "stretch",
minHeight: 1,
flexShrink: 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: terminalWidth, flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: backgroundColor, children: "\u2584".repeat(terminalWidth) }) }),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
Box_default,
{
width: terminalWidth,
flexDirection: "column",
alignItems: "stretch",
backgroundColor,
children
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: terminalWidth, flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: backgroundColor, children: "\u2580".repeat(terminalWidth) }) })
]
}
);
};
// packages/cli/src/ui/components/messages/UserMessage.tsx
var import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
var UserMessage = ({ text, width }) => {
const prefix = "> ";
const prefixWidth = prefix.length;
const isSlashCommand2 = isSlashCommand(text);
const config = useConfig();
const useBackgroundColorSetting = config.getUseBackgroundColor();
const useBackgroundColor = useBackgroundColorSetting && !!theme.background.message;
const textColor = isSlashCommand2 ? theme.text.accent : theme.text.primary;
const displayText = (0, import_react2.useMemo)(() => {
if (!text) return text;
return text.split("\n").map((line) => {
const transformations = calculateTransformationsForLine(line);
const { transformedLine } = calculateTransformedLine(
line,
0,
// line index doesn't matter since cursor is [-1, -1]
[-1, -1],
transformations
);
return transformedLine;
}).join("\n");
}, [text]);
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
HalfLinePaddedBox,
{
backgroundBaseColor: theme.background.message,
backgroundOpacity: 1,
useBackgroundColor,
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
Box_default,
{
flexDirection: "row",
paddingY: 0,
marginY: useBackgroundColor ? 0 : 1,
paddingX: useBackgroundColor ? 1 : 0,
alignSelf: "flex-start",
width,
children: [
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { width: prefixWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
Text,
{
color: theme.text.accent,
"aria-label": SCREEN_READER_USER_PREFIX,
children: prefix
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { wrap: "wrap", color: textColor, children: displayText }) })
]
}
)
}
);
};
// packages/cli/src/ui/components/messages/UserShellMessage.tsx
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
var UserShellMessage = ({
text,
width
}) => {
const config = useConfig();
const useBackgroundColorSetting = config.getUseBackgroundColor();
const useBackgroundColor = useBackgroundColorSetting && !!theme.background.message;
const commandToDisplay = text.startsWith("!") ? text.substring(1) : text;
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
HalfLinePaddedBox,
{
backgroundBaseColor: theme.background.message,
backgroundOpacity: 1,
useBackgroundColor,
children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
Box_default,
{
paddingY: 0,
marginY: useBackgroundColor ? 0 : 1,
paddingX: useBackgroundColor ? 1 : 0,
width,
children: [
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.ui.symbol, children: "$ " }),
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: theme.text.primary, children: commandToDisplay })
]
}
)
}
);
};
// packages/cli/src/ui/utils/MarkdownDisplay.tsx
var import_react5 = __toESM(require_react(), 1);
// packages/cli/src/ui/utils/TableRenderer.tsx
var import_react3 = __toESM(require_react(), 1);
// packages/cli/src/ui/utils/markdownParsingUtils.ts
var import_chalk = __toESM(require_source(), 1);
// packages/cli/src/ui/utils/latexToUnicode.ts
var GREEK_LETTERS = Object.freeze({
alpha: "\u03B1",
beta: "\u03B2",
gamma: "\u03B3",
delta: "\u03B4",
epsilon: "\u03B5",
zeta: "\u03B6",
eta: "\u03B7",
theta: "\u03B8",
iota: "\u03B9",
kappa: "\u03BA",
lambda: "\u03BB",
mu: "\u03BC",
nu: "\u03BD",
xi: "\u03BE",
omicron: "\u03BF",
pi: "\u03C0",
rho: "\u03C1",
sigma: "\u03C3",
tau: "\u03C4",
upsilon: "\u03C5",
phi: "\u03C6",
chi: "\u03C7",
psi: "\u03C8",
omega: "\u03C9",
Alpha: "\u0391",
Beta: "\u0392",
Gamma: "\u0393",
Delta: "\u0394",
Epsilon: "\u0395",
Zeta: "\u0396",
Eta: "\u0397",
Theta: "\u0398",
Iota: "\u0399",
Kappa: "\u039A",
Lambda: "\u039B",
Mu: "\u039C",
Nu: "\u039D",
Xi: "\u039E",
Omicron: "\u039F",
Pi: "\u03A0",
Rho: "\u03A1",
Sigma: "\u03A3",
Tau: "\u03A4",
Upsilon: "\u03A5",
Phi: "\u03A6",
Chi: "\u03A7",
Psi: "\u03A8",
Omega: "\u03A9",
varepsilon: "\u03B5",
vartheta: "\u03D1",
varphi: "\u03C6",
varrho: "\u03F1",
varsigma: "\u03C2",
varpi: "\u03D6"
});
var LATEX_COMMANDS = Object.freeze({
// Arrows
to: "\u2192",
rightarrow: "\u2192",
Rightarrow: "\u21D2",
leftarrow: "\u2190",
Leftarrow: "\u21D0",
leftrightarrow: "\u2194",
Leftrightarrow: "\u21D4",
mapsto: "\u21A6",
longrightarrow: "\u27F6",
longleftarrow: "\u27F5",
longleftrightarrow: "\u27F7",
uparrow: "\u2191",
downarrow: "\u2193",
Uparrow: "\u21D1",
Downarrow: "\u21D3",
hookrightarrow: "\u21AA",
hookleftarrow: "\u21A9",
// Ellipses
dots: "\u2026",
ldots: "\u2026",
cdots: "\u22EF",
vdots: "\u22EE",
ddots: "\u22F1",
// Arithmetic / comparison
times: "\xD7",
cdot: "\xB7",
div: "\xF7",
pm: "\xB1",
mp: "\u2213",
ast: "\u2217",
leq: "\u2264",
le: "\u2264",
geq: "\u2265",
ge: "\u2265",
neq: "\u2260",
ne: "\u2260",
ll: "\u226A",
gg: "\u226B",
approx: "\u2248",
equiv: "\u2261",
sim: "\u223C",
simeq: "\u2243",
cong: "\u2245",
propto: "\u221D",
// Set theory
in: "\u2208",
notin: "\u2209",
ni: "\u220B",
subset: "\u2282",
supset: "\u2283",
subseteq: "\u2286",
supseteq: "\u2287",
cup: "\u222A",
cap: "\u2229",
setminus: "\u2216",
emptyset: "\u2205",
varnothing: "\u2205",
// Logic
forall: "\u2200",
exists: "\u2203",
nexists: "\u2204",
neg: "\xAC",
lnot: "\xAC",
land: "\u2227",
wedge: "\u2227",
lor: "\u2228",
vee: "\u2228",
oplus: "\u2295",
otimes: "\u2297",
implies: "\u27F9",
iff: "\u27FA",
// Large operators
sum: "\u2211",
prod: "\u220F",
coprod: "\u2210",
int: "\u222B",
iint: "\u222C",
iiint: "\u222D",
oint: "\u222E",
// Calculus
partial: "\u2202",
nabla: "\u2207",
infty: "\u221E",
// Misc letters / constants
ell: "\u2113",
hbar: "\u210F",
Re: "\u211C",
Im: "\u2111",
aleph: "\u2135",
beth: "\u2136",
// Brackets / delimiters
lbrace: "{",
rbrace: "}",
lbrack: "[",
rbrack: "]",
langle: "\u27E8",
rangle: "\u27E9",
lceil: "\u2308",
rceil: "\u2309",
lfloor: "\u230A",
rfloor: "\u230B",
// Geometry / misc
perp: "\u22A5",
parallel: "\u2225",
angle: "\u2220",
triangle: "\u25B3",
square: "\u25A1",
circ: "\u2218",
bullet: "\u2022",
star: "\u22C6",
prime: "\u2032",
dag: "\u2020",
ddag: "\u2021",
therefore: "\u2234",
because: "\u2235",
top: "\u22A4",
bot: "\u22A5",
// Operator names (`\log`, `\sin`, …) render in LaTeX as upright text. In a
// terminal the closest equivalent is the lowercase word itself.
log: "log",
ln: "ln",
lg: "lg",
exp: "exp",
sin: "sin",
cos: "cos",
tan: "tan",
cot: "cot",
sec: "sec",
csc: "csc",
arcsin: "arcsin",
arccos: "arccos",
arctan: "arctan",
sinh: "sinh",
cosh: "cosh",
tanh: "tanh",
max: "max",
min: "min",
sup: "sup",
inf: "inf",
lim: "lim",
limsup: "lim sup",
liminf: "lim inf",
arg: "arg",
det: "det",
dim: "dim",
ker: "ker",
gcd: "gcd",
deg: "deg",
hom: "hom",
mod: "mod",
bmod: "mod",
pmod: "mod",
// Whitespace commands — render as visible space so layout is roughly right.
quad: " ",
qquad: " ",
// These are all "thin-space" style commands in LaTeX; render as a single
// space so the surrounding tokens don't jam together.
",": " ",
";": " ",
":": " ",
"!": ""
});
var SUBSCRIPT_MAP = Object.freeze({
"0": "\u2080",
"1": "\u2081",
"2": "\u2082",
"3": "\u2083",
"4": "\u2084",
"5": "\u2085",
"6": "\u2086",
"7": "\u2087",
"8": "\u2088",
"9": "\u2089",
"+": "\u208A",
"-": "\u208B",
"=": "\u208C",
"(": "\u208D",
")": "\u208E",
a: "\u2090",
e: "\u2091",
h: "\u2095",
i: "\u1D62",
j: "\u2C7C",
k: "\u2096",
l: "\u2097",
m: "\u2098",
n: "\u2099",
o: "\u2092",
p: "\u209A",
r: "\u1D63",
s: "\u209B",
t: "\u209C",
u: "\u1D64",
v: "\u1D65",
x: "\u2093"
});
var SUPERSCRIPT_MAP = Object.freeze({
"0": "\u2070",
"1": "\xB9",
"2": "\xB2",
"3": "\xB3",
"4": "\u2074",
"5": "\u2075",
"6": "\u2076",
"7": "\u2077",
"8": "\u2078",
"9": "\u2079",
"+": "\u207A",
"-": "\u207B",
"=": "\u207C",
"(": "\u207D",
")": "\u207E",
a: "\u1D43",
b: "\u1D47",
c: "\u1D9C",
d: "\u1D48",
e: "\u1D49",
f: "\u1DA0",
g: "\u1D4D",
h: "\u02B0",
i: "\u2071",
j: "\u02B2",
k: "\u1D4F",
l: "\u02E1",
m: "\u1D50",
n: "\u207F",
o: "\u1D52",
p: "\u1D56",
r: "\u02B3",
s: "\u02E2",
t: "\u1D57",
u: "\u1D58",
v: "\u1D5B",
w: "\u02B7",
x: "\u02E3",
y: "\u02B8",
z: "\u1DBB"
});
function stripMathDelimiters(text) {
let out = text.replace(
/\$\$([^$]+)\$\$/g,
(_, inner) => applyMathModeConversions(inner)
);
out = out.replace(/\$([^$\n]+?)\$/g, (match, inner) => {
const hasLatexMarkers = /\\[A-Za-z]|[\\_^]/.test(inner);
const isSingleVariable = /^\s*[A-Za-z]\s*$/.test(inner);
if (hasLatexMarkers || isSingleVariable) {
return applyMathModeConversions(inner);
}
return match;
});
return out;
}
function convertTextFormatting(text) {
let out = text;
out = out.replace(
/\\(?:textbf|mathbf)\{([^{}]*)\}/g,
(_, inner) => `**${inner}**`
);
out = out.replace(
/\\(?:textit|emph|mathit)\{([^{}]*)\}/g,
(_, inner) => `*${inner}*`
);
out = out.replace(
/\\(?:text|mathrm|mathsf|mathtt|mathbb|mathcal|mathfrak|operatorname)\{([^{}]*)\}/g,
(_, inner) => inner
);
return out;
}
function convertFractionsAndRoots(text) {
let out = text;
out = out.replace(
/\\frac\{([^{}]*)\}\{([^{}]*)\}/g,
(_, num, den) => `(${num})/(${den})`
);
out = out.replace(
/\\sqrt\[([^\]]*)\]\{([^{}]*)\}/g,
(_, index, radicand) => `${index}\u221A(${radicand})`
);
out = out.replace(
/\\sqrt\{([^{}]*)\}/g,
(_, radicand) => `\u221A(${radicand})`
);
return out;
}
function convertEscapedSpecials(text) {
let out = text.replace(/\\([{}[\]_%&#$|])/g, (_, ch) => ch);
out = out.replace(/\\ /g, " ");
return out;
}
function convertNamedCommands(text) {
return text.replace(
/\\([A-Za-z]+)(?![A-Za-z])/g,
(match, name) => GREEK_LETTERS[name] ?? LATEX_COMMANDS[name] ?? match
);
}
function convertPunctuationCommands(text) {
return text.replace(/\\([,;:!])/g, (_, ch) => {
switch (ch) {
case ",":
case ";":
case ":":
return " ";
case "!":
return "";
default:
return ch;
}
});
}
function convertLineBreaks(text) {
return text.replace(/\\\\/g, "\n");
}
function convertSubSuperScripts(text) {
const charsOf = (s) => Array.from(s);
let out = text.replace(/_\{([^{}]+)\}/g, (match, inner) => {
const chars = charsOf(inner);
if (chars.every((c) => SUBSCRIPT_MAP[c] !== void 0)) {
return chars.map((c) => SUBSCRIPT_MAP[c]).join("");
}
return match;
});
out = out.replace(/\^\{([^{}]+)\}/g, (match, inner) => {
const chars = charsOf(inner);
if (chars.every((c) => SUPERSCRIPT_MAP[c] !== void 0)) {
return chars.map((c) => SUPERSCRIPT_MAP[c]).join("");
}
return match;
});
out = out.replace(
/([A-Za-z0-9)\]])_([A-Za-z0-9+\-=()])/g,
(match, base, c) => {
const sub = SUBSCRIPT_MAP[c];
return sub ? `${base}${sub}` : match;
}
);
out = out.replace(
/([A-Za-z0-9)\]])\^([A-Za-z0-9+\-=()])/g,
(match, base, c) => {
const sup = SUPERSCRIPT_MAP[c];
return sup ? `${base}${sup}` : match;
}
);
return out;
}
function applyMathModeConversions(text) {
let out = text;
out = convertTextFormatting(out);
out = convertFractionsAndRoots(out);
out = convertEscapedSpecials(out);
out = convertLineBreaks(out);
out = convertNamedCommands(out);
out = convertPunctuationCommands(out);
out = convertSubSuperScripts(out);
return out;
}
function applyProseConversions(text) {
let out = text;
out = convertTextFormatting(out);
out = convertFractionsAndRoots(out);
out = convertEscapedSpecials(out);
out = convertNamedCommands(out);
out = convertPunctuationCommands(out);
return out;
}
function convertLatexToUnicode(input) {
if (!input) return input;
if (input.indexOf("\\") === -1 && input.indexOf("$") === -1) {
return input;
}
let text = input;
text = stripMathDelimiters(text);
text = applyProseConversions(text);
return text;
}
// packages/cli/src/ui/utils/markdownParsingUtils.ts
var BOLD_MARKER_LENGTH = 2;
var ITALIC_MARKER_LENGTH = 1;
var STRIKETHROUGH_MARKER_LENGTH = 2;
var INLINE_CODE_MARKER_LENGTH = 1;
var UNDERLINE_TAG_START_LENGTH = 3;
var UNDERLINE_TAG_END_LENGTH = 4;
var ansiColorize = (str, color) => {
if (!color) return str;
const resolved = resolveColor(color);
if (!resolved) return str;
if (resolved.startsWith("#")) {
return import_chalk.default.hex(resolved)(str);
}
const mappedHex = INK_NAME_TO_HEX_MAP[resolved];
if (mappedHex) {
return import_chalk.default.hex(mappedHex)(str);
}
if (INK_SUPPORTED_NAMES.has(resolved)) {
switch (resolved) {
case "black":
return import_chalk.default.black(str);
case "red":
return import_chalk.default.red(str);
case "green":
return import_chalk.default.green(str);
case "yellow":
return import_chalk.default.yellow(str);
case "blue":
return import_chalk.default.blue(str);
case "magenta":
return import_chalk.default.magenta(str);
case "cyan":
return import_chalk.default.cyan(str);
case "white":
return import_chalk.default.white(str);
case "gray":
case "grey":
return import_chalk.default.gray(str);
default:
return str;
}
}
return str;
};
var MASK_SENTINEL = "\uE000";
var MASK_PATTERN = /\uE000(\d+)\uE000/g;
var convertLatexPreservingSpans = (text) => {
const preserved = [];
const masked = text.replace(/(`+)([^`\n]+?)\1|https?:\/\/\S+/g, (match) => {
const index = preserved.push(match) - 1;
return `${MASK_SENTINEL}${index}${MASK_SENTINEL}`;
});
const converted = convertLatexToUnicode(masked);
return converted.replace(
MASK_PATTERN,
// Fallback to the literal match if the index is somehow out of range —
// defensive against the unlikely case where the PUA sentinel appears in
// user input. Without the fallback, replace would emit "undefined".
(match, i) => preserved[Number(i)] ?? match
);
};
var parseMarkdownToANSI = (rawText, defaultColor) => {
const baseColor = defaultColor ?? theme.text.primary;
const text = convertLatexPreservingSpans(rawText);
if (!/[*_~`<[https?:]/.test(text)) {
return ansiColorize(text, baseColor);
}
let result = "";
const inlineRegex = /(\*\*\*.*?\*\*\*|\*\*.*?\*\*|\*.*?\*|_.*?_|~~.*?~~|\[.*?\]\(.*?\)|`+.+?`+|<u>.*?<\/u>|https?:\/\/\S+)/g;
let lastIndex = 0;
let match;
while ((match = inlineRegex.exec(text)) !== null) {
if (match.index > lastIndex) {
result += ansiColorize(text.slice(lastIndex, match.index), baseColor);
}
const fullMatch = match[0];
let styledPart = "";
try {
if (fullMatch.endsWith("***") && fullMatch.startsWith("***") && fullMatch.length > (BOLD_MARKER_LENGTH + ITALIC_MARKER_LENGTH) * 2) {
styledPart = import_chalk.default.bold(
import_chalk.default.italic(
parseMarkdownToANSI(
fullMatch.slice(
BOLD_MARKER_LENGTH + ITALIC_MARKER_LENGTH,
-BOLD_MARKER_LENGTH - ITALIC_MARKER_LENGTH
),
baseColor
)
)
);
} else if (fullMatch.endsWith("**") && fullMatch.startsWith("**") && fullMatch.length > BOLD_MARKER_LENGTH * 2) {
styledPart = import_chalk.default.bold(
parseMarkdownToANSI(
fullMatch.slice(BOLD_MARKER_LENGTH, -BOLD_MARKER_LENGTH),
baseColor
)
);
} else if (fullMatch.length > ITALIC_MARKER_LENGTH * 2 && (fullMatch.startsWith("*") && fullMatch.endsWith("*") || fullMatch.startsWith("_") && fullMatch.endsWith("_")) && !/\w/.test(text.substring(match.index - 1, match.index)) && !/\w/.test(
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 1)
) && !/\S[./\\]/.test(text.substring(match.index - 2, match.index)) && !/[./\\]\S/.test(
text.substring(inlineRegex.lastIndex, inlineRegex.lastIndex + 2)
)) {
styledPart = import_chalk.default.italic(
parseMarkdownToANSI(
fullMatch.slice(ITALIC_MARKER_LENGTH, -ITALIC_MARKER_LENGTH),
baseColor
)
);
} else if (fullMatch.startsWith("~~") && fullMatch.endsWith("~~") && fullMatch.length > STRIKETHROUGH_MARKER_LENGTH * 2) {
styledPart = import_chalk.default.strikethrough(
parseMarkdownToANSI(
fullMatch.slice(
STRIKETHROUGH_MARKER_LENGTH,
-STRIKETHROUGH_MARKER_LENGTH
),
baseColor
)
);
} else if (fullMatch.startsWith("`") && fullMatch.endsWith("`") && fullMatch.length > INLINE_CODE_MARKER_LENGTH) {
const codeMatch = fullMatch.match(/^(`+)(.+?)\1$/s);
if (codeMatch && codeMatch[2]) {
styledPart = ansiColorize(codeMatch[2], theme.text.accent);
}
} else if (fullMatch.startsWith("[") && fullMatch.includes("](") && fullMatch.endsWith(")")) {
const linkMatch = fullMatch.match(/\[(.*?)\]\((.*?)\)/);
if (linkMatch) {
const linkText = linkMatch[1];
const url2 = linkMatch[2];
styledPart = parseMarkdownToANSI(linkText, baseColor) + ansiColorize(" (", baseColor) + ansiColorize(url2, theme.text.link) + ansiColorize(")", baseColor);
}
} else if (fullMatch.startsWith("<u>") && fullMatch.endsWith("</u>") && fullMatch.length > UNDERLINE_TAG_START_LENGTH + UNDERLINE_TAG_END_LENGTH - 1) {
styledPart = import_chalk.default.underline(
parseMarkdownToANSI(
fullMatch.slice(
UNDERLINE_TAG_START_LENGTH,
-UNDERLINE_TAG_END_LENGTH
),
baseColor
)
);
} else if (fullMatch.match(/^https?:\/\//)) {
styledPart = ansiColorize(fullMatch, theme.text.link);
}
} catch (e) {
debugLogger.warn("Error parsing inline markdown part:", fullMatch, e);
styledPart = "";
}
result += styledPart || ansiColorize(fullMatch, baseColor);
lastIndex = inlineRegex.lastIndex;
}
if (lastIndex < text.length) {
result += ansiColorize(text.slice(lastIndex), baseColor);
}
return result;
};
// packages/cli/src/ui/utils/TableRenderer.tsx
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
var MIN_COLUMN_WIDTH = 5;
var COLUMN_PADDING = 2;
var TABLE_MARGIN = 2;
var parseMarkdownToStyledLine = (text, defaultColor) => {
const ansi = parseMarkdownToANSI(text, defaultColor);
return toStyledCharacters(ansi);
};
var calculateWidths = (styledLine) => {
const contentWidth = styledCharsWidth(styledLine);
const words = wordBreakStyledChars(styledLine);
const maxWordWidth = widestLineFromStyledChars(words);
return { contentWidth, maxWordWidth };
};
var TableRenderer = ({
headers,
rows,
terminalWidth
}) => {
const styledHeaders = (0, import_react3.useMemo)(
() => headers.map(
(header) => parseMarkdownToStyledLine(
stripUnsafeCharacters(header),
theme.text.link
)
),
[headers]
);
const styledRows = (0, import_react3.useMemo)(
() => rows.map(
(row) => row.map(
(cell) => parseMarkdownToStyledLine(
stripUnsafeCharacters(cell),
theme.text.primary
)
)
),
[rows]
);
const { wrappedHeaders, wrappedRows, adjustedWidths } = (0, import_react3.useMemo)(() => {
const numColumns = styledRows.reduce(
(max, row) => Math.max(max, row.length),
styledHeaders.length
);
const constraints = Array.from({ length: numColumns }).map(
(_, colIndex) => {
const headerStyledLine = styledHeaders[colIndex] || StyledLine.empty(0);
let { contentWidth: maxContentWidth, maxWordWidth } = calculateWidths(headerStyledLine);
styledRows.forEach((row) => {
const cellStyledLine = row[colIndex] || StyledLine.empty(0);
const { contentWidth: cellWidth, maxWordWidth: cellWordWidth } = calculateWidths(cellStyledLine);
maxContentWidth = Math.max(maxContentWidth, cellWidth);
maxWordWidth = Math.max(maxWordWidth, cellWordWidth);
});
const minWidth = maxWordWidth;
const maxWidth = Math.max(minWidth, maxContentWidth);
return { minWidth, maxWidth };
}
);
const fixedOverhead = numColumns + 1 + numColumns * COLUMN_PADDING;
const availableWidth = Math.max(
0,
terminalWidth - fixedOverhead - TABLE_MARGIN
);
const totalMinWidth = constraints.reduce((sum, c) => sum + c.minWidth, 0);
let finalContentWidths;
if (totalMinWidth > availableWidth) {
const shortColumns = constraints.filter(
(c) => c.maxWidth <= MIN_COLUMN_WIDTH
);
const totalShortColumnWidth = shortColumns.reduce(
(sum, c) => sum + c.minWidth,
0
);
const finalTotalShortColumnWidth = totalShortColumnWidth >= availableWidth ? 0 : totalShortColumnWidth;
const scale = (availableWidth - finalTotalShortColumnWidth) / (totalMinWidth - finalTotalShortColumnWidth) || 0;
finalContentWidths = constraints.map((c) => {
if (c.maxWidth <= MIN_COLUMN_WIDTH && finalTotalShortColumnWidth > 0) {
return c.minWidth;
}
return Math.floor(c.minWidth * scale);
});
} else {
const surplus = availableWidth - totalMinWidth;
const totalGrowthNeed = constraints.reduce(
(sum, c) => sum + (c.maxWidth - c.minWidth),
0
);
if (totalGrowthNeed === 0) {
finalContentWidths = constraints.map((c) => c.minWidth);
} else {
finalContentWidths = constraints.map((c) => {
const growthNeed = c.maxWidth - c.minWidth;
const share = growthNeed / totalGrowthNeed;
const extra = Math.floor(surplus * share);
return Math.min(c.maxWidth, c.minWidth + extra);
});
}
}
const actualColumnWidths = new Array(numColumns).fill(0);
const wrapAndProcessRow = (row) => {
const rowResult = [];
for (let colIndex = 0; colIndex < numColumns; colIndex++) {
const cellStyledLine = row[colIndex] || StyledLine.empty(0);
const allocatedWidth = finalContentWidths[colIndex];
const contentWidth = Math.max(1, allocatedWidth);
const wrappedStyledLines = wrapStyledChars(
cellStyledLine,
contentWidth
);
const maxLineWidth = widestLineFromStyledChars(wrappedStyledLines);
actualColumnWidths[colIndex] = Math.max(
actualColumnWidths[colIndex],
maxLineWidth
);
const lines = wrappedStyledLines.map((line) => ({
text: styledLineToString(line),
width: styledCharsWidth(line)
}));
rowResult.push(lines);
}
return rowResult;
};
const wrappedHeaders2 = wrapAndProcessRow(styledHeaders);
const wrappedRows2 = styledRows.map((row) => wrapAndProcessRow(row));
const adjustedWidths2 = actualColumnWidths.map(
(w) => (
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
w + COLUMN_PADDING
)
);
return { wrappedHeaders: wrappedHeaders2, wrappedRows: wrappedRows2, adjustedWidths: adjustedWidths2 };
}, [styledHeaders, styledRows, terminalWidth]);
const renderCell = (content, width, isHeader = false) => {
const contentWidth = Math.max(0, width - COLUMN_PADDING);
const displayWidth = content.width;
const paddingNeeded = Math.max(0, contentWidth - displayWidth);
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Text, { children: [
isHeader ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { bold: true, color: theme.text.link, children: content.text }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { children: content.text }),
" ".repeat(paddingNeeded)
] });
};
const renderBorder = (type) => {
const chars = {
top: { left: "\u250C", middle: "\u252C", right: "\u2510", horizontal: "\u2500" },
middle: { left: "\u251C", middle: "\u253C", right: "\u2524", horizontal: "\u2500" },
bottom: { left: "\u2514", middle: "\u2534", right: "\u2518", horizontal: "\u2500" }
};
const char = chars[type];
const borderParts = adjustedWidths.map((w) => char.horizontal.repeat(w));
const border = char.left + borderParts.join(char.middle) + char.right;
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: theme.border.default, children: border });
};
const renderVisualRow = (cells, isHeader = false) => {
const renderedCells = cells.map((cell, index) => {
const width = adjustedWidths[index] || 0;
return renderCell(cell, width, isHeader);
});
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: theme.border.default, children: "\u2502" }),
renderedCells.map((cell, index) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_react3.default.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Box_default, { paddingX: 1, children: cell }),
index < renderedCells.length - 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: theme.border.default, children: "\u2502" })
] }, index)),
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: theme.border.default, children: "\u2502" })
] });
};
const renderDataRow = (wrappedCells, rowIndex, isHeader = false) => {
const key = rowIndex === -1 ? "header" : `${rowIndex}`;
const maxHeight = Math.max(...wrappedCells.map((lines) => lines.length), 1);
const visualRows = [];
for (let i = 0; i < maxHeight; i++) {
const visualRowCells = wrappedCells.map(
(lines) => lines[i] || { text: "", width: 0 }
);
visualRows.push(
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react3.default.Fragment, { children: renderVisualRow(visualRowCells, isHeader) }, `${key}-${i}`)
);
}
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_react3.default.Fragment, { children: visualRows }, rowIndex);
};
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Box_default, { flexDirection: "column", children: [
renderBorder("top"),
renderDataRow(wrappedHeaders, -1, true),
renderBorder("middle"),
wrappedRows.map((row, index) => renderDataRow(row, index)),
renderBorder("bottom")
] });
};
// packages/cli/src/ui/utils/InlineMarkdownRenderer.tsx
var import_react4 = __toESM(require_react(), 1);
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
var RenderInlineInternal = ({
text: rawText,
defaultColor
}) => {
const text = stripUnsafeCharacters(rawText);
const ansiText = parseMarkdownToANSI(text, defaultColor);
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: ansiText });
};
var RenderInline = import_react4.default.memo(RenderInlineInternal);
// packages/cli/src/ui/utils/MarkdownDisplay.tsx
var import_jsx_runtime6 = __toESM(require_jsx_runtime(), 1);
var EMPTY_LINE_HEIGHT = 1;
var CODE_BLOCK_PREFIX_PADDING = 1;
var LIST_ITEM_PREFIX_PADDING = 1;
var LIST_ITEM_TEXT_FLEX_GROW = 1;
var MarkdownDisplayInternal = ({
text,
isPending,
availableTerminalHeight,
terminalWidth,
renderMarkdown = true
}) => {
const settings = useSettings();
const isAlternateBuffer = useAlternateBuffer();
const responseColor = theme.text.response ?? theme.text.primary;
if (!text) return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, {});
if (!renderMarkdown) {
const colorizedMarkdown = colorizeCode({
code: text,
language: "markdown",
availableHeight: isAlternateBuffer ? void 0 : availableTerminalHeight,
maxWidth: terminalWidth - CODE_BLOCK_PREFIX_PADDING,
settings,
hideLineNumbers: true
});
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { paddingLeft: CODE_BLOCK_PREFIX_PADDING, flexDirection: "column", children: colorizedMarkdown });
}
const lines = text.split(/\r?\n/);
const headerRegex = /^ *(#{1,4}) +(.*)/;
const codeFenceRegex = /^ *(`{3,}|~{3,}) *(\w*?) *$/;
const ulItemRegex = /^([ \t]*)([-*+]) +(.*)/;
const olItemRegex = /^([ \t]*)(\d+)\. +(.*)/;
const hrRegex = /^ *([-*_] *){3,} *$/;
const tableRowRegex = /^\s*\|(.+)\|\s*$/;
const tableSeparatorRegex = /^\s*\|?\s*(:?-+:?)\s*(\|\s*(:?-+:?)\s*)+\|?\s*$/;
const contentBlocks = [];
let inCodeBlock = false;
let lastLineEmpty = true;
let codeBlockContent = [];
let codeBlockLang = null;
let codeBlockFence = "";
let inTable = false;
let tableRows = [];
let tableHeaders = [];
function addContentBlock(block) {
if (block) {
contentBlocks.push(block);
lastLineEmpty = false;
}
}
lines.forEach((line, index) => {
const key = `line-${index}`;
if (inCodeBlock) {
const fenceMatch = line.match(codeFenceRegex);
if (fenceMatch && fenceMatch[1].startsWith(codeBlockFence[0]) && fenceMatch[1].length >= codeBlockFence.length) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderCodeBlock,
{
content: codeBlockContent,
lang: codeBlockLang,
isPending,
availableTerminalHeight: isAlternateBuffer ? void 0 : availableTerminalHeight,
terminalWidth
},
key
)
);
inCodeBlock = false;
codeBlockContent = [];
codeBlockLang = null;
codeBlockFence = "";
} else {
codeBlockContent.push(line);
}
return;
}
const codeFenceMatch = line.match(codeFenceRegex);
const headerMatch = line.match(headerRegex);
const ulMatch = line.match(ulItemRegex);
const olMatch = line.match(olItemRegex);
const hrMatch = line.match(hrRegex);
const tableRowMatch = line.match(tableRowRegex);
const tableSeparatorMatch = line.match(tableSeparatorRegex);
if (codeFenceMatch) {
inCodeBlock = true;
codeBlockFence = codeFenceMatch[1];
codeBlockLang = codeFenceMatch[2] || null;
} else if (tableRowMatch && !inTable) {
if (index + 1 < lines.length && lines[index + 1].match(tableSeparatorRegex)) {
inTable = true;
tableHeaders = tableRowMatch[1].split("|").map((cell) => cell.trim());
tableRows = [];
} else {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { wrap: "wrap", color: responseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: line, defaultColor: responseColor }) }) }, key)
);
}
} else if (inTable && tableSeparatorMatch) {
} else if (inTable && tableRowMatch) {
const cells = tableRowMatch[1].split("|").map((cell) => cell.trim());
while (cells.length < tableHeaders.length) {
cells.push("");
}
if (cells.length > tableHeaders.length) {
cells.length = tableHeaders.length;
}
tableRows.push(cells);
} else if (inTable && !tableRowMatch) {
if (tableHeaders.length > 0 && tableRows.length > 0) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderTable,
{
headers: tableHeaders,
rows: tableRows,
terminalWidth
},
`table-${contentBlocks.length}`
)
);
}
inTable = false;
tableRows = [];
tableHeaders = [];
if (line.trim().length > 0) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { wrap: "wrap", color: responseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: line, defaultColor: responseColor }) }) }, key)
);
}
} else if (hrMatch) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { dimColor: true, children: "---" }) }, key)
);
} else if (headerMatch) {
const level = headerMatch[1].length;
const headerText = headerMatch[2];
let headerNode = null;
switch (level) {
case 1:
headerNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: theme.text.link, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: headerText, defaultColor: theme.text.link }) });
break;
case 2:
headerNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: theme.text.link, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: headerText, defaultColor: theme.text.link }) });
break;
case 3:
headerNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { bold: true, color: responseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: headerText, defaultColor: responseColor }) });
break;
case 4:
headerNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { italic: true, color: theme.text.secondary, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderInline,
{
text: headerText,
defaultColor: theme.text.secondary
}
) });
break;
default:
headerNode = /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: responseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: headerText, defaultColor: responseColor }) });
break;
}
if (headerNode) addContentBlock(/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: headerNode }, key));
} else if (ulMatch) {
const leadingWhitespace = ulMatch[1];
const marker = ulMatch[2];
const itemText = ulMatch[3];
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderListItem,
{
itemText,
type: "ul",
marker,
leadingWhitespace
},
key
)
);
} else if (olMatch) {
const leadingWhitespace = olMatch[1];
const marker = olMatch[2];
const itemText = olMatch[3];
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderListItem,
{
itemText,
type: "ol",
marker,
leadingWhitespace
},
key
)
);
} else {
if (line.trim().length === 0 && !inCodeBlock) {
if (!lastLineEmpty) {
contentBlocks.push(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { height: EMPTY_LINE_HEIGHT }, `spacer-${index}`)
);
lastLineEmpty = true;
}
} else {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { wrap: "wrap", color: responseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: line, defaultColor: responseColor }) }) }, key)
);
}
}
});
if (inCodeBlock) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderCodeBlock,
{
content: codeBlockContent,
lang: codeBlockLang,
isPending,
availableTerminalHeight: isAlternateBuffer ? void 0 : availableTerminalHeight,
terminalWidth
},
"line-eof"
)
);
}
if (inTable && tableHeaders.length > 0 && tableRows.length > 0) {
addContentBlock(
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
RenderTable,
{
headers: tableHeaders,
rows: tableRows,
terminalWidth
},
`table-${contentBlocks.length}`
)
);
}
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(import_jsx_runtime6.Fragment, { children: contentBlocks });
};
var RenderCodeBlockInternal = ({
content,
lang,
isPending,
availableTerminalHeight,
terminalWidth
}) => {
const settings = useSettings();
const isAlternateBuffer = useAlternateBuffer();
const MIN_LINES_FOR_MESSAGE = 1;
const RESERVED_LINES = 2;
if (!isAlternateBuffer && isPending && availableTerminalHeight !== void 0) {
const MAX_CODE_LINES_WHEN_PENDING = Math.max(
0,
availableTerminalHeight - RESERVED_LINES
);
if (content.length > MAX_CODE_LINES_WHEN_PENDING) {
if (MAX_CODE_LINES_WHEN_PENDING < MIN_LINES_FOR_MESSAGE) {
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { paddingLeft: CODE_BLOCK_PREFIX_PADDING, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: theme.text.secondary, children: "... code is being written ..." }) });
}
const truncatedContent = content.slice(0, MAX_CODE_LINES_WHEN_PENDING);
const colorizedTruncatedCode = colorizeCode({
code: truncatedContent.join("\n"),
language: lang,
availableHeight: availableTerminalHeight,
maxWidth: terminalWidth - CODE_BLOCK_PREFIX_PADDING,
settings
});
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(Box_default, { paddingLeft: CODE_BLOCK_PREFIX_PADDING, flexDirection: "column", children: [
colorizedTruncatedCode,
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: theme.text.secondary, children: "... generating more ..." })
] });
}
}
const fullContent = content.join("\n");
const colorizedCode = colorizeCode({
code: fullContent,
language: lang,
availableHeight: isAlternateBuffer ? void 0 : availableTerminalHeight,
maxWidth: terminalWidth - CODE_BLOCK_PREFIX_PADDING,
settings
});
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
Box_default,
{
paddingLeft: CODE_BLOCK_PREFIX_PADDING,
flexDirection: "column",
width: terminalWidth,
flexShrink: 0,
children: colorizedCode
}
);
};
var RenderCodeBlock = import_react5.default.memo(RenderCodeBlockInternal);
var RenderListItemInternal = ({
itemText,
type,
marker,
leadingWhitespace = ""
}) => {
const prefix = type === "ol" ? `${marker}. ` : `${marker} `;
const prefixWidth = prefix.length;
const indentation = leadingWhitespace.length;
const listResponseColor = theme.text.response ?? theme.text.primary;
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
Box_default,
{
paddingLeft: indentation + LIST_ITEM_PREFIX_PADDING,
flexDirection: "row",
children: [
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { width: prefixWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { color: listResponseColor, children: prefix }) }),
/* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Box_default, { flexGrow: LIST_ITEM_TEXT_FLEX_GROW, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(Text, { wrap: "wrap", color: listResponseColor, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(RenderInline, { text: itemText, defaultColor: listResponseColor }) }) })
]
}
);
};
var RenderListItem = import_react5.default.memo(RenderListItemInternal);
var RenderTableInternal = ({
headers,
rows,
terminalWidth
}) => /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(TableRenderer, { headers, rows, terminalWidth });
var RenderTable = import_react5.default.memo(RenderTableInternal);
var MarkdownDisplay = import_react5.default.memo(MarkdownDisplayInternal);
// packages/cli/src/ui/components/messages/GeminiMessage.tsx
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
var GeminiMessage = ({
text,
isPending,
availableTerminalHeight,
terminalWidth
}) => {
const { renderMarkdown } = useUIState();
const prefix = "\u2726 ";
const prefixWidth = prefix.length;
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { width: prefixWidth, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { color: theme.text.accent, "aria-label": SCREEN_READER_MODEL_PREFIX, children: prefix }) }),
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Box_default, { flexGrow: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
MarkdownDisplay,
{
text,
isPending,
availableTerminalHeight: availableTerminalHeight === void 0 ? void 0 : Math.max(availableTerminalHeight - 1, 1),
terminalWidth: Math.max(terminalWidth - prefixWidth, 0),
renderMarkdown
}
) })
] });
};
// packages/cli/src/ui/components/messages/InfoMessage.tsx
var import_jsx_runtime8 = __toESM(require_jsx_runtime(), 1);
var InfoMessage = ({
text,
secondaryText,
source,
icon,
color,
marginBottom
}) => {
color ??= theme.status.warning;
const prefix = icon ?? "\u2139 ";
const prefixWidth = prefix.length;
return /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Box_default, { flexDirection: "row", marginTop: 1, marginBottom: marginBottom ?? 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { width: prefixWidth, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Text, { color, children: prefix }) }),
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(Box_default, { flexGrow: 1, flexDirection: "column", children: text.split("\n").map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { wrap: "wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime8.jsx)(RenderInline, { text: line, defaultColor: color }),
index === text.split("\n").length - 1 && secondaryText && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.text.secondary, children: [
" ",
secondaryText
] }),
index === text.split("\n").length - 1 && source && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(Text, { color: theme.text.secondary, children: [
" [",
source,
"]"
] })
] }, index)) })
] });
};
// packages/cli/src/ui/components/messages/ErrorMessage.tsx
var import_jsx_runtime9 = __toESM(require_jsx_runtime(), 1);
var ErrorMessage = ({ text }) => {
const prefix = "\u2715 ";
const prefixWidth = prefix.length;
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(Box_default, { flexDirection: "row", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { width: prefixWidth, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { color: theme.status.error, children: prefix }) }),
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(Text, { wrap: "wrap", color: theme.status.error, children: text }) })
] });
};
// packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
var import_react21 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/StickyHeader.tsx
var import_jsx_runtime10 = __toESM(require_jsx_runtime(), 1);
var StickyHeader = ({
children,
width,
isFirst,
borderColor,
borderDimColor,
containerRef
}) => /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Box_default,
{
ref: containerRef,
sticky: true,
minHeight: 1,
flexShrink: 0,
width,
stickyChildren: /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
Box_default,
{
borderStyle: "round",
flexDirection: "column",
width,
opaque: true,
borderColor,
borderDimColor,
borderBottom: false,
borderTop: isFirst,
paddingTop: isFirst ? 0 : 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(Box_default, { paddingX: 1, children }),
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Box_default,
{
width: width - 2,
borderColor: theme.ui.dark,
borderStyle: "single",
borderTop: false,
borderBottom: true,
borderLeft: false,
borderRight: false
}
)
]
}
),
children: /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
Box_default,
{
borderStyle: "round",
width,
borderColor,
borderDimColor,
borderBottom: false,
borderTop: isFirst,
borderLeft: true,
borderRight: true,
paddingX: 1,
paddingBottom: 1,
paddingTop: isFirst ? 0 : 1,
children
}
)
}
);
// packages/cli/src/ui/components/messages/ToolResultDisplay.tsx
var import_react10 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/AnsiOutput.tsx
var import_jsx_runtime11 = __toESM(require_jsx_runtime(), 1);
var DEFAULT_HEIGHT = 24;
var AnsiOutputText = ({
data,
availableTerminalHeight,
width,
maxLines,
disableTruncation
}) => {
const availableHeightLimit = availableTerminalHeight && availableTerminalHeight > 0 ? availableTerminalHeight : void 0;
const numLinesRetained = availableHeightLimit !== void 0 && maxLines !== void 0 ? Math.min(availableHeightLimit, maxLines) : availableHeightLimit ?? maxLines ?? DEFAULT_HEIGHT;
const lastLines = Array.isArray(data) ? disableTruncation ? data : numLinesRetained === 0 ? [] : data.slice(-numLinesRetained) : [];
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { flexDirection: "column", width, flexShrink: 0, overflow: "hidden", children: lastLines.map((line, lineIndex) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Box_default, { height: 1, overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(AnsiLineText, { line }) }, lineIndex)) });
};
var AnsiLineText = ({ line }) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(Text, { children: line.length > 0 ? line.map((token, tokenIndex) => /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
Text,
{
color: token.fg,
backgroundColor: token.bg,
inverse: token.inverse,
dimColor: token.dim,
bold: token.bold,
italic: token.italic,
underline: token.underline,
children: token.text
},
tokenIndex
)) : null });
// packages/cli/src/ui/components/shared/SlicingMaxSizedBox.tsx
var import_react6 = __toESM(require_react(), 1);
var import_jsx_runtime12 = __toESM(require_jsx_runtime(), 1);
var MAXIMUM_RESULT_DISPLAY_CHARACTERS = 2e4;
function SlicingMaxSizedBox({
data,
maxLines,
isAlternateBuffer,
children,
...boxProps
}) {
const { truncatedData, hiddenLinesCount } = (0, import_react6.useMemo)(() => {
let hiddenLines = 0;
const overflowDirection = boxProps.overflowDirection ?? "top";
if (typeof data === "string" && !isAlternateBuffer) {
let text = data;
if (text.length > MAXIMUM_RESULT_DISPLAY_CHARACTERS) {
if (overflowDirection === "bottom") {
text = text.slice(0, MAXIMUM_RESULT_DISPLAY_CHARACTERS) + "...";
} else {
text = "..." + text.slice(-MAXIMUM_RESULT_DISPLAY_CHARACTERS);
}
}
if (maxLines !== void 0) {
const hasTrailingNewline = text.endsWith("\n");
const contentText = hasTrailingNewline ? text.slice(0, -1) : text;
const lines = contentText.split("\n");
if (lines.length > maxLines) {
const targetLines = Math.max(1, maxLines - 1);
hiddenLines = lines.length - targetLines;
if (overflowDirection === "bottom") {
text = lines.slice(0, targetLines).join("\n") + (hasTrailingNewline ? "\n" : "");
} else {
text = lines.slice(-targetLines).join("\n") + (hasTrailingNewline ? "\n" : "");
}
}
}
return {
truncatedData: text,
hiddenLinesCount: hiddenLines
};
}
if (Array.isArray(data) && !isAlternateBuffer && maxLines !== void 0) {
if (data.length > maxLines) {
const targetLines = Math.max(1, maxLines - 1);
const hiddenCount = data.length - targetLines;
return {
truncatedData: overflowDirection === "bottom" ? data.slice(0, targetLines) : data.slice(-targetLines),
hiddenLinesCount: hiddenCount
};
}
}
return { truncatedData: data, hiddenLinesCount: 0 };
}, [data, isAlternateBuffer, maxLines, boxProps.overflowDirection]);
return /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
MaxSizedBox,
{
...boxProps,
additionalHiddenLinesCount: (boxProps.additionalHiddenLinesCount ?? 0) + hiddenLinesCount,
children: children(truncatedData)
}
);
}
// packages/cli/src/utils/jsonoutput.ts
function checkInput(input) {
if (input === null || input === void 0) {
return false;
}
const trimmed = input.trim();
if (!trimmed) {
return false;
}
if (!/^(?:\[|\{)/.test(trimmed)) {
return false;
}
if (stripAnsi(trimmed) !== trimmed) return false;
return true;
}
function tryParseJSON(input) {
if (!checkInput(input)) return null;
const trimmed = input.trim();
try {
const parsed = JSON.parse(trimmed);
if (parsed === null || typeof parsed !== "object") {
return null;
}
if (Array.isArray(parsed) && parsed.length === 0) {
return null;
}
if (!Array.isArray(parsed) && Object.keys(parsed).length === 0) return null;
return parsed;
} catch {
return null;
}
}
// packages/cli/src/ui/components/shared/Scrollable.tsx
var import_react7 = __toESM(require_react(), 1);
var import_jsx_runtime13 = __toESM(require_jsx_runtime(), 1);
var Scrollable = ({
children,
width,
height,
maxWidth,
maxHeight,
hasFocus,
scrollToBottom,
flexGrow,
reportOverflow = false,
overflowToBackbuffer,
scrollbar = true,
stableScrollback
}) => {
const keyMatchers = useKeyMatchers();
const [scrollTop, setScrollTop] = (0, import_react7.useState)(0);
const viewportRef = (0, import_react7.useRef)(null);
const contentRef = (0, import_react7.useRef)(null);
const overflowActions = useOverflowActions();
const id = (0, import_react7.useId)();
const [size, setSize] = (0, import_react7.useState)({
innerHeight: typeof height === "number" ? height : 0,
scrollHeight: 0
});
const sizeRef = (0, import_react7.useRef)(size);
const scrollTopRef = (0, import_react7.useRef)(scrollTop);
(0, import_react7.useLayoutEffect)(() => {
sizeRef.current = size;
}, [size]);
(0, import_react7.useLayoutEffect)(() => {
scrollTopRef.current = scrollTop;
}, [scrollTop]);
(0, import_react7.useEffect)(() => {
if (reportOverflow && size.scrollHeight > size.innerHeight) {
overflowActions?.addOverflowingId?.(id);
} else {
overflowActions?.removeOverflowingId?.(id);
}
}, [
reportOverflow,
size.scrollHeight,
size.innerHeight,
id,
overflowActions
]);
(0, import_react7.useEffect)(
() => () => {
overflowActions?.removeOverflowingId?.(id);
},
[id, overflowActions]
);
const viewportObserverRef = (0, import_react7.useRef)(null);
const contentObserverRef = (0, import_react7.useRef)(null);
(0, import_react7.useEffect)(
() => () => {
viewportObserverRef.current?.disconnect();
contentObserverRef.current?.disconnect();
},
[]
);
const viewportRefCallback = (0, import_react7.useCallback)((node) => {
viewportObserverRef.current?.disconnect();
viewportRef.current = node;
if (node) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const innerHeight = Math.round(entry.contentRect.height);
setSize((prev) => {
const scrollHeight = prev.scrollHeight;
const isAtBottom = scrollHeight > prev.innerHeight && scrollTopRef.current >= scrollHeight - prev.innerHeight - 1;
if (isAtBottom) {
setScrollTop(Number.MAX_SAFE_INTEGER);
}
return { ...prev, innerHeight };
});
}
});
observer.observe(node);
viewportObserverRef.current = observer;
}
}, []);
const contentRefCallback = (0, import_react7.useCallback)(
(node) => {
contentObserverRef.current?.disconnect();
contentRef.current = node;
if (node) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const scrollHeight = Math.round(entry.contentRect.height);
setSize((prev) => {
const innerHeight = prev.innerHeight;
const isAtBottom = prev.scrollHeight > innerHeight && scrollTopRef.current >= prev.scrollHeight - innerHeight - 1;
if (isAtBottom || scrollToBottom && scrollHeight > prev.scrollHeight) {
setScrollTop(Number.MAX_SAFE_INTEGER);
}
return { ...prev, scrollHeight };
});
}
});
observer.observe(node);
contentObserverRef.current = observer;
}
},
[scrollToBottom]
);
const { getScrollTop, setPendingScrollTop } = useBatchedScroll(scrollTop);
const scrollBy = (0, import_react7.useCallback)(
(delta) => {
const { scrollHeight, innerHeight } = sizeRef.current;
const maxScroll = Math.max(0, scrollHeight - innerHeight);
const current = Math.min(getScrollTop(), maxScroll);
let next = Math.max(0, current + delta);
if (next >= maxScroll) {
next = Number.MAX_SAFE_INTEGER;
}
setPendingScrollTop(next);
setScrollTop(next);
},
[getScrollTop, setPendingScrollTop]
);
const { scrollbarColor, flashScrollbar, scrollByWithAnimation } = useAnimatedScrollbar(hasFocus, scrollBy);
useKeypress(
(key) => {
const { scrollHeight, innerHeight } = sizeRef.current;
const scrollTop2 = getScrollTop();
const maxScroll = Math.max(0, scrollHeight - innerHeight);
const actualScrollTop = Math.min(scrollTop2, maxScroll);
if (actualScrollTop > 0) {
if (keyMatchers["scroll.pageUp" /* PAGE_UP */](key)) {
scrollByWithAnimation(-innerHeight);
return true;
}
if (keyMatchers["scroll.up" /* SCROLL_UP */](key)) {
scrollByWithAnimation(-1);
return true;
}
}
if (actualScrollTop < maxScroll) {
if (keyMatchers["scroll.pageDown" /* PAGE_DOWN */](key)) {
scrollByWithAnimation(innerHeight);
return true;
}
if (keyMatchers["scroll.down" /* SCROLL_DOWN */](key)) {
scrollByWithAnimation(1);
return true;
}
}
return false;
},
{ isActive: hasFocus }
);
const getScrollState = (0, import_react7.useCallback)(() => {
const maxScroll = Math.max(0, size.scrollHeight - size.innerHeight);
return {
scrollTop: Math.min(getScrollTop(), maxScroll),
scrollHeight: size.scrollHeight,
innerHeight: size.innerHeight
};
}, [getScrollTop, size.scrollHeight, size.innerHeight]);
const hasFocusCallback = (0, import_react7.useCallback)(() => hasFocus, [hasFocus]);
const scrollableEntry = (0, import_react7.useMemo)(
() => ({
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
ref: viewportRef,
getScrollState,
scrollBy: scrollByWithAnimation,
hasFocus: hasFocusCallback,
flashScrollbar
}),
[getScrollState, scrollByWithAnimation, hasFocusCallback, flashScrollbar]
);
useScrollable(scrollableEntry, true);
return /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
Box_default,
{
ref: viewportRefCallback,
maxHeight,
width: width ?? maxWidth,
height,
flexDirection: "column",
overflowY: "scroll",
overflowX: "hidden",
scrollTop,
flexGrow,
scrollbarThumbColor: scrollbarColor,
overflowToBackbuffer,
scrollbar,
stableScrollback,
children: /* @__PURE__ */ (0, import_jsx_runtime13.jsx)(
Box_default,
{
ref: contentRefCallback,
flexShrink: 0,
paddingRight: 1,
flexDirection: "column",
children
}
)
}
);
};
// packages/cli/src/ui/utils/toolLayoutUtils.ts
var TOOL_RESULT_STATIC_HEIGHT = 1;
var TOOL_RESULT_ASB_RESERVED_LINE_COUNT = 6;
var TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT = 4;
var TOOL_RESULT_MIN_LINES_SHOWN = 2;
var SHELL_CONTENT_OVERHEAD = TOOL_RESULT_STATIC_HEIGHT + TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT;
function calculateToolContentMaxLines(options) {
const { availableTerminalHeight, isAlternateBuffer, maxLinesLimit } = options;
const reservedLines = isAlternateBuffer ? TOOL_RESULT_ASB_RESERVED_LINE_COUNT : TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT;
let contentHeight = availableTerminalHeight !== void 0 ? Math.max(
availableTerminalHeight - TOOL_RESULT_STATIC_HEIGHT - reservedLines,
TOOL_RESULT_MIN_LINES_SHOWN + 1
) : void 0;
if (maxLinesLimit !== void 0) {
contentHeight = contentHeight !== void 0 ? Math.min(contentHeight, maxLinesLimit) : maxLinesLimit;
}
return contentHeight;
}
function calculateShellMaxLines(options) {
const {
status,
isAlternateBuffer,
isThisShellFocused: isThisShellFocused2,
availableTerminalHeight,
constrainHeight,
isExpandable
} = options;
if (!constrainHeight && isExpandable) {
return void 0;
}
if (availableTerminalHeight === void 0) {
return isAlternateBuffer ? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD : void 0;
}
const maxLinesBasedOnHeight = Math.max(
1,
availableTerminalHeight - TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT
);
if (isAlternateBuffer && isThisShellFocused2 && !constrainHeight) {
return maxLinesBasedOnHeight;
}
const isExecuting = status === CoreToolCallStatus.Executing;
const shellMaxLinesLimit = isExecuting ? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD : COMPLETED_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD;
return Math.min(maxLinesBasedOnHeight, shellMaxLinesLimit);
}
// packages/cli/src/ui/components/messages/ToolShared.tsx
var import_react9 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useInactivityTimer.ts
var import_react8 = __toESM(require_react(), 1);
var useInactivityTimer = (isActive, trigger, delayMs = 5e3) => {
const [isInactive, setIsInactive] = (0, import_react8.useState)(false);
(0, import_react8.useEffect)(() => {
if (!isActive) {
setIsInactive(false);
return;
}
setIsInactive(false);
const timer = setTimeout(() => {
setIsInactive(true);
}, delayMs);
return () => clearTimeout(timer);
}, [isActive, trigger, delayMs]);
return isInactive;
};
// packages/cli/src/ui/components/messages/ToolShared.tsx
var import_jsx_runtime14 = __toESM(require_jsx_runtime(), 1);
var STATUS_INDICATOR_WIDTH = 3;
function isShellTool(name) {
const normalized = name.toLowerCase();
return name === SHELL_COMMAND_NAME || name === SHELL_NAME || name === SHELL_TOOL_NAME || normalized === "shell";
}
function isThisShellFocusable(name, status, config) {
return !!(isShellTool(name) && status === CoreToolCallStatus.Executing && config?.getEnableInteractiveShell());
}
function isThisShellFocused(name, status, ptyId, activeShellPtyId, embeddedShellFocused) {
return !!(isShellTool(name) && status === CoreToolCallStatus.Executing && ptyId === activeShellPtyId && embeddedShellFocused);
}
function useFocusHint(isThisShellFocusable2, isThisShellFocused2, resultDisplay) {
const [userHasFocused, setUserHasFocused] = (0, import_react9.useState)(false);
const resetKey = typeof resultDisplay === "string" ? resultDisplay.length : Array.isArray(resultDisplay) ? resultDisplay.length : !!resultDisplay;
const showFocusHint = useInactivityTimer(
isThisShellFocusable2,
resetKey,
SHELL_FOCUS_HINT_DELAY_MS
);
(0, import_react9.useEffect)(() => {
if (isThisShellFocused2) {
setUserHasFocused(true);
}
}, [isThisShellFocused2]);
const shouldShowFocusHint = isThisShellFocusable2 && (showFocusHint || userHasFocused);
return { shouldShowFocusHint };
}
var FocusHint = ({ shouldShowFocusHint, isThisShellFocused: isThisShellFocused2 }) => {
if (!shouldShowFocusHint) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { marginLeft: 1, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: isThisShellFocused2 ? theme.ui.focus : theme.ui.active, children: isThisShellFocused2 ? `(${formatCommand("app.unfocusShellInput" /* UNFOCUS_SHELL_INPUT */)} to unfocus)` : `(${formatCommand("app.focusShellInput" /* FOCUS_SHELL_INPUT */)} to focus)` }) });
};
var ToolStatusIndicator = ({
status: coreStatus,
name,
isFocused
}) => {
const status = mapCoreStatusToDisplayStatus(coreStatus);
const isShell = isShellTool(name);
const statusColor = isFocused ? theme.ui.focus : isShell ? theme.ui.active : theme.status.warning;
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { minWidth: STATUS_INDICATOR_WIDTH, children: [
status === "Pending" /* Pending */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.status.success, children: TOOL_STATUS.PENDING }),
status === "Executing" /* Executing */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: statusColor, children: /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(CliSpinner, { type: "toggle" }) }),
status === "Success" /* Success */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.status.success, "aria-label": "Success:", children: TOOL_STATUS.SUCCESS }),
status === "Confirming" /* Confirming */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: statusColor, "aria-label": "Confirming:", children: TOOL_STATUS.CONFIRMING }),
status === "Canceled" /* Canceled */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: statusColor, "aria-label": "Canceled:", bold: true, children: TOOL_STATUS.CANCELED }),
status === "Error" /* Error */ && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.status.error, "aria-label": "Error:", bold: true, children: TOOL_STATUS.ERROR })
] });
};
var ToolInfo = ({
name,
description,
status: coreStatus,
emphasis,
progressMessage: _progressMessage,
originalRequestName,
isExpanded = false
}) => {
const status = mapCoreStatusToDisplayStatus(coreStatus);
const nameColor = import_react9.default.useMemo(() => {
switch (emphasis) {
case "high":
return theme.text.primary;
case "medium":
return theme.text.primary;
case "low":
return theme.text.secondary;
default: {
const exhaustiveCheck = emphasis;
return exhaustiveCheck;
}
}
}, [emphasis]);
const isCompletedAskUser = isCompletedAskUserTool(name, status);
return /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(
Box_default,
{
overflow: "hidden",
height: isExpanded ? void 0 : 1,
flexGrow: 1,
flexShrink: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(
Text,
{
strikethrough: status === "Canceled" /* Canceled */,
wrap: isExpanded ? "wrap" : "truncate",
children: [
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: nameColor, bold: true, children: name }),
originalRequestName && originalRequestName !== name && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { color: theme.text.secondary, italic: true, children: [
" ",
"(redirection from ",
originalRequestName,
")"
] }),
!isCompletedAskUser && /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(import_jsx_runtime14.Fragment, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.text.secondary, children: description })
] })
]
}
)
}
);
};
var McpProgressIndicator = ({
progress,
total,
message,
barWidth
}) => {
const percentage = total && total > 0 ? Math.min(100, Math.round(progress / total * 100)) : null;
let rawFilled;
if (total && total > 0) {
rawFilled = Math.round(progress / total * barWidth);
} else {
rawFilled = Math.floor(progress) % (barWidth + 1);
}
const filled = Math.max(
0,
Math.min(Number.isFinite(rawFilled) ? rawFilled : 0, barWidth)
);
const empty = Math.max(0, barWidth - filled);
const progressBar = "\u2588".repeat(filled) + "\u2591".repeat(empty);
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { color: theme.text.accent, children: [
progressBar,
" ",
percentage !== null ? `${percentage}%` : `${progress}`
] }) }),
message && /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Text, { color: theme.text.secondary, wrap: "truncate", children: message })
] });
};
var TrailingIndicator = () => /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)(Text, { color: theme.text.primary, wrap: "truncate", children: [
" ",
"\u2190"
] });
// packages/cli/src/ui/components/messages/SubagentProgressDisplay.tsx
var import_jsx_runtime15 = __toESM(require_jsx_runtime(), 1);
var formatToolArgs = (args) => {
if (!args) return "";
try {
const parsed = JSON.parse(args);
if (typeof parsed !== "object" || parsed === null) {
return args;
}
if ("description" in parsed && typeof parsed.description === "string" && parsed.description) {
return parsed.description;
}
if ("command" in parsed && typeof parsed.command === "string")
return parsed.command;
if ("file_path" in parsed && typeof parsed.file_path === "string")
return parsed.file_path;
if ("dir_path" in parsed && typeof parsed.dir_path === "string")
return parsed.dir_path;
if ("query" in parsed && typeof parsed.query === "string")
return parsed.query;
if ("url" in parsed && typeof parsed.url === "string") return parsed.url;
if ("target" in parsed && typeof parsed.target === "string")
return parsed.target;
return args;
} catch {
return args;
}
};
var SubagentProgressDisplay = ({ progress, terminalWidth, historyOverrides }) => {
let headerText;
let headerColor = theme.text.secondary;
if (progress.state === SubagentState.CANCELLED) {
headerText = `Subagent ${progress.agentName} was cancelled.`;
headerColor = theme.status.warning;
} else if (progress.state === SubagentState.ERROR) {
headerText = `Subagent ${progress.agentName} failed.`;
headerColor = theme.status.error;
} else if (progress.state === SubagentState.COMPLETED) {
headerText = `Subagent ${progress.agentName} completed.`;
headerColor = theme.status.success;
} else {
headerText = `Running subagent ${progress.agentName}...`;
headerColor = theme.text.primary;
}
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", paddingY: 0, children: [
headerText && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: headerColor, italic: true, children: headerText }) }),
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { flexDirection: "column", marginLeft: 0, gap: 0, children: (historyOverrides ?? progress.recentActivity).map(
(item) => {
if (item.type === "thought") {
const isCancellation = item.content === "Request cancelled.";
const icon = isCancellation ? "\u2139 " : "\u{1F4AD}";
const color = isCancellation ? theme.status.warning : theme.text.secondary;
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { minWidth: STATUS_INDICATOR_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color, children: icon }) }),
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color, children: item.content }) })
] }, item.id);
} else if (item.type === "tool_call") {
const statusSymbol = item.status === SubagentState.RUNNING ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(build_default, { type: "dots" }) : item.status === SubagentState.COMPLETED ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: theme.status.success, children: TOOL_STATUS.SUCCESS }) : item.status === SubagentState.CANCELLED ? /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: theme.status.warning, bold: true, children: TOOL_STATUS.CANCELED }) : /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Text, { color: theme.status.error, children: TOOL_STATUS.ERROR });
const formattedArgs = item.description || formatToolArgs(item.args);
const displayArgs = formattedArgs.length > 60 ? formattedArgs.slice(0, 60) + "..." : formattedArgs;
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { minWidth: STATUS_INDICATOR_WIDTH, children: statusSymbol }),
/* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "row", flexGrow: 1, flexWrap: "wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
Text,
{
bold: true,
color: theme.text.primary,
strikethrough: item.status === SubagentState.CANCELLED,
children: item.displayName || item.content
}
),
displayArgs && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
Text,
{
color: theme.text.secondary,
wrap: "truncate",
strikethrough: item.status === SubagentState.CANCELLED,
children: displayArgs
}
) })
] })
] }, item.id);
}
return null;
}
) }),
progress.result && /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
progress.terminateReason && progress.terminateReason !== "GOAL" && /* @__PURE__ */ (0, import_jsx_runtime15.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(Text, { color: theme.status.warning, bold: true, children: [
"Agent Finished Early (",
progress.terminateReason,
")"
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime15.jsx)(
MarkdownDisplay,
{
text: safeJsonToMarkdown(progress.result),
isPending: progress.state !== SubagentState.COMPLETED,
terminalWidth
}
)
] })
] });
};
// packages/cli/src/ui/components/messages/ToolResultDisplay.tsx
var import_jsx_runtime16 = __toESM(require_jsx_runtime(), 1);
var ToolResultDisplay = ({
resultDisplay,
availableTerminalHeight,
terminalWidth,
renderOutputAsMarkdown = true,
maxLines,
hasFocus = false,
overflowDirection = "top"
}) => {
const { renderMarkdown, constrainHeight } = useUIState();
const isAlternateBuffer = useAlternateBuffer();
const availableHeight = calculateToolContentMaxLines({
availableTerminalHeight,
isAlternateBuffer,
maxLinesLimit: maxLines
});
const combinedPaddingAndBorderWidth = 4;
const childWidth = terminalWidth - combinedPaddingAndBorderWidth;
const keyExtractor = import_react10.default.useCallback(
(_, index) => index.toString(),
[]
);
const renderVirtualizedAnsiLine = import_react10.default.useCallback(
({ item }) => /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { height: 1, overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AnsiLineText, { line: item }) }),
[]
);
if (!resultDisplay) return null;
if (typeof resultDisplay === "object" && "todos" in resultDisplay) {
return null;
}
const renderContent = (contentData) => {
const prettyJSON = typeof contentData === "string" ? tryParseJSON(contentData) : null;
const formattedJSON = prettyJSON ? JSON.stringify(prettyJSON, null, 2) : null;
let content;
if (formattedJSON) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { wrap: "wrap", color: theme.text.primary, children: formattedJSON });
} else if (isSubagentProgress(contentData)) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
SubagentProgressDisplay,
{
progress: contentData,
terminalWidth: childWidth
}
);
} else if (typeof contentData === "string" && renderOutputAsMarkdown) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
MarkdownDisplay,
{
text: contentData,
terminalWidth: childWidth,
renderMarkdown,
isPending: false
}
);
} else if (typeof contentData === "string" && !renderOutputAsMarkdown) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { wrap: "wrap", color: theme.text.primary, children: contentData });
} else if (isStructuredToolResult(contentData)) {
if (renderOutputAsMarkdown) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
MarkdownDisplay,
{
text: contentData.summary,
terminalWidth: childWidth,
renderMarkdown,
isPending: false
}
);
} else {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { wrap: "wrap", color: theme.text.primary, children: contentData.summary });
}
} else if (typeof contentData === "object" && contentData !== null && "fileDiff" in contentData) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
DiffRenderer,
{
diffContent: (
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
contentData.fileDiff
),
filename: contentData.fileName,
availableTerminalHeight: availableHeight,
terminalWidth: childWidth
}
);
} else if (Array.isArray(contentData)) {
const shouldDisableTruncation = isAlternateBuffer || availableTerminalHeight === void 0 && maxLines === void 0;
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
AnsiOutputText,
{
data: contentData,
availableTerminalHeight: isAlternateBuffer ? void 0 : availableHeight,
width: childWidth,
maxLines: isAlternateBuffer ? void 0 : maxLines,
disableTruncation: shouldDisableTruncation
}
);
} else if (typeof contentData === "object" && contentData !== null) {
content = /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Text, { wrap: "wrap", color: theme.text.primary, children: JSON.stringify(contentData, null, 2) });
} else {
content = null;
}
if (isAlternateBuffer) {
const effectiveMaxHeight = maxLines ?? availableHeight;
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Scrollable,
{
width: childWidth,
maxHeight: effectiveMaxHeight,
hasFocus,
scrollToBottom: true,
reportOverflow: true,
children: content
}
);
}
return content;
};
if (Array.isArray(resultDisplay)) {
const limit = maxLines ?? availableHeight ?? ACTIVE_SHELL_MAX_LINES;
const data = resultDisplay;
const listHeight = !constrainHeight ? data.length : Math.min(data.length, limit);
if (isAlternateBuffer) {
const initialScrollIndex = overflowDirection === "bottom" ? 0 : SCROLL_TO_ITEM_END;
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: childWidth, flexDirection: "column", maxHeight: listHeight, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
ScrollableListWithForwardRef,
{
width: childWidth,
containerHeight: listHeight,
data,
renderItem: renderVirtualizedAnsiLine,
estimatedItemHeight: () => 1,
fixedItemHeight: true,
keyExtractor,
initialScrollIndex,
hasFocus
}
) });
} else {
let displayData = data;
let hiddenLines = 0;
if (constrainHeight && data.length > listHeight) {
hiddenLines = data.length - listHeight;
if (overflowDirection === "top") {
displayData = data.slice(hiddenLines);
} else {
displayData = data.slice(0, listHeight);
}
}
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: childWidth, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
MaxSizedBox,
{
maxHeight: constrainHeight ? listHeight : void 0,
maxWidth: childWidth,
overflowDirection,
additionalHiddenLinesCount: hiddenLines,
children: displayData.map((item, index) => {
const actualIndex = (overflowDirection === "top" ? hiddenLines : 0) + index;
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
Box_default,
{
height: 1,
overflow: "hidden",
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(AnsiLineText, { line: item })
},
keyExtractor(item, actualIndex)
);
})
}
) });
}
}
if (isAlternateBuffer) {
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: childWidth, flexDirection: "column", children: renderContent(resultDisplay) });
}
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(Box_default, { width: childWidth, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
SlicingMaxSizedBox,
{
data: resultDisplay,
maxLines,
isAlternateBuffer,
maxHeight: availableHeight,
maxWidth: childWidth,
overflowDirection,
children: (truncatedResultDisplay) => renderContent(truncatedResultDisplay)
}
) });
};
// packages/cli/src/ui/components/ShellInputPrompt.tsx
var import_react11 = __toESM(require_react(), 1);
// packages/cli/src/ui/key/keyToAnsi.ts
var SPECIAL_KEYS = {
up: "\x1B[A",
down: "\x1B[B",
right: "\x1B[C",
left: "\x1B[D",
escape: "\x1B",
tab: " ",
backspace: "\x7F",
delete: "\x1B[3~",
home: "\x1B[H",
end: "\x1B[F",
pageup: "\x1B[5~",
pagedown: "\x1B[6~",
enter: "\r"
};
function keyToAnsi(key) {
if (key.ctrl) {
if (key.name >= "a" && key.name <= "z") {
return String.fromCharCode(
key.name.charCodeAt(0) - "a".charCodeAt(0) + 1
);
}
}
if (key.name in SPECIAL_KEYS) {
return SPECIAL_KEYS[key.name];
}
if (!key.ctrl && !key.cmd && key.sequence) {
return key.sequence;
}
return null;
}
// packages/cli/src/ui/components/ShellInputPrompt.tsx
var ShellInputPrompt = ({
activeShellPtyId,
focus = true,
scrollPageSize = ACTIVE_SHELL_MAX_LINES
}) => {
const keyMatchers = useKeyMatchers();
const handleShellInputSubmit = (0, import_react11.useCallback)(
(input) => {
if (activeShellPtyId) {
ShellExecutionService.writeToPty(activeShellPtyId, input);
}
},
[activeShellPtyId]
);
const handleInput = (0, import_react11.useCallback)(
(key) => {
if (!focus || !activeShellPtyId) {
return false;
}
if (keyMatchers["background.toggle" /* TOGGLE_BACKGROUND_SHELL */](key)) {
return false;
}
if (keyMatchers["app.unfocusShellInput" /* UNFOCUS_SHELL_INPUT */](key)) {
return false;
}
if (keyMatchers["scroll.up" /* SCROLL_UP */](key)) {
ShellExecutionService.scrollPty(activeShellPtyId, -1);
return true;
}
if (keyMatchers["scroll.down" /* SCROLL_DOWN */](key)) {
ShellExecutionService.scrollPty(activeShellPtyId, 1);
return true;
}
if (keyMatchers["scroll.pageUp" /* PAGE_UP */](key)) {
ShellExecutionService.scrollPty(activeShellPtyId, -scrollPageSize);
return true;
}
if (keyMatchers["scroll.pageDown" /* PAGE_DOWN */](key)) {
ShellExecutionService.scrollPty(activeShellPtyId, scrollPageSize);
return true;
}
const ansiSequence = keyToAnsi(key);
if (ansiSequence) {
handleShellInputSubmit(ansiSequence);
return true;
}
return false;
},
[
focus,
handleShellInputSubmit,
activeShellPtyId,
scrollPageSize,
keyMatchers
]
);
useKeypress(handleInput, { isActive: focus });
return null;
};
// packages/cli/src/ui/contexts/ToolActionsContext.tsx
var import_react12 = __toESM(require_react(), 1);
var import_jsx_runtime17 = __toESM(require_jsx_runtime(), 1);
function hasLegacyCallback(details) {
return !!details && "onConfirm" in details && typeof details.onConfirm === "function";
}
var ToolActionsContext = (0, import_react12.createContext)(null);
var useToolActions = () => {
const context = (0, import_react12.useContext)(ToolActionsContext);
if (!context) {
throw new Error("useToolActions must be used within a ToolActionsProvider");
}
return context;
};
var ToolActionsProvider = (props) => {
const {
children,
config,
toolCalls,
isExpanded,
toggleExpansion,
toggleAllExpansion
} = props;
const [ideClient, setIdeClient] = (0, import_react12.useState)(null);
const [isDiffingEnabled, setIsDiffingEnabled] = (0, import_react12.useState)(false);
(0, import_react12.useEffect)(() => {
let isMounted = true;
let activeClient = null;
const handleStatusChange = () => {
if (isMounted && activeClient) {
setIsDiffingEnabled(activeClient.isDiffingEnabled());
}
};
if (config.getIdeMode()) {
IdeClient.getInstance().then((client) => {
if (!isMounted) return;
activeClient = client;
setIdeClient(client);
setIsDiffingEnabled(client.isDiffingEnabled());
client.addStatusChangeListener(handleStatusChange);
}).catch((error) => {
debugLogger.error("Failed to get IdeClient instance:", error);
});
}
return () => {
isMounted = false;
if (activeClient) {
activeClient.removeStatusChangeListener(handleStatusChange);
}
};
}, [config]);
const confirm = (0, import_react12.useCallback)(
async (callId, outcome, payload) => {
const tool = toolCalls.find((t) => t.callId === callId);
if (!tool) {
debugLogger.warn(`ToolActions: Tool ${callId} not found`);
return;
}
const details = tool.confirmationDetails;
if (details?.type === "edit" && isDiffingEnabled && "filePath" in details) {
const cliOutcome = outcome === ToolConfirmationOutcome.Cancel ? "rejected" : "accepted";
await ideClient?.resolveDiffFromCli(details.filePath, cliOutcome);
}
if (tool.correlationId) {
await config.getMessageBus().publish({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: tool.correlationId,
confirmed: outcome !== ToolConfirmationOutcome.Cancel,
requiresUserConfirmation: false,
outcome,
payload
});
return;
}
if (hasLegacyCallback(details)) {
await details.onConfirm(outcome, payload);
return;
}
debugLogger.warn(
`ToolActions: No correlationId or callback for ${callId}`
);
},
[config, ideClient, toolCalls, isDiffingEnabled]
);
const cancel = (0, import_react12.useCallback)(
async (callId) => {
await confirm(callId, ToolConfirmationOutcome.Cancel);
},
[confirm]
);
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
ToolActionsContext.Provider,
{
value: {
confirm,
cancel,
isDiffingEnabled,
isExpanded,
toggleExpansion,
toggleAllExpansion
},
children
}
);
};
// packages/cli/src/ui/components/messages/ToolMessage.tsx
var import_jsx_runtime18 = (
// It is crucial we don't replace this <> with a Box because otherwise the
// sticky header inside it would be sticky to that box rather than to the
// parent component of this ToolMessage.
__toESM(require_jsx_runtime(), 1)
);
var ToolMessage = ({
callId,
name,
description,
resultDisplay,
status,
kind,
availableTerminalHeight,
terminalWidth,
emphasis = "medium",
renderOutputAsMarkdown = true,
isFirst,
borderColor,
borderDimColor,
activeShellPtyId,
embeddedShellFocused,
ptyId,
config,
progressMessage,
originalRequestName,
progress,
progressTotal
}) => {
const { isExpanded: isExpandedInContext } = useToolActions();
const isExpanded = (isExpandedInContext ? isExpandedInContext(callId) : false) || availableTerminalHeight === void 0;
const isThisShellFocused2 = isThisShellFocused(
name,
status,
ptyId,
activeShellPtyId,
embeddedShellFocused
);
const isThisShellFocusable2 = isThisShellFocusable(name, status, config);
const { shouldShowFocusHint } = useFocusHint(
isThisShellFocusable2,
isThisShellFocused2,
resultDisplay
);
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(import_jsx_runtime18.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
StickyHeader,
{
width: terminalWidth,
isFirst,
borderColor,
borderDimColor,
children: [
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
ToolStatusIndicator,
{
status,
name,
isFocused: isThisShellFocused2
}
),
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
ToolInfo,
{
name,
status,
description,
emphasis,
progressMessage,
originalRequestName,
isExpanded
}
),
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
FocusHint,
{
shouldShowFocusHint,
isThisShellFocused: isThisShellFocused2
}
),
emphasis === "high" && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(TrailingIndicator, {})
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime18.jsxs)(
Box_default,
{
width: terminalWidth,
borderStyle: "round",
borderColor,
borderDimColor,
borderTop: false,
borderBottom: false,
borderLeft: true,
borderRight: true,
paddingX: 1,
flexDirection: "column",
children: [
status === CoreToolCallStatus.Executing && progress !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
McpProgressIndicator,
{
progress,
total: progressTotal,
message: progressMessage,
barWidth: 20
}
),
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
ToolResultDisplay,
{
resultDisplay,
availableTerminalHeight,
terminalWidth,
renderOutputAsMarkdown,
hasFocus: isThisShellFocused2,
maxLines: kind === Kind.Agent && availableTerminalHeight !== void 0 ? SUBAGENT_MAX_LINES : void 0,
overflowDirection: kind === Kind.Agent ? "bottom" : "top"
}
),
isThisShellFocused2 && config && /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(Box_default, { paddingLeft: STATUS_INDICATOR_WIDTH, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(
ShellInputPrompt,
{
activeShellPtyId: activeShellPtyId ?? null,
focus: embeddedShellFocused
}
) })
]
}
)
] });
};
// packages/cli/src/ui/components/messages/ShellToolMessage.tsx
var import_react17 = __toESM(require_react(), 1);
// packages/cli/src/ui/contexts/UIActionsContext.tsx
var import_react16 = __toESM(require_react(), 1);
// packages/cli/src/ui/IdeIntegrationNudge.tsx
var import_jsx_runtime19 = __toESM(require_jsx_runtime(), 1);
function IdeIntegrationNudge({
ide,
onComplete
}) {
useKeypress(
(key) => {
if (key.name === "escape") {
onComplete({
userSelection: "no",
isExtensionPreInstalled: false
});
return true;
}
return false;
},
{ isActive: true }
);
const { displayName: ideName } = ide;
const isExtensionPreInstalled = !!process.env["GEMINI_CLI_IDE_SERVER_PORT"] && !!process.env["GEMINI_CLI_IDE_WORKSPACE_PATH"];
const OPTIONS = [
{
label: "Yes",
value: {
userSelection: "yes",
isExtensionPreInstalled
},
key: "Yes"
},
{
label: "No (esc)",
value: {
userSelection: "no",
isExtensionPreInstalled
},
key: "No (esc)"
},
{
label: "No, don't ask again",
value: {
userSelection: "dismiss",
isExtensionPreInstalled
},
key: "No, don't ask again"
}
];
const installText = isExtensionPreInstalled ? `If you select Yes, the CLI will have access to your open files and display diffs directly in ${ideName ?? "your editor"}.` : `If you select Yes, we'll install an extension that allows the CLI to access your open files and display diffs directly in ${ideName ?? "your editor"}.`;
return /* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.status.warning,
padding: 1,
width: "100%",
marginLeft: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Box_default, { marginBottom: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime19.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Text, { color: theme.status.warning, children: "> " }),
`Do you want to connect ${ideName ?? "your editor"} to Gemini CLI?`
] }),
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(Text, { color: theme.text.secondary, children: installText })
] }),
/* @__PURE__ */ (0, import_jsx_runtime19.jsx)(RadioButtonSelect, { items: OPTIONS, onSelect: onComplete })
]
}
);
}
// packages/cli/src/ui/components/FolderTrustDialog.tsx
var import_react13 = __toESM(require_react(), 1);
import * as process2 from "node:process";
import * as path from "node:path";
var import_jsx_runtime20 = __toESM(require_jsx_runtime(), 1);
var FolderTrustDialog = ({
onSelect,
isRestarting,
discoveryResults
}) => {
const [exiting, setExiting] = (0, import_react13.useState)(false);
const { terminalHeight, terminalWidth, constrainHeight } = useUIState();
const isAlternateBuffer = useAlternateBuffer();
const isExpanded = !constrainHeight;
(0, import_react13.useEffect)(() => {
let timer;
if (isRestarting) {
timer = setTimeout(relaunchApp, 250);
}
return () => {
if (timer) clearTimeout(timer);
};
}, [isRestarting]);
const handleExit = (0, import_react13.useCallback)(() => {
setExiting(true);
setTimeout(async () => {
await runExitCleanup();
process2.exit(ExitCodes.FATAL_CANCELLATION_ERROR);
}, 100);
}, []);
useKeypress(
(key) => {
if (key.name === "escape") {
handleExit();
return true;
}
return false;
},
{ isActive: !isRestarting }
);
const dirName = path.basename(process2.cwd());
const parentFolder = path.basename(path.dirname(process2.cwd()));
const options = [
{
label: `Trust folder (${dirName})`,
value: "trust_folder" /* TRUST_FOLDER */,
key: `Trust folder (${dirName})`
},
{
label: `Trust parent folder (${parentFolder})`,
value: "trust_parent" /* TRUST_PARENT */,
key: `Trust parent folder (${parentFolder})`
},
{
label: "Don't trust",
value: "do_not_trust" /* DO_NOT_TRUST */,
key: "Don't trust"
}
];
const hasDiscovery = discoveryResults && (discoveryResults.commands.length > 0 || discoveryResults.mcps.length > 0 || discoveryResults.hooks.length > 0 || discoveryResults.skills.length > 0 || discoveryResults.settings.length > 0);
const hasWarnings = discoveryResults && discoveryResults.securityWarnings.length > 0;
const hasErrors = discoveryResults && discoveryResults.discoveryErrors && discoveryResults.discoveryErrors.length > 0;
const dialogWidth = terminalWidth - 2;
const borderColor = theme.status.warning;
const overhead = 3 + options.length + 2 + 1 + 2;
const scrollableHeight = Math.max(4, terminalHeight - overhead);
const groups = [
{ label: "Commands", items: discoveryResults?.commands ?? [] },
{ label: "MCP Servers", items: discoveryResults?.mcps ?? [] },
{ label: "Hooks", items: discoveryResults?.hooks ?? [] },
{ label: "Skills", items: discoveryResults?.skills ?? [] },
{ label: "Agents", items: discoveryResults?.agents ?? [] },
{ label: "Setting overrides", items: discoveryResults?.settings ?? [] }
].filter((g) => g.items.length > 0);
const discoveryContent = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.text.primary, children: "Trusting a folder allows Gemini CLI to load its local configurations, including custom commands, hooks, MCP servers, agent skills, and settings. These configurations could execute code on your behalf or change the behavior of the CLI." }) }),
hasErrors && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.status.error, bold: true, children: "\u274C Discovery Errors:" }),
discoveryResults.discoveryErrors.map((error, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Text, { color: theme.status.error, children: [
"\u2022 ",
stripAnsi(error)
] }) }, i))
] }),
hasWarnings && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.status.warning, bold: true, children: "\u26A0\uFE0F Security Warnings:" }),
discoveryResults.securityWarnings.map((warning, i) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Text, { color: theme.status.warning, children: [
"\u2022 ",
stripAnsi(warning)
] }) }, i))
] }),
hasDiscovery && /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.text.primary, bold: true, children: "This folder contains:" }),
groups.map((group) => /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Text, { color: theme.text.primary, bold: true, children: [
"\u2022 ",
group.label,
" (",
group.items.length,
"):"
] }),
group.items.map((item, idx) => /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Text, { color: theme.text.primary, children: [
"- ",
stripAnsi(item)
] }) }, idx))
] }, group.label))
] })
] });
const title = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { bold: true, color: theme.text.primary, children: "Do you trust the files in this folder?" });
const selectOptions = /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
RadioButtonSelect,
{
items: options,
onSelect,
isFocused: !isRestarting
}
);
const renderContent = () => {
if (isAlternateBuffer) {
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", width: dialogWidth, children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
StickyHeader,
{
width: dialogWidth,
isFirst: true,
borderColor,
borderDimColor: false,
children: title
}
),
/* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
Box_default,
{
flexDirection: "column",
borderLeft: true,
borderRight: true,
borderColor,
borderStyle: "round",
borderTop: false,
borderBottom: false,
width: dialogWidth,
children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
Scrollable,
{
hasFocus: !isRestarting,
height: scrollableHeight,
width: dialogWidth - 2,
children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { flexDirection: "column", paddingX: 1, children: discoveryContent })
}
),
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { paddingX: 1, marginY: 1, children: selectOptions })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
Box_default,
{
height: 0,
width: dialogWidth,
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: true,
borderColor,
borderStyle: "round"
}
)
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor,
padding: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginBottom: 1, children: title }),
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(
MaxSizedBox,
{
maxHeight: isExpanded ? void 0 : Math.max(4, terminalHeight - 12),
overflowDirection: "bottom",
children: discoveryContent
}
),
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginTop: 1, children: selectOptions })
]
}
);
};
const content = /* @__PURE__ */ (0, import_jsx_runtime20.jsxs)(Box_default, { flexDirection: "column", width: "100%", children: [
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { flexDirection: "column", marginLeft: 1, marginRight: 1, children: renderContent() }),
/* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { paddingX: 2, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(ShowMoreLines, { constrainHeight }) }),
isRestarting && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginLeft: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.status.warning, children: "Gemini CLI is restarting to apply the trust changes..." }) }),
exiting && /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Box_default, { marginLeft: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(Text, { color: theme.status.warning, children: "A folder trust level must be selected to continue. Exiting since escape was pressed." }) })
] });
return /* @__PURE__ */ (0, import_jsx_runtime20.jsx)(OverflowProvider, { children: content });
};
// packages/cli/src/ui/components/PermissionsModifyTrustDialog.tsx
import * as process4 from "node:process";
import * as path3 from "node:path";
// packages/cli/src/ui/hooks/usePermissionsModifyTrust.ts
var import_react14 = __toESM(require_react(), 1);
import * as process3 from "node:process";
import * as path2 from "node:path";
function getInitialTrustState(settings, cwd5, isCurrentWorkspace) {
const folders = loadTrustedFolders();
const explicitTrustLevel = folders.user.config[cwd5];
if (!isCurrentWorkspace) {
return {
currentTrustLevel: explicitTrustLevel,
isInheritedTrustFromParent: false,
isInheritedTrustFromIde: false
};
}
const { isTrusted, source } = isWorkspaceTrusted(
settings.merged,
process3.cwd()
);
const isInheritedTrust = isTrusted && (!explicitTrustLevel || explicitTrustLevel === TrustLevel.DO_NOT_TRUST);
return {
currentTrustLevel: explicitTrustLevel,
isInheritedTrustFromParent: !!(source === "file" && isInheritedTrust),
isInheritedTrustFromIde: !!(source === "ide" && isInheritedTrust)
};
}
var usePermissionsModifyTrust = (onExit, addItem, targetDirectory) => {
const settings = useSettings();
const cwd5 = targetDirectory;
const isCurrentWorkspace = path2.resolve(targetDirectory).toLowerCase() === path2.resolve(process3.cwd()).toLowerCase();
const [initialState5] = (0, import_react14.useState)(
() => getInitialTrustState(settings, cwd5, isCurrentWorkspace)
);
const [currentTrustLevel] = (0, import_react14.useState)(
initialState5.currentTrustLevel
);
const [pendingTrustLevel, setPendingTrustLevel] = (0, import_react14.useState)();
const [isInheritedTrustFromParent] = (0, import_react14.useState)(
initialState5.isInheritedTrustFromParent
);
const [isInheritedTrustFromIde] = (0, import_react14.useState)(
initialState5.isInheritedTrustFromIde
);
const [needsRestart, setNeedsRestart] = (0, import_react14.useState)(false);
const isFolderTrustEnabled = settings.merged.security.folderTrust.enabled ?? true;
const updateTrustLevel = (0, import_react14.useCallback)(
async (trustLevel) => {
if (!isCurrentWorkspace) {
const folders = loadTrustedFolders();
await folders.setValue(cwd5, trustLevel);
onExit();
return;
}
const wasTrusted = isWorkspaceTrusted(
settings.merged,
process3.cwd()
).isTrusted;
const currentConfig = loadTrustedFolders().user.config;
const newConfig = { ...currentConfig, [cwd5]: trustLevel };
const { isTrusted, source } = isWorkspaceTrusted(
settings.merged,
process3.cwd(),
newConfig
);
if (trustLevel === TrustLevel.DO_NOT_TRUST && isTrusted) {
let message = "Note: This folder is still trusted because the connected IDE workspace is trusted.";
if (source === "file") {
message = "Note: This folder is still trusted because a parent folder is trusted.";
}
addItem(
{
type: "warning" /* WARNING */,
text: message
},
Date.now()
);
}
if (wasTrusted !== isTrusted) {
setPendingTrustLevel(trustLevel);
setNeedsRestart(true);
} else {
const folders = loadTrustedFolders();
try {
await folders.setValue(cwd5, trustLevel);
} catch {
coreEvents.emitFeedback(
"error",
"Failed to save trust settings. Your changes may not persist."
);
}
onExit();
}
},
[cwd5, settings.merged, onExit, addItem, isCurrentWorkspace]
);
const commitTrustLevelChange = (0, import_react14.useCallback)(async () => {
if (pendingTrustLevel) {
const folders = loadTrustedFolders();
try {
await folders.setValue(cwd5, pendingTrustLevel);
return true;
} catch {
coreEvents.emitFeedback(
"error",
"Failed to save trust settings. Your changes may not persist."
);
setNeedsRestart(false);
setPendingTrustLevel(void 0);
return false;
}
}
return true;
}, [cwd5, pendingTrustLevel]);
return {
cwd: cwd5,
currentTrustLevel,
isInheritedTrustFromParent,
isInheritedTrustFromIde,
needsRestart,
updateTrustLevel,
commitTrustLevelChange,
isFolderTrustEnabled
};
};
// packages/cli/src/ui/components/PermissionsModifyTrustDialog.tsx
var import_jsx_runtime21 = __toESM(require_jsx_runtime(), 1);
function PermissionsModifyTrustDialog({
onExit,
addItem,
targetDirectory
}) {
const currentDirectory = targetDirectory ?? process4.cwd();
const dirName = path3.basename(currentDirectory);
const parentFolder = path3.basename(path3.dirname(currentDirectory));
const TRUST_LEVEL_ITEMS = [
{
label: `Trust this folder (${dirName})`,
value: TrustLevel.TRUST_FOLDER,
key: TrustLevel.TRUST_FOLDER
},
{
label: `Trust parent folder (${parentFolder})`,
value: TrustLevel.TRUST_PARENT,
key: TrustLevel.TRUST_PARENT
},
{
label: "Don't trust",
value: TrustLevel.DO_NOT_TRUST,
key: TrustLevel.DO_NOT_TRUST
}
];
const {
cwd: cwd5,
currentTrustLevel,
isInheritedTrustFromParent,
isInheritedTrustFromIde,
needsRestart,
updateTrustLevel,
commitTrustLevelChange
} = usePermissionsModifyTrust(onExit, addItem, currentDirectory);
useKeypress(
(key) => {
if (key.name === "escape") {
onExit();
return true;
}
if (needsRestart && key.name === "r") {
void (async () => {
const success = await commitTrustLevelChange();
if (success) {
void relaunchApp();
} else {
onExit();
}
})();
return true;
}
return false;
},
{ isActive: true }
);
const index = TRUST_LEVEL_ITEMS.findIndex(
(item) => item.value === currentTrustLevel
);
const initialIndex = index === -1 ? 0 : index;
return /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(import_jsx_runtime21.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
padding: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Box_default, { flexDirection: "column", paddingBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Text, { bold: true, children: [
"> ",
"Modify Trust Level"
] }),
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Box_default, { marginTop: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Text, { children: [
"Folder: ",
cwd5
] }),
/* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(Text, { children: [
"Current Level: ",
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Text, { bold: true, children: currentTrustLevel || "Not Set" })
] }),
isInheritedTrustFromParent && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Text, { color: theme.text.secondary, children: "Note: This folder behaves as a trusted folder because one of the parent folders is trusted. It will remain trusted even if you set a different trust level here. To change this, you need to modify the trust setting in the parent folder." }),
isInheritedTrustFromIde && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Text, { color: theme.text.secondary, children: "Note: This folder behaves as a trusted folder because the connected IDE workspace is trusted. It will remain trusted even if you set a different trust level here." })
] }),
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(
RadioButtonSelect,
{
items: TRUST_LEVEL_ITEMS,
onSelect: updateTrustLevel,
isFocused: true,
initialIndex
}
),
/* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Text, { color: theme.text.secondary, children: "(Use Enter to select, Esc to close)" }) })
]
}
),
needsRestart && /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Box_default, { marginLeft: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)(Text, { color: theme.status.warning, children: "To apply the trust changes, Gemini CLI must be restarted. Press 'r' to restart CLI now." }) })
] });
}
// packages/cli/src/ui/components/NewAgentsNotification.tsx
var import_react15 = __toESM(require_react(), 1);
var import_jsx_runtime22 = __toESM(require_jsx_runtime(), 1);
var NewAgentsNotification = ({
agents,
onSelect
}) => {
const [isProcessing, setIsProcessing] = (0, import_react15.useState)(false);
const options = [
{
label: "Acknowledge and Enable",
value: "acknowledge" /* ACKNOWLEDGE */,
key: "acknowledge"
},
{
label: "Do not enable (Ask again next time)",
value: "ignore" /* IGNORE */,
key: "ignore"
}
];
const handleSelect = async (choice) => {
setIsProcessing(true);
try {
await onSelect(choice);
} finally {
setIsProcessing(false);
}
};
const MAX_DISPLAYED_AGENTS = 5;
const displayAgents = agents.slice(0, MAX_DISPLAYED_AGENTS);
const remaining = agents.length - MAX_DISPLAYED_AGENTS;
return /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Box_default, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.status.warning,
padding: 1,
marginLeft: 1,
marginRight: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Text, { bold: true, color: theme.text.primary, children: "New Agents Discovered" }),
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Text, { color: theme.text.primary, children: "The following agents were found in this project. Please review them:" }),
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(
Box_default,
{
flexDirection: "column",
marginTop: 1,
borderStyle: "single",
padding: 1,
children: [
displayAgents.map((agent) => {
const mcpServers = agent.kind === "local" ? agent.mcpServers : void 0;
const hasMcpServers = mcpServers && Object.keys(mcpServers).length > 0;
return /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
"- ",
agent.name,
":",
" "
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Text, { color: theme.text.secondary, children: [
" ",
agent.description
] })
] }),
hasMcpServers && /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Text, { color: theme.text.secondary, children: [
"(Includes MCP servers:",
" ",
Object.keys(mcpServers).join(", "),
")"
] }) })
] }, agent.name);
}),
remaining > 0 && /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Text, { color: theme.text.secondary, children: [
"... and ",
remaining,
" more."
] })
]
}
)
] }),
isProcessing ? /* @__PURE__ */ (0, import_jsx_runtime22.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(CliSpinner, {}),
/* @__PURE__ */ (0, import_jsx_runtime22.jsx)(Text, { color: theme.text.primary, children: " Processing..." })
] }) : /* @__PURE__ */ (0, import_jsx_runtime22.jsx)(
RadioButtonSelect,
{
items: options,
onSelect: handleSelect,
isFocused: true
}
)
]
}
) });
};
// packages/cli/src/ui/contexts/UIActionsContext.tsx
var UIActionsContext = (0, import_react16.createContext)(null);
var useUIActions = () => {
const context = (0, import_react16.useContext)(UIActionsContext);
if (!context) {
throw new Error("useUIActions must be used within a UIActionsProvider");
}
return context;
};
// packages/cli/src/ui/components/messages/ShellToolMessage.tsx
var import_jsx_runtime23 = __toESM(require_jsx_runtime(), 1);
var ShellToolMessage = ({
callId,
name,
description,
resultDisplay,
status,
availableTerminalHeight,
terminalWidth,
emphasis = "medium",
renderOutputAsMarkdown = true,
ptyId,
config,
isFirst,
borderColor,
borderDimColor,
isExpandable,
originalRequestName
}) => {
const { isExpanded: isExpandedInContext } = useToolActions();
const isExpanded = (isExpandedInContext ? isExpandedInContext(callId) : false) || availableTerminalHeight === void 0;
const {
activePtyId: activeShellPtyId,
embeddedShellFocused,
constrainHeight
} = useUIState();
const isAlternateBuffer = useAlternateBuffer();
const isThisShellFocused2 = isThisShellFocused(
name,
status,
ptyId,
activeShellPtyId,
embeddedShellFocused
);
const maxLines = calculateShellMaxLines({
status,
isAlternateBuffer,
isThisShellFocused: isThisShellFocused2,
availableTerminalHeight,
constrainHeight,
isExpandable
});
const availableHeight = calculateToolContentMaxLines({
availableTerminalHeight,
isAlternateBuffer,
maxLinesLimit: maxLines
});
import_react17.default.useEffect(() => {
const isExecuting = status === CoreToolCallStatus.Executing;
if (isExecuting && ptyId) {
try {
const childWidth = terminalWidth - 4;
const finalHeight = availableHeight ?? ACTIVE_SHELL_MAX_LINES - SHELL_CONTENT_OVERHEAD;
ShellExecutionService.resizePty(
ptyId,
Math.max(1, childWidth),
Math.max(1, finalHeight)
);
} catch (e) {
if (!(e instanceof Error && e.message.includes("Cannot resize a pty that has already exited"))) {
throw e;
}
}
}
}, [ptyId, status, terminalWidth, availableHeight]);
const { setEmbeddedShellFocused } = useUIActions();
const wasFocusedRef = import_react17.default.useRef(false);
import_react17.default.useEffect(() => {
if (isThisShellFocused2) {
wasFocusedRef.current = true;
} else if (wasFocusedRef.current) {
if (embeddedShellFocused) {
setEmbeddedShellFocused(false);
}
wasFocusedRef.current = false;
}
}, [isThisShellFocused2, embeddedShellFocused, setEmbeddedShellFocused]);
const headerRef = import_react17.default.useRef(null);
const contentRef = import_react17.default.useRef(null);
const isThisShellFocusable2 = isThisShellFocusable(name, status, config);
const handleFocus = () => {
if (isThisShellFocusable2) {
setEmbeddedShellFocused(true);
}
};
useMouseClick(headerRef, handleFocus, { isActive: !!isThisShellFocusable2 });
useMouseClick(contentRef, handleFocus, { isActive: !!isThisShellFocusable2 });
const { shouldShowFocusHint } = useFocusHint(
isThisShellFocusable2,
isThisShellFocused2,
resultDisplay
);
return /* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(import_jsx_runtime23.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
StickyHeader,
{
width: terminalWidth,
isFirst,
borderColor,
borderDimColor,
containerRef: headerRef,
children: [
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
ToolStatusIndicator,
{
status,
name,
isFocused: isThisShellFocused2
}
),
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
ToolInfo,
{
name,
status,
description,
emphasis,
originalRequestName,
isExpanded
}
),
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
FocusHint,
{
shouldShowFocusHint,
isThisShellFocused: isThisShellFocused2
}
),
emphasis === "high" && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(TrailingIndicator, {})
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime23.jsxs)(
Box_default,
{
ref: contentRef,
width: terminalWidth,
borderStyle: "round",
borderColor,
borderDimColor,
borderTop: false,
borderBottom: false,
borderLeft: true,
borderRight: true,
paddingX: 1,
flexDirection: "column",
children: [
/* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
ToolResultDisplay,
{
resultDisplay,
availableTerminalHeight,
terminalWidth,
renderOutputAsMarkdown,
hasFocus: isThisShellFocused2,
maxLines
}
),
isThisShellFocused2 && config && /* @__PURE__ */ (0, import_jsx_runtime23.jsx)(
ShellInputPrompt,
{
activeShellPtyId: activeShellPtyId ?? null,
focus: embeddedShellFocused,
scrollPageSize: availableTerminalHeight ?? ACTIVE_SHELL_MAX_LINES
}
)
]
}
)
] });
};
// packages/cli/src/ui/components/messages/TopicMessage.tsx
var import_react18 = __toESM(require_react(), 1);
var import_jsx_runtime24 = __toESM(require_jsx_runtime(), 1);
var isTopicTool = (name) => name === UPDATE_TOPIC_TOOL_NAME || name === UPDATE_TOPIC_DISPLAY_NAME;
var TopicMessage = ({
callId,
args,
availableTerminalHeight,
isExpandable = true
}) => {
const { isExpanded: isExpandedInContext, toggleExpansion } = useToolActions();
const isExpanded = (isExpandedInContext ? isExpandedInContext(callId) : false) || availableTerminalHeight === void 0;
const overflowActions = useOverflowActions();
const uniqueId = (0, import_react18.useId)();
const overflowId = `topic-${uniqueId}`;
const containerRef = (0, import_react18.useRef)(null);
const rawTitle = args?.[TOPIC_PARAM_TITLE];
const title = typeof rawTitle === "string" ? rawTitle : void 0;
const rawStrategicIntent = args?.[TOPIC_PARAM_STRATEGIC_INTENT];
const strategicIntent = typeof rawStrategicIntent === "string" ? rawStrategicIntent : void 0;
const rawSummary = args?.[TOPIC_PARAM_SUMMARY];
const summary = typeof rawSummary === "string" ? rawSummary : void 0;
const intent = strategicIntent || summary;
const hasExtraSummary = !!(strategicIntent && summary && strategicIntent !== summary);
const handleToggle = (0, import_react18.useCallback)(() => {
if (toggleExpansion && hasExtraSummary) {
toggleExpansion(callId);
}
}, [toggleExpansion, hasExtraSummary, callId]);
useMouseClick(containerRef, handleToggle, {
isActive: isExpandable && hasExtraSummary
});
(0, import_react18.useEffect)(() => {
const hasHiddenContent = isExpandable && hasExtraSummary && !isExpanded;
if (hasHiddenContent && overflowActions) {
overflowActions.addOverflowingId(overflowId);
} else if (overflowActions) {
overflowActions.removeOverflowingId(overflowId);
}
return () => {
overflowActions?.removeOverflowingId(overflowId);
};
}, [isExpandable, hasExtraSummary, isExpanded, overflowActions, overflowId]);
return /* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Box_default, { ref: containerRef, flexDirection: "column", marginLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Box_default, { flexDirection: "row", flexWrap: "wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime24.jsxs)(Text, { color: theme.text.primary, bold: true, wrap: "truncate-end", children: [
title || "Topic",
intent && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { children: ": " })
] }),
intent && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { color: theme.text.secondary, wrap: "wrap", children: intent })
] }),
isExpanded && hasExtraSummary && summary && /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Box_default, { marginTop: 1, marginLeft: 0, children: /* @__PURE__ */ (0, import_jsx_runtime24.jsx)(Text, { color: theme.text.secondary, wrap: "wrap", children: summary }) })
] });
};
// packages/cli/src/ui/components/messages/SubagentGroupDisplay.tsx
var import_react19 = __toESM(require_react(), 1);
var import_jsx_runtime25 = __toESM(require_jsx_runtime(), 1);
var SubagentGroupDisplay = ({
toolCalls,
availableTerminalHeight,
terminalWidth,
borderColor,
borderDimColor,
isFirst,
isExpandable = true
}) => {
const isExpanded = availableTerminalHeight === void 0;
const overflowActions = useOverflowActions();
const uniqueId = (0, import_react19.useId)();
const overflowId = `subagent-${uniqueId}`;
(0, import_react19.useEffect)(() => {
if (isExpandable && overflowActions) {
overflowActions.addOverflowingId(overflowId);
}
return () => {
if (overflowActions) {
overflowActions.removeOverflowingId(overflowId);
}
};
}, [isExpandable, overflowActions, overflowId]);
if (toolCalls.length === 0) {
return null;
}
let headerText = "";
if (toolCalls.length === 1) {
const singleAgent = toolCalls[0].resultDisplay;
if (isSubagentProgress(singleAgent)) {
switch (singleAgent.state) {
case SubagentState.COMPLETED:
headerText = "Agent Completed";
break;
case SubagentState.CANCELLED:
headerText = "Agent Cancelled";
break;
case SubagentState.ERROR:
headerText = "Agent Error";
break;
default:
headerText = "Running Agent...";
break;
}
} else {
headerText = "Running Agent...";
}
} else {
let completedCount = 0;
let runningCount = 0;
for (const tc of toolCalls) {
const progress = tc.resultDisplay;
if (isSubagentProgress(progress)) {
if (progress.state === SubagentState.COMPLETED) completedCount++;
else if (progress.state === SubagentState.RUNNING) runningCount++;
} else {
runningCount++;
}
}
if (completedCount === toolCalls.length) {
headerText = `${toolCalls.length} Agents Completed`;
} else if (completedCount > 0) {
headerText = `${toolCalls.length} Agents (${runningCount} running, ${completedCount} completed)...`;
} else {
headerText = `Running ${toolCalls.length} Agents...`;
}
}
const toggleText = `(ctrl+o to ${isExpanded ? "collapse" : "expand"})`;
const renderCollapsedRow = (key, agentName, icon, content, displayArgs) => /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Box_default, { flexDirection: "row", marginLeft: 0, marginTop: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Box_default, { minWidth: 2, flexShrink: 0, children: icon }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { bold: true, color: theme.text.primary, wrap: "truncate", children: agentName }) }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.secondary, children: " \xB7 " }) }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Box_default, { flexShrink: 1, minWidth: 0, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Text, { color: theme.text.secondary, wrap: "truncate", children: [
content,
displayArgs && ` ${displayArgs}`
] }) })
] }, key);
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
Box_default,
{
flexDirection: "column",
width: terminalWidth,
borderLeft: true,
borderRight: true,
borderTop: isFirst,
borderBottom: false,
borderColor,
borderDimColor,
borderStyle: "round",
paddingLeft: 1,
paddingTop: 0,
paddingBottom: 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Box_default, { flexDirection: "row", gap: 1, marginBottom: isExpanded ? 1 : 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.secondary, children: "\u2261" }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { bold: true, color: theme.text.primary, children: headerText }),
isExpandable && /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.secondary, children: toggleText })
] }),
toolCalls.map((toolCall) => {
const progress = toolCall.resultDisplay;
if (!isSubagentProgress(progress)) {
const agentName = toolCall.name || "agent";
if (!isExpanded) {
return renderCollapsedRow(
toolCall.callId,
agentName,
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.primary, children: "!" }),
"Starting..."
);
} else {
return /* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(
Box_default,
{
flexDirection: "column",
marginLeft: 0,
marginBottom: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime25.jsxs)(Box_default, { flexDirection: "row", gap: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.primary, children: "!" }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { bold: true, color: theme.text.primary, children: agentName })
] }),
/* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.secondary, children: "Starting..." }) })
]
},
toolCall.callId
);
}
}
const history = toolCall.subagentHistory ?? progress.recentActivity;
const lastActivity = history[history.length - 1];
if (!isExpanded) {
let content = "Starting...";
let formattedArgs;
if (progress.state === SubagentState.COMPLETED) {
if (progress.terminateReason && progress.terminateReason !== "GOAL") {
content = `Finished Early (${progress.terminateReason})`;
} else {
content = "Completed successfully";
}
} else if (lastActivity) {
content = lastActivity.displayName || lastActivity.content;
if (lastActivity.description) {
formattedArgs = lastActivity.description;
} else if (lastActivity.type === "tool_call" && lastActivity.args) {
formattedArgs = formatToolArgs(lastActivity.args);
}
}
const displayArgs = progress.state === SubagentState.COMPLETED ? "" : formattedArgs;
const renderStatusIcon = () => {
const state = progress.state ?? SubagentState.RUNNING;
switch (state) {
case SubagentState.RUNNING:
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.text.primary, children: "!" });
case SubagentState.COMPLETED:
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.status.success, children: "\u2713" });
case SubagentState.CANCELLED:
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.status.warning, children: "\u2139" });
case SubagentState.ERROR:
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(Text, { color: theme.status.error, children: "\u2717" });
default:
return checkExhaustive(state);
}
};
return renderCollapsedRow(
toolCall.callId,
progress.agentName,
renderStatusIcon(),
lastActivity?.type === "thought" ? `\u{1F4AD} ${content}` : content,
displayArgs
);
}
return /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
Box_default,
{
flexDirection: "column",
marginLeft: 0,
marginBottom: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime25.jsx)(
SubagentProgressDisplay,
{
progress,
terminalWidth,
historyOverrides: toolCall.subagentHistory
}
)
},
toolCall.callId
);
})
]
}
);
};
// packages/cli/src/ui/components/messages/DenseToolMessage.tsx
var import_react20 = __toESM(require_react(), 1);
var import_jsx_runtime26 = __toESM(require_jsx_runtime(), 1);
var PAYLOAD_MARGIN_LEFT = 6;
var PAYLOAD_BORDER_CHROME_WIDTH = 4;
var PAYLOAD_SCROLL_GUTTER = 4;
var PAYLOAD_MAX_WIDTH = 120 + PAYLOAD_SCROLL_GUTTER;
var hasPayload = (res) => {
if (!hasSummary(res)) return false;
if (!("payload" in res)) return false;
const value = res.payload;
return typeof value === "string";
};
function getFileOpData(diff, status, resultDisplay, terminalWidth, availableTerminalHeight, isClickable) {
const added = (diff.diffStat?.model_added_lines ?? 0) + (diff.diffStat?.user_added_lines ?? 0);
const removed = (diff.diffStat?.model_removed_lines ?? 0) + (diff.diffStat?.user_removed_lines ?? 0);
const isAcceptedOrConfirming = status === CoreToolCallStatus.Success || status === CoreToolCallStatus.Executing || status === CoreToolCallStatus.AwaitingApproval;
const addColor = isAcceptedOrConfirming ? theme.status.success : theme.text.secondary;
const removeColor = isAcceptedOrConfirming ? theme.status.error : theme.text.secondary;
const showDiffStat = !!diff.diffStat;
const description = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: diff.fileName }) });
let resultSummary = "";
let resultColor = theme.text.secondary;
if (status === CoreToolCallStatus.AwaitingApproval) {
resultSummary = "Confirming";
} else if (status === CoreToolCallStatus.Success || status === CoreToolCallStatus.Executing) {
resultSummary = "Accepted";
resultColor = theme.text.accent;
} else if (status === CoreToolCallStatus.Cancelled) {
resultSummary = "Rejected";
resultColor = theme.status.error;
} else if (status === CoreToolCallStatus.Error) {
resultSummary = typeof resultDisplay === "string" ? resultDisplay : "Failed";
resultColor = theme.status.error;
}
const summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Box_default, { flexDirection: "row", children: [
resultSummary && /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: resultColor, wrap: "truncate-end", children: [
"\u2192",
" ",
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { underline: isClickable, children: resultSummary.replace(/\n/g, " ") })
] }),
showDiffStat && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { marginLeft: 1, marginRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: addColor, children: [
"+",
added
] }),
", ",
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: removeColor, children: [
"-",
removed
] }),
")"
] }) })
] });
const payload = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
DiffRenderer,
{
diffContent: diff.fileDiff,
filename: diff.fileName,
terminalWidth: terminalWidth - PAYLOAD_MARGIN_LEFT,
availableTerminalHeight,
disableColor: status === CoreToolCallStatus.Cancelled
}
);
return { description, summary, payload };
}
function getReadManyFilesData(result) {
const includePatterns = result.include?.join(", ") ?? "";
const description = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: [
"Attempting to read files from ",
includePatterns
] });
const skippedCount = result.skipped?.length ?? 0;
const summaryStr = `Read ${result.files.length} file(s)${skippedCount > 0 ? ` (${skippedCount} ignored)` : ""}`;
const summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.accent, children: [
"\u2192 ",
summaryStr
] });
return { description, summary, payload: void 0 };
}
function getListDirectoryData(result, originalDescription) {
const description = originalDescription ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: originalDescription }) : void 0;
const summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.accent, children: [
"\u2192 ",
result.summary
] });
return { description, summary, payload: void 0 };
}
function getListResultData(result, originalDescription) {
if (isReadManyFilesResult(result)) {
return getReadManyFilesData(result);
}
return getListDirectoryData(result, originalDescription);
}
function getGenericSuccessData(resultDisplay, originalDescription) {
let summary;
let payload;
const description = originalDescription ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: originalDescription }) : void 0;
if (typeof resultDisplay === "string") {
const flattened = resultDisplay.replace(/\n/g, " ").trim();
summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.accent, wrap: "truncate-end", children: [
"\u2192 ",
flattened
] });
} else if (isGrepResult(resultDisplay)) {
summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.accent, wrap: "truncate-end", children: [
"\u2192 ",
resultDisplay.summary
] });
} else if (isTodoList(resultDisplay)) {
summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.accent, wrap: "wrap", children: "\u2192 Todos updated" });
} else if (hasPayload(resultDisplay)) {
summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.accent, children: [
"\u2192 ",
resultDisplay.summary
] });
payload = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, children: resultDisplay.payload }) });
} else {
summary = /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.accent, wrap: "wrap", children: "\u2192 Returned (possible empty result)" });
}
return { description, summary, payload };
}
var DenseToolMessage = (props) => {
const {
callId,
name,
status,
resultDisplay,
confirmationDetails,
outputFile,
terminalWidth,
availableTerminalHeight,
description: originalDescription
} = props;
const settings = useSettings();
const isAlternateBuffer = useAlternateBuffer();
const { isExpanded: isExpandedInContext, toggleExpansion } = useToolActions();
const [localIsExpanded, setLocalIsExpanded] = (0, import_react20.useState)(false);
const isExpanded = isExpandedInContext ? isExpandedInContext(callId) : localIsExpanded;
const [isFocused, setIsFocused] = (0, import_react20.useState)(false);
const toggleRef = (0, import_react20.useRef)(null);
const diff = (0, import_react20.useMemo)(() => {
if (isFileDiff(resultDisplay)) return resultDisplay;
if (confirmationDetails?.type === "edit") {
const details = confirmationDetails;
return {
fileName: details.fileName,
fileDiff: details.fileDiff,
filePath: details.filePath,
originalContent: details.originalContent,
newContent: details.newContent,
diffStat: details.diffStat
};
}
return void 0;
}, [resultDisplay, confirmationDetails]);
const handleToggle = () => {
const next = !isExpanded;
if (!next) {
setIsFocused(false);
} else {
setIsFocused(true);
}
if (toggleExpansion) {
toggleExpansion(callId);
} else {
setLocalIsExpanded(next);
}
};
useMouseClick(toggleRef, handleToggle, {
isActive: isAlternateBuffer && !!diff
});
const viewParts = (0, import_react20.useMemo)(() => {
if (diff) {
return getFileOpData(
diff,
status,
resultDisplay,
terminalWidth,
availableTerminalHeight,
isAlternateBuffer
);
}
if (isListResult(resultDisplay)) {
return getListResultData(resultDisplay, originalDescription);
}
if (isGrepResult(resultDisplay)) {
return getGenericSuccessData(resultDisplay, originalDescription);
}
if (status === CoreToolCallStatus.Success && resultDisplay) {
return getGenericSuccessData(resultDisplay, originalDescription);
}
if (status === CoreToolCallStatus.Error) {
const text = typeof resultDisplay === "string" ? resultDisplay.replace(/\n/g, " ") : "Failed";
const errorSummary = /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.status.error, wrap: "truncate-end", children: [
"\u2192 ",
text
] });
const descriptionText2 = originalDescription ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: originalDescription }) : void 0;
return {
description: descriptionText2,
summary: errorSummary,
payload: void 0
};
}
const descriptionText = originalDescription ? /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: originalDescription }) : void 0;
return {
description: descriptionText,
summary: void 0,
payload: void 0
};
}, [
diff,
status,
resultDisplay,
terminalWidth,
availableTerminalHeight,
originalDescription,
isAlternateBuffer
]);
const { description, summary } = viewParts;
const diffLines = (0, import_react20.useMemo)(() => {
if (!diff || !isExpanded || !isAlternateBuffer) return [];
const parsedLines = parseDiffWithLineNumbers(diff.fileDiff);
const isNewFileResult = isNewFile(parsedLines);
if (isNewFileResult) {
const addedContent = parsedLines.filter((line) => line.type === "add").map((line) => line.content).join("\n");
const fileExtension = getFileExtension(diff.fileName);
return colorizeCode({
code: addedContent,
language: fileExtension,
maxWidth: terminalWidth - PAYLOAD_MARGIN_LEFT,
settings,
disableColor: status === CoreToolCallStatus.Cancelled,
returnLines: true
});
} else {
return renderDiffLines({
parsedLines,
filename: diff.fileName,
terminalWidth: terminalWidth - PAYLOAD_MARGIN_LEFT,
disableColor: status === CoreToolCallStatus.Cancelled
});
}
}, [diff, isExpanded, isAlternateBuffer, terminalWidth, settings, status]);
const showPayload = (0, import_react20.useMemo)(() => {
const policy = !isAlternateBuffer || !diff || isExpanded;
if (!policy) return false;
if (diff) {
if (isAlternateBuffer) {
return isExpanded && diffLines.length > 0;
}
return true;
}
return !!(viewParts.payload || outputFile);
}, [
isAlternateBuffer,
diff,
isExpanded,
diffLines.length,
viewParts.payload,
outputFile
]);
const keyExtractor = (_item, index) => `diff-line-${index}`;
const renderItem = ({ item }) => /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { minHeight: 1, children: item });
return /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Box_default, { marginLeft: 2, flexDirection: "row", flexWrap: "wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Box_default, { flexDirection: "row", flexShrink: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(ToolStatusIndicator, { status, name }),
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { maxWidth: 25, flexShrink: 0, flexGrow: 0, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.primary, bold: true, wrap: "truncate-end", children: [
name,
" "
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { marginLeft: 1, flexShrink: 1, flexGrow: 0, children: description })
] }),
summary && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
Box_default,
{
ref: isAlternateBuffer && diff ? toggleRef : void 0,
marginLeft: 1,
flexGrow: 0,
children: summary
},
"tool-summary"
)
] }),
showPayload && isAlternateBuffer && diffLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
Box_default,
{
marginLeft: PAYLOAD_MARGIN_LEFT,
marginTop: 1,
marginBottom: 1,
paddingX: 1,
flexDirection: "column",
height: Math.min(diffLines.length, COMPACT_TOOL_SUBVIEW_MAX_LINES) + 2,
maxHeight: COMPACT_TOOL_SUBVIEW_MAX_LINES + 2,
borderStyle: "round",
borderColor: theme.border.default,
borderDimColor: true,
maxWidth: Math.min(
PAYLOAD_MAX_WIDTH,
terminalWidth - PAYLOAD_MARGIN_LEFT
),
children: /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
ScrollableListWithForwardRef,
{
data: diffLines,
renderItem,
keyExtractor,
estimatedItemHeight: () => 1,
hasFocus: isFocused,
width: Math.min(
PAYLOAD_MAX_WIDTH,
terminalWidth - PAYLOAD_MARGIN_LEFT - PAYLOAD_BORDER_CHROME_WIDTH - PAYLOAD_SCROLL_GUTTER
)
}
)
}
),
showPayload && (!isAlternateBuffer || !diff) && viewParts.payload && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { marginLeft: PAYLOAD_MARGIN_LEFT, marginTop: 1, marginBottom: 1, children: viewParts.payload }),
showPayload && outputFile && /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(Box_default, { marginLeft: PAYLOAD_MARGIN_LEFT, marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)(Text, { color: theme.text.secondary, children: [
"(Output saved to: ",
outputFile,
")"
] }) })
] });
};
// packages/cli/src/ui/utils/historyUtils.ts
function buildToolVisibilityContextFromDisplay(tool) {
return {
name: tool.originalRequestName ?? tool.name,
displayName: tool.name,
// In CLI, 'name' is usually the resolved display name
status: tool.status,
hasResult: !!tool.resultDisplay,
approvalMode: tool.approvalMode,
isClientInitiated: tool.isClientInitiated,
parentCallId: tool.parentCallId
};
}
function getLastTurnToolCallIds(history, pendingHistoryItems) {
const targetToolCallIds = [];
let lastUserPromptIndex = -1;
for (let i = history.length - 1; i >= 0; i--) {
const type = history[i].type;
if (type === "user" || type === "user_shell") {
lastUserPromptIndex = i;
break;
}
}
history.forEach((item, index) => {
if (index > lastUserPromptIndex && item.type === "tool_group") {
item.tools.forEach((t) => {
if (t.callId) targetToolCallIds.push(t.callId);
});
}
});
pendingHistoryItems.forEach((item) => {
if (item.type === "tool_group") {
item.tools.forEach((t) => {
if (t.callId) targetToolCallIds.push(t.callId);
});
}
});
return targetToolCallIds;
}
function isToolExecuting(pendingHistoryItems) {
return pendingHistoryItems.some((item) => {
if (item && item.type === "tool_group") {
return item.tools.some(
(tool) => CoreToolCallStatus.Executing === tool.status
);
}
return false;
});
}
function isToolAwaitingConfirmation(pendingHistoryItems) {
return pendingHistoryItems.filter((item) => item.type === "tool_group").some(
(item) => item.tools.some(
(tool) => CoreToolCallStatus.AwaitingApproval === tool.status
)
);
}
function getAllToolCalls(historyItems) {
return historyItems.filter((item) => item.type === "tool_group").flatMap((group) => group.tools);
}
// packages/cli/src/ui/utils/borderStyles.ts
function isTrackedToolCall(tool) {
return "request" in tool;
}
function getToolGroupBorderAppearance(item, activeShellPtyId, embeddedShellFocused, allPendingItems = [], backgroundTasks = /* @__PURE__ */ new Map()) {
if (item.type !== "tool_group") {
return { borderColor: "", borderDimColor: false };
}
const toolsToInspect = item.tools.length > 0 ? item.tools : allPendingItems.filter(
(i) => i !== null && i !== void 0 && i.type === "tool_group" && i.tools.length > 0
).slice(-1).flatMap((i) => i.tools);
const hasPending = toolsToInspect.some((t) => {
if (isTrackedToolCall(t)) {
return t.status !== "success" && t.status !== "error" && t.status !== "cancelled";
} else {
return t.status !== CoreToolCallStatus.Success && t.status !== CoreToolCallStatus.Error && t.status !== CoreToolCallStatus.Cancelled;
}
});
const isEmbeddedShellFocused = toolsToInspect.some((t) => {
if (isTrackedToolCall(t)) {
return isShellTool(t.request.name) && t.status === "executing" && t.pid === activeShellPtyId && !!embeddedShellFocused;
} else {
return isShellTool(t.name) && t.status === CoreToolCallStatus.Executing && t.ptyId === activeShellPtyId && !!embeddedShellFocused;
}
});
const isShellCommand = toolsToInspect.some((t) => {
if (isTrackedToolCall(t)) {
return isShellTool(t.request.name);
} else {
return isShellTool(t.name);
}
});
const isCurrentlyInShellTurn = !!activeShellPtyId && !backgroundTasks.has(activeShellPtyId);
const isShell = isShellCommand || item.tools.length === 0 && isCurrentlyInShellTurn;
const isPending = hasPending || item.tools.length === 0 && isCurrentlyInShellTurn;
const isEffectivelyFocused = isEmbeddedShellFocused || item.tools.length === 0 && isCurrentlyInShellTurn && !!embeddedShellFocused;
const borderColor = isEffectivelyFocused ? theme.ui.focus : isShell && isPending ? theme.ui.active : isPending ? theme.status.warning : theme.border.default;
const borderDimColor = isPending && (!isShell || !isEffectivelyFocused);
return { borderColor, borderDimColor };
}
// packages/cli/src/ui/components/messages/ToolGroupMessage.tsx
var import_jsx_runtime27 = __toESM(require_jsx_runtime(), 1);
var COMPACT_OUTPUT_ALLOWLIST = /* @__PURE__ */ new Set([
EDIT_DISPLAY_NAME,
GLOB_DISPLAY_NAME,
WEB_SEARCH_DISPLAY_NAME,
READ_FILE_DISPLAY_NAME,
LS_DISPLAY_NAME,
GREP_DISPLAY_NAME,
WEB_FETCH_DISPLAY_NAME,
WRITE_FILE_DISPLAY_NAME,
READ_MANY_FILES_DISPLAY_NAME
]);
var isCompactTool = (tool, isCompactModeEnabled) => {
const hasCompactOutputSupport = COMPACT_OUTPUT_ALLOWLIST.has(tool.name);
const displayStatus = mapCoreStatusToDisplayStatus(tool.status);
return isCompactModeEnabled && hasCompactOutputSupport && displayStatus !== "Confirming" /* Confirming */;
};
var hasDensePayload = (tool) => {
if (tool.outputFile) return true;
const res = tool.resultDisplay;
if (!res) return false;
if (isFileDiff(res)) return true;
if (tool.confirmationDetails?.type === "edit") return true;
if (typeof res === "object" && res !== null && "summary" in res && "payload" in res) {
return true;
}
return false;
};
var TOOL_MESSAGE_HORIZONTAL_MARGIN = 4;
var ToolGroupMessage = ({
item,
toolCalls: allToolCalls,
availableTerminalHeight,
terminalWidth,
borderTop: borderTopOverride,
borderBottom: borderBottomOverride,
isExpandable
}) => {
const settings = useSettings();
const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== "full";
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
const visibleToolCalls = (0, import_react21.useMemo)(
() => allToolCalls.filter(
(t) => (
// Use the unified visibility utility
isVisibleInToolGroup(
buildToolVisibilityContextFromDisplay(t),
isLowErrorVerbosity ? "low" : "full"
)
)
),
[allToolCalls, isLowErrorVerbosity]
);
const {
activePtyId,
embeddedShellFocused,
backgroundTasks,
pendingHistoryItems
} = useUIState();
const config = useConfig();
const { borderColor, borderDimColor } = (0, import_react21.useMemo)(
() => getToolGroupBorderAppearance(
item,
activePtyId,
embeddedShellFocused,
pendingHistoryItems,
backgroundTasks
),
[
item,
activePtyId,
embeddedShellFocused,
pendingHistoryItems,
backgroundTasks
]
);
const groupedTools = (0, import_react21.useMemo)(() => {
const groups = [];
for (const tool of visibleToolCalls) {
if (tool.kind === Kind.Agent) {
const lastGroup = groups[groups.length - 1];
if (Array.isArray(lastGroup)) {
lastGroup.push(tool);
} else {
groups.push([tool]);
}
} else {
groups.push(tool);
}
}
return groups;
}, [visibleToolCalls]);
const staticHeight = (0, import_react21.useMemo)(() => {
let height = 0;
for (let i = 0; i < groupedTools.length; i++) {
const group = groupedTools[i];
const isLast = i === groupedTools.length - 1;
const prevGroup = i > 0 ? groupedTools[i - 1] : null;
const prevIsCompact = prevGroup && !Array.isArray(prevGroup) && isCompactTool(prevGroup, isCompactModeEnabled);
const prevIsTopic = prevGroup && !Array.isArray(prevGroup) && isTopicTool(prevGroup.name);
const nextGroup = !isLast ? groupedTools[i + 1] : null;
const nextIsCompact = nextGroup && !Array.isArray(nextGroup) && isCompactTool(nextGroup, isCompactModeEnabled);
const nextIsTopicToolCall = nextGroup && !Array.isArray(nextGroup) && isTopicTool(nextGroup.name);
const isAgentGroup = Array.isArray(group);
const isCompact = !isAgentGroup && isCompactTool(group, isCompactModeEnabled);
const isTopicToolCall = !isAgentGroup && isTopicTool(group.name);
let isFirst = i === 0;
if (!isFirst) {
let allPreviousTopics = true;
for (let j = 0; j < i; j++) {
const prevGroupItem = groupedTools[j];
if (Array.isArray(prevGroupItem) || !isTopicTool(prevGroupItem.name)) {
allPreviousTopics = false;
break;
}
}
isFirst = allPreviousTopics;
}
const isFirstProp = !!(isFirst ? borderTopOverride ?? true : prevIsCompact || prevIsTopic);
const showClosingBorder = !isCompact && !isTopicToolCall && (nextIsCompact || nextIsTopicToolCall || isLast);
if (isAgentGroup) {
height += (isFirstProp ? 1 : 0) + 1 + group.length + (showClosingBorder ? 1 : 0);
} else if (isTopicToolCall) {
height += 1 + 1 + (showClosingBorder ? 1 : 0);
} else if (isCompact) {
height += 1;
} else {
height += TOOL_RESULT_STANDARD_RESERVED_LINE_COUNT - 1 + TOOL_RESULT_STATIC_HEIGHT + (group.outputFile ? 1 : 0) + (showClosingBorder ? 1 : 0);
}
}
return height;
}, [groupedTools, isCompactModeEnabled, borderTopOverride]);
let countToolCallsWithResults = 0;
for (const tool of visibleToolCalls) {
if (tool.kind !== Kind.Agent) {
if (isCompactTool(tool, isCompactModeEnabled)) {
if (hasDensePayload(tool)) {
countToolCallsWithResults++;
}
} else if (tool.resultDisplay !== void 0 && tool.resultDisplay !== "") {
countToolCallsWithResults++;
}
}
}
const availableTerminalHeightPerToolMessage = availableTerminalHeight ? Math.max(
Math.floor(
(availableTerminalHeight - staticHeight) / Math.max(1, countToolCallsWithResults)
),
1
) : void 0;
const contentWidth = terminalWidth - TOOL_MESSAGE_HORIZONTAL_MARGIN;
const isExplicitClosingSlice = allToolCalls.length === 0;
const shouldShowGroup = visibleToolCalls.length > 0 || isExplicitClosingSlice && borderBottomOverride === true;
if (!shouldShowGroup) {
return null;
}
const content = /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
Box_default,
{
flexDirection: "column",
width: terminalWidth,
paddingRight: TOOL_MESSAGE_HORIZONTAL_MARGIN,
marginBottom: 0,
children: [
visibleToolCalls.length === 0 && isExplicitClosingSlice && borderBottomOverride === true && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
Box_default,
{
width: contentWidth,
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: true,
borderColor,
borderDimColor,
borderStyle: "round"
}
),
groupedTools.map((group, index) => {
let isFirst = index === 0;
if (!isFirst) {
let allPreviousWereTopics = true;
for (let i = 0; i < index; i++) {
const prevGroup2 = groupedTools[i];
if (Array.isArray(prevGroup2) || !isTopicTool(prevGroup2.name)) {
allPreviousWereTopics = false;
break;
}
}
isFirst = allPreviousWereTopics;
}
const isLast = index === groupedTools.length - 1;
const prevGroup = index > 0 ? groupedTools[index - 1] : null;
const prevIsCompact = prevGroup && !Array.isArray(prevGroup) && isCompactTool(prevGroup, isCompactModeEnabled);
const prevIsTopic = prevGroup && !Array.isArray(prevGroup) && isTopicTool(prevGroup.name);
const nextGroup = !isLast ? groupedTools[index + 1] : null;
const nextIsCompact = nextGroup && !Array.isArray(nextGroup) && isCompactTool(nextGroup, isCompactModeEnabled);
const nextIsTopicToolCall = nextGroup && !Array.isArray(nextGroup) && isTopicTool(nextGroup.name);
const isAgentGroup = Array.isArray(group);
const isCompact = !isAgentGroup && isCompactTool(group, isCompactModeEnabled);
const isTopicToolCall = !isAgentGroup && isTopicTool(group.name);
const isFirstProp = !!(isFirst ? borderTopOverride ?? true : prevIsCompact || prevIsTopic);
const showClosingBorder = !isCompact && !isTopicToolCall && (nextIsCompact || nextIsTopicToolCall || isLast);
if (isAgentGroup) {
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(
Box_default,
{
flexDirection: "column",
width: contentWidth,
children: [
/* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
SubagentGroupDisplay,
{
toolCalls: group,
availableTerminalHeight,
terminalWidth: contentWidth,
borderColor,
borderDimColor,
isFirst: isFirstProp,
isExpandable
}
),
showClosingBorder && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
Box_default,
{
width: contentWidth,
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: isLast ? borderBottomOverride ?? true : true,
borderColor,
borderDimColor,
borderStyle: "round"
}
)
]
},
group[0].callId
);
}
const tool = group;
const isShellToolCall = isShellTool(tool.name);
const commonProps = {
...tool,
availableTerminalHeight: availableTerminalHeightPerToolMessage,
terminalWidth: contentWidth,
emphasis: "medium",
isFirst: isCompact ? false : isFirstProp,
borderColor,
borderDimColor,
isExpandable
};
return /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(import_react21.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Box_default, { flexDirection: "column", minHeight: 1, width: contentWidth, children: [
isCompact ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(DenseToolMessage, { ...commonProps }) : isTopicToolCall ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(TopicMessage, { ...commonProps }) }) : isShellToolCall ? /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ShellToolMessage, { ...commonProps, config }) : /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(ToolMessage, { ...commonProps }),
!isCompact && tool.outputFile && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
Box_default,
{
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: false,
borderColor,
borderDimColor,
flexDirection: "column",
borderStyle: "round",
paddingLeft: 1,
paddingRight: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime27.jsxs)(Text, { color: theme.text.primary, children: [
"Output too long and was saved to: ",
tool.outputFile
] }) })
}
)
] }),
showClosingBorder && /* @__PURE__ */ (0, import_jsx_runtime27.jsx)(
Box_default,
{
width: contentWidth,
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: isLast ? borderBottomOverride ?? true : true,
borderColor,
borderDimColor,
borderStyle: "round"
}
)
] }, tool.callId);
})
]
}
);
return content;
};
// packages/cli/src/ui/components/messages/ToolGroupDisplay.tsx
var import_jsx_runtime28 = __toESM(require_jsx_runtime(), 1);
var ToolGroupDisplay = ({
item,
isToolGroupBoundary
}) => {
const settings = useSettings();
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
if (item.type !== "tool_display_group") {
return null;
}
const { tools, borderColor, borderDimColor, borderTop, borderBottom } = item;
const visibleTools = tools.filter(
(t) => t.status !== CoreToolCallStatus.AwaitingApproval
);
const noticeTools = visibleTools.filter((t) => t.format === "notice");
const otherTools = visibleTools.filter(
(t) => t.format !== "notice" && t.format !== "hidden"
);
const hasOtherTools = otherTools.length > 0;
const isClosingSlice = tools.length === 0 && borderBottom;
if (visibleTools.length === 0 && !isClosingSlice) {
return null;
}
const shouldShowBox = (hasOtherTools || isClosingSlice) && !isCompactModeEnabled;
const boxBorderTop = borderTop || noticeTools.length > 0;
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { flexDirection: "column", children: [
noticeTools.map((tool, index) => {
const isFirstInGroup = index === 0 && borderTop;
const isLastElementInGroup = index === noticeTools.length - 1 && !shouldShowBox && borderBottom;
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
Box_default,
{
marginTop: isFirstInGroup ? 1 : index > 0 ? 1 : 0,
marginBottom: isLastElementInGroup ? 1 : 0,
children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ToolDisplayMessage, { tool })
},
`notice-${index}`
);
}),
shouldShowBox ? /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor,
borderDimColor,
borderTop: boxBorderTop,
borderBottom,
borderLeft: !isToolGroupBoundary,
borderRight: !isToolGroupBoundary,
marginTop: boxBorderTop ? 1 : 0,
marginBottom: borderBottom ? 1 : 0,
paddingX: 1,
children: otherTools.map((tool, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ToolDisplayMessage, { tool }, `tool-${index}`))
}
) : otherTools.length > 0 ? (
// Compact mode or no tools to box
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
Box_default,
{
flexDirection: "column",
marginTop: noticeTools.length > 0 ? 1 : 0,
marginBottom: borderBottom ? 1 : 0,
children: otherTools.map((tool, index) => /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ToolDisplayMessage, { tool }, `tool-${index}`))
}
)
) : null
] });
};
var ToolDisplayMessage = ({ tool }) => {
const settings = useSettings();
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
if (!tool.name && !tool.description && !tool.result && !tool.resultSummary) {
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { paddingLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
ToolStatusIndicator,
{
status: tool.status,
name: tool.originalRequestName || "unknown"
}
),
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.status.error, children: " Error: Tool display missing" })
] });
}
const {
status,
format: preferredFormat,
name,
description,
resultSummary,
result
} = tool;
const format = preferredFormat || "auto";
if (format === "hidden") {
return null;
}
if (format === "notice") {
const isRedundant = !!(name && description?.includes(`"${name}"`));
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { paddingLeft: 2, flexDirection: "column", children: [
name && !isRedundant && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Text, { color: theme.text.primary, bold: true, wrap: "truncate-end", children: [
name,
":"
] }),
description && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.secondary, wrap: "wrap", children: description })
] });
}
const isCompact = format === "compact" || format === "auto" && isCompactModeEnabled;
if (isCompact) {
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { paddingLeft: 2, flexDirection: "row", flexWrap: "wrap", children: [
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
ToolStatusIndicator,
{
status,
name: name || tool.originalRequestName || ""
}
),
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
" ",
name || tool.originalRequestName,
" "
] }),
description && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.secondary, children: description }),
resultSummary && /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Text, { color: theme.text.accent, children: [
" ",
"\u2192 ",
resultSummary.replace(/\n/g, " ")
] })
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { flexDirection: "row", paddingLeft: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
ToolStatusIndicator,
{
status,
name: name || tool.originalRequestName || ""
}
),
/* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
" ",
name || tool.originalRequestName,
" "
] }),
description && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.secondary, children: description })
] }),
resultSummary && !result && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Box_default, { paddingLeft: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.primary, children: resultSummary }) }),
result && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Box_default, { paddingLeft: 1, marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(ToolResultDisplayContent, { content: result, summary: resultSummary }) })
] });
};
var ToolResultDisplayContent = ({
content,
summary
}) => {
if (!content) return null;
switch (content.type) {
case "text":
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.primary, children: content.text }),
summary && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Box_default, { marginTop: 0, children: /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.primary, children: summary }) })
] });
case "diff":
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Box_default, { flexDirection: "column", children: [
summary && /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.primary, children: summary }),
/* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.secondary, children: `[Diff Display: ${content.beforeText.length} -> ${content.afterText.length} chars]` })
] });
case "terminal":
return /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(Text, { color: theme.text.secondary, children: "[Terminal Output]" });
case "agent":
return /* @__PURE__ */ (0, import_jsx_runtime28.jsxs)(Text, { color: theme.text.secondary, children: [
"[Subagent: ",
content.threadId,
"]"
] });
default:
return null;
}
};
// packages/cli/src/ui/components/messages/GeminiMessageContent.tsx
var import_jsx_runtime29 = __toESM(require_jsx_runtime(), 1);
var GeminiMessageContent = ({
text,
isPending,
availableTerminalHeight,
terminalWidth
}) => {
const { renderMarkdown } = useUIState();
const originalPrefix = "\u2726 ";
const prefixWidth = originalPrefix.length;
return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(Box_default, { flexDirection: "column", paddingLeft: prefixWidth, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(
MarkdownDisplay,
{
text,
isPending,
availableTerminalHeight: availableTerminalHeight === void 0 ? void 0 : Math.max(availableTerminalHeight - 1, 1),
terminalWidth: Math.max(terminalWidth - prefixWidth, 0),
renderMarkdown
}
) });
};
// packages/cli/src/ui/components/messages/CompressionMessage.tsx
var import_jsx_runtime30 = __toESM(require_jsx_runtime(), 1);
function CompressionMessage({
compression
}) {
const { isPending, originalTokenCount, newTokenCount, compressionStatus } = compression;
const originalTokens = originalTokenCount ?? 0;
const newTokens = newTokenCount ?? 0;
const getCompressionText = () => {
if (isPending) {
return "Compressing chat history";
}
switch (compressionStatus) {
case CompressionStatus.COMPRESSED:
return `Chat history compressed from ${originalTokens} to ${newTokens} tokens.`;
case CompressionStatus.COMPRESSION_FAILED_INFLATED_TOKEN_COUNT:
if (originalTokens < 5e4) {
return "Compression was not beneficial for this history size.";
}
return "Chat history compression did not reduce size. This may indicate issues with the compression prompt.";
case CompressionStatus.COMPRESSION_FAILED_TOKEN_COUNT_ERROR:
return "Could not compress chat history due to a token counting error.";
case CompressionStatus.COMPRESSION_FAILED_EMPTY_SUMMARY:
return "Chat history compression failed: the model returned an empty summary.";
case CompressionStatus.NOOP:
return "Nothing to compress.";
default:
return "";
}
};
const text = getCompressionText();
return /* @__PURE__ */ (0, import_jsx_runtime30.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Box_default, { marginRight: 1, children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(CliSpinner, { type: "dots" }) : /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Text, { color: theme.text.accent, children: "\u2726" }) }),
/* @__PURE__ */ (0, import_jsx_runtime30.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime30.jsx)(
Text,
{
color: compression.isPending ? theme.text.accent : theme.status.success,
"aria-label": SCREEN_READER_MODEL_PREFIX,
children: text
}
) })
] });
}
// packages/cli/src/ui/components/messages/ExportSessionMessage.tsx
var import_jsx_runtime31 = __toESM(require_jsx_runtime(), 1);
function ExportSessionMessage({
exportSession
}) {
const { isPending, targetPath } = exportSession;
return /* @__PURE__ */ (0, import_jsx_runtime31.jsxs)(Box_default, { flexDirection: "row", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Box_default, { marginRight: 1, children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(CliSpinner, { type: "dots" }) : /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Text, { color: theme.status.success, children: "\u2713" }) }),
/* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime31.jsx)(Text, { color: isPending ? theme.text.accent : theme.status.success, children: isPending ? "Exporting session..." : `Successfully exported session to ${targetPath}` }) })
] });
}
// packages/cli/src/ui/components/messages/WarningMessage.tsx
var import_jsx_runtime32 = __toESM(require_jsx_runtime(), 1);
var WarningMessage = ({ text }) => {
const prefix = "\u26A0 ";
const prefixWidth = 3;
return /* @__PURE__ */ (0, import_jsx_runtime32.jsxs)(Box_default, { flexDirection: "row", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Box_default, { width: prefixWidth, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Text, { color: theme.status.warning, children: prefix }) }),
/* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(Text, { wrap: "wrap", children: /* @__PURE__ */ (0, import_jsx_runtime32.jsx)(RenderInline, { text, defaultColor: theme.status.warning }) }) })
] });
};
// packages/cli/src/ui/components/messages/SubagentHistoryMessage.tsx
var import_jsx_runtime33 = __toESM(require_jsx_runtime(), 1);
var SubagentHistoryMessage = ({
item,
terminalWidth
}) => /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(Box_default, { flexDirection: "column", width: terminalWidth, marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(Text, { bold: true, color: "cyan", children: [
"\u{1F916} ",
item.agentName,
" Trace (",
item.history.length,
" items)"
] }) }),
item.history.map((activity) => /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(Box_default, { marginLeft: 2, marginBottom: 0, children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)(Text, { color: activity.type === "thought" ? "gray" : "white", children: [
activity.type === "thought" ? "\u{1F9E0}" : "\u{1F6E0}\uFE0F",
" ",
activity.content,
activity.status === "running" && " (Running...)",
activity.status === "completed" && " \u2705",
activity.status === "error" && " \u274C"
] }) }, activity.id))
] });
// packages/cli/src/ui/components/AboutBox.tsx
var import_jsx_runtime34 = __toESM(require_jsx_runtime(), 1);
var AboutBox = ({
cliVersion,
osVersion,
sandboxEnv,
modelVersion,
selectedAuthType,
gcpProject,
ideClient,
userEmail,
tier
}) => {
const settings = useSettings();
const showUserIdentity = settings.merged.ui.showUserIdentity;
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
padding: 1,
marginY: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.accent, children: "About Gemini CLI" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "CLI Version" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: cliVersion }) })
] }),
GIT_COMMIT_INFO && !["N/A"].includes(GIT_COMMIT_INFO) && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "Git Commit" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: GIT_COMMIT_INFO }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "Model" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: getDisplayString(modelVersion) }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "Sandbox" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: sandboxEnv }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "OS" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: osVersion }) })
] }),
showUserIdentity && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "Auth Method" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: selectedAuthType.startsWith("oauth") ? userEmail ? `Signed in with Google (${userEmail})` : "Signed in with Google" : selectedAuthType }) })
] }),
showUserIdentity && tier && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "Tier" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: tier }) })
] }),
gcpProject && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "GCP Project" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: gcpProject }) })
] }),
ideClient && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { width: "35%", children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { bold: true, color: theme.text.link, children: "IDE Client" }) }),
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(Text, { color: theme.text.primary, children: ideClient }) })
] })
]
}
);
};
// node_modules/ink-gradient/dist/index.js
var import_react22 = __toESM(require_react(), 1);
var import_prop_types = __toESM(require_prop_types(), 1);
var import_gradient_string = __toESM(require_gradient_string(), 1);
var Gradient = (props) => {
if (props.name && props.colors) {
throw new Error("The `name` and `colors` props are mutually exclusive");
}
let gradient;
if (props.name) {
gradient = import_gradient_string.default[props.name];
} else if (props.colors) {
gradient = (0, import_gradient_string.default)(props.colors);
} else {
throw new Error("Either `name` or `colors` prop must be provided");
}
const applyGradient = (text) => gradient.multiline(stripAnsi(text));
return import_react22.default.createElement(Transform, { transform: applyGradient }, props.children);
};
Gradient.propTypes = {
children: import_prop_types.default.oneOfType([
import_prop_types.default.arrayOf(import_prop_types.default.node),
import_prop_types.default.node
]).isRequired,
name: import_prop_types.default.oneOf([
"cristal",
"teen",
"mind",
"morning",
"vice",
"passion",
"fruit",
"instagram",
"atlas",
"retro",
"summer",
"pastel",
"rainbow"
]),
colors: import_prop_types.default.arrayOf(import_prop_types.default.oneOfType([
import_prop_types.default.string,
import_prop_types.default.object
]))
};
var dist_default = Gradient;
// packages/cli/src/ui/components/ThemedGradient.tsx
var import_jsx_runtime35 = __toESM(require_jsx_runtime(), 1);
var ThemedGradient = ({ children, ...props }) => {
const gradient = theme.ui.gradient;
if (gradient && gradient.length >= 2) {
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(dist_default, { colors: gradient, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Text, { ...props, children }) });
}
if (gradient && gradient.length === 1) {
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Text, { color: gradient[0], ...props, children });
}
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(Text, { color: theme.text.accent, ...props, children });
};
// packages/cli/src/ui/contexts/SessionContext.tsx
var import_react23 = __toESM(require_react(), 1);
var import_jsx_runtime36 = __toESM(require_jsx_runtime(), 1);
function areModelMetricsEqual(a, b) {
if (a.api.totalRequests !== b.api.totalRequests || a.api.totalErrors !== b.api.totalErrors || a.api.totalLatencyMs !== b.api.totalLatencyMs) {
return false;
}
if (a.tokens.input !== b.tokens.input || a.tokens.prompt !== b.tokens.prompt || a.tokens.candidates !== b.tokens.candidates || a.tokens.total !== b.tokens.total || a.tokens.cached !== b.tokens.cached || a.tokens.thoughts !== b.tokens.thoughts || a.tokens.tool !== b.tokens.tool) {
return false;
}
return true;
}
function areToolCallStatsEqual(a, b) {
if (a.count !== b.count || a.success !== b.success || a.fail !== b.fail || a.durationMs !== b.durationMs) {
return false;
}
if (a.decisions["accept" /* ACCEPT */] !== b.decisions["accept" /* ACCEPT */] || a.decisions["reject" /* REJECT */] !== b.decisions["reject" /* REJECT */] || a.decisions["modify" /* MODIFY */] !== b.decisions["modify" /* MODIFY */] || a.decisions["auto_accept" /* AUTO_ACCEPT */] !== b.decisions["auto_accept" /* AUTO_ACCEPT */]) {
return false;
}
return true;
}
function areMetricsEqual(a, b) {
if (a === b) return true;
if (!a || !b) return false;
if (a.files.totalLinesAdded !== b.files.totalLinesAdded || a.files.totalLinesRemoved !== b.files.totalLinesRemoved) {
return false;
}
const toolsA = a.tools;
const toolsB = b.tools;
if (toolsA.totalCalls !== toolsB.totalCalls || toolsA.totalSuccess !== toolsB.totalSuccess || toolsA.totalFail !== toolsB.totalFail || toolsA.totalDurationMs !== toolsB.totalDurationMs) {
return false;
}
if (toolsA.totalDecisions["accept" /* ACCEPT */] !== toolsB.totalDecisions["accept" /* ACCEPT */] || toolsA.totalDecisions["reject" /* REJECT */] !== toolsB.totalDecisions["reject" /* REJECT */] || toolsA.totalDecisions["modify" /* MODIFY */] !== toolsB.totalDecisions["modify" /* MODIFY */] || toolsA.totalDecisions["auto_accept" /* AUTO_ACCEPT */] !== toolsB.totalDecisions["auto_accept" /* AUTO_ACCEPT */]) {
return false;
}
const toolsByNameAKeys = Object.keys(toolsA.byName);
const toolsByNameBKeys = Object.keys(toolsB.byName);
if (toolsByNameAKeys.length !== toolsByNameBKeys.length) return false;
for (const key of toolsByNameAKeys) {
const toolA = toolsA.byName[key];
const toolB = toolsB.byName[key];
if (!toolB || !areToolCallStatsEqual(toolA, toolB)) {
return false;
}
}
const modelsAKeys = Object.keys(a.models);
const modelsBKeys = Object.keys(b.models);
if (modelsAKeys.length !== modelsBKeys.length) return false;
for (const key of modelsAKeys) {
if (!b.models[key] || !areModelMetricsEqual(a.models[key], b.models[key])) {
return false;
}
}
return true;
}
var SessionStatsContext = (0, import_react23.createContext)(
void 0
);
var SessionStatsProvider = ({ children, sessionId }) => {
const [stats, setStats] = (0, import_react23.useState)({
sessionId,
sessionStartTime: /* @__PURE__ */ new Date(),
metrics: uiTelemetryService.getMetrics(),
lastPromptTokenCount: 0,
promptCount: 0
});
(0, import_react23.useEffect)(() => {
const handleUpdate = ({
metrics,
lastPromptTokenCount
}) => {
setStats((prevState) => {
if (prevState.lastPromptTokenCount === lastPromptTokenCount && areMetricsEqual(prevState.metrics, metrics)) {
return prevState;
}
return {
...prevState,
metrics,
lastPromptTokenCount
};
});
};
const handleClear = (newSessionId) => {
setStats((prevState) => ({
...prevState,
sessionId: newSessionId || prevState.sessionId,
sessionStartTime: /* @__PURE__ */ new Date(),
promptCount: 0
}));
};
uiTelemetryService.on("update", handleUpdate);
uiTelemetryService.on("clear", handleClear);
handleUpdate({
metrics: uiTelemetryService.getMetrics(),
lastPromptTokenCount: uiTelemetryService.getLastPromptTokenCount()
});
return () => {
uiTelemetryService.off("update", handleUpdate);
uiTelemetryService.off("clear", handleClear);
};
}, []);
const startNewPrompt = (0, import_react23.useCallback)(() => {
setStats((prevState) => ({
...prevState,
promptCount: prevState.promptCount + 1
}));
}, []);
const getPromptCount = (0, import_react23.useCallback)(
() => stats.promptCount,
[stats.promptCount]
);
const value = (0, import_react23.useMemo)(
() => ({
stats,
startNewPrompt,
getPromptCount
}),
[stats, startNewPrompt, getPromptCount]
);
return /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(SessionStatsContext.Provider, { value, children });
};
var useSessionStats = () => {
const context = (0, import_react23.useContext)(SessionStatsContext);
if (context === void 0) {
throw new Error(
"useSessionStats must be used within a SessionStatsProvider"
);
}
return context;
};
// packages/cli/src/ui/utils/computeStats.ts
function calculateErrorRate(metrics) {
if (metrics.api.totalRequests === 0) {
return 0;
}
return metrics.api.totalErrors / metrics.api.totalRequests * 100;
}
function calculateAverageLatency(metrics) {
if (metrics.api.totalRequests === 0) {
return 0;
}
return metrics.api.totalLatencyMs / metrics.api.totalRequests;
}
function calculateCacheHitRate(metrics) {
if (metrics.tokens.prompt === 0) {
return 0;
}
return metrics.tokens.cached / metrics.tokens.prompt * 100;
}
var computeSessionStats = (metrics) => {
const { models, tools, files } = metrics;
const totalApiTime = Object.values(models).reduce(
(acc, model) => acc + model.api.totalLatencyMs,
0
);
const totalToolTime = tools.totalDurationMs;
const agentActiveTime = totalApiTime + totalToolTime;
const apiTimePercent = agentActiveTime > 0 ? totalApiTime / agentActiveTime * 100 : 0;
const toolTimePercent = agentActiveTime > 0 ? totalToolTime / agentActiveTime * 100 : 0;
const totalCachedTokens = Object.values(models).reduce(
(acc, model) => acc + model.tokens.cached,
0
);
const totalInputTokens = Object.values(models).reduce(
(acc, model) => acc + model.tokens.input,
0
);
const totalPromptTokens = Object.values(models).reduce(
(acc, model) => acc + model.tokens.prompt,
0
);
const cacheEfficiency = totalPromptTokens > 0 ? totalCachedTokens / totalPromptTokens * 100 : 0;
const totalDecisions = tools.totalDecisions.accept + tools.totalDecisions.reject + tools.totalDecisions.modify + tools.totalDecisions.auto_accept;
const successRate = tools.totalCalls > 0 ? tools.totalSuccess / tools.totalCalls * 100 : 0;
const agreementRate = totalDecisions > 0 ? (tools.totalDecisions.accept + tools.totalDecisions.auto_accept) / totalDecisions * 100 : 0;
return {
totalApiTime,
totalToolTime,
agentActiveTime,
apiTimePercent,
toolTimePercent,
cacheEfficiency,
totalDecisions,
successRate,
agreementRate,
totalCachedTokens,
totalInputTokens,
totalPromptTokens,
totalLinesAdded: files.totalLinesAdded,
totalLinesRemoved: files.totalLinesRemoved
};
};
// packages/cli/src/ui/components/StatsDisplay.tsx
var import_jsx_runtime37 = __toESM(require_jsx_runtime(), 1);
var StatRow = ({ title, children }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: 28, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.link, children: title }) }),
children
] });
var SubStatRow = ({ title, children }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Box_default, { paddingLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: 26, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.secondary, children: [
"\xBB ",
title
] }) }),
children
] });
var Section = ({ title, children }) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.primary, children: title }),
children
] });
var ModelUsageTable = ({ models }) => {
const nameWidth = 28;
const requestsWidth = 8;
const inputTokensWidth = 14;
const cacheReadsWidth = 14;
const outputTokensWidth = 14;
const rows = [];
Object.entries(models).forEach(([name, metrics]) => {
rows.push({
name,
displayName: name,
requests: metrics.api.totalRequests,
cachedTokens: metrics.tokens.cached.toLocaleString(),
inputTokens: metrics.tokens.prompt.toLocaleString(),
outputTokens: metrics.tokens.candidates.toLocaleString(),
isSubRow: false
});
if (metrics.roles) {
const roleEntries = Object.entries(metrics.roles).filter(
(entry) => entry[1] !== void 0 && entry[1].totalRequests > 0
);
roleEntries.sort(([a], [b]) => {
if (a === b) return 0;
if (a === LlmRole.MAIN) return -1;
if (b === LlmRole.MAIN) return 1;
return a.localeCompare(b);
});
roleEntries.forEach(([role, roleMetrics]) => {
rows.push({
name: `${name}-${role}`,
displayName: ` \u21B3 ${role}`,
requests: roleMetrics.totalRequests,
cachedTokens: roleMetrics.tokens.cached.toLocaleString(),
inputTokens: roleMetrics.tokens.prompt.toLocaleString(),
outputTokens: roleMetrics.tokens.candidates.toLocaleString(),
isSubRow: true
});
});
}
});
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.primary, children: "Model Usage" }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.secondary, children: "Use /model to view model quota information" }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
Box_default,
{
borderBottom: true,
borderStyle: "single",
borderColor: theme.border.default,
borderTop: false,
borderLeft: false,
borderRight: false,
children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: nameWidth, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Model" }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: requestsWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Reqs" }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: inputTokensWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Input Tokens" }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: cacheReadsWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Cache Reads" }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: outputTokensWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Output Tokens" }) })
]
}
),
rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: nameWidth, children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: row.isSubRow ? theme.text.secondary : theme.text.primary,
wrap: "truncate-end",
children: row.displayName
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: requestsWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: row.isSubRow ? theme.text.secondary : theme.text.primary,
children: row.requests
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: inputTokensWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: row.isSubRow ? theme.text.secondary : theme.text.primary,
children: row.inputTokens
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: cacheReadsWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: row.isSubRow ? theme.text.secondary : theme.text.primary,
children: row.cachedTokens
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { width: outputTokensWidth, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: row.isSubRow ? theme.text.secondary : theme.text.primary,
children: row.outputTokens
}
) })
] }, row.name))
] });
};
var StatsDisplay = ({
duration,
title,
footer,
selectedAuthType,
userEmail,
tier,
creditBalance
}) => {
const { stats } = useSessionStats();
const { metrics } = stats;
const { tools, files, models } = metrics;
const computed = computeSessionStats(metrics);
const settings = useSettings();
const showUserIdentity = settings.merged.ui.showUserIdentity;
const successThresholds = {
green: TOOL_SUCCESS_RATE_HIGH,
yellow: TOOL_SUCCESS_RATE_MEDIUM
};
const agreementThresholds = {
green: USER_AGREEMENT_RATE_HIGH,
yellow: USER_AGREEMENT_RATE_MEDIUM
};
const successColor = getStatusColor(computed.successRate, successThresholds);
const agreementColor = getStatusColor(
computed.agreementRate,
agreementThresholds
);
const renderTitle = () => {
if (title) {
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ThemedGradient, { bold: true, children: title });
}
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { bold: true, color: theme.text.accent, children: "Session Stats" });
};
const renderFooter = () => {
if (!footer) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ThemedGradient, { bold: true, children: footer });
};
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
paddingTop: 1,
paddingX: 2,
overflow: "hidden",
children: [
renderTitle(),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Section, { title: "Interaction Summary", children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Session ID:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.primary, children: stats.sessionId }) }),
showUserIdentity && selectedAuthType && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Auth Method:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.primary, children: selectedAuthType.startsWith("oauth") ? userEmail ? `Signed in with Google (${userEmail})` : "Signed in with Google" : selectedAuthType }) }),
showUserIdentity && tier && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Tier:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.primary, children: tier }) }),
showUserIdentity && creditBalance != null && creditBalance >= 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Google AI Credits:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
Text,
{
color: creditBalance > 0 ? theme.text.primary : theme.text.secondary,
children: creditBalance.toLocaleString()
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Tool Calls:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.primary, children: [
tools.totalCalls,
" (",
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.status.success, children: [
"\u2713 ",
tools.totalSuccess
] }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.status.error, children: [
"x ",
tools.totalFail
] }),
" )"
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Success Rate:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: successColor, children: [
computed.successRate.toFixed(1),
"%"
] }) }),
computed.totalDecisions > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "User Agreement:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: agreementColor, children: [
computed.agreementRate.toFixed(1),
"%",
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
computed.totalDecisions,
" reviewed)"
] })
] }) }),
files && (files.totalLinesAdded > 0 || files.totalLinesRemoved > 0) && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Code Changes:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.status.success, children: [
"+",
files.totalLinesAdded
] }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.status.error, children: [
"-",
files.totalLinesRemoved
] })
] }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Section, { title: "Performance", children: [
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Wall Time:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.primary, children: duration }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(StatRow, { title: "Agent Active:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(Text, { color: theme.text.primary, children: formatDuration(computed.agentActiveTime) }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SubStatRow, { title: "API Time:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.primary, children: [
formatDuration(computed.totalApiTime),
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
computed.apiTimePercent.toFixed(1),
"%)"
] })
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(SubStatRow, { title: "Tool Time:", children: /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.primary, children: [
formatDuration(computed.totalToolTime),
" ",
/* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
computed.toolTimePercent.toFixed(1),
"%)"
] })
] }) })
] }),
Object.keys(models).length > 0 && /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(ModelUsageTable, { models }),
renderFooter()
]
}
);
};
// packages/cli/src/ui/components/Table.tsx
var import_jsx_runtime38 = __toESM(require_jsx_runtime(), 1);
function Table({ data, columns }) {
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Box_default, { flexDirection: "row", children: columns.map((col, index) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
Box_default,
{
width: col.width,
flexGrow: col.flexGrow,
flexShrink: col.flexShrink,
flexBasis: col.flexBasis ?? (col.width ? void 0 : 0),
paddingRight: 1,
children: typeof col.header === "string" ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { bold: true, color: theme.text.primary, children: col.header }) : col.header
},
`header-${index}`
)) }),
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
Box_default,
{
borderStyle: "single",
borderBottom: true,
borderTop: false,
borderLeft: false,
borderRight: false,
borderColor: theme.border.default,
marginBottom: 0
}
),
data.map((item, rowIndex) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Box_default, { flexDirection: "row", children: columns.map((col, colIndex) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
Box_default,
{
width: col.width,
flexGrow: col.flexGrow,
flexShrink: col.flexShrink,
flexBasis: col.flexBasis ?? (col.width ? void 0 : 0),
paddingRight: 1,
children: col.renderCell ? col.renderCell(item) : /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(Text, { color: theme.text.primary, children: String(item[col.key]) })
},
`cell-${rowIndex}-${colIndex}`
)) }, `row-${rowIndex}`))
] });
}
// packages/cli/src/ui/components/QuotaStatsInfo.tsx
var import_jsx_runtime39 = __toESM(require_jsx_runtime(), 1);
var QuotaStatsInfo = ({
remaining,
limit,
resetTime,
showDetails = true
}) => {
if (remaining === void 0 || limit === void 0 || limit === 0) {
return null;
}
const usedPercentage = 100 - remaining / limit * 100;
const color = getUsedStatusColor(usedPercentage, {
warning: QUOTA_USED_WARNING_THRESHOLD,
critical: QUOTA_USED_CRITICAL_THRESHOLD
});
return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(Box_default, { flexDirection: "column", marginTop: 0, marginBottom: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Text, { color, children: remaining === 0 ? `Limit reached${resetTime ? `, resets in ${formatResetTime(resetTime, "terse")}` : ""}` : `${usedPercentage.toFixed(0)}% used${resetTime ? ` (Limit resets in ${formatResetTime(resetTime, "terse")})` : ""}` }),
showDetails && /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(import_jsx_runtime39.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(Text, { color: theme.text.primary, children: [
"Usage limit: ",
limit.toLocaleString()
] }),
/* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Text, { color: theme.text.primary, children: "Usage limits span all sessions and reset daily." }),
remaining === 0 && /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(Text, { color: theme.text.primary, children: "Please /auth to upgrade or switch to an API key to continue." })
] })
] });
};
// packages/cli/src/ui/components/ModelStatsDisplay.tsx
var import_jsx_runtime40 = __toESM(require_jsx_runtime(), 1);
var ModelStatsDisplay = ({
selectedAuthType,
userEmail,
tier,
currentModel,
quotaStats
}) => {
const { stats } = useSessionStats();
const pooledRemaining = quotaStats?.remaining;
const pooledLimit = quotaStats?.limit;
const pooledResetTime = quotaStats?.resetTime;
const { models } = stats.metrics;
const settings = useSettings();
const showUserIdentity = settings.merged.ui.showUserIdentity;
const activeModels = Object.entries(models).filter(
([, metrics]) => metrics.api.totalRequests > 0
);
if (activeModels.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingTop: 1,
paddingX: 2,
children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: "No API calls have been made in this session." })
}
);
}
const modelNames = activeModels.map(([name]) => name);
const hasThoughts = activeModels.some(
([, metrics]) => metrics.tokens.thoughts > 0
);
const hasTool = activeModels.some(([, metrics]) => metrics.tokens.tool > 0);
const hasCached = activeModels.some(
([, metrics]) => metrics.tokens.cached > 0
);
const allRoles = [
...new Set(
activeModels.flatMap(([, metrics]) => Object.keys(metrics.roles ?? {}))
)
].filter((role) => {
const validRoles = Object.values(LlmRole);
return validRoles.includes(role);
}).sort((a, b) => {
if (a === b) return 0;
if (a === LlmRole.MAIN) return -1;
if (b === LlmRole.MAIN) return 1;
return a.localeCompare(b);
});
const createRow = (metric, getValue, options = {}) => {
const row = {
metric,
isSection: options.isSection,
isSubtle: options.isSubtle
};
activeModels.forEach(([name, metrics]) => {
row[name] = getValue(metrics);
});
return row;
};
const rows = [];
rows.push({ metric: "API", isSection: true });
rows.push(createRow("Requests", (m) => m.api.totalRequests.toLocaleString()));
rows.push(
createRow("Errors", (m) => {
const errorRate = calculateErrorRate(m);
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
Text,
{
color: m.api.totalErrors > 0 ? theme.status.error : theme.text.primary,
children: [
m.api.totalErrors.toLocaleString(),
" (",
errorRate.toFixed(1),
"%)"
]
}
);
})
);
rows.push(
createRow("Avg Latency", (m) => formatDuration(calculateAverageLatency(m)))
);
rows.push({ metric: "" });
rows.push({ metric: "Tokens", isSection: true });
rows.push(
createRow("Total", (m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.secondary, children: m.tokens.total.toLocaleString() }))
);
rows.push(
createRow(
"Input",
(m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: m.tokens.input.toLocaleString() }),
{ isSubtle: true }
)
);
if (hasCached) {
rows.push(
createRow(
"Cache Reads",
(m) => {
const cacheHitRate = calculateCacheHitRate(m);
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Text, { color: theme.text.secondary, children: [
m.tokens.cached.toLocaleString(),
" (",
cacheHitRate.toFixed(1),
"%)"
] });
},
{ isSubtle: true }
)
);
}
if (hasThoughts) {
rows.push(
createRow(
"Thoughts",
(m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: m.tokens.thoughts.toLocaleString() }),
{ isSubtle: true }
)
);
}
if (hasTool) {
rows.push(
createRow(
"Tool",
(m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: m.tokens.tool.toLocaleString() }),
{ isSubtle: true }
)
);
}
rows.push(
createRow(
"Output",
(m) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: m.tokens.candidates.toLocaleString() }),
{ isSubtle: true }
)
);
if (allRoles.length > 0) {
rows.push({ metric: "" });
rows.push({ metric: "Roles", isSection: true });
allRoles.forEach((role) => {
const roleHeaderRow = {
metric: role,
isSection: true,
color: theme.text.primary
};
rows.push(roleHeaderRow);
const addRoleMetric = (metric, getValue) => {
const row = {
metric,
isSubtle: true
};
activeModels.forEach(([name, metrics]) => {
const roleMetrics = metrics.roles?.[role];
if (roleMetrics) {
row[name] = getValue(roleMetrics);
} else {
row[name] = /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.secondary, children: "-" });
}
});
rows.push(row);
};
addRoleMetric("Requests", (r) => r.totalRequests.toLocaleString());
addRoleMetric("Input", (r) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: r.tokens.input.toLocaleString() }));
addRoleMetric("Output", (r) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: r.tokens.candidates.toLocaleString() }));
addRoleMetric("Cache Reads", (r) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.secondary, children: r.tokens.cached.toLocaleString() }));
});
}
const columns = [
{
key: "metric",
header: "Metric",
width: 28,
renderCell: (row) => /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
Text,
{
bold: row.isSection,
color: row.isSection ? theme.text.primary : theme.text.link,
children: row.isSubtle ? ` \u21B3 ${row.metric}` : row.metric
}
)
},
...modelNames.map((name) => ({
key: name,
header: name,
flexGrow: 1,
renderCell: (row) => {
if (row.isSection) return null;
const val = row[name];
if (val === void 0 || val === null) return null;
if (typeof val === "string" || typeof val === "number") {
return /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: val });
}
return val;
}
}))
];
const isAuto = currentModel && isAutoModel(currentModel);
const statsTitle = isAuto ? `${getDisplayString(currentModel)} Stats For Nerds` : "Model Stats For Nerds";
return /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
paddingTop: 1,
paddingX: 2,
children: [
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { bold: true, color: theme.text.accent, children: statsTitle }),
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { height: 1 }),
showUserIdentity && selectedAuthType && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { width: 28, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.link, children: "Auth Method:" }) }),
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: selectedAuthType.startsWith("oauth") ? userEmail ? `Signed in with Google (${userEmail})` : "Signed in with Google" : selectedAuthType })
] }),
showUserIdentity && tier && /* @__PURE__ */ (0, import_jsx_runtime40.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { width: 28, children: /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.link, children: "Tier:" }) }),
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Text, { color: theme.text.primary, children: tier })
] }),
isAuto && pooledRemaining !== void 0 && pooledLimit !== void 0 && pooledLimit > 0 && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(
QuotaStatsInfo,
{
remaining: pooledRemaining,
limit: pooledLimit,
resetTime: pooledResetTime
}
),
(showUserIdentity || isAuto) && /* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime40.jsx)(Table, { data: rows, columns })
]
}
);
};
// packages/cli/src/ui/components/ToolStatsDisplay.tsx
var import_jsx_runtime41 = __toESM(require_jsx_runtime(), 1);
var TOOL_NAME_COL_WIDTH = 25;
var CALLS_COL_WIDTH = 8;
var SUCCESS_RATE_COL_WIDTH = 15;
var AVG_DURATION_COL_WIDTH = 15;
var StatRow2 = ({ name, stats }) => {
const successRate = stats.count > 0 ? stats.success / stats.count * 100 : 0;
const avgDuration = stats.count > 0 ? stats.durationMs / stats.count : 0;
const successColor = getStatusColor(successRate, {
green: TOOL_SUCCESS_RATE_HIGH,
yellow: TOOL_SUCCESS_RATE_MEDIUM
});
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: TOOL_NAME_COL_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.link, children: name }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: CALLS_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: stats.count }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: SUCCESS_RATE_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Text, { color: successColor, children: [
successRate.toFixed(1),
"%"
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: formatDuration(avgDuration) }) })
] });
};
var ToolStatsDisplay = () => {
const { stats } = useSessionStats();
const { tools } = stats.metrics;
const activeTools = Object.entries(tools.byName).filter(
([, metrics]) => metrics.count > 0
);
if (activeTools.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingTop: 1,
paddingX: 2,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: "No tool calls have been made in this session." })
}
);
}
const totalDecisions = Object.values(tools.byName).reduce(
(acc, tool) => {
acc.accept += tool.decisions.accept;
acc.reject += tool.decisions.reject;
acc.modify += tool.decisions.modify;
return acc;
},
{ accept: 0, reject: 0, modify: 0 }
);
const totalReviewed = totalDecisions.accept + totalDecisions.reject + totalDecisions.modify;
const agreementRate = totalReviewed > 0 ? totalDecisions.accept / totalReviewed * 100 : 0;
const agreementColor = getStatusColor(agreementRate, {
green: USER_AGREEMENT_RATE_HIGH,
yellow: USER_AGREEMENT_RATE_MEDIUM
});
return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
paddingTop: 1,
paddingX: 2,
width: 70,
children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.accent, children: "Tool Stats For Nerds" }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: TOOL_NAME_COL_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.primary, children: "Tool Name" }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: CALLS_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.primary, children: "Calls" }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: SUCCESS_RATE_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.primary, children: "Success Rate" }) }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.primary, children: "Avg Duration" }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
borderStyle: "single",
borderBottom: true,
borderTop: false,
borderLeft: false,
borderRight: false,
borderColor: theme.border.default,
width: "100%"
}
),
activeTools.map(([name, stats2]) => /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(StatRow2, { name, stats: stats2 }, name)),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: theme.text.primary, children: "User Decision Summary" }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
width: TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.link, children: "Total Reviewed Suggestions:" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: totalReviewed }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
width: TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: " \xBB Accepted:" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.status.success, children: totalDecisions.accept }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
width: TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: " \xBB Rejected:" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.status.error, children: totalDecisions.reject }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
width: TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: " \xBB Modified:" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.status.warning, children: totalDecisions.modify }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
borderStyle: "single",
borderBottom: true,
borderTop: false,
borderLeft: false,
borderRight: false,
borderColor: theme.border.default,
width: "100%"
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
Box_default,
{
width: TOOL_NAME_COL_WIDTH + CALLS_COL_WIDTH + SUCCESS_RATE_COL_WIDTH,
children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { color: theme.text.primary, children: " Overall Agreement Rate:" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Box_default, { width: AVG_DURATION_COL_WIDTH, justifyContent: "flex-end", children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Text, { bold: true, color: totalReviewed > 0 ? agreementColor : void 0, children: totalReviewed > 0 ? `${agreementRate.toFixed(1)}%` : "--" }) })
] })
]
}
);
};
// packages/cli/src/ui/components/SessionSummaryDisplay.tsx
var import_jsx_runtime42 = __toESM(require_jsx_runtime(), 1);
var SessionSummaryDisplay = ({
duration
}) => {
const { stats } = useSessionStats();
const config = useConfig();
const { shell } = getShellConfiguration();
const worktreeSettings = config.getWorktreeSettings();
const escapedSessionId = escapeShellArg(stats.sessionId, shell);
const footerSessionId = isWindows() && !escapedSessionId.startsWith('"') && !escapedSessionId.startsWith("'") ? `"${escapedSessionId}"` : escapedSessionId;
let footer = `To resume this session: gemini --resume ${footerSessionId}`;
if (worktreeSettings) {
footer = `To resume work in this worktree: cd ${escapeShellArg(worktreeSettings.path, shell)} && gemini --resume ${footerSessionId}
To remove manually: git worktree remove ${escapeShellArg(worktreeSettings.path, shell)}`;
}
return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
StatsDisplay,
{
title: "Agent powering down. Goodbye!",
duration,
footer
}
);
};
// packages/cli/src/ui/components/Help.tsx
var import_jsx_runtime43 = __toESM(require_jsx_runtime(), 1);
var Help = ({ commands }) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
Box_default,
{
flexDirection: "column",
marginBottom: 1,
borderColor: theme.border.default,
borderStyle: "round",
padding: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.primary, children: "Basics:" }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "Add context" }),
": Use",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "@" }),
" ",
"to specify files for context (e.g.,",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "@src/myFile.ts" }),
") to target specific files or folders."
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "Shell mode" }),
": Execute shell commands via",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "!" }),
" ",
"(e.g.,",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "!npm run start" }),
") or use natural language (e.g.",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: "start server" }),
")."
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.primary, children: "Commands:" }),
commands.filter((command) => command.description && !command.hidden).map((command) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
" ",
"/",
command.name
] }),
command.kind === "mcp-prompt" /* MCP_PROMPT */ && /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { color: theme.text.secondary, children: " [MCP]" }),
command.description && " - " + sanitizeForDisplay(command.description, 100)
] }),
command.subCommands && command.subCommands.filter((subCommand) => !subCommand.hidden).map((subCommand) => /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
" ",
subCommand.name
] }),
subCommand.description && " - " + sanitizeForDisplay(subCommand.description, 100)
] }, subCommand.name))
] }, command.name)),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
" ",
"!",
" "
] }),
"- shell command"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { color: theme.text.secondary, children: "[MCP]" }),
" - Model Context Protocol command (from external servers)"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.primary, children: "Keyboard Shortcuts:" }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
formatCommand("cursor.wordLeft" /* MOVE_WORD_LEFT */),
"/",
formatCommand("cursor.wordRight" /* MOVE_WORD_RIGHT */)
] }),
" ",
"- Jump through words in the input"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("basic.quit" /* QUIT */) }),
" ",
"- Quit application"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("input.newline" /* NEWLINE */) }),
" ",
"- New line"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("app.clearScreen" /* CLEAR_SCREEN */) }),
" ",
"- Clear the screen"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("app.toggleCopyMode" /* TOGGLE_COPY_MODE */) }),
" ",
"- Enter selection mode to copy text"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */) }),
" ",
"- Open input in external editor"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("app.toggleYolo" /* TOGGLE_YOLO */) }),
" ",
"- Toggle YOLO mode"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("input.submit" /* SUBMIT */) }),
" ",
"- Send message"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("basic.cancel" /* ESCAPE */) }),
" ",
"- Cancel operation / Clear input (double press)"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
formatCommand("scroll.pageUp" /* PAGE_UP */),
"/",
formatCommand("scroll.pageDown" /* PAGE_DOWN */)
] }),
" ",
"- Scroll page up/down"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: formatCommand("app.cycleApprovalMode" /* CYCLE_APPROVAL_MODE */) }),
" ",
"- Toggle auto-accepting edits"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
formatCommand("history.previous" /* HISTORY_UP */),
"/",
formatCommand("history.next" /* HISTORY_DOWN */)
] }),
" ",
"- Cycle through your prompt history"
] }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(Text, { color: theme.text.primary, children: [
"For a full list of shortcuts, see",
" ",
/* @__PURE__ */ (0, import_jsx_runtime43.jsx)(Text, { bold: true, color: theme.text.accent, children: KEYBOARD_SHORTCUTS_URL })
] })
]
}
);
// packages/cli/src/ui/components/views/ExtensionsList.tsx
var import_jsx_runtime44 = __toESM(require_jsx_runtime(), 1);
var ExtensionsList = ({ extensions }) => {
const { extensionsUpdateState } = useUIState();
if (extensions.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { children: "No extensions installed." });
}
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { children: "Installed extensions: " }),
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Box_default, { flexDirection: "column", paddingLeft: 2, children: extensions.map((ext) => {
const state = extensionsUpdateState.get(ext.name);
const isActive = ext.isActive;
const activeString = isActive ? "active" : "disabled";
const activeColor = isActive ? "green" : "grey";
let stateColor = "gray";
const stateText = state || "unknown state";
switch (state) {
case "checking for updates" /* CHECKING_FOR_UPDATES */:
case "updating" /* UPDATING */:
stateColor = "cyan";
break;
case "update available" /* UPDATE_AVAILABLE */:
case "updated, needs restart" /* UPDATED_NEEDS_RESTART */:
stateColor = "yellow";
break;
case "error" /* ERROR */:
stateColor = "red";
break;
case "up to date" /* UP_TO_DATE */:
case "not updatable" /* NOT_UPDATABLE */:
case "updated" /* UPDATED */:
stateColor = "green";
break;
case void 0:
break;
default:
debugLogger.warn(`Unhandled ExtensionUpdateState ${state}`);
break;
}
return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { color: "cyan", children: `${ext.name} (v${ext.version})` }),
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { color: activeColor, children: ` - ${activeString}` }),
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { color: stateColor, children: ` (${stateText})` })
] }),
ext.resolvedSettings && ext.resolvedSettings.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Box_default, { flexDirection: "column", paddingLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime44.jsx)(Text, { children: "settings:" }),
ext.resolvedSettings.map((setting) => /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Text, { children: [
"- ",
setting.name,
": ",
getFormattedSettingValue(setting),
setting.scope && /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(Text, { color: "gray", children: [
" ",
"(",
setting.scope.charAt(0).toUpperCase() + setting.scope.slice(1),
setting.source ? ` - ${setting.source}` : "",
")"
] })
] }, setting.name))
] })
] }, ext.name);
}) })
] });
};
// packages/cli/src/ui/components/views/ToolsList.tsx
var import_jsx_runtime45 = __toESM(require_jsx_runtime(), 1);
var ToolsList = ({
tools,
showDescriptions,
terminalWidth
}) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Text, { bold: true, color: theme.text.primary, children: "Available Gemini CLI tools:" }),
/* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Box_default, { height: 1 }),
tools.length > 0 ? tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Text, { color: theme.text.primary, children: [
" ",
"- "
] }),
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime45.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
tool.displayName,
" (",
tool.name,
")"
] }),
showDescriptions && tool.description && /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(
MarkdownDisplay,
{
terminalWidth,
text: tool.description,
isPending: false
}
)
] })
] }, tool.name)) : /* @__PURE__ */ (0, import_jsx_runtime45.jsx)(Text, { color: theme.text.primary, children: " No tools available" })
] });
// packages/cli/src/ui/components/views/SkillsList.tsx
var import_jsx_runtime46 = __toESM(require_jsx_runtime(), 1);
var SkillsList = ({
skills,
showDescriptions
}) => {
const sortSkills = (a, b) => {
if (a.isBuiltin === b.isBuiltin) {
return a.name.localeCompare(b.name);
}
return a.isBuiltin ? 1 : -1;
};
const enabledSkills = skills.filter((s) => !s.disabled).sort(sortSkills);
const disabledSkills = skills.filter((s) => s.disabled).sort(sortSkills);
const renderSkill = (skill) => /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Text, { color: theme.text.primary, children: [
" ",
"- "
] }),
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
Text,
{
bold: true,
color: skill.disabled ? theme.text.secondary : theme.text.link,
children: skill.name
}
),
skill.isBuiltin && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { color: theme.text.secondary, children: " [Built-in]" })
] }),
showDescriptions && skill.description && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(
Text,
{
color: skill.disabled ? theme.text.secondary : theme.text.primary,
children: skill.description
}
) })
] })
] }, skill.name);
return /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
enabledSkills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { bold: true, color: theme.text.primary, children: "Available Agent Skills:" }),
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Box_default, { height: 1 }),
enabledSkills.map(renderSkill)
] }),
enabledSkills.length > 0 && disabledSkills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Box_default, { marginY: 1, children: /* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { color: theme.text.secondary, children: "-".repeat(20) }) }),
disabledSkills.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { bold: true, color: theme.text.secondary, children: "Disabled Skills:" }),
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Box_default, { height: 1 }),
disabledSkills.map(renderSkill)
] }),
skills.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { color: theme.text.primary, children: "No skills available." }),
/* @__PURE__ */ (0, import_jsx_runtime46.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { color: theme.text.primary, children: "Learn how to add skills: " }),
/* @__PURE__ */ (0, import_jsx_runtime46.jsx)(Text, { color: theme.text.link, children: SKILLS_DOCS_URL })
] })
] })
] });
};
// packages/cli/src/ui/components/views/AgentsStatus.tsx
var import_jsx_runtime47 = __toESM(require_jsx_runtime(), 1);
var AgentsStatus = ({
agents,
terminalWidth
}) => {
const localAgents = agents.filter((a) => a.kind === "local");
const remoteAgents = agents.filter((a) => a.kind === "remote");
if (agents.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Box_default, { flexDirection: "column", marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Text, { children: "No agents available." }) });
}
const renderAgentList = (title, agentList) => {
if (agentList.length === 0) return null;
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Text, { bold: true, color: theme.text.primary, children: title }),
/* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Box_default, { height: 1 }),
agentList.map((agent) => /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Text, { color: theme.text.primary, children: [
" ",
"- "
] }),
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Text, { bold: true, color: theme.text.accent, children: [
agent.displayName || agent.name,
agent.displayName && agent.displayName !== agent.name && /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Text, { bold: false, children: [
" (",
agent.name,
")"
] })
] }),
agent.description && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(
MarkdownDisplay,
{
terminalWidth,
text: agent.description,
isPending: false
}
)
] })
] }, agent.name))
] });
};
return /* @__PURE__ */ (0, import_jsx_runtime47.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
renderAgentList("Local Agents", localAgents),
localAgents.length > 0 && remoteAgents.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime47.jsx)(Box_default, { height: 1 }),
renderAgentList("Remote Agents", remoteAgents)
] });
};
// packages/cli/src/ui/components/views/McpStatus.tsx
var import_jsx_runtime48 = __toESM(require_jsx_runtime(), 1);
var McpStatus = ({
servers,
tools,
prompts,
resources,
blockedServers,
serverStatus,
authStatus,
enablementState,
errors,
discoveryInProgress,
connectingServers,
showDescriptions,
showSchema
}) => {
const serverNames = Object.keys(servers).filter(
(serverName) => !blockedServers.some(
(blockedServer) => blockedServer.name === serverName
)
);
if (serverNames.length === 0 && blockedServers.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { children: "No MCP servers configured." }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
"Please view MCP documentation in your browser:",
" ",
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.link, children: "https://goo.gle/gemini-cli-docs-mcp" }),
" ",
"or use the cli /docs command"
] })
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", children: [
discoveryInProgress && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { color: theme.status.warning, children: [
"\u23F3 MCP servers are starting up (",
connectingServers.length,
" ",
"initializing)..."
] }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: "Note: First startup may take longer. Tool availability will update automatically." })
] }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { bold: true, children: "Configured MCP servers:" }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Box_default, { height: 1 }),
serverNames.map((serverName) => {
const server = servers[serverName];
const serverTools = tools.filter(
(tool) => tool.serverName === serverName
);
const serverPrompts = prompts.filter(
(prompt) => prompt.serverName === serverName
);
const serverResources = resources.filter(
(resource) => resource.serverName === serverName
);
const originalStatus = serverStatus(serverName);
const hasCachedItems = serverTools.length > 0 || serverPrompts.length > 0 || serverResources.length > 0;
const status = originalStatus === MCPServerStatus.DISCONNECTED && hasCachedItems ? MCPServerStatus.CONNECTED : originalStatus;
let statusIndicator = "";
let statusText = "";
let statusColor = theme.text.primary;
const serverEnablement = enablementState[serverName];
const isDisabled = serverEnablement && !serverEnablement.enabled;
if (isDisabled) {
statusIndicator = "\u23F8\uFE0F";
statusText = serverEnablement.isSessionDisabled ? "Disabled (session)" : "Disabled";
statusColor = theme.text.secondary;
} else {
switch (status) {
case MCPServerStatus.CONNECTED:
statusIndicator = "\u{1F7E2}";
statusText = "Ready";
statusColor = theme.status.success;
break;
case MCPServerStatus.CONNECTING:
statusIndicator = "\u{1F504}";
statusText = "Starting... (first startup may take longer)";
statusColor = theme.status.warning;
break;
case MCPServerStatus.DISCONNECTED:
default:
statusIndicator = "\u{1F534}";
statusText = "Disconnected";
statusColor = theme.status.error;
break;
}
}
let serverDisplayName = serverName;
if (server.extension?.name) {
serverDisplayName += ` (from ${server.extension?.name})`;
}
const toolCount = serverTools.length;
const promptCount = serverPrompts.length;
const resourceCount = serverResources.length;
const parts = [];
if (toolCount > 0) {
parts.push(`${toolCount} ${toolCount === 1 ? "tool" : "tools"}`);
}
if (promptCount > 0) {
parts.push(
`${promptCount} ${promptCount === 1 ? "prompt" : "prompts"}`
);
}
if (resourceCount > 0) {
parts.push(
`${resourceCount} ${resourceCount === 1 ? "resource" : "resources"}`
);
}
const serverAuthStatus = authStatus[serverName];
let authStatusNode = null;
if (serverAuthStatus === "authenticated") {
authStatusNode = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { children: " (OAuth)" });
} else if (serverAuthStatus === "expired") {
authStatusNode = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.status.error, children: " (OAuth expired)" });
} else if (serverAuthStatus === "unauthenticated") {
authStatusNode = /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.status.warning, children: " (OAuth not authenticated)" });
}
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { color: statusColor, children: [
statusIndicator,
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { bold: true, children: serverDisplayName }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
" - ",
statusText,
status === MCPServerStatus.CONNECTED && parts.length > 0 && ` (${parts.join(", ")})`
] }),
authStatusNode
] }),
status === MCPServerStatus.CONNECTING && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { children: " (tools and prompts will appear when ready)" }),
status === MCPServerStatus.DISCONNECTED && toolCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
" (",
toolCount,
" tools cached)"
] }),
errors[serverName] && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { color: theme.status.error, children: [
"Error: ",
errors[serverName]
] }) }),
showDescriptions && server?.description && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.secondary, children: server.description.trim() }),
serverTools.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: "Tools:" }),
serverTools.map((tool) => {
const schemaContent = showSchema && tool.schema && (tool.schema.parametersJsonSchema || tool.schema.parameters) ? JSON.stringify(
tool.schema.parametersJsonSchema ?? tool.schema.parameters,
null,
2
) : null;
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
"- ",
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: tool.name })
] }),
showDescriptions && tool.description && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.secondary, children: tool.description.trim() }) }),
schemaContent && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginLeft: 4, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.secondary, children: "Parameters:" }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.secondary, children: schemaContent })
] })
] }, tool.name);
})
] }),
serverPrompts.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: "Prompts:" }),
serverPrompts.map((prompt) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
"- ",
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: prompt.name })
] }),
showDescriptions && prompt.description && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: prompt.description.trim() }) })
] }, prompt.name))
] }),
serverResources.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { flexDirection: "column", marginLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: "Resources:" }),
serverResources.slice(0, MAX_MCP_RESOURCES_TO_SHOW).map((resource, index) => {
const label = resource.name || resource.uri || "resource";
return /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
Box_default,
{
flexDirection: "column",
children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { children: [
"- ",
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.primary, children: label }),
resource.uri ? ` (${resource.uri})` : "",
resource.mimeType ? ` [${resource.mimeType}]` : ""
] }),
showDescriptions && resource.description && /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.text.secondary, children: resource.description.trim() }) })
]
},
`${resource.serverName}-resource-${index}`
);
}),
serverResources.length > MAX_MCP_RESOURCES_TO_SHOW && /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { color: theme.text.secondary, children: [
" ",
"...",
" ",
serverResources.length - MAX_MCP_RESOURCES_TO_SHOW,
" ",
serverResources.length - MAX_MCP_RESOURCES_TO_SHOW === 1 ? "resource" : "resources",
" ",
"hidden"
] })
] })
] }, serverName);
}),
blockedServers.map((server) => /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Box_default, { marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { color: theme.status.error, children: "\u{1F534} " }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(Text, { bold: true, children: [
server.name,
server.extensionName ? ` (from ${server.extensionName})` : ""
] }),
/* @__PURE__ */ (0, import_jsx_runtime48.jsx)(Text, { children: " - Blocked" })
] }, server.name))
] });
};
// packages/cli/src/ui/components/views/GemmaStatus.tsx
var import_jsx_runtime49 = __toESM(require_jsx_runtime(), 1);
var StatusDot = ({ ok }) => /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: ok ? theme.status.success : theme.status.error, children: ok ? "\u25CF" : "\u25CB" });
var GemmaStatus = ({
binaryInstalled,
binaryPath,
modelName,
modelDownloaded,
serverRunning,
serverPid,
serverPort,
settingsEnabled,
allPassing
}) => /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Gemma Local Model Routing" }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusDot, { ok: binaryInstalled }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Binary: " }),
binaryInstalled ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.text.secondary, children: binaryPath }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.status.error, children: "Not installed" })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusDot, { ok: modelDownloaded }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Model: " }),
modelDownloaded ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { children: modelName }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { color: theme.status.error, children: [
modelName,
" not found"
] })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusDot, { ok: serverRunning }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Server: " }),
serverRunning ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { children: [
"port ",
serverPort,
serverPid ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { color: theme.text.secondary, children: [
" (PID ",
serverPid,
")"
] }) : null
] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { color: theme.status.error, children: [
"not running on port ",
serverPort
] })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(StatusDot, { ok: settingsEnabled }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Text, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Settings: " }),
settingsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { children: "enabled" }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.status.error, children: "not enabled" })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { bold: true, children: "Active for: " }),
allPassing ? /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.status.success, children: "[routing]" }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.text.secondary, children: "none" })
] }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Box_default, { marginTop: 1, children: allPassing ? /* @__PURE__ */ (0, import_jsx_runtime49.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.text.secondary, children: "Simple requests route to Flash, complex requests to Pro." }),
/* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.text.secondary, children: "This happens automatically on every request." })
] }) : /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(Text, { color: theme.status.warning, children: 'Run "gemini gemma setup" to install and configure.' }) })
] });
// packages/cli/src/ui/components/views/ChatList.tsx
var import_jsx_runtime50 = __toESM(require_jsx_runtime(), 1);
var ChatList = ({ chats }) => {
if (chats.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Text, { children: "No saved conversation checkpoints found." });
}
return /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Text, { children: "List of saved conversations:" }),
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Box_default, { height: 1 }),
chats.map((chat) => {
const isoString = chat.mtime;
const match = isoString.match(
/(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}:\d{2})/
);
const formattedDate = match ? `${match[1]} ${match[2]}` : "Invalid Date";
return /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Box_default, { flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(Text, { children: [
" ",
"- ",
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Text, { color: theme.text.accent, children: chat.name }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime50.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
formattedDate,
")"
] })
] }) }, chat.name);
}),
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime50.jsx)(Text, { color: theme.text.secondary, children: "Note: Newest last, oldest first" })
] });
};
// packages/cli/src/ui/components/messages/ModelMessage.tsx
var import_jsx_runtime51 = __toESM(require_jsx_runtime(), 1);
var ModelMessage = ({ model }) => /* @__PURE__ */ (0, import_jsx_runtime51.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime51.jsxs)(Text, { color: theme.ui.comment, italic: true, children: [
"Responding with ",
getDisplayString(model)
] }) });
// packages/cli/src/ui/components/messages/ThinkingMessage.tsx
var import_react24 = __toESM(require_react(), 1);
var import_jsx_runtime52 = __toESM(require_jsx_runtime(), 1);
var THINKING_LEFT_PADDING = 1;
function normalizeThoughtLines(thought) {
const subject = normalizeEscapedNewlines(thought.subject).trim();
const description = normalizeEscapedNewlines(thought.description).trim();
const isNoise = (text) => {
const trimmed = text.trim();
return !trimmed || /^\.+$/.test(trimmed);
};
const lines = [];
if (subject && !isNoise(subject)) {
lines.push(subject);
}
if (description) {
const descriptionLines = description.split("\n").map((line) => line.trim()).filter((line) => !isNoise(line));
lines.push(...descriptionLines);
}
return lines;
}
var ThinkingMessage = ({
thought,
terminalWidth,
isFirstThinking
}) => {
const fullLines = (0, import_react24.useMemo)(() => normalizeThoughtLines(thought), [thought]);
if (fullLines.length === 0) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(Box_default, { width: terminalWidth, flexDirection: "column", children: [
isFirstThinking && /* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(Text, { color: theme.text.primary, italic: true, children: [
" ",
"Thinking...",
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime52.jsxs)(
Box_default,
{
marginLeft: THINKING_LEFT_PADDING,
paddingLeft: 1,
borderStyle: "single",
borderLeft: true,
borderRight: false,
borderTop: false,
borderBottom: false,
borderColor: theme.text.secondary,
flexDirection: "column",
children: [
/* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Text, { children: " " }),
fullLines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Text, { color: theme.text.primary, bold: true, italic: true, children: fullLines[0] }),
fullLines.slice(1).map((line, index) => /* @__PURE__ */ (0, import_jsx_runtime52.jsx)(Text, { color: theme.text.secondary, italic: true, children: line }, `body-line-${index}`))
]
}
)
] });
};
// packages/cli/src/ui/components/messages/HintMessage.tsx
var import_jsx_runtime53 = __toESM(require_jsx_runtime(), 1);
var HintMessage = ({ text }) => {
const prefix = "\u{1F4A1} ";
const prefixWidth = prefix.length;
const config = useConfig();
const useBackgroundColorSetting = config.getUseBackgroundColor();
const useBackgroundColor = useBackgroundColorSetting && !!theme.background.message;
return /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
HalfLinePaddedBox,
{
backgroundBaseColor: theme.text.accent,
backgroundOpacity: 0.1,
useBackgroundColor,
children: /* @__PURE__ */ (0, import_jsx_runtime53.jsxs)(
Box_default,
{
flexDirection: "row",
paddingY: 0,
marginY: useBackgroundColor ? 0 : 1,
paddingX: useBackgroundColor ? 1 : 0,
alignSelf: "flex-start",
children: [
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Box_default, { width: prefixWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(
Text,
{
color: theme.text.accent,
"aria-label": SCREEN_READER_USER_PREFIX,
children: prefix
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime53.jsx)(Text, { wrap: "wrap", italic: true, color: theme.text.accent, children: `Steering Hint: ${text}` }) })
]
}
)
}
);
};
// packages/cli/src/ui/utils/inlineThinkingMode.ts
function getInlineThinkingMode(settings) {
return settings.merged.ui?.inlineThinkingMode ?? "off";
}
// packages/cli/src/ui/components/HistoryItemDisplay.tsx
var import_jsx_runtime54 = __toESM(require_jsx_runtime(), 1);
var HistoryItemDisplay = ({
item,
availableTerminalHeight,
terminalWidth,
isPending,
commands,
availableTerminalHeightGemini,
isExpandable,
isFirstThinking = false,
isFirstAfterThinking = false,
isToolGroupBoundary = false
}) => {
const settings = useSettings();
const inlineThinkingMode = getInlineThinkingMode(settings);
const itemForDisplay = (0, import_react25.useMemo)(() => escapeAnsiCtrlCodes(item), [item]);
const needTopMargin = !!(isFirstAfterThinking && inlineThinkingMode !== "off" || isToolGroupBoundary);
return /* @__PURE__ */ (0, import_jsx_runtime54.jsxs)(
Box_default,
{
flexDirection: "column",
width: terminalWidth,
marginTop: needTopMargin ? 1 : 0,
children: [
itemForDisplay.type === "thinking" && inlineThinkingMode !== "off" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
ThinkingMessage,
{
thought: itemForDisplay.thought,
terminalWidth,
isFirstThinking
}
),
itemForDisplay.type === "hint" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(HintMessage, { text: itemForDisplay.text }),
itemForDisplay.type === "user" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(UserMessage, { text: itemForDisplay.text, width: terminalWidth }),
itemForDisplay.type === "user_shell" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(UserShellMessage, { text: itemForDisplay.text, width: terminalWidth }),
itemForDisplay.type === "gemini" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
GeminiMessage,
{
text: itemForDisplay.text,
isPending,
availableTerminalHeight: availableTerminalHeightGemini ?? availableTerminalHeight,
terminalWidth
}
),
itemForDisplay.type === "gemini_content" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
GeminiMessageContent,
{
text: itemForDisplay.text,
isPending,
availableTerminalHeight: availableTerminalHeightGemini ?? availableTerminalHeight,
terminalWidth
}
),
itemForDisplay.type === "info" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
InfoMessage,
{
text: itemForDisplay.text,
secondaryText: itemForDisplay.secondaryText,
source: itemForDisplay.source,
icon: itemForDisplay.icon,
color: itemForDisplay.color,
marginBottom: itemForDisplay.marginBottom
}
),
itemForDisplay.type === "warning" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(WarningMessage, { text: itemForDisplay.text }),
itemForDisplay.type === "error" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ErrorMessage, { text: itemForDisplay.text }),
itemForDisplay.type === "about" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
AboutBox,
{
cliVersion: itemForDisplay.cliVersion,
osVersion: itemForDisplay.osVersion,
sandboxEnv: itemForDisplay.sandboxEnv,
modelVersion: itemForDisplay.modelVersion,
selectedAuthType: itemForDisplay.selectedAuthType,
gcpProject: itemForDisplay.gcpProject,
ideClient: itemForDisplay.ideClient,
userEmail: itemForDisplay.userEmail,
tier: itemForDisplay.tier
}
),
itemForDisplay.type === "help" && commands && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(Help, { commands }),
itemForDisplay.type === "stats" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
StatsDisplay,
{
duration: itemForDisplay.duration,
selectedAuthType: itemForDisplay.selectedAuthType,
userEmail: itemForDisplay.userEmail,
tier: itemForDisplay.tier
}
),
itemForDisplay.type === "model_stats" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
ModelStatsDisplay,
{
selectedAuthType: itemForDisplay.selectedAuthType,
userEmail: itemForDisplay.userEmail,
tier: itemForDisplay.tier,
currentModel: itemForDisplay.currentModel,
quotaStats: itemForDisplay.pooledRemaining !== void 0 || itemForDisplay.pooledLimit !== void 0 || itemForDisplay.pooledResetTime !== void 0 ? {
remaining: itemForDisplay.pooledRemaining,
limit: itemForDisplay.pooledLimit,
resetTime: itemForDisplay.pooledResetTime
} : void 0
}
),
itemForDisplay.type === "tool_stats" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ToolStatsDisplay, {}),
itemForDisplay.type === "model" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ModelMessage, { model: itemForDisplay.model }),
itemForDisplay.type === "quit" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(SessionSummaryDisplay, { duration: itemForDisplay.duration }),
itemForDisplay.type === "tool_group" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
ToolGroupMessage,
{
item: itemForDisplay,
toolCalls: itemForDisplay.tools,
availableTerminalHeight,
terminalWidth,
borderTop: itemForDisplay.borderTop,
borderBottom: itemForDisplay.borderBottom,
isExpandable
}
),
itemForDisplay.type === "tool_display_group" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
ToolGroupDisplay,
{
item: itemForDisplay,
isToolGroupBoundary
}
),
itemForDisplay.type === "subagent" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
SubagentHistoryMessage,
{
item: itemForDisplay,
terminalWidth
}
),
itemForDisplay.type === "compression" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(CompressionMessage, { compression: itemForDisplay.compression }),
itemForDisplay.type === "export_session" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ExportSessionMessage, { exportSession: itemForDisplay.exportSession }),
itemForDisplay.type === "extensions_list" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ExtensionsList, { extensions: itemForDisplay.extensions }),
itemForDisplay.type === "tools_list" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
ToolsList,
{
terminalWidth,
tools: itemForDisplay.tools,
showDescriptions: itemForDisplay.showDescriptions
}
),
itemForDisplay.type === "skills_list" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
SkillsList,
{
skills: itemForDisplay.skills,
showDescriptions: itemForDisplay.showDescriptions
}
),
itemForDisplay.type === "agents_list" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(
AgentsStatus,
{
agents: itemForDisplay.agents,
terminalWidth
}
),
itemForDisplay.type === "mcp_status" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(McpStatus, { ...itemForDisplay, serverStatus: getMCPServerStatus }),
itemForDisplay.type === "gemma_status" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(GemmaStatus, { ...itemForDisplay }),
itemForDisplay.type === "chat_list" && /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(ChatList, { chats: itemForDisplay.chats })
]
},
itemForDisplay.id
);
};
// packages/cli/src/ui/hooks/useTerminalSize.ts
var import_react26 = __toESM(require_react(), 1);
function useTerminalSize() {
const [size, setSize] = (0, import_react26.useState)({
columns: process.stdout.columns || 60,
rows: process.stdout.rows || 20
});
(0, import_react26.useEffect)(() => {
function updateSize() {
setSize({
columns: process.stdout.columns || 60,
rows: process.stdout.rows || 20
});
}
process.stdout.on("resize", updateSize);
return () => {
process.stdout.off("resize", updateSize);
};
}, []);
return size;
}
// packages/cli/src/ui/components/QuittingDisplay.tsx
var import_jsx_runtime55 = __toESM(require_jsx_runtime(), 1);
var QuittingDisplay = () => {
const uiState = useUIState();
const { rows: terminalHeight, columns: terminalWidth } = useTerminalSize();
const availableTerminalHeight = terminalHeight;
if (!uiState.quittingMessages) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(Box_default, { flexDirection: "column", marginBottom: 1, children: uiState.quittingMessages.map((item) => /* @__PURE__ */ (0, import_jsx_runtime55.jsx)(
HistoryItemDisplay,
{
availableTerminalHeight: uiState.constrainHeight ? availableTerminalHeight : void 0,
terminalWidth,
item,
isPending: false
},
item.id
)) });
};
// packages/cli/src/ui/components/Notifications.tsx
var import_react28 = __toESM(require_react(), 1);
// packages/cli/src/ui/contexts/AppContext.tsx
var import_react27 = __toESM(require_react(), 1);
var AppContext = (0, import_react27.createContext)(null);
var useAppContext = () => {
const context = (0, import_react27.useContext)(AppContext);
if (!context) {
throw new Error("useAppContext must be used within an AppProvider");
}
return context;
};
// packages/cli/src/ui/components/UpdateNotification.tsx
var import_jsx_runtime56 = __toESM(require_jsx_runtime(), 1);
var UpdateNotification = ({ message }) => /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.status.warning,
paddingX: 1,
marginY: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime56.jsx)(Text, { color: theme.status.warning, children: message })
}
);
// packages/cli/src/ui/components/Notifications.tsx
var import_jsx_runtime57 = __toESM(require_jsx_runtime(), 1);
import * as fs from "node:fs/promises";
import path4 from "node:path";
var settingsPath = path4.join(homedir(), GEMINI_DIR, "settings.json");
var screenReaderNudgeFilePath = path4.join(
Storage.getGlobalTempDir(),
"seen_screen_reader_nudge.json"
);
var MAX_STARTUP_WARNING_SHOW_COUNT = 3;
var Notifications = () => {
const { startupWarnings } = useAppContext();
const { initError, streamingState, updateInfo } = useUIState();
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
const showInitError = initError && streamingState !== "responding" /* Responding */;
const [hasSeenScreenReaderNudge, setHasSeenScreenReaderNudge] = (0, import_react28.useState)(
() => persistentState.get("hasSeenScreenReaderNudge")
);
const [dismissed, setDismissed] = (0, import_react28.useState)(false);
const hasIncrementedRef = (0, import_react28.useRef)(false);
const visibleWarnings = (0, import_react28.useMemo)(() => {
if (dismissed) return [];
const counts = persistentState.get("startupWarningCounts") || {};
return startupWarnings.filter((w) => {
if (w.priority === WarningPriority.Low) {
const count = counts[w.id] || 0;
return count < MAX_STARTUP_WARNING_SHOW_COUNT;
}
return true;
});
}, [startupWarnings, dismissed]);
const showStartupWarnings = visibleWarnings.length > 0;
(0, import_react28.useEffect)(() => {
if (visibleWarnings.length > 0 && !hasIncrementedRef.current) {
const counts = { ...persistentState.get("startupWarningCounts") || {} };
let changed = false;
visibleWarnings.forEach((w) => {
if (w.priority === WarningPriority.Low) {
counts[w.id] = (counts[w.id] || 0) + 1;
changed = true;
}
});
if (changed) {
persistentState.set("startupWarningCounts", counts);
}
hasIncrementedRef.current = true;
}
}, [visibleWarnings]);
const handleKeyPress = (0, import_react28.useCallback)(() => {
if (showStartupWarnings) {
setDismissed(true);
}
return false;
}, [showStartupWarnings]);
useKeypress(handleKeyPress, {
isActive: showStartupWarnings,
priority: 200 /* Critical */
});
(0, import_react28.useEffect)(() => {
const checkLegacyScreenReaderNudge = async () => {
if (hasSeenScreenReaderNudge !== void 0) return;
try {
await fs.access(screenReaderNudgeFilePath);
persistentState.set("hasSeenScreenReaderNudge", true);
setHasSeenScreenReaderNudge(true);
await fs.unlink(screenReaderNudgeFilePath).catch(() => {
});
} catch {
setHasSeenScreenReaderNudge(false);
}
};
if (isScreenReaderEnabled) {
checkLegacyScreenReaderNudge();
}
}, [isScreenReaderEnabled, hasSeenScreenReaderNudge]);
const showScreenReaderNudge = isScreenReaderEnabled && hasSeenScreenReaderNudge === false;
(0, import_react28.useEffect)(() => {
if (showScreenReaderNudge) {
persistentState.set("hasSeenScreenReaderNudge", true);
}
}, [showScreenReaderNudge]);
if (!showStartupWarnings && !showInitError && !updateInfo && !showScreenReaderNudge) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(import_jsx_runtime57.Fragment, { children: [
showScreenReaderNudge && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { children: [
"You are currently in screen reader-friendly view. To switch out, open",
" ",
settingsPath,
" and remove the entry for ",
'"screenReader"',
". This will disappear on next run."
] }),
updateInfo && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(UpdateNotification, { message: updateInfo.message }),
showStartupWarnings && /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Box_default, { marginY: 1, flexDirection: "column", children: visibleWarnings.map((warning, index) => /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Box_default, { width: 3, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { color: theme.status.warning, children: "\u26A0 " }) }),
/* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime57.jsx)(Text, { color: theme.status.warning, children: warning.message }) })
] }, index)) }),
showInitError && /* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.status.error,
paddingX: 1,
marginBottom: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { color: theme.status.error, children: [
"Initialization Error: ",
initError
] }),
/* @__PURE__ */ (0, import_jsx_runtime57.jsxs)(Text, { color: theme.status.error, children: [
" ",
"Please check API key and configuration."
] })
]
}
)
] });
};
// packages/cli/src/ui/components/UserIdentity.tsx
var import_react29 = __toESM(require_react(), 1);
var import_jsx_runtime58 = __toESM(require_jsx_runtime(), 1);
var UserIdentity = ({ config }) => {
const authType = config.getContentGeneratorConfig()?.authType;
const [email, setEmail] = (0, import_react29.useState)();
(0, import_react29.useEffect)(() => {
if (authType) {
const userAccountManager = new UserAccountManager();
setEmail(userAccountManager.getCachedGoogleAccount() ?? void 0);
} else {
setEmail(void 0);
}
}, [authType]);
const tierName = (0, import_react29.useMemo)(
() => authType ? config.getUserTierName() : void 0,
[config, authType]
);
const isUltra = (0, import_react29.useMemo)(() => isUltraTier(tierName), [tierName]);
if (!authType) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { color: theme.text.primary, wrap: "truncate-end", children: authType === AuthType.LOGIN_WITH_GOOGLE ? /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { bold: true, children: [
"Signed in with Google",
email ? ":" : ""
] }),
email ? ` ${email}` : ""
] }) : `Authenticated with ${authType}` }),
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { color: theme.text.secondary, children: " /auth" })
] }),
tierName && /* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime58.jsxs)(Text, { color: theme.text.primary, wrap: "truncate-end", children: [
/* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { bold: true, children: "Plan:" }),
" ",
tierName
] }),
!isUltra && /* @__PURE__ */ (0, import_jsx_runtime58.jsx)(Text, { color: theme.text.secondary, children: " /upgrade" })
] })
] });
};
// packages/cli/src/ui/components/Tips.tsx
var import_jsx_runtime59 = __toESM(require_jsx_runtime(), 1);
var Tips = ({ config }) => {
const geminiMdFileCount = config.getGeminiMdFileCount();
return /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { color: theme.text.primary, children: "Tips for getting started:" }),
geminiMdFileCount === 0 && /* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { color: theme.text.primary, children: [
"1. Create ",
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { bold: true, children: "GEMINI.md" }),
" files to customize your interactions"
] }),
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { color: theme.text.primary, children: [
geminiMdFileCount === 0 ? "2." : "1.",
" ",
/* @__PURE__ */ (0, import_jsx_runtime59.jsx)(Text, { color: theme.text.secondary, children: "/help" }),
" for more information"
] }),
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { color: theme.text.primary, children: [
geminiMdFileCount === 0 ? "3." : "2.",
" Ask coding questions, edit code or run commands"
] }),
/* @__PURE__ */ (0, import_jsx_runtime59.jsxs)(Text, { color: theme.text.primary, children: [
geminiMdFileCount === 0 ? "4." : "3.",
" Be specific for the best results"
] })
] });
};
// packages/cli/src/ui/components/Banner.tsx
var import_jsx_runtime60 = __toESM(require_jsx_runtime(), 1);
function getFormattedBannerContent(rawText, isWarning, subsequentLineColor) {
const text = rawText.replace(/\\n/g, "\n");
const lines = text.split("\n");
return lines.map((line, index) => {
if (index === 0) {
if (isWarning) {
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { bold: true, color: theme.status.warning, children: line }, index);
}
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(ThemedGradient, { children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { bold: true, children: line }) }, index);
}
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(Text, { color: subsequentLineColor, children: line }, index);
});
}
var Banner = ({ bannerText, isWarning, width }) => {
const subsequentLineColor = theme.text.primary;
const formattedBannerContent = getFormattedBannerContent(
bannerText,
isWarning,
subsequentLineColor
);
return /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: isWarning ? theme.status.warning : theme.border.default,
width,
paddingLeft: 1,
paddingRight: 1,
children: formattedBannerContent
}
);
};
// packages/cli/src/ui/hooks/useBanner.ts
var import_react30 = __toESM(require_react(), 1);
import crypto from "node:crypto";
var DEFAULT_MAX_BANNER_SHOWN_COUNT = 5;
var sessionIncrementedBanners = /* @__PURE__ */ new Set();
function useBanner(bannerData) {
const { defaultText, warningText } = bannerData;
const [bannerCounts] = (0, import_react30.useState)(
() => persistentState.get("defaultBannerShownCount") || {}
);
const activeText = warningText ? warningText : defaultText;
const hashedText = crypto.createHash("sha256").update(activeText).digest("hex");
const currentBannerCount = bannerCounts[hashedText] || 0;
const showBanner = activeText !== "" && currentBannerCount < DEFAULT_MAX_BANNER_SHOWN_COUNT;
const rawBannerText = showBanner ? activeText : "";
const bannerText = rawBannerText.replace(/\\n/g, "\n");
(0, import_react30.useEffect)(() => {
if (showBanner && activeText) {
if (!sessionIncrementedBanners.has(activeText)) {
sessionIncrementedBanners.add(activeText);
const allCounts = persistentState.get("defaultBannerShownCount") || {};
const current = allCounts[hashedText] || 0;
persistentState.set("defaultBannerShownCount", {
...allCounts,
[hashedText]: current + 1
});
}
}
}, [showBanner, activeText, hashedText]);
return {
bannerText
};
}
// packages/cli/src/ui/hooks/useTips.ts
var import_react31 = __toESM(require_react(), 1);
function useTips() {
const [tipsCount] = (0, import_react31.useState)(() => persistentState.get("tipsShown") ?? 0);
const showTips = tipsCount < 10;
(0, import_react31.useEffect)(() => {
if (showTips) {
persistentState.set("tipsShown", tipsCount + 1);
}
}, [tipsCount, showTips]);
return { showTips };
}
// packages/cli/src/ui/components/AsciiArt.ts
var longAsciiLogoCompactText = `
\u2597\u2588\u2580\u2580\u259C\u2599\u259D\u2588\u259B\u2580\u2580\u258C\u259C\u2588\u2588\u2596\u259F\u2588\u2588\u2598\u259C\u2588\u2598\u259C\u2588\u2588\u2596\u259D\u2588\u259B\u259D\u2588\u259B
\u2588\u258C \u2588\u2599\u259F \u2590\u2588\u259D\u2588\u259B\u2590\u2588 \u2590\u2588 \u2590\u2588\u259D\u2588\u2596\u2588\u258C \u2588\u258C
\u259C\u2599 \u259D\u2588\u259B \u2588\u258C\u259D \u2596\u2590\u2588 \u2590\u2588 \u2590\u2588 \u2590\u2588 \u259D\u2588\u2588\u258C \u2588\u258C
\u2580\u2580\u2580\u2580\u2598\u259D\u2580\u2580\u2580\u2580\u2598\u2580\u2580\u2598 \u2580\u2580\u2598\u2580\u2580\u2598\u2580\u2580\u2598 \u259D\u2580\u2580\u259D\u2580\u2580
`;
// packages/cli/src/ui/components/AppHeader.tsx
var import_jsx_runtime61 = __toESM(require_jsx_runtime(), 1);
var DEFAULT_ICON = `\u259D\u259C\u2584
\u259D\u259C\u2584
\u2597\u259F\u2580
\u259D\u2580 `;
var MAC_TERMINAL_ICON = `\u259D\u259C\u2584
\u259D\u259C\u2584
\u2597\u259F\u2580
\u2597\u259F\u2580 `;
var LOGO_METADATA_PADDING = 20;
var NARROW_TERMINAL_BREAKPOINT = 60;
var AppHeader = ({ version, showDetails = true }) => {
const settings = useSettings();
const config = useConfig();
const {
terminalWidth,
bannerData,
bannerVisible,
updateInfo,
isConfigInitialized,
isAuthenticating
} = useUIState();
const { bannerText } = useBanner(bannerData);
const { showTips } = useTips();
const authType = config.getContentGeneratorConfig()?.authType;
const loggedOut = isConfigInitialized && !isAuthenticating && !authType;
const showHeader = !(settings.merged.ui.hideBanner || config.getScreenReader());
const ICON = isAppleTerminal() ? MAC_TERMINAL_ICON : DEFAULT_ICON;
let logoTextArt = "";
if (loggedOut) {
const widthOfLongLogo = getAsciiArtWidth(longAsciiLogoCompactText) + LOGO_METADATA_PADDING;
if (terminalWidth >= widthOfLongLogo) {
logoTextArt = longAsciiLogoCompactText.trim();
}
}
const isNarrow = terminalWidth < NARROW_TERMINAL_BREAKPOINT;
const renderLogo = () => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(ThemedGradient, { children: ICON }) }),
logoTextArt && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Box_default, { marginLeft: 3, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { color: theme.text.primary, children: logoTextArt }) })
] });
const renderMetadata = (isBelow = false) => /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Box_default, { marginLeft: isBelow ? 0 : 2, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Text, { bold: true, color: theme.text.primary, children: "Gemini CLI" }),
/* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Text, { color: theme.text.secondary, children: [
" v",
version
] }),
updateInfo?.isUpdating && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Box_default, { marginLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Text, { color: theme.text.secondary, children: [
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(CliSpinner, {}),
" Updating"
] }) })
] }),
showDetails && /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(import_jsx_runtime61.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Box_default, { height: 1 }),
settings.merged.ui.showUserIdentity !== false && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(UserIdentity, { config })
] })
] });
const useColumnLayout = !!logoTextArt || isNarrow;
return /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(Box_default, { flexDirection: "column", children: [
showHeader && /* @__PURE__ */ (0, import_jsx_runtime61.jsxs)(
Box_default,
{
flexDirection: useColumnLayout ? "column" : "row",
marginTop: 1,
marginBottom: 1,
paddingLeft: 1,
children: [
renderLogo(),
useColumnLayout ? /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Box_default, { marginTop: 1, children: renderMetadata(true) }) : renderMetadata(false)
]
}
),
bannerVisible && bannerText && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(
Banner,
{
width: terminalWidth,
bannerText,
isWarning: bannerData.warningText !== ""
}
),
!(settings.merged.ui.hideTips || config.getScreenReader()) && showTips && /* @__PURE__ */ (0, import_jsx_runtime61.jsx)(Tips, { config })
] });
};
// packages/cli/src/ui/components/MainContent.tsx
var import_react38 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useConfirmingTool.ts
var import_react32 = __toESM(require_react(), 1);
// packages/cli/src/ui/utils/confirmingTool.ts
function getConfirmingToolState(pendingHistoryItems) {
const allPendingTools = getAllToolCalls(pendingHistoryItems);
const confirmingTools = allPendingTools.filter(
(tool) => tool.status === CoreToolCallStatus.AwaitingApproval
);
if (confirmingTools.length === 0) {
return null;
}
const actionablePendingTools = allPendingTools.filter(
(tool) => belongsInConfirmationQueue(buildToolVisibilityContextFromDisplay(tool))
);
const head = confirmingTools[0];
const headIndexInFullList = actionablePendingTools.findIndex(
(tool) => tool.callId === head.callId
);
return {
tool: head,
index: headIndexInFullList + 1,
total: actionablePendingTools.length
};
}
// packages/cli/src/ui/hooks/useConfirmingTool.ts
function useConfirmingTool() {
const { pendingHistoryItems } = useUIState();
return (0, import_react32.useMemo)(
() => getConfirmingToolState(pendingHistoryItems),
[pendingHistoryItems]
);
}
// packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
var import_react37 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/AskUserDialog.tsx
var import_react35 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/shared/TabHeader.tsx
var import_react33 = __toESM(require_react(), 1);
var import_jsx_runtime62 = __toESM(require_jsx_runtime(), 1);
function TabHeader({
tabs,
currentIndex,
completedIndices = /* @__PURE__ */ new Set(),
showArrows = true,
showStatusIcons = true,
renderStatusIcon
}) {
if (tabs.length <= 1) return null;
const getStatusIcon = (tab, index) => {
const isCompleted = completedIndices.has(index);
if (renderStatusIcon) {
const customIcon = renderStatusIcon(tab, index, isCompleted);
if (customIcon !== void 0) return customIcon;
}
if (tab.statusIcon) return tab.statusIcon;
if (tab.isSpecial) return "\u2261";
return isCompleted ? "\u2713" : "\u25A1";
};
return /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(Box_default, { flexDirection: "row", marginBottom: 1, "aria-role": "tablist", children: [
showArrows && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { color: theme.text.secondary, children: "\u2190 " }),
tabs.map((tab, i) => /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(import_react33.default.Fragment, { children: [
i > 0 && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { color: theme.text.secondary, children: " \u2502 " }),
showStatusIcons && /* @__PURE__ */ (0, import_jsx_runtime62.jsxs)(Text, { color: theme.text.secondary, children: [
getStatusIcon(tab, i),
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Box_default, { maxWidth: i !== currentIndex ? 16 : 100, children: /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(
Text,
{
color: i === currentIndex ? theme.status.success : theme.text.secondary,
bold: i === currentIndex,
underline: i === currentIndex,
"aria-current": i === currentIndex ? "step" : void 0,
wrap: "truncate",
children: tab.header
}
) })
] }, tab.key)),
showArrows && /* @__PURE__ */ (0, import_jsx_runtime62.jsx)(Text, { color: theme.text.secondary, children: " \u2192" })
] });
}
// packages/cli/src/ui/hooks/useTabbedNavigation.ts
var import_react34 = __toESM(require_react(), 1);
function tabbedNavigationReducer(state, action) {
switch (action.type) {
case "NEXT_TAB": {
const { tabCount, wrapAround, currentIndex } = state;
if (tabCount === 0) return state;
let nextIndex = currentIndex + 1;
if (nextIndex >= tabCount) {
nextIndex = wrapAround ? 0 : tabCount - 1;
}
if (nextIndex === currentIndex) return state;
return { ...state, currentIndex: nextIndex, pendingTabChange: true };
}
case "PREV_TAB": {
const { tabCount, wrapAround, currentIndex } = state;
if (tabCount === 0) return state;
let nextIndex = currentIndex - 1;
if (nextIndex < 0) {
nextIndex = wrapAround ? tabCount - 1 : 0;
}
if (nextIndex === currentIndex) return state;
return { ...state, currentIndex: nextIndex, pendingTabChange: true };
}
case "SET_INDEX": {
const { index } = action.payload;
const { tabCount, currentIndex } = state;
if (index === currentIndex) return state;
if (index < 0 || index >= tabCount) return state;
return { ...state, currentIndex: index, pendingTabChange: true };
}
case "INITIALIZE": {
const { tabCount, initialIndex, wrapAround } = action.payload;
const validIndex = Math.max(0, Math.min(initialIndex, tabCount - 1));
return {
...state,
tabCount,
wrapAround,
currentIndex: tabCount > 0 ? validIndex : 0,
pendingTabChange: false
};
}
case "CLEAR_PENDING": {
return { ...state, pendingTabChange: false };
}
default: {
return state;
}
}
}
function useTabbedNavigation({
tabCount,
initialIndex = 0,
wrapAround = false,
enableArrowNavigation = true,
enableTabKey = true,
isNavigationBlocked,
isActive = true,
onTabChange
}) {
const keyMatchers = useKeyMatchers();
const [state, dispatch] = (0, import_react34.useReducer)(tabbedNavigationReducer, {
currentIndex: Math.max(0, Math.min(initialIndex, tabCount - 1)),
tabCount,
wrapAround,
pendingTabChange: false
});
const prevTabCountRef = (0, import_react34.useRef)(tabCount);
const prevInitialIndexRef = (0, import_react34.useRef)(initialIndex);
const prevWrapAroundRef = (0, import_react34.useRef)(wrapAround);
(0, import_react34.useEffect)(() => {
const tabCountChanged = prevTabCountRef.current !== tabCount;
const initialIndexChanged = prevInitialIndexRef.current !== initialIndex;
const wrapAroundChanged = prevWrapAroundRef.current !== wrapAround;
if (tabCountChanged || initialIndexChanged || wrapAroundChanged) {
dispatch({
type: "INITIALIZE",
payload: { tabCount, initialIndex, wrapAround }
});
prevTabCountRef.current = tabCount;
prevInitialIndexRef.current = initialIndex;
prevWrapAroundRef.current = wrapAround;
}
}, [tabCount, initialIndex, wrapAround]);
(0, import_react34.useEffect)(() => {
if (state.pendingTabChange) {
onTabChange?.(state.currentIndex);
dispatch({ type: "CLEAR_PENDING" });
}
}, [state.pendingTabChange, state.currentIndex, onTabChange]);
const goToNextTab = (0, import_react34.useCallback)(() => {
if (isNavigationBlocked?.()) return;
dispatch({ type: "NEXT_TAB" });
}, [isNavigationBlocked]);
const goToPrevTab = (0, import_react34.useCallback)(() => {
if (isNavigationBlocked?.()) return;
dispatch({ type: "PREV_TAB" });
}, [isNavigationBlocked]);
const setCurrentIndex = (0, import_react34.useCallback)(
(index) => {
if (isNavigationBlocked?.()) return;
dispatch({ type: "SET_INDEX", payload: { index } });
},
[isNavigationBlocked]
);
const handleKeypress = (0, import_react34.useCallback)(
(key) => {
if (isNavigationBlocked?.()) return;
if (enableArrowNavigation) {
if (keyMatchers["cursor.right" /* MOVE_RIGHT */](key)) {
goToNextTab();
return;
}
if (keyMatchers["cursor.left" /* MOVE_LEFT */](key)) {
goToPrevTab();
return;
}
}
if (enableTabKey) {
if (keyMatchers["nav.dialog.next" /* DIALOG_NEXT */](key)) {
goToNextTab();
return;
}
if (keyMatchers["nav.dialog.previous" /* DIALOG_PREV */](key)) {
goToPrevTab();
return;
}
}
},
[
enableArrowNavigation,
enableTabKey,
goToNextTab,
goToPrevTab,
isNavigationBlocked,
keyMatchers
]
);
useKeypress(handleKeypress, { isActive: isActive && tabCount > 1 });
return {
currentIndex: state.currentIndex,
setCurrentIndex,
goToNextTab,
goToPrevTab,
isFirstTab: state.currentIndex === 0,
isLastTab: state.currentIndex === tabCount - 1
};
}
// packages/cli/src/ui/components/AskUserDialog.tsx
var import_jsx_runtime63 = __toESM(require_jsx_runtime(), 1);
var DIALOG_PADDING = 4;
function isPlainSingleLine(text) {
if (text.includes("\n") || text.includes("\r")) {
return false;
}
const markdownPatterns = [
/^#{1,6}\s/,
// Headers
/^[`~]{3,}/,
// Code fences
/^[-*+]\s/,
// Unordered lists
/^\d+\.\s/,
// Ordered lists
/^[-*_]{3,}$/,
// Horizontal rules
/\|/,
// Tables
/\*\*|__/,
// Bold
/(?<!\*)\*(?!\*)/,
// Italic (single asterisk not part of bold)
/(?<!_)_(?!_)/,
// Italic (single underscore not part of bold)
/`[^`]+`/,
// Inline code
/\[.*?\]\(.*?\)/,
// Links
/!\[/
// Images
];
for (const pattern of markdownPatterns) {
if (pattern.test(text)) {
return false;
}
}
return true;
}
function autoBoldIfPlain(text) {
if (isPlainSingleLine(text)) {
return `**${text}**`;
}
return text;
}
var ClickableCheckbox = ({ isChecked, onClick }) => {
const ref = (0, import_react35.useRef)(null);
useMouseClick(ref, () => {
onClick();
});
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { ref, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Text, { color: isChecked ? theme.status.success : theme.text.secondary, children: [
"[",
isChecked ? "x" : " ",
"]"
] }) });
};
var initialState = {
answers: {},
isEditingCustomOption: false,
submitted: false
};
function askUserDialogReducerLogic(state, action) {
if (state.submitted) {
return state;
}
switch (action.type) {
case "SET_ANSWER": {
const { index, answer, submit } = action.payload;
const hasAnswer = answer !== void 0 && answer !== null && answer.trim() !== "";
const newAnswers = { ...state.answers };
if (hasAnswer) {
newAnswers[index] = answer;
} else {
delete newAnswers[index];
}
return {
...state,
answers: newAnswers,
submitted: submit ? true : state.submitted
};
}
case "SET_EDITING_CUSTOM": {
if (state.isEditingCustomOption === action.payload.isEditing) {
return state;
}
return {
...state,
isEditingCustomOption: action.payload.isEditing
};
}
case "SUBMIT": {
return {
...state,
submitted: true
};
}
default:
checkExhaustive(action);
return state;
}
}
var ReviewView = ({
questions,
answers,
onSubmit,
progressHeader,
extraParts
}) => {
const keyMatchers = useKeyMatchers();
const unansweredCount = questions.length - Object.keys(answers).length;
const hasUnanswered = unansweredCount > 0;
useKeypress(
(key) => {
if (keyMatchers["basic.confirm" /* RETURN */](key)) {
onSubmit();
return true;
}
return false;
},
{ isActive: true }
);
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "column", children: [
progressHeader,
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { bold: true, color: theme.text.primary, children: "Review your answers:" }) }),
hasUnanswered && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Text, { color: theme.status.warning, children: [
"\u26A0 You have ",
unansweredCount,
" unanswered question",
unansweredCount > 1 ? "s" : ""
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { flexDirection: "column", children: questions.map((q, i) => /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { marginBottom: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.text.secondary, children: q.header }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.text.secondary, children: " \u2192 " }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
Text,
{
color: answers[i] ? theme.text.primary : theme.status.warning,
children: answers[i] || "(not answered)"
}
)
] }, i)) }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
DialogFooter,
{
primaryAction: "Enter to submit",
navigationActions: `${formatCommand("nav.dialog.next" /* DIALOG_NEXT */)}/${formatCommand("nav.dialog.previous" /* DIALOG_PREV */)} to edit answers`,
extraParts
}
)
] });
};
var TextQuestionView = ({
question,
onAnswer,
onSelectionChange,
onEditingCustomOption,
availableWidth,
availableHeight,
initialAnswer,
progressHeader,
keyboardHints
}) => {
const keyMatchers = useKeyMatchers();
const isAlternateBuffer = useAlternateBuffer();
const prefix = "> ";
const horizontalPadding = 1;
const bufferWidth = availableWidth - getCachedStringWidth(prefix) - horizontalPadding;
const buffer = useTextBuffer({
initialText: initialAnswer,
viewport: { width: Math.max(1, bufferWidth), height: 3 },
singleLine: false
});
const { text: textValue } = buffer;
const lastTextValueRef = (0, import_react35.useRef)(textValue);
(0, import_react35.useEffect)(() => {
if (textValue !== lastTextValueRef.current) {
onSelectionChange?.(
expandPastePlaceholders(textValue, buffer.pastedContent)
);
lastTextValueRef.current = textValue;
}
}, [textValue, onSelectionChange, buffer.pastedContent]);
const handleExtraKeys = (0, import_react35.useCallback)(
(key) => {
if (keyMatchers["basic.quit" /* QUIT */](key)) {
if (textValue === "") {
return false;
}
buffer.setText("");
return true;
}
return false;
},
[buffer, textValue, keyMatchers]
);
useKeypress(handleExtraKeys, { isActive: true, priority: true });
const handleSubmit = (0, import_react35.useCallback)(
(val) => {
onAnswer(val.trim());
},
[onAnswer]
);
(0, import_react35.useEffect)(() => {
onEditingCustomOption?.(true);
return () => {
onEditingCustomOption?.(false);
};
}, [onEditingCustomOption]);
const placeholder = question.placeholder || "Enter your response";
const HEADER_HEIGHT2 = progressHeader ? 2 : 0;
const INPUT_HEIGHT = 2;
const FOOTER_HEIGHT2 = 2;
const overhead = HEADER_HEIGHT2 + INPUT_HEIGHT + FOOTER_HEIGHT2;
const questionHeight = availableHeight && !isAlternateBuffer ? Math.max(1, availableHeight - overhead) : void 0;
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "column", children: [
progressHeader,
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
MaxSizedBox,
{
maxHeight: questionHeight,
maxWidth: availableWidth,
overflowDirection: "bottom",
children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
MarkdownDisplay,
{
text: autoBoldIfPlain(question.question),
terminalWidth: availableWidth - DIALOG_PADDING,
isPending: false
}
)
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "row", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.status.success, children: "> " }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
TextInput,
{
buffer,
placeholder,
onSubmit: handleSubmit
}
)
] }),
keyboardHints
] });
};
function choiceQuestionReducer(state, action) {
switch (action.type) {
case "TOGGLE_ALL": {
const { totalOptions } = action.payload;
const allSelected = state.selectedIndices.size === totalOptions;
if (allSelected) {
return {
...state,
selectedIndices: /* @__PURE__ */ new Set()
};
} else {
const newIndices = /* @__PURE__ */ new Set();
for (let i = 0; i < totalOptions; i++) {
newIndices.add(i);
}
return {
...state,
selectedIndices: newIndices
};
}
}
case "TOGGLE_INDEX": {
const { index, multiSelect } = action.payload;
const newIndices = new Set(multiSelect ? state.selectedIndices : []);
if (newIndices.has(index)) {
newIndices.delete(index);
} else {
newIndices.add(index);
}
return {
...state,
selectedIndices: newIndices,
// In single select, selecting an option deselects custom
isCustomOptionSelected: multiSelect ? state.isCustomOptionSelected : false
};
}
case "SET_CUSTOM_SELECTED": {
const { selected, multiSelect } = action.payload;
return {
...state,
isCustomOptionSelected: selected,
// In single-select, selecting custom deselects others
selectedIndices: multiSelect ? state.selectedIndices : /* @__PURE__ */ new Set()
};
}
case "TOGGLE_CUSTOM_SELECTED": {
const { multiSelect } = action.payload;
if (!multiSelect) return state;
return {
...state,
isCustomOptionSelected: !state.isCustomOptionSelected
};
}
case "SET_CUSTOM_FOCUSED": {
return {
...state,
isCustomOptionFocused: action.payload.focused
};
}
default:
checkExhaustive(action);
return state;
}
}
var ChoiceQuestionView = ({
question,
onAnswer,
onSelectionChange,
onEditingCustomOption,
availableWidth,
availableHeight,
initialAnswer,
progressHeader,
keyboardHints
}) => {
const keyMatchers = useKeyMatchers();
const isAlternateBuffer = useAlternateBuffer();
const hasAll = question.multiSelect && (question.options?.length ?? 0) > 1;
const numOptions = (question.options?.length ?? 0) + (hasAll ? 1 : 0) + 1;
const numLen = String(numOptions).length;
const radioWidth = 2;
const numberWidth = numLen + 2;
const checkboxWidth = question.multiSelect ? 4 : 1;
const checkmarkWidth = question.multiSelect ? 0 : 2;
const cursorPadding = 1;
const horizontalPadding = radioWidth + numberWidth + checkboxWidth + checkmarkWidth + cursorPadding;
const bufferWidth = availableWidth - horizontalPadding;
const questionOptions = (0, import_react35.useMemo)(
() => question.options ?? [],
[question.options]
);
const initialReducerState = (0, import_react35.useMemo)(() => {
if (!initialAnswer) {
return {
selectedIndices: /* @__PURE__ */ new Set(),
isCustomOptionSelected: false,
isCustomOptionFocused: false
};
}
const selectedIndices2 = /* @__PURE__ */ new Set();
let isCustomOptionSelected2 = false;
if (question.multiSelect) {
const answers = initialAnswer.split(", ");
answers.forEach((answer) => {
const index = questionOptions.findIndex((opt) => opt.label === answer);
if (index !== -1) {
selectedIndices2.add(index);
} else {
isCustomOptionSelected2 = true;
}
});
} else {
const index = questionOptions.findIndex(
(opt) => opt.label === initialAnswer
);
if (index !== -1) {
selectedIndices2.add(index);
} else {
isCustomOptionSelected2 = true;
}
}
return {
selectedIndices: selectedIndices2,
isCustomOptionSelected: isCustomOptionSelected2,
isCustomOptionFocused: false
};
}, [initialAnswer, questionOptions, question.multiSelect]);
const [state, dispatch] = (0, import_react35.useReducer)(
choiceQuestionReducer,
initialReducerState
);
const { selectedIndices, isCustomOptionSelected, isCustomOptionFocused } = state;
const initialCustomText = (0, import_react35.useMemo)(() => {
if (!initialAnswer) return "";
if (question.multiSelect) {
const answers = initialAnswer.split(", ");
const custom = answers.find(
(a) => !questionOptions.some((opt) => opt.label === a)
);
return custom || "";
} else {
const isPredefined = questionOptions.some(
(opt) => opt.label === initialAnswer
);
return isPredefined ? "" : initialAnswer;
}
}, [initialAnswer, questionOptions, question.multiSelect]);
const customBuffer = useTextBuffer({
initialText: initialCustomText,
viewport: { width: Math.max(1, bufferWidth), height: 3 },
singleLine: false
});
const customOptionText = customBuffer.text;
const buildAnswerString = (0, import_react35.useCallback)(
(indices, includeCustomOption, customOption) => {
const answers = [];
questionOptions.forEach((opt, i) => {
if (indices.has(i)) {
answers.push(opt.label);
}
});
if (includeCustomOption && customOption.trim()) {
const expanded = expandPastePlaceholders(
customOption,
customBuffer.pastedContent
);
answers.push(expanded.trim());
}
return answers.join(", ");
},
[questionOptions, customBuffer.pastedContent]
);
const lastBuiltAnswerRef = (0, import_react35.useRef)("");
(0, import_react35.useEffect)(() => {
const newAnswer = buildAnswerString(
selectedIndices,
isCustomOptionSelected,
customOptionText
);
if (newAnswer !== lastBuiltAnswerRef.current) {
onSelectionChange?.(newAnswer);
lastBuiltAnswerRef.current = newAnswer;
}
}, [
selectedIndices,
isCustomOptionSelected,
customOptionText,
buildAnswerString,
onSelectionChange
]);
const handleExtraKeys = (0, import_react35.useCallback)(
(key) => {
if (isCustomOptionFocused && keyMatchers["basic.quit" /* QUIT */](key)) {
if (customOptionText === "") {
return false;
}
customBuffer.setText("");
return true;
}
if (keyMatchers["nav.dialog.up" /* DIALOG_NAVIGATION_UP */](key) || keyMatchers["nav.dialog.down" /* DIALOG_NAVIGATION_DOWN */](key) || keyMatchers["nav.dialog.next" /* DIALOG_NEXT */](key) || keyMatchers["nav.dialog.previous" /* DIALOG_PREV */](key) || keyMatchers["cursor.left" /* MOVE_LEFT */](key) || keyMatchers["cursor.right" /* MOVE_RIGHT */](key) || keyMatchers["basic.confirm" /* RETURN */](key) || keyMatchers["basic.cancel" /* ESCAPE */](key) || keyMatchers["basic.quit" /* QUIT */](key)) {
return false;
}
const isNumeric = /^[0-9]$/.test(key.sequence);
if (isNumeric) {
return false;
}
const isPrintable = key.sequence && !key.ctrl && !key.alt && (key.sequence.length > 1 || key.sequence.charCodeAt(0) >= 32);
if (isPrintable && !isCustomOptionFocused) {
dispatch({ type: "SET_CUSTOM_FOCUSED", payload: { focused: true } });
onEditingCustomOption?.(true);
customBuffer.setText(key.sequence);
return true;
}
return false;
},
[
isCustomOptionFocused,
customBuffer,
onEditingCustomOption,
customOptionText,
keyMatchers
]
);
useKeypress(handleExtraKeys, { isActive: true, priority: true });
const selectionItems = (0, import_react35.useMemo)(() => {
const list = questionOptions.map(
(opt, i) => {
const item = {
key: `opt-${i}`,
label: opt.label,
description: opt.description,
type: "option",
index: i
};
return { key: item.key, value: item };
}
);
if (question.multiSelect && questionOptions.length > 1) {
const allItem = {
key: "all",
label: "All of the above",
description: "Select all options",
type: "all",
index: list.length
};
list.push({ key: "all", value: allItem });
}
const otherItem = {
key: "other",
label: customOptionText || "",
description: "",
type: "other",
index: list.length
};
list.push({ key: "other", value: otherItem });
if (question.multiSelect) {
const doneItem = {
key: "done",
label: "Done",
description: "Finish selection",
type: "done",
index: list.length
};
list.push({ key: doneItem.key, value: doneItem, hideNumber: true });
}
return list;
}, [questionOptions, question.multiSelect, customOptionText]);
const handleHighlight = (0, import_react35.useCallback)(
(itemValue) => {
const nowFocusingCustomOption = itemValue.type === "other";
dispatch({
type: "SET_CUSTOM_FOCUSED",
payload: { focused: nowFocusingCustomOption }
});
onEditingCustomOption?.(nowFocusingCustomOption);
},
[onEditingCustomOption]
);
const handleSelect = (0, import_react35.useCallback)(
(itemValue) => {
if (question.multiSelect) {
if (itemValue.type === "option") {
dispatch({
type: "TOGGLE_INDEX",
payload: { index: itemValue.index, multiSelect: true }
});
} else if (itemValue.type === "other") {
dispatch({
type: "TOGGLE_CUSTOM_SELECTED",
payload: { multiSelect: true }
});
} else if (itemValue.type === "all") {
dispatch({
type: "TOGGLE_ALL",
payload: { totalOptions: questionOptions.length }
});
} else if (itemValue.type === "done") {
onAnswer(
buildAnswerString(
selectedIndices,
isCustomOptionSelected,
customOptionText
)
);
}
} else {
if (itemValue.type === "option") {
onAnswer(itemValue.label);
} else if (itemValue.type === "other") {
if (customOptionText.trim()) {
onAnswer(
expandPastePlaceholders(
customOptionText,
customBuffer.pastedContent
).trim()
);
}
}
}
},
[
question.multiSelect,
questionOptions.length,
selectedIndices,
isCustomOptionSelected,
customOptionText,
customBuffer.pastedContent,
onAnswer,
buildAnswerString
]
);
(0, import_react35.useEffect)(() => {
if (customOptionText.trim() && !isCustomOptionSelected) {
dispatch({
type: "SET_CUSTOM_SELECTED",
payload: { selected: true, multiSelect: !!question.multiSelect }
});
}
}, [customOptionText, isCustomOptionSelected, question.multiSelect]);
const HEADER_HEIGHT2 = progressHeader ? 2 : 0;
const TITLE_MARGIN = 1;
const FOOTER_HEIGHT2 = 2;
const overhead = HEADER_HEIGHT2 + TITLE_MARGIN + FOOTER_HEIGHT2;
const listHeight = availableHeight ? Math.max(1, availableHeight - overhead) : void 0;
const reservedListHeight = Math.min(selectionItems.length * 2, 6);
const questionHeightLimit = listHeight && !isAlternateBuffer ? question.unconstrainedHeight ? Math.max(1, listHeight - selectionItems.length * 2) : Math.max(1, listHeight - Math.max(DIALOG_PADDING, reservedListHeight)) : void 0;
const maxItemsToShow = listHeight && (!isAlternateBuffer || availableHeight !== void 0) ? Math.min(
selectionItems.length,
Math.max(
1,
Math.floor((listHeight - (questionHeightLimit ?? 0)) / 2)
)
) : selectionItems.length;
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "column", children: [
progressHeader,
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { marginBottom: TITLE_MARGIN, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
MaxSizedBox,
{
maxHeight: questionHeightLimit,
maxWidth: availableWidth,
overflowDirection: "bottom",
children: /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
MarkdownDisplay,
{
text: autoBoldIfPlain(question.question),
terminalWidth: availableWidth - DIALOG_PADDING,
isPending: false
}
),
question.multiSelect && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.text.secondary, italic: true, children: "(Select all that apply)" })
] })
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
BaseSelectionList,
{
items: selectionItems,
onSelect: handleSelect,
onHighlight: handleHighlight,
focusKey: isCustomOptionFocused ? "other" : void 0,
maxItemsToShow,
showScrollArrows: true,
renderItem: (item, context) => {
const optionItem = item.value;
const isChecked = optionItem.type === "option" && selectedIndices.has(optionItem.index) || optionItem.type === "other" && isCustomOptionSelected || optionItem.type === "all" && selectedIndices.size === questionOptions.length;
const showCheck = question.multiSelect && (optionItem.type === "option" || optionItem.type === "other" || optionItem.type === "all");
if (optionItem.type === "other") {
const placeholder = question.placeholder || "Enter a custom value";
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "row", children: [
showCheck && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
ClickableCheckbox,
{
isChecked,
onClick: () => {
if (!context.isSelected) {
handleSelect(optionItem);
}
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.text.primary, children: " " }),
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
TextInput,
{
buffer: customBuffer,
placeholder,
focus: context.isSelected,
onSubmit: (val) => {
if (question.multiSelect) {
const fullAnswer = buildAnswerString(
selectedIndices,
true,
val
);
if (fullAnswer) {
onAnswer(fullAnswer);
}
} else if (val.trim()) {
onAnswer(val.trim());
}
}
}
),
isChecked && !question.multiSelect && !context.isSelected && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.status.success, children: " \u2713" })
] });
}
const labelColor = isChecked && !question.multiSelect ? theme.status.success : context.isSelected ? context.titleColor : theme.text.primary;
return /* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Box_default, { flexDirection: "row", children: [
showCheck && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
ClickableCheckbox,
{
isChecked,
onClick: () => {
if (!context.isSelected) {
handleSelect(optionItem);
}
}
}
),
/* @__PURE__ */ (0, import_jsx_runtime63.jsxs)(Text, { color: labelColor, bold: optionItem.type === "done", children: [
" ",
optionItem.label
] }),
isChecked && !question.multiSelect && /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.status.success, children: " \u2713" })
] }),
optionItem.description && // Padding aligns with option label: 4 for multi-select (checkbox + space), 1 for single-select
/* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { paddingLeft: showCheck ? 4 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Text, { color: theme.text.secondary, wrap: "wrap", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
RenderInline,
{
text: optionItem.description,
defaultColor: theme.text.secondary
}
) }) })
] });
}
}
),
keyboardHints
] });
};
var AskUserDialog = ({
questions,
onSubmit,
onCancel,
onActiveTextInputChange,
width,
availableHeight: availableHeightProp,
extraParts
}) => {
const keyMatchers = useKeyMatchers();
const uiState = (0, import_react35.useContext)(UIStateContext);
const availableHeight = availableHeightProp ?? (uiState?.constrainHeight !== false ? uiState?.availableTerminalHeight : void 0);
const [state, dispatch] = (0, import_react35.useReducer)(askUserDialogReducerLogic, initialState);
const { answers, isEditingCustomOption, submitted } = state;
const reviewTabIndex = questions.length;
const tabCount = questions.length > 1 ? questions.length + 1 : questions.length;
const { currentIndex, goToNextTab, goToPrevTab } = useTabbedNavigation({
tabCount,
isActive: !submitted && questions.length > 1,
enableArrowNavigation: false,
// We'll handle arrows via textBuffer callbacks or manually
enableTabKey: false
// We'll handle tab manually to match existing behavior
});
const currentQuestionIndex = currentIndex;
const handleEditingCustomOption = (0, import_react35.useCallback)((isEditing) => {
dispatch({ type: "SET_EDITING_CUSTOM", payload: { isEditing } });
}, []);
(0, import_react35.useEffect)(() => {
onActiveTextInputChange?.(isEditingCustomOption);
return () => {
onActiveTextInputChange?.(false);
};
}, [isEditingCustomOption, onActiveTextInputChange]);
const handleCancel = (0, import_react35.useCallback)(
(key) => {
if (submitted) return false;
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
onCancel();
return true;
} else if (keyMatchers["basic.quit" /* QUIT */](key)) {
if (!isEditingCustomOption) {
onCancel();
}
return false;
}
return false;
},
[onCancel, submitted, isEditingCustomOption, keyMatchers]
);
useKeypress(handleCancel, {
isActive: !submitted
});
const isOnReviewTab = currentQuestionIndex === reviewTabIndex;
const handleNavigation = (0, import_react35.useCallback)(
(key) => {
if (submitted || questions.length <= 1) return false;
const isNextKey = keyMatchers["nav.dialog.next" /* DIALOG_NEXT */](key);
const isPrevKey = keyMatchers["nav.dialog.previous" /* DIALOG_PREV */](key);
const isRight = keyMatchers["cursor.right" /* MOVE_RIGHT */](key);
const isLeft = keyMatchers["cursor.left" /* MOVE_LEFT */](key);
const shouldGoNext = isNextKey || isRight;
const shouldGoPrev = isPrevKey || isLeft;
if (shouldGoNext) {
goToNextTab();
return true;
} else if (shouldGoPrev) {
goToPrevTab();
return true;
}
return false;
},
[questions.length, submitted, goToNextTab, goToPrevTab, keyMatchers]
);
useKeypress(handleNavigation, {
isActive: questions.length > 1 && !submitted
});
(0, import_react35.useEffect)(() => {
if (submitted) {
onSubmit(answers);
}
}, [submitted, answers, onSubmit]);
const handleAnswer = (0, import_react35.useCallback)(
(answer) => {
if (submitted) return;
if (questions.length > 1) {
dispatch({
type: "SET_ANSWER",
payload: {
index: currentQuestionIndex,
answer
}
});
goToNextTab();
} else {
dispatch({
type: "SET_ANSWER",
payload: {
index: currentQuestionIndex,
answer,
submit: true
}
});
}
},
[currentQuestionIndex, questions, submitted, goToNextTab]
);
const handleReviewSubmit = (0, import_react35.useCallback)(() => {
if (submitted) return;
dispatch({ type: "SUBMIT" });
}, [submitted]);
const handleSelectionChange = (0, import_react35.useCallback)(
(answer) => {
if (submitted) return;
dispatch({
type: "SET_ANSWER",
payload: {
index: currentQuestionIndex,
answer
}
});
},
[submitted, currentQuestionIndex]
);
const answeredIndices = (0, import_react35.useMemo)(
() => new Set(Object.keys(answers).map(Number)),
[answers]
);
const currentQuestion = questions[currentQuestionIndex];
const effectiveQuestion = (0, import_react35.useMemo)(() => {
if (currentQuestion?.type === "yesno") {
return {
...currentQuestion,
options: [
{ label: "Yes", description: "" },
{ label: "No", description: "" }
],
multiSelect: false
};
}
return currentQuestion;
}, [currentQuestion]);
const tabs = (0, import_react35.useMemo)(() => {
const questionTabs = questions.map((q, i) => ({
key: String(i),
header: q.header
}));
if (questions.length > 1) {
questionTabs.push({
key: "review",
header: "Review",
isSpecial: true
});
}
return questionTabs;
}, [questions]);
const progressHeader = questions.length > 1 ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
TabHeader,
{
tabs,
currentIndex: currentQuestionIndex,
completedIndices: answeredIndices
}
) : null;
if (isOnReviewTab) {
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(Box_default, { "aria-label": "Review your answers", children: /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
ReviewView,
{
questions,
answers,
onSubmit: handleReviewSubmit,
progressHeader,
extraParts
}
) });
}
if (!currentQuestion) return null;
const keyboardHints = /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
DialogFooter,
{
primaryAction: currentQuestion.type === "text" || isEditingCustomOption ? "Enter to submit" : "Enter to select",
navigationActions: questions.length > 1 ? currentQuestion.type === "text" || isEditingCustomOption ? `${formatCommand("nav.dialog.next" /* DIALOG_NEXT */)}/${formatCommand("nav.dialog.previous" /* DIALOG_PREV */)} to switch questions` : "\u2190/\u2192 to switch questions" : currentQuestion.type === "text" || isEditingCustomOption ? void 0 : "\u2191/\u2193 to navigate",
extraParts
}
);
const questionView = currentQuestion.type === "text" ? /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
TextQuestionView,
{
question: currentQuestion,
onAnswer: handleAnswer,
onSelectionChange: handleSelectionChange,
onEditingCustomOption: handleEditingCustomOption,
availableWidth: width,
availableHeight,
initialAnswer: answers[currentQuestionIndex],
progressHeader,
keyboardHints
},
currentQuestionIndex
) : /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
ChoiceQuestionView,
{
question: effectiveQuestion,
onAnswer: handleAnswer,
onSelectionChange: handleSelectionChange,
onEditingCustomOption: handleEditingCustomOption,
availableWidth: width,
availableHeight,
initialAnswer: answers[currentQuestionIndex],
progressHeader,
keyboardHints
},
currentQuestionIndex
);
return /* @__PURE__ */ (0, import_jsx_runtime63.jsx)(
Box_default,
{
flexDirection: "column",
width,
"aria-label": `Question ${currentQuestionIndex + 1} of ${questions.length}: ${currentQuestion.question}`,
children: questionView
}
);
};
// packages/cli/src/ui/components/ExitPlanModeDialog.tsx
var import_react36 = __toESM(require_react(), 1);
var import_jsx_runtime64 = __toESM(require_jsx_runtime(), 1);
var StatusMessage = ({ children }) => /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(Box_default, { paddingX: 1, children });
function usePlanContent(planPath, config) {
const [version, setVersion] = (0, import_react36.useState)(0);
const [state, setState] = (0, import_react36.useState)({
status: "loading" /* Loading */
});
const refresh = (0, import_react36.useCallback)(() => {
setVersion((v) => v + 1);
}, []);
(0, import_react36.useEffect)(() => {
let ignore = false;
setState({ status: "loading" /* Loading */ });
const load = async () => {
try {
const pathError = await validatePlanPath(
planPath,
config.storage.getPlansDir(),
config.getProjectRoot()
);
if (ignore) return;
if (pathError) {
setState({ status: "error" /* Error */, error: pathError });
return;
}
const contentError = await validatePlanContent(planPath);
if (ignore) return;
if (contentError) {
setState({ status: "error" /* Error */, error: contentError });
return;
}
const result = await processSingleFileContent(
planPath,
config.storage.getPlansDir(),
config.getFileSystemService()
);
if (ignore) return;
if (result.error) {
setState({ status: "error" /* Error */, error: result.error });
return;
}
if (typeof result.llmContent !== "string") {
setState({
status: "error" /* Error */,
error: "Plan file format not supported (binary or image)."
});
return;
}
const content = result.llmContent;
if (!content) {
setState({ status: "error" /* Error */, error: "Plan file is empty." });
return;
}
setState({ status: "loaded" /* Loaded */, content });
} catch (err) {
if (ignore) return;
const errorMessage = err instanceof Error ? err.message : String(err);
setState({ status: "error" /* Error */, error: errorMessage });
}
};
void load();
return () => {
ignore = true;
};
}, [planPath, config, version]);
return { ...state, refresh };
}
var ExitPlanModeDialog = ({
planPath,
onApprove,
onFeedback,
onCancel,
getPreferredEditor,
width,
availableHeight
}) => {
const keyMatchers = useKeyMatchers();
const config = useConfig();
const { stdin, setRawMode } = use_stdin_default();
const planState = usePlanContent(planPath, config);
const { refresh } = planState;
const [showLoading, setShowLoading] = (0, import_react36.useState)(false);
const handleOpenEditor = (0, import_react36.useCallback)(async () => {
try {
await openFileInEditor(planPath, stdin, setRawMode, getPreferredEditor());
onFeedback(
"I have edited the plan or annotated it with feedback. Review the edited plan, update if necessary, and present it again for approval."
);
refresh();
} catch (err) {
debugLogger.error("Failed to open plan in editor:", err);
}
}, [planPath, stdin, setRawMode, getPreferredEditor, refresh, onFeedback]);
useKeypress(
(key) => {
if (keyMatchers["input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */](key)) {
void handleOpenEditor();
return true;
}
if (keyMatchers["input.deprecatedOpenExternalEditor" /* DEPRECATED_OPEN_EXTERNAL_EDITOR */](key)) {
const cmdKey = formatCommand("input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */);
appEvents.emit("transient-message" /* TransientMessage */, {
message: `Use ${cmdKey} to open the external editor.`,
type: "hint" /* Hint */
});
return true;
}
return false;
},
{ isActive: true, priority: true }
);
(0, import_react36.useEffect)(() => {
if (planState.status !== "loading" /* Loading */) {
setShowLoading(false);
return;
}
const timer = setTimeout(() => {
setShowLoading(true);
}, 200);
return () => clearTimeout(timer);
}, [planState.status]);
if (planState.status === "loading" /* Loading */) {
if (!showLoading) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(StatusMessage, { children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(Text, { color: theme.text.secondary, italic: true, children: "Loading plan..." }) });
}
if (planState.status === "error" /* Error */) {
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(StatusMessage, { children: /* @__PURE__ */ (0, import_jsx_runtime64.jsxs)(Text, { color: theme.status.error, children: [
"Error reading plan: ",
planState.error
] }) });
}
const planContent = planState.content?.trim();
if (!planContent) {
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(StatusMessage, { children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(Text, { color: theme.status.error, children: "Error: Plan content is empty." }) });
}
const editHint = formatCommand("input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */);
return /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(Box_default, { flexDirection: "column", width, children: /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
AskUserDialog,
{
questions: [
{
type: QuestionType.CHOICE,
header: "Approval",
question: planContent,
options: [
{
label: "Yes, automatically accept edits" /* Auto */,
description: "Approves plan and allows tools to run automatically"
},
{
label: "Yes, manually accept edits" /* Manual */,
description: "Approves plan but requires confirmation for each tool"
}
],
placeholder: "Type your feedback...",
multiSelect: false,
unconstrainedHeight: false
}
],
onSubmit: (answers) => {
const answer = answers["0"];
if (answer === "Yes, automatically accept edits" /* Auto */) {
onApprove(ApprovalMode.AUTO_EDIT);
} else if (answer === "Yes, manually accept edits" /* Manual */) {
onApprove(ApprovalMode.DEFAULT);
} else if (answer) {
onFeedback(answer);
}
},
onCancel,
width,
availableHeight,
extraParts: [`${editHint} to edit plan`]
}
) });
};
// packages/cli/src/ui/utils/urlSecurityUtils.ts
import url from "node:url";
function containsDeceptiveMarkers(hostname) {
return (
// eslint-disable-next-line no-control-regex
hostname.toLowerCase().includes("xn--") || /[^\x00-\x7F]/.test(hostname)
);
}
function toUnicodeUrl(urlInput) {
try {
const urlObj = typeof urlInput === "string" ? new URL(urlInput) : urlInput;
const punycodeHost = urlObj.hostname;
const unicodeHost = url.domainToUnicode(punycodeHost);
const protocol = urlObj.protocol + "//";
const credentials = urlObj.username ? `${urlObj.username}${urlObj.password ? ":" + urlObj.password : ""}@` : "";
const port = urlObj.port ? ":" + urlObj.port : "";
return `${protocol}${credentials}${unicodeHost}${port}${urlObj.pathname}${urlObj.search}${urlObj.hash}`;
} catch {
return typeof urlInput === "string" ? urlInput : urlInput.href;
}
}
function getDeceptiveUrlDetails(urlString) {
try {
if (!urlString.includes("://")) {
return null;
}
const urlObj = new URL(urlString);
if (!containsDeceptiveMarkers(urlObj.hostname)) {
return null;
}
return {
originalUrl: toUnicodeUrl(urlObj),
punycodeUrl: urlObj.href
};
} catch {
return null;
}
}
// packages/cli/src/ui/components/messages/ToolConfirmationMessage.tsx
var import_jsx_runtime65 = __toESM(require_jsx_runtime(), 1);
var ToolConfirmationMessage = ({
callId,
confirmationDetails,
config,
getPreferredEditor,
isFocused = true,
availableTerminalHeight,
terminalWidth,
toolName
}) => {
const keyMatchers = useKeyMatchers();
const { confirm, isDiffingEnabled } = useToolActions();
const [mcpDetailsExpansionState, setMcpDetailsExpansionState] = (0, import_react37.useState)({
callId,
expanded: false
});
const [isCancelling, setIsCancelling] = (0, import_react37.useState)(false);
const isMcpToolDetailsExpanded = mcpDetailsExpansionState.callId === callId ? mcpDetailsExpansionState.expanded : false;
const [measuredSecurityWarningsHeight, setMeasuredSecurityWarningsHeight] = (0, import_react37.useState)(0);
const observerRef = (0, import_react37.useRef)(null);
(0, import_react37.useEffect)(
() => () => {
observerRef.current?.disconnect();
},
[]
);
const deceptiveUrlWarnings = (0, import_react37.useMemo)(() => {
const urls = [];
if (confirmationDetails.type === "info" && confirmationDetails.urls) {
urls.push(...confirmationDetails.urls);
} else if (confirmationDetails.type === "exec") {
const commands = confirmationDetails.commands && confirmationDetails.commands.length > 0 ? confirmationDetails.commands : [confirmationDetails.command];
for (const cmd of commands) {
const matches = cmd.match(/https?:\/\/[^\s"'`<>;&|()]+/g);
if (matches) urls.push(...matches);
}
}
const uniqueUrls = Array.from(new Set(urls));
return uniqueUrls.map(getDeceptiveUrlDetails).filter((d) => d !== null);
}, [confirmationDetails]);
const deceptiveUrlWarningText = (0, import_react37.useMemo)(() => {
if (deceptiveUrlWarnings.length === 0) return null;
return `**Warning:** Deceptive URL(s) detected:
${deceptiveUrlWarnings.map(
(w) => ` **Original:** ${w.originalUrl}
**Actual Host (Punycode):** ${w.punycodeUrl}`
).join("\n\n")}`;
}, [deceptiveUrlWarnings]);
const onSecurityWarningsRefChange = (0, import_react37.useCallback)((node) => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
}
if (node) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const newHeight = Math.round(entry.contentRect.height);
setMeasuredSecurityWarningsHeight(
(prev) => newHeight !== prev ? newHeight : prev
);
}
});
observer.observe(node);
observerRef.current = observer;
} else {
setMeasuredSecurityWarningsHeight((prev) => prev !== 0 ? 0 : prev);
}
}, []);
const settings = useSettings();
const activeTheme = themeManager.getActiveTheme();
const allowPermanentApproval = settings.merged.security.enablePermanentToolApproval && !config.getDisableAlwaysAllow();
const handlesOwnUI = confirmationDetails.type === "ask_user" || confirmationDetails.type === "exit_plan_mode";
const isTrustedFolder = config.isTrustedFolder() && !config.getDisableAlwaysAllow();
const handleConfirm = (0, import_react37.useCallback)(
(outcome, payload) => {
void confirm(callId, outcome, payload).catch((error) => {
debugLogger.error(
`Failed to handle tool confirmation for ${callId}:`,
error
);
});
},
[confirm, callId]
);
const mcpToolDetailsText = (0, import_react37.useMemo)(() => {
if (confirmationDetails.type !== "mcp") {
return null;
}
const detailsLines = [];
const hasNonEmptyToolArgs = confirmationDetails.toolArgs !== void 0 && !(typeof confirmationDetails.toolArgs === "object" && confirmationDetails.toolArgs !== null && Object.keys(confirmationDetails.toolArgs).length === 0);
if (hasNonEmptyToolArgs) {
let argsText;
try {
argsText = stripUnsafeCharacters(
JSON.stringify(confirmationDetails.toolArgs, null, 2)
);
} catch {
argsText = "[unserializable arguments]";
}
detailsLines.push("Invocation Arguments:");
detailsLines.push(argsText);
}
const description = confirmationDetails.toolDescription?.trim();
if (description) {
if (detailsLines.length > 0) {
detailsLines.push("");
}
detailsLines.push("Description:");
detailsLines.push(stripUnsafeCharacters(description));
}
if (confirmationDetails.toolParameterSchema !== void 0) {
let schemaText;
try {
schemaText = stripUnsafeCharacters(
JSON.stringify(confirmationDetails.toolParameterSchema, null, 2)
);
} catch {
schemaText = "[unserializable schema]";
}
if (detailsLines.length > 0) {
detailsLines.push("");
}
detailsLines.push("Input Schema:");
detailsLines.push(schemaText);
}
if (detailsLines.length === 0) {
return null;
}
return detailsLines.join("\n");
}, [confirmationDetails]);
const hasMcpToolDetails = !!mcpToolDetailsText;
const expandDetailsHintKey = formatCommand("app.showMoreLines" /* SHOW_MORE_LINES */);
useKeypress(
(key) => {
if (!isFocused) return false;
if (confirmationDetails.type === "mcp" && hasMcpToolDetails && keyMatchers["app.showMoreLines" /* SHOW_MORE_LINES */](key)) {
setMcpDetailsExpansionState({
callId,
expanded: !isMcpToolDetailsExpanded
});
return true;
}
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
setIsCancelling(true);
return true;
}
if (keyMatchers["basic.quit" /* QUIT */](key)) {
return false;
}
return false;
},
{ isActive: isFocused, priority: true }
);
(0, import_react37.useEffect)(() => {
if (isCancelling) {
handleConfirm(ToolConfirmationOutcome.Cancel);
}
}, [isCancelling, handleConfirm]);
const handleSelect = (0, import_react37.useCallback)(
(item) => handleConfirm(item),
[handleConfirm]
);
const getOptions = (0, import_react37.useCallback)(() => {
const options2 = [];
if (confirmationDetails.type === "edit") {
if (!confirmationDetails.isModifying) {
options2.push({
label: "Allow once",
value: ToolConfirmationOutcome.ProceedOnce,
key: "Allow once"
});
if (isTrustedFolder) {
options2.push({
label: "Allow for this session",
value: ToolConfirmationOutcome.ProceedAlways,
key: "Allow for this session"
});
if (allowPermanentApproval) {
options2.push({
label: "Allow for this file in all future sessions",
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: "Allow for this file in all future sessions"
});
}
}
if (!config.getIdeMode() || !isDiffingEnabled) {
options2.push({
label: "Modify with external editor",
value: ToolConfirmationOutcome.ModifyWithEditor,
key: "Modify with external editor"
});
}
options2.push({
label: "No, suggest changes (esc)",
value: ToolConfirmationOutcome.Cancel,
key: "No, suggest changes (esc)"
});
}
} else if (confirmationDetails.type === "sandbox_expansion") {
options2.push({
label: "Allow once",
value: ToolConfirmationOutcome.ProceedOnce,
key: "Allow once"
});
if (isTrustedFolder) {
options2.push({
label: "Allow for this session",
value: ToolConfirmationOutcome.ProceedAlways,
key: "Allow for this session"
});
if (allowPermanentApproval) {
options2.push({
label: "Allow for all future sessions",
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: "Allow for all future sessions"
});
}
}
options2.push({
label: "No, suggest changes (esc)",
value: ToolConfirmationOutcome.Cancel,
key: "No, suggest changes (esc)"
});
} else if (confirmationDetails.type === "exec") {
options2.push({
label: "Allow once",
value: ToolConfirmationOutcome.ProceedOnce,
key: "Allow once"
});
if (isTrustedFolder) {
options2.push({
label: `Allow for this session`,
value: ToolConfirmationOutcome.ProceedAlways,
key: `Allow for this session`
});
if (allowPermanentApproval) {
options2.push({
label: `Allow this command for all future sessions`,
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: `Allow for all future sessions`
});
}
}
options2.push({
label: "No, suggest changes (esc)",
value: ToolConfirmationOutcome.Cancel,
key: "No, suggest changes (esc)"
});
} else if (confirmationDetails.type === "info") {
options2.push({
label: "Allow once",
value: ToolConfirmationOutcome.ProceedOnce,
key: "Allow once"
});
if (isTrustedFolder) {
options2.push({
label: "Allow for this session",
value: ToolConfirmationOutcome.ProceedAlways,
key: "Allow for this session"
});
if (allowPermanentApproval) {
options2.push({
label: "Allow for all future sessions",
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: "Allow for all future sessions"
});
}
}
options2.push({
label: "No, suggest changes (esc)",
value: ToolConfirmationOutcome.Cancel,
key: "No, suggest changes (esc)"
});
} else if (confirmationDetails.type === "mcp") {
options2.push({
label: "Allow once",
value: ToolConfirmationOutcome.ProceedOnce,
key: "Allow once"
});
if (isTrustedFolder) {
options2.push({
label: "Allow tool for this session",
value: ToolConfirmationOutcome.ProceedAlwaysTool,
key: "Allow tool for this session"
});
options2.push({
label: "Allow all server tools for this session",
value: ToolConfirmationOutcome.ProceedAlwaysServer,
key: "Allow all server tools for this session"
});
if (allowPermanentApproval) {
options2.push({
label: "Allow tool for all future sessions",
value: ToolConfirmationOutcome.ProceedAlwaysAndSave,
key: "Allow tool for all future sessions"
});
}
}
options2.push({
label: "No, suggest changes (esc)",
value: ToolConfirmationOutcome.Cancel,
key: "No, suggest changes (esc)"
});
}
return options2;
}, [
confirmationDetails,
isTrustedFolder,
allowPermanentApproval,
config,
isDiffingEnabled
]);
const availableBodyContentHeight = (0, import_react37.useCallback)(() => {
if (availableTerminalHeight === void 0) {
return void 0;
}
if (handlesOwnUI) {
return availableTerminalHeight;
}
const PADDING_OUTER_Y = 0;
const HEIGHT_QUESTION = 1;
const MARGIN_QUESTION_TOP = 0;
const MARGIN_QUESTION_BOTTOM = 1;
const SECURITY_WARNING_BOTTOM_MARGIN = 1;
const SHOW_MORE_LINES_HEIGHT = 1;
const optionsCount = getOptions().length;
const securityWarningsHeight = deceptiveUrlWarningText ? measuredSecurityWarningsHeight + SECURITY_WARNING_BOTTOM_MARGIN : 0;
let extraInfoLines = 0;
if (confirmationDetails.type === "sandbox_expansion") {
const { additionalPermissions } = confirmationDetails;
if (additionalPermissions?.network) extraInfoLines++;
extraInfoLines += additionalPermissions?.fileSystem?.read?.length || 0;
extraInfoLines += additionalPermissions?.fileSystem?.write?.length || 0;
} else if (confirmationDetails.type === "exec") {
const executionProps = confirmationDetails;
const commandsToDisplay = executionProps.commands && executionProps.commands.length > 0 ? executionProps.commands : [executionProps.command];
const containsRedirection = commandsToDisplay.some(
(cmd) => hasRedirection(cmd)
);
const isAutoEdit = config.getApprovalMode() === ApprovalMode.YOLO || config.getApprovalMode() === ApprovalMode.AUTO_EDIT;
if (containsRedirection && !isAutoEdit) {
extraInfoLines = 1;
}
}
const surroundingElementsHeight = PADDING_OUTER_Y + HEIGHT_QUESTION + MARGIN_QUESTION_TOP + MARGIN_QUESTION_BOTTOM + SHOW_MORE_LINES_HEIGHT + optionsCount + securityWarningsHeight + extraInfoLines;
return Math.max(availableTerminalHeight - surroundingElementsHeight, 2);
}, [
availableTerminalHeight,
handlesOwnUI,
getOptions,
measuredSecurityWarningsHeight,
deceptiveUrlWarningText,
confirmationDetails,
config
]);
const { question, bodyContent, options, securityWarnings, initialIndex } = (0, import_react37.useMemo)(() => {
let bodyContent2 = null;
let securityWarnings2 = null;
let question2 = "";
const options2 = getOptions();
let initialIndex2 = 0;
if (isTrustedFolder && allowPermanentApproval) {
const isSafeToPersist = confirmationDetails.type === "info" || confirmationDetails.type === "edit" || confirmationDetails.type === "mcp";
if (isSafeToPersist && settings.merged.security.autoAddToPolicyByDefault) {
const alwaysAndSaveIndex = options2.findIndex(
(o) => o.value === ToolConfirmationOutcome.ProceedAlwaysAndSave
);
if (alwaysAndSaveIndex !== -1) {
initialIndex2 = alwaysAndSaveIndex;
}
}
}
if (deceptiveUrlWarningText) {
securityWarnings2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(WarningMessage, { text: deceptiveUrlWarningText });
}
const bodyHeight = availableBodyContentHeight();
if (confirmationDetails.type === "ask_user") {
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
AskUserDialog,
{
questions: confirmationDetails.questions,
onSubmit: (answers) => {
handleConfirm(ToolConfirmationOutcome.ProceedOnce, { answers });
},
onCancel: () => {
handleConfirm(ToolConfirmationOutcome.Cancel);
},
width: terminalWidth,
availableHeight: bodyHeight
}
);
return {
question: "",
bodyContent: bodyContent2,
options: [],
securityWarnings: null,
initialIndex: 0
};
}
if (confirmationDetails.type === "exit_plan_mode") {
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
ExitPlanModeDialog,
{
planPath: confirmationDetails.planPath,
getPreferredEditor,
onApprove: (approvalMode) => {
handleConfirm(ToolConfirmationOutcome.ProceedOnce, {
approved: true,
approvalMode
});
},
onFeedback: (feedback) => {
handleConfirm(ToolConfirmationOutcome.ProceedOnce, {
approved: false,
feedback
});
},
onCancel: () => {
handleConfirm(ToolConfirmationOutcome.Cancel);
},
width: terminalWidth,
availableHeight: bodyHeight
}
);
return {
question: "",
bodyContent: bodyContent2,
options: [],
securityWarnings: null,
initialIndex: 0
};
}
if (confirmationDetails.type === "edit") {
if (!confirmationDetails.isModifying) {
question2 = `Apply this change?`;
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(import_jsx_runtime65.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingX: 1,
paddingY: 0,
marginBottom: 0,
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
DiffRenderer,
{
diffContent: stripUnsafeCharacters(
confirmationDetails.fileDiff
),
filename: sanitizeForDisplay(confirmationDetails.fileName),
availableTerminalHeight: bodyHeight !== void 0 ? Math.max(bodyHeight - 2, 2) : void 0,
terminalWidth: Math.max(terminalWidth, 1) - 4
}
)
}
) });
}
} else if (confirmationDetails.type === "sandbox_expansion") {
const { additionalPermissions, command } = confirmationDetails;
const readPaths = additionalPermissions?.fileSystem?.read || [];
const writePaths = additionalPermissions?.fileSystem?.write || [];
const network = additionalPermissions?.network;
const isShell = isShellTool(toolName);
const commandNames = isShell ? "Shell" : toolName;
question2 = "";
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_jsx_runtime65.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingX: 1,
paddingY: 0,
marginBottom: 0,
children: colorizeCode({
code: command.trim(),
language: "bash",
maxWidth: Math.max(terminalWidth, 1) - 6,
settings,
theme: activeTheme,
hideLineNumbers: true,
availableHeight: bodyHeight !== void 0 ? Math.max(bodyHeight - 2, 2) : void 0
})
}
),
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
"To run",
" ",
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
Text,
{
color: isShell ? theme.status.warning : void 0,
bold: isShell,
children: [
"[",
sanitizeForDisplay(commandNames),
"]"
]
}
),
", allow access to the following?"
] }),
network && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: isShell ? theme.status.warning : void 0, bold: true, children: "\u2022 Network:" }),
" ",
"All Urls"
] }),
writePaths.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: isShell ? theme.status.warning : void 0, bold: true, children: "\u2022 Write:" }),
" ",
writePaths.map((p) => sanitizeForDisplay(p)).join(", ")
] }),
readPaths.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: isShell ? theme.status.warning : void 0, bold: true, children: "\u2022 Read:" }),
" ",
readPaths.map((p) => sanitizeForDisplay(p)).join(", ")
] })
] })
] });
} else if (confirmationDetails.type === "exec") {
const executionProps = confirmationDetails;
const isShell = isShellTool(toolName);
const commandsToDisplay = executionProps.commands && executionProps.commands.length > 1 ? executionProps.commands : [executionProps.command];
const containsRedirection = commandsToDisplay.some(
(cmd) => hasRedirection(cmd)
);
const isAutoEdit = config.getApprovalMode() === ApprovalMode.YOLO || config.getApprovalMode() === ApprovalMode.AUTO_EDIT;
let warnings = null;
if (containsRedirection && !isAutoEdit) {
const tipText = `To auto-accept, press ${formatCommand("app.cycleApprovalMode" /* CYCLE_APPROVAL_MODE */)}`;
warnings = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Box_default, { flexDirection: "column", marginBottom: 0, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: theme.text.primary, children: [
"Redirection detected.",
" ",
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.secondary, children: tipText })
] }) });
}
const commandNames = isShell ? "Shell" : toolName;
const allowQuestion = /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
"Allow execution of",
" ",
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
Text,
{
color: isShell ? theme.status.warning : void 0,
bold: isShell,
children: [
"[",
sanitizeForDisplay(commandNames),
"]"
]
}
),
"?"
] });
question2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", children: [
allowQuestion,
warnings
] });
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(import_jsx_runtime65.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingX: 1,
paddingY: 0,
marginBottom: 0,
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
MaxSizedBox,
{
maxHeight: bodyHeight !== void 0 ? Math.max(bodyHeight - 2, 2) : void 0,
maxWidth: Math.max(terminalWidth, 1) - 4,
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Box_default, { flexDirection: "column", children: commandsToDisplay.map((cmd, idx) => /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
flexDirection: "column",
paddingBottom: idx < commandsToDisplay.length - 1 ? 1 : 0,
children: colorizeCode({
code: cmd.trim(),
language: "bash",
maxWidth: Math.max(terminalWidth, 1) - 6,
settings,
theme: activeTheme,
hideLineNumbers: true,
availableHeight: bodyHeight !== void 0 ? Math.max(bodyHeight - 2, 2) : void 0
})
},
idx
)) })
}
)
}
) });
} else if (confirmationDetails.type === "info") {
question2 = `Do you want to proceed?`;
const infoProps = confirmationDetails;
const displayUrls = infoProps.urls && !(infoProps.urls.length === 1 && infoProps.urls[0] === infoProps.prompt);
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.link, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
RenderInline,
{
text: infoProps.prompt,
defaultColor: theme.text.link
}
) }),
displayUrls && infoProps.urls && infoProps.urls.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.primary, children: "URLs to fetch:" }),
infoProps.urls.map((urlString) => /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { children: [
" ",
"- ",
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(RenderInline, { text: toUnicodeUrl(urlString) })
] }, urlString))
] })
] });
} else if (confirmationDetails.type === "mcp") {
const mcpProps = confirmationDetails;
question2 = `Allow execution of MCP tool "${sanitizeForDisplay(mcpProps.toolName)}" from server "${sanitizeForDisplay(mcpProps.serverName)}"?`;
bodyContent2 = /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_jsx_runtime65.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: theme.text.link, children: [
"MCP Server: ",
sanitizeForDisplay(mcpProps.serverName)
] }),
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: theme.text.link, children: [
"Tool: ",
sanitizeForDisplay(mcpProps.toolName)
] })
] }),
hasMcpToolDetails && /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.primary, children: "MCP Tool Details:" }),
isMcpToolDetailsExpanded ? /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_jsx_runtime65.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: theme.text.secondary, children: [
"(press ",
expandDetailsHintKey,
" to collapse MCP tool details)"
] }),
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingX: 1,
paddingY: 0,
marginBottom: 0,
children: colorizeCode({
code: mcpToolDetailsText || "",
language: "json",
maxWidth: Math.max(terminalWidth, 1) - 4,
settings,
theme: activeTheme,
hideLineNumbers: true,
availableHeight: bodyHeight !== void 0 ? Math.max(bodyHeight - 2, 2) : void 0
})
}
)
] }) : /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: theme.text.secondary, children: [
"(press ",
expandDetailsHintKey,
" to expand MCP tool details)"
] })
] })
] });
}
return { question: question2, bodyContent: bodyContent2, options: options2, securityWarnings: securityWarnings2, initialIndex: initialIndex2 };
}, [
confirmationDetails,
getOptions,
availableBodyContentHeight,
terminalWidth,
handleConfirm,
deceptiveUrlWarningText,
isMcpToolDetailsExpanded,
hasMcpToolDetails,
mcpToolDetailsText,
expandDetailsHintKey,
getPreferredEditor,
isTrustedFolder,
allowPermanentApproval,
settings,
activeTheme,
config,
toolName
]);
const bodyOverflowDirection = confirmationDetails.type === "mcp" && isMcpToolDetailsExpanded ? "bottom" : "top";
const renderRadioItem = (0, import_react37.useCallback)(
(item, { titleColor }) => {
if (item.value === ToolConfirmationOutcome.ProceedAlwaysAndSave) {
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Text, { color: titleColor, wrap: "truncate", children: [
item.label,
" ",
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.secondary, children: "~/.gemini/policies/auto-saved.toml" })
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: titleColor, wrap: "truncate", children: item.label });
},
[]
);
if (confirmationDetails.type === "edit") {
if (confirmationDetails.isModifying) {
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(
Box_default,
{
width: terminalWidth,
borderStyle: "round",
borderColor: theme.border.default,
justifyContent: "space-around",
paddingTop: 1,
paddingBottom: 1,
overflow: "hidden",
children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.primary, children: "Modify in progress: " }),
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.status.success, children: "Save and close external editor to continue" })
]
}
);
}
}
return /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(Box_default, { flexDirection: "column", paddingTop: 0, paddingBottom: 0, children: [
!!confirmationDetails.systemMessage && /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.status.warning, children: confirmationDetails.systemMessage }) }),
handlesOwnUI ? bodyContent : /* @__PURE__ */ (0, import_jsx_runtime65.jsxs)(import_jsx_runtime65.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
flexShrink: 1,
overflow: "hidden",
marginBottom: !question && !securityWarnings ? 1 : 0,
children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
MaxSizedBox,
{
maxHeight: availableBodyContentHeight(),
maxWidth: terminalWidth,
overflowDirection: bodyOverflowDirection,
children: bodyContent
}
)
}
),
securityWarnings && /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
Box_default,
{
flexShrink: 0,
marginBottom: 1,
ref: onSecurityWarningsRefChange,
children: securityWarnings
}
),
!!question && /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Box_default, { marginBottom: 1, flexShrink: 0, children: typeof question === "string" ? /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Text, { color: theme.text.primary, children: question }) : question }),
/* @__PURE__ */ (0, import_jsx_runtime65.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime65.jsx)(
RadioButtonSelect,
{
items: options,
onSelect: handleSelect,
isFocused,
initialIndex,
renderItem: renderRadioItem
}
) })
] })
] });
};
// packages/cli/src/ui/components/ToolConfirmationQueue.tsx
var import_jsx_runtime66 = __toESM(require_jsx_runtime(), 1);
function getConfirmationHeader(details) {
const headers = {
ask_user: "Answer Questions",
exit_plan_mode: "Ready to start implementation?"
};
if (!details?.type) {
return "Action Required";
}
return headers[details.type] ?? "Action Required";
}
function getConfirmationLabel(toolName, details) {
if (details?.type === "ask_user") return "Questions";
if (details?.type === "exit_plan_mode") return "Implementation";
if (isShellTool(toolName)) return "Shell";
return toolName;
}
var ToolConfirmationQueue = ({
confirmingTool
}) => {
const config = useConfig();
const { getPreferredEditor } = useUIActions();
const {
mainAreaWidth,
terminalHeight,
constrainHeight,
availableTerminalHeight: uiAvailableHeight
} = useUIState();
const { tool, index, total } = confirmingTool;
if (!tool.confirmationDetails) return null;
const maxHeight = uiAvailableHeight !== void 0 ? Math.max(uiAvailableHeight, 4) : Math.floor(terminalHeight * 0.5);
const isShell = isShellTool(tool.name);
const isEdit = tool.confirmationDetails?.type === "edit";
if (isShell || isEdit) {
const borderColor2 = theme.border.default;
const availableContentHeight2 = constrainHeight ? Math.max(maxHeight - 3, 4) : void 0;
const toolLabel = getConfirmationLabel(tool.name, tool.confirmationDetails);
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
Box_default,
{
flexDirection: "column",
width: mainAreaWidth,
flexShrink: 0,
borderStyle: "round",
borderColor: borderColor2,
paddingX: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box_default, { justifyContent: "space-between", marginBottom: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box_default, { flexDirection: "row", flexShrink: 1, overflow: "hidden", children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Text, { color: theme.status.warning, bold: true, children: [
"? ",
toolLabel,
!!tool.description && " "
] }),
!!tool.description && /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(Box_default, { flexShrink: 1, overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(Text, { color: theme.text.primary, wrap: "truncate-end", children: tool.description }) })
] }),
total > 1 && /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Text, { color: theme.text.secondary, children: [
index,
" of ",
total
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
ToolConfirmationMessage,
{
callId: tool.callId,
confirmationDetails: tool.confirmationDetails,
config,
getPreferredEditor,
terminalWidth: mainAreaWidth - 4,
availableTerminalHeight: availableContentHeight2,
toolName: tool.name,
isFocused: true
}
) })
]
}
);
}
const isRoutine = tool.confirmationDetails?.type === "ask_user" || tool.confirmationDetails?.type === "exit_plan_mode";
const borderColor = isRoutine ? theme.status.success : theme.status.warning;
const hideToolIdentity = isRoutine;
const availableContentHeight = constrainHeight ? Math.max(maxHeight - (hideToolIdentity ? 4 : 6), 4) : void 0;
return /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box_default, { flexDirection: "column", width: mainAreaWidth, flexShrink: 0, children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
StickyHeader,
{
width: mainAreaWidth,
isFirst: true,
borderColor,
borderDimColor: false,
children: /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box_default, { flexDirection: "column", width: mainAreaWidth - 4, children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(
Box_default,
{
marginBottom: hideToolIdentity ? 0 : 1,
justifyContent: "space-between",
children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(Text, { color: borderColor, bold: true, children: getConfirmationHeader(tool.confirmationDetails) }),
total > 1 && /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Text, { color: theme.text.secondary, children: [
index,
" of ",
total
] })
]
}
),
!hideToolIdentity && /* @__PURE__ */ (0, import_jsx_runtime66.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(ToolStatusIndicator, { status: tool.status, name: tool.name }),
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
ToolInfo,
{
name: tool.name,
status: tool.status,
description: tool.description,
emphasis: "high"
}
)
] })
] })
}
),
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
Box_default,
{
width: mainAreaWidth,
borderStyle: "round",
borderColor,
borderTop: false,
borderBottom: false,
borderLeft: true,
borderRight: true,
paddingX: 1,
flexDirection: "column",
children: /* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
ToolConfirmationMessage,
{
callId: tool.callId,
confirmationDetails: tool.confirmationDetails,
config,
getPreferredEditor,
terminalWidth: mainAreaWidth - 4,
availableTerminalHeight: availableContentHeight,
toolName: tool.name,
isFocused: true
}
)
}
),
/* @__PURE__ */ (0, import_jsx_runtime66.jsx)(
Box_default,
{
height: 1,
width: mainAreaWidth,
borderLeft: true,
borderRight: true,
borderTop: false,
borderBottom: true,
borderColor,
borderStyle: "round"
}
)
] });
};
// packages/cli/src/ui/components/MainContent.tsx
var import_jsx_runtime67 = __toESM(require_jsx_runtime(), 1);
var MemoizedHistoryItemDisplay = (0, import_react38.memo)(HistoryItemDisplay);
var MemoizedAppHeader = (0, import_react38.memo)(AppHeader);
var MainContent = () => {
const { version } = useAppContext();
const uiState = useUIState();
const isAlternateBufferOrTerminalBuffer = useAlternateBuffer();
const config = useConfig();
const useTerminalBuffer = config.getUseTerminalBuffer();
const isAlternateBuffer = config.getUseAlternateBuffer();
const confirmingTool = useConfirmingTool();
const showConfirmationQueue = confirmingTool !== null;
const confirmingToolCallId = confirmingTool?.tool.callId;
const scrollableListRef = (0, import_react38.useRef)(null);
(0, import_react38.useEffect)(() => {
if (showConfirmationQueue) {
scrollableListRef.current?.scrollToEnd();
}
}, [showConfirmationQueue, confirmingToolCallId]);
(0, import_react38.useEffect)(() => {
const handleScroll = () => {
scrollableListRef.current?.scrollToEnd();
};
appEvents.on("scroll-to-bottom" /* ScrollToBottom */, handleScroll);
return () => {
appEvents.off("scroll-to-bottom" /* ScrollToBottom */, handleScroll);
};
}, []);
const {
pendingHistoryItems,
mainAreaWidth,
staticAreaMaxItemHeight,
availableTerminalHeight,
cleanUiDetailsVisible,
mouseMode
} = uiState;
const showHeaderDetails = cleanUiDetailsVisible;
const lastUserPromptIndex = (0, import_react38.useMemo)(() => {
for (let i = uiState.history.length - 1; i >= 0; i--) {
const type = uiState.history[i].type;
if (type === "user" || type === "user_shell") {
return i;
}
}
return -1;
}, [uiState.history]);
const augmentedHistory = (0, import_react38.useMemo)(
() => uiState.history.map((item, i) => {
const prevType = i > 0 ? uiState.history[i - 1]?.type : void 0;
const isFirstThinking = item.type === "thinking" && prevType !== "thinking";
const isFirstAfterThinking = item.type !== "thinking" && prevType === "thinking";
const isToolGroupBoundary = item.type !== "tool_group" && prevType === "tool_group" || item.type === "tool_group" && prevType !== "tool_group";
return {
item,
isExpandable: i > lastUserPromptIndex,
isFirstThinking,
isFirstAfterThinking,
isToolGroupBoundary
};
}),
[uiState.history, lastUserPromptIndex]
);
const historyItems = (0, import_react38.useMemo)(
() => augmentedHistory.map(
({
item,
isExpandable,
isFirstThinking,
isFirstAfterThinking,
isToolGroupBoundary
}) => /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
MemoizedHistoryItemDisplay,
{
terminalWidth: mainAreaWidth,
availableTerminalHeight: uiState.constrainHeight || !isExpandable ? staticAreaMaxItemHeight : void 0,
availableTerminalHeightGemini: MAX_GEMINI_MESSAGE_LINES,
item,
isPending: false,
commands: uiState.slashCommands,
isExpandable,
isFirstThinking,
isFirstAfterThinking,
isToolGroupBoundary
},
item.id
)
),
[
augmentedHistory,
mainAreaWidth,
staticAreaMaxItemHeight,
uiState.slashCommands,
uiState.constrainHeight
]
);
const staticHistoryItems = (0, import_react38.useMemo)(
() => historyItems.slice(0, lastUserPromptIndex + 1),
[historyItems, lastUserPromptIndex]
);
const lastResponseHistoryItems = (0, import_react38.useMemo)(
() => historyItems.slice(lastUserPromptIndex + 1),
[historyItems, lastUserPromptIndex]
);
const pendingItems = (0, import_react38.useMemo)(
() => /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(Box_default, { flexDirection: "column", children: [
pendingHistoryItems.map((item, i) => {
const prevType = i === 0 ? uiState.history.at(-1)?.type : pendingHistoryItems[i - 1]?.type;
const isFirstThinking = item.type === "thinking" && prevType !== "thinking";
const isFirstAfterThinking = item.type !== "thinking" && prevType === "thinking";
const isToolGroupBoundary = item.type !== "tool_group" && prevType === "tool_group" || item.type === "tool_group" && prevType !== "tool_group";
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
HistoryItemDisplay,
{
availableTerminalHeight: uiState.constrainHeight ? availableTerminalHeight : void 0,
terminalWidth: mainAreaWidth,
item: { ...item, id: -(i + 1) },
isPending: true,
isExpandable: true,
isFirstThinking,
isFirstAfterThinking,
isToolGroupBoundary
},
`pending-${i}`
);
}),
showConfirmationQueue && confirmingTool && /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
ToolConfirmationQueue,
{
confirmingTool
},
"confirmation-queue"
)
] }, "pending-items-group"),
[
pendingHistoryItems,
uiState.constrainHeight,
availableTerminalHeight,
mainAreaWidth,
showConfirmationQueue,
confirmingTool,
uiState.history
]
);
const virtualizedData = (0, import_react38.useMemo)(
() => [
{ type: "header" },
...augmentedHistory.map((data, index) => ({
type: "history",
item: data.item,
element: historyItems[index]
})),
{ type: "pending" }
],
[augmentedHistory, historyItems]
);
const renderItem = (0, import_react38.useCallback)(
({ item }) => {
if (item.type === "header") {
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
MemoizedAppHeader,
{
version,
showDetails: showHeaderDetails
},
"app-header"
);
} else if (item.type === "history") {
return item.element;
} else {
return pendingItems;
}
},
[showHeaderDetails, version, pendingItems]
);
const estimatedItemHeight = (0, import_react38.useCallback)(() => 100, []);
const keyExtractor = (0, import_react38.useCallback)(
(item, _index) => {
if (item.type === "header") return "header";
if (item.type === "history") return item.item.id.toString();
return "pending";
},
[]
);
const isStaticItem = (0, import_react38.useCallback)(
(item) => item.type === "header",
[]
);
const scrollableList = (0, import_react38.useMemo)(() => {
if (isAlternateBufferOrTerminalBuffer) {
return /* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
ScrollableListWithForwardRef,
{
ref: scrollableListRef,
hasFocus: !uiState.isEditorDialogOpen && !uiState.embeddedShellFocused,
width: uiState.terminalWidth,
data: virtualizedData,
renderItem,
estimatedItemHeight,
keyExtractor,
initialScrollIndex: SCROLL_TO_ITEM_END,
initialScrollOffsetInIndex: SCROLL_TO_ITEM_END,
renderStatic: useTerminalBuffer,
isStaticItem: useTerminalBuffer ? isStaticItem : void 0,
overflowToBackbuffer: useTerminalBuffer && !isAlternateBuffer,
scrollbar: mouseMode
}
);
}
return null;
}, [
isAlternateBufferOrTerminalBuffer,
uiState.isEditorDialogOpen,
uiState.embeddedShellFocused,
uiState.terminalWidth,
virtualizedData,
renderItem,
estimatedItemHeight,
keyExtractor,
useTerminalBuffer,
isStaticItem,
mouseMode,
isAlternateBuffer
]);
if (!uiState.isConfigInitialized) {
return null;
}
if (isAlternateBufferOrTerminalBuffer) {
return scrollableList;
}
return /* @__PURE__ */ (0, import_jsx_runtime67.jsxs)(import_jsx_runtime67.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)(
Static,
{
items: [
/* @__PURE__ */ (0, import_jsx_runtime67.jsx)(AppHeader, { version }, "app-header"),
...staticHistoryItems,
...lastResponseHistoryItems
],
children: (item) => item
},
uiState.historyRemountKey
),
pendingItems
] });
};
// packages/cli/src/ui/components/LoopDetectionConfirmation.tsx
var import_jsx_runtime68 = __toESM(require_jsx_runtime(), 1);
function LoopDetectionConfirmation({
onComplete
}) {
useKeypress(
(key) => {
if (key.name === "escape") {
onComplete({
userSelection: "keep"
});
return true;
}
return false;
},
{ isActive: true }
);
const OPTIONS = [
{
label: "Keep loop detection enabled (esc)",
value: {
userSelection: "keep"
},
key: "Keep loop detection enabled (esc)"
},
{
label: "Disable loop detection for this session",
value: {
userSelection: "disable"
},
key: "Disable loop detection for this session"
}
];
return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Box_default, { width: "100%", flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.status.warning,
flexGrow: 1,
marginLeft: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(Box_default, { paddingX: 1, paddingY: 0, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(Box_default, { minHeight: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Box_default, { minWidth: 3, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Text, { color: theme.status.warning, "aria-label": "Loop detected:", children: "?" }) }),
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(Text, { wrap: "truncate-end", children: [
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Text, { color: theme.text.primary, bold: true, children: "A potential loop was detected" }),
" "
] }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Text, { color: theme.text.secondary, children: "This can happen due to repetitive tool calls or other model behavior. Do you want to keep loop detection enabled or disable it for this session?" }),
/* @__PURE__ */ (0, import_jsx_runtime68.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(RadioButtonSelect, { items: OPTIONS, onSelect: onComplete }) })
] }) })
] })
}
) });
}
// packages/cli/src/ui/components/ConsentPrompt.tsx
var import_react39 = __toESM(require_react(), 1);
var import_jsx_runtime69 = __toESM(require_jsx_runtime(), 1);
var ConsentPrompt = (props) => {
const { prompt, onConfirm, terminalWidth } = props;
return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
paddingTop: 1,
paddingX: 2,
children: [
typeof prompt === "string" ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
MarkdownDisplay,
{
isPending: true,
text: prompt,
terminalWidth
}
) : prompt,
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
RadioButtonSelect,
{
items: [
{ label: "Yes", value: true, key: "Yes" },
{ label: "No", value: false, key: "No" }
],
onSelect: onConfirm
}
),
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
DialogFooter,
{
primaryAction: "Enter to select",
navigationActions: "\u2191/\u2193 to navigate"
}
)
] })
]
}
);
};
// packages/cli/src/ui/components/ThemeDialog.tsx
var import_react40 = __toESM(require_react(), 1);
// packages/cli/src/utils/dialogScopeUtils.ts
var SCOPE_LABELS = {
["User" /* User */]: "User Settings",
["Workspace" /* Workspace */]: "Workspace Settings",
["System" /* System */]: "System Settings"
};
function getScopeItems() {
return [
{ label: SCOPE_LABELS["User" /* User */], value: "User" /* User */ },
{
label: SCOPE_LABELS["Workspace" /* Workspace */],
value: "Workspace" /* Workspace */
},
{ label: SCOPE_LABELS["System" /* System */], value: "System" /* System */ }
];
}
function getScopeMessageForSetting(settingKey, selectedScope, settings) {
const otherScopes = Object.values(SettingScope).filter(isLoadableSettingScope).filter((scope) => scope !== selectedScope);
const modifiedInOtherScopes = otherScopes.filter((scope) => {
const scopeSettings = settings.forScope(scope).settings;
return isInSettingsScope(settingKey, scopeSettings);
});
if (modifiedInOtherScopes.length === 0) {
return "";
}
const modifiedScopesStr = modifiedInOtherScopes.join(", ");
const currentScopeSettings = settings.forScope(selectedScope).settings;
const existsInCurrentScope = isInSettingsScope(
settingKey,
currentScopeSettings
);
return existsInCurrentScope ? `(Also modified in ${modifiedScopesStr})` : `(Modified in ${modifiedScopesStr})`;
}
// packages/cli/src/ui/components/shared/ScopeSelector.tsx
var import_jsx_runtime70 = __toESM(require_jsx_runtime(), 1);
function ScopeSelector({
onSelect,
onHighlight,
isFocused,
initialScope
}) {
const scopeItems = getScopeItems().map((item) => ({
...item,
key: item.value
}));
const initialIndex = scopeItems.findIndex(
(item) => item.value === initialScope
);
const safeInitialIndex = initialIndex >= 0 ? initialIndex : 0;
return /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime70.jsxs)(Text, { bold: isFocused, wrap: "truncate", children: [
isFocused ? "> " : " ",
"Apply To"
] }),
/* @__PURE__ */ (0, import_jsx_runtime70.jsx)(
RadioButtonSelect,
{
items: scopeItems,
initialIndex: safeInitialIndex,
onSelect,
onHighlight,
isFocused,
showNumbers: isFocused
}
)
] });
}
// packages/cli/src/ui/components/ColorsDisplay.tsx
var import_jsx_runtime71 = __toESM(require_jsx_runtime(), 1);
var VALUE_COLUMN_WIDTH = 10;
var COLOR_DESCRIPTIONS = {
"text.primary": "Primary text color (uses terminal default if blank)",
"text.secondary": "Secondary/dimmed text color",
"text.link": "Hyperlink and highlighting color",
"text.accent": "Accent color for emphasis",
"text.response": "Color for model response text (uses terminal default if blank)",
"background.primary": "Main terminal background color",
"background.message": "Subtle background for message blocks",
"background.input": "Background for the input prompt",
"background.focus": "Background highlight for selected/focused items",
"background.diff.added": "Background for added lines in diffs",
"background.diff.removed": "Background for removed lines in diffs",
"border.default": "Standard border color",
"ui.comment": "Color for code comments and metadata",
"ui.symbol": "Color for technical symbols and UI icons",
"ui.active": "Border color for active or running elements",
"ui.dark": "Deeply dimmed color for subtle UI elements",
"ui.focus": "Color for focused elements (e.g. selected menu items, focused borders)",
"status.error": "Color for error messages and critical status",
"status.success": "Color for success messages and positive status",
"status.warning": "Color for warnings and cautionary status"
};
function getContrastingTextColor(hex) {
if (!hex || !hex.startsWith("#") || hex.length < 7) {
return theme.text.primary;
}
const r = parseInt(hex.slice(1, 3), 16);
const g = parseInt(hex.slice(3, 5), 16);
const b = parseInt(hex.slice(5, 7), 16);
const yiq = (r * 299 + g * 587 + b * 114) / 1e3;
return yiq >= 128 ? "#000000" : "#FFFFFF";
}
var ColorsDisplay = ({
activeTheme
}) => {
const semanticColors = activeTheme.semanticColors;
const backgroundRows = [];
const standardRows = [];
let gradientRow = null;
if (semanticColors.ui.gradient && semanticColors.ui.gradient.length > 0) {
gradientRow = {
type: "gradient",
name: "ui.gradient",
value: semanticColors.ui.gradient
};
}
const flattenColors = (obj, path14 = "") => {
for (const [key, value] of Object.entries(obj)) {
if (value === void 0 || value === null) continue;
const newPath = path14 ? `${path14}.${key}` : key;
if (key === "gradient" && Array.isArray(value)) {
continue;
}
if (typeof value === "object" && !Array.isArray(value)) {
flattenColors(value, newPath);
} else if (typeof value === "string") {
if (newPath.startsWith("background.")) {
backgroundRows.push({
type: "background",
name: newPath,
value
});
} else {
standardRows.push({
type: "standard",
name: newPath,
value
});
}
}
}
};
flattenColors(semanticColors);
const allRows = [
...backgroundRows,
...standardRows,
...gradientRow ? [gradientRow] : []
];
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(
Box_default,
{
flexDirection: "column",
paddingX: 1,
paddingY: 0,
borderStyle: "round",
borderColor: theme.border.default,
children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { marginBottom: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, color: theme.text.accent, children: "DEVELOPER TOOLS (Not visible to users)" }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: theme.text.primary, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, children: "How do colors get applied?" }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { marginLeft: 2, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Text, { color: theme.text.primary, children: [
"\u2022 ",
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, children: "Hex:" }),
" Rendered exactly by modern terminals. Not overridden by app themes."
] }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Text, { color: theme.text.primary, children: [
"\u2022 ",
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, children: "Blank:" }),
" Uses your terminal's default foreground/background."
] }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Text, { color: theme.text.primary, children: [
"\u2022 ",
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, children: "Compatibility:" }),
" On older terminals, hex is approximated to the nearest ANSI color."
] }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Text, { color: theme.text.primary, children: [
"\u2022 ",
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, children: "ANSI Names:" }),
" 'red', 'green', etc. are mapped to your terminal app's palette."
] })
] })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexDirection: "row", marginBottom: 0, paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: VALUE_COLUMN_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, color: theme.text.link, dimColor: true, children: "Value" }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { bold: true, color: theme.text.link, dimColor: true, children: "Name" }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { flexDirection: "column", children: allRows.map((row) => {
if (row.type === "standard") return renderStandardRow(row);
if (row.type === "gradient") return renderGradientRow(row);
if (row.type === "background") return renderBackgroundRow(row);
return null;
}) })
]
}
);
};
function renderStandardRow({ name, value }) {
const isHex = value.startsWith("#");
const displayColor = isHex ? value : theme.text.primary;
const description = COLOR_DESCRIPTIONS[name] || "";
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexDirection: "row", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: VALUE_COLUMN_WIDTH, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: displayColor, children: value || "(blank)" }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexGrow: 1, flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: "30%", children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: displayColor, children: name }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { flexGrow: 1, paddingLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: theme.text.secondary, children: description }) })
] })
] }, name);
}
function renderGradientRow({ name, value }) {
const description = COLOR_DESCRIPTIONS[name] || "";
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexDirection: "row", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: VALUE_COLUMN_WIDTH, flexDirection: "column", children: value.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: c, children: c }, i)) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexGrow: 1, flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: "30%", children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(dist_default, { colors: value, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { children: name }) }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { flexGrow: 1, paddingLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: theme.text.secondary, children: description }) })
] })
] }, name);
}
function renderBackgroundRow({ name, value }) {
const description = COLOR_DESCRIPTIONS[name] || "";
return /* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexDirection: "row", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(
Box_default,
{
width: VALUE_COLUMN_WIDTH,
backgroundColor: value,
justifyContent: "center",
paddingX: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: getContrastingTextColor(value), bold: true, wrap: "truncate", children: value || "default" })
}
),
/* @__PURE__ */ (0, import_jsx_runtime71.jsxs)(Box_default, { flexGrow: 1, flexDirection: "row", paddingLeft: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { width: "30%", children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: theme.text.primary, children: name }) }),
/* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Box_default, { flexGrow: 1, paddingLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime71.jsx)(Text, { color: theme.text.secondary, children: description }) })
] })
] }, name);
}
// packages/cli/src/ui/components/ThemeDialog.constants.ts
var SELECTION_PANE_WIDTH_PERCENTAGE = 0.45;
var PREVIEW_PANE_WIDTH_PERCENTAGE = 0.55;
var PREVIEW_PANE_WIDTH_SAFETY_MARGIN = 0.9;
var TOTAL_HORIZONTAL_PADDING = 4;
var DIALOG_PADDING2 = 2;
var PREVIEW_PANE_FIXED_VERTICAL_SPACE = 8;
var TAB_TO_SELECT_HEIGHT = 2;
// packages/cli/src/ui/components/ThemeDialog.tsx
var import_jsx_runtime72 = __toESM(require_jsx_runtime(), 1);
function generateThemeItem(name, typeDisplay, fullTheme, terminalBackgroundColor) {
const isCompatible = fullTheme ? themeManager.isThemeCompatible(fullTheme, terminalBackgroundColor) : true;
const themeBackground = fullTheme ? resolveColor(fullTheme.colors.Background) : void 0;
const isBackgroundMatch = terminalBackgroundColor && themeBackground && terminalBackgroundColor.toLowerCase() === themeBackground.toLowerCase();
return {
label: name,
value: name,
themeNameDisplay: name,
themeTypeDisplay: typeDisplay,
themeWarning: isCompatible ? "" : " (Incompatible)",
themeMatch: isBackgroundMatch ? " (Matches terminal)" : "",
key: name,
isCompatible
};
}
function ThemeDialog({
onSelect,
onCancel,
onHighlight,
settings,
availableTerminalHeight,
terminalWidth
}) {
const isAlternateBuffer = useAlternateBuffer();
const { terminalBackgroundColor } = useUIState();
const [selectedScope, setSelectedScope] = (0, import_react40.useState)(
"User" /* User */
);
const [highlightedThemeName, setHighlightedThemeName] = (0, import_react40.useState)(
() => {
if (settings.merged.ui.theme) {
return settings.merged.ui.theme;
}
return pickDefaultThemeName(
terminalBackgroundColor,
themeManager.getAllThemes(),
DEFAULT_THEME.name,
"Default Light"
);
}
);
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
const themeItems = themeManager.getAvailableThemes().map((theme2) => {
const fullTheme = themeManager.getTheme(theme2.name);
const capitalizedType = capitalize(theme2.type);
const typeDisplay = theme2.name.endsWith(capitalizedType) ? "" : capitalizedType;
return generateThemeItem(
theme2.name,
typeDisplay,
fullTheme,
terminalBackgroundColor
);
}).sort((a, b) => {
if (a.isCompatible && !b.isCompatible) return -1;
if (!a.isCompatible && b.isCompatible) return 1;
return a.label.localeCompare(b.label);
});
const initialThemeIndex = themeItems.findIndex(
(item) => item.value === highlightedThemeName
);
const safeInitialThemeIndex = initialThemeIndex >= 0 ? initialThemeIndex : 0;
const handleThemeSelect = (0, import_react40.useCallback)(
async (themeName) => {
await onSelect(themeName, selectedScope);
},
[onSelect, selectedScope]
);
const handleThemeHighlight = (themeName) => {
setHighlightedThemeName(themeName);
onHighlight(themeName);
};
const handleScopeHighlight = (0, import_react40.useCallback)((scope) => {
setSelectedScope(scope);
}, []);
const handleScopeSelect = (0, import_react40.useCallback)(
async (scope) => {
await onSelect(highlightedThemeName, scope);
},
[onSelect, highlightedThemeName]
);
const [mode, setMode] = (0, import_react40.useState)("theme");
useKeypress(
(key) => {
if (key.name === "tab") {
setMode((prev) => prev === "theme" ? "scope" : "theme");
return true;
}
if (key.name === "escape") {
onCancel();
return true;
}
return false;
},
{ isActive: true }
);
const otherScopeModifiedMessage = getScopeMessageForSetting(
"ui.theme",
selectedScope,
settings
);
const colorizeCodeWidth = Math.max(
Math.floor(
(terminalWidth - TOTAL_HORIZONTAL_PADDING) * PREVIEW_PANE_WIDTH_PERCENTAGE * PREVIEW_PANE_WIDTH_SAFETY_MARGIN
),
1
);
const selectThemeHeight = themeItems.length + 1;
availableTerminalHeight = availableTerminalHeight ?? Number.MAX_SAFE_INTEGER;
availableTerminalHeight -= 2;
availableTerminalHeight -= TAB_TO_SELECT_HEIGHT;
let totalLeftHandSideHeight = DIALOG_PADDING2 + selectThemeHeight;
let includePadding = true;
if (totalLeftHandSideHeight > availableTerminalHeight) {
includePadding = false;
totalLeftHandSideHeight -= DIALOG_PADDING2;
}
availableTerminalHeight = Math.max(
availableTerminalHeight,
totalLeftHandSideHeight
);
const availableTerminalHeightCodeBlock = availableTerminalHeight - PREVIEW_PANE_FIXED_VERTICAL_SPACE - (includePadding ? 2 : 0) * 2;
const availableHeightForPanes = Math.max(
0,
availableTerminalHeightCodeBlock - 1
);
const codeBlockHeight = Math.ceil(availableHeightForPanes * 0.6);
const diffHeight = Math.floor(availableHeightForPanes * 0.4);
const previewTheme = themeManager.getTheme(highlightedThemeName || DEFAULT_THEME.name) || DEFAULT_THEME;
const leftColumnWidth = `${SELECTION_PANE_WIDTH_PERCENTAGE * 100}%`;
const rightColumnWidth = `${PREVIEW_PANE_WIDTH_PERCENTAGE * 100}%`;
return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
paddingTop: includePadding ? 1 : 0,
paddingBottom: includePadding ? 1 : 0,
paddingLeft: 1,
paddingRight: 1,
width: "100%",
children: [
mode === "theme" ? /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Box_default, { flexDirection: "column", width: leftColumnWidth, paddingRight: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Text, { bold: mode === "theme", wrap: "truncate", children: [
mode === "theme" ? "> " : " ",
"Select Theme",
" ",
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { color: theme.text.secondary, children: otherScopeModifiedMessage })
] }),
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
RadioButtonSelect,
{
items: themeItems,
initialIndex: safeInitialThemeIndex,
onSelect: handleThemeSelect,
onHighlight: handleThemeHighlight,
isFocused: mode === "theme",
maxItemsToShow: 12,
showScrollArrows: true,
showNumbers: mode === "theme",
renderItem: (item, { titleColor }) => {
const itemWithExtras = item;
if (itemWithExtras.themeNameDisplay) {
const match = itemWithExtras.themeNameDisplay.match(/^(.*) \((.*)\)$/);
let themeNamePart = itemWithExtras.themeNameDisplay;
if (match) {
themeNamePart = /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(import_jsx_runtime72.Fragment, { children: [
match[1],
" ",
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Text, { color: theme.text.secondary, children: [
"(",
match[2],
")"
] })
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Text, { color: titleColor, wrap: "truncate", children: [
themeNamePart,
itemWithExtras.themeTypeDisplay ? /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(import_jsx_runtime72.Fragment, { children: [
" ",
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { color: theme.text.secondary, children: itemWithExtras.themeTypeDisplay })
] }) : null,
itemWithExtras.themeMatch && /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { color: theme.status.success, children: itemWithExtras.themeMatch }),
itemWithExtras.themeWarning && /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { color: theme.status.warning, children: itemWithExtras.themeWarning })
] }, item.key);
}
return /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { color: titleColor, wrap: "truncate", children: item.label });
}
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Box_default, { flexDirection: "column", width: rightColumnWidth, paddingLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Text, { bold: true, color: theme.text.primary, children: "Preview" }),
/* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(
Box_default,
{
borderStyle: "single",
borderColor: theme.border.default,
paddingTop: includePadding ? 1 : 0,
paddingBottom: includePadding ? 1 : 0,
paddingLeft: 1,
paddingRight: 1,
flexDirection: "column",
children: [
colorizeCode({
code: `# function
def fibonacci(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a`,
language: "python",
availableHeight: isAlternateBuffer === false ? codeBlockHeight : void 0,
maxWidth: colorizeCodeWidth,
settings
}),
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Box_default, { marginTop: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
DiffRenderer,
{
diffContent: `--- a/util.py
+++ b/util.py
@@ -1,2 +1,2 @@
- print("Hello, " + name)
+ print(f"Hello, {name}!")
`,
availableTerminalHeight: isAlternateBuffer === false ? diffHeight : void 0,
terminalWidth: colorizeCodeWidth,
theme: previewTheme
}
)
]
}
),
isDevelopment && /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(ColorsDisplay, { activeTheme: previewTheme }) })
] })
] }) : /* @__PURE__ */ (0, import_jsx_runtime72.jsx)(
ScopeSelector,
{
onSelect: handleScopeSelect,
onHighlight: handleScopeHighlight,
isFocused: mode === "scope",
initialScope: selectedScope
}
),
/* @__PURE__ */ (0, import_jsx_runtime72.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime72.jsxs)(Text, { color: theme.text.secondary, wrap: "truncate", children: [
"(Use Enter to ",
mode === "theme" ? "select" : "apply scope",
", Tab to",
" ",
mode === "theme" ? "configure scope" : "select theme",
", Esc to close)"
] }) })
]
}
);
}
// packages/cli/src/ui/components/SettingsDialog.tsx
var import_react44 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx
var import_react43 = __toESM(require_react(), 1);
var import_chalk2 = __toESM(require_source(), 1);
// packages/cli/src/ui/hooks/useSettingsNavigation.ts
var import_react41 = __toESM(require_react(), 1);
function calculateSlidingWindow(start, activeIndex, itemCount, windowSize) {
if (activeIndex < start) {
start = activeIndex;
} else if (activeIndex >= start + windowSize) {
start = activeIndex - windowSize + 1;
}
const maxScroll = Math.max(0, itemCount - windowSize);
const bounded = Math.min(start, maxScroll);
return Math.max(0, bounded);
}
function createNavReducer(items, maxItemsToShow) {
return function navReducer(state, action) {
if (items.length === 0) return state;
const currentIndex = items.findIndex((i) => i.key === state.activeItemKey);
const activeIndex = currentIndex !== -1 ? currentIndex : 0;
switch (action.type) {
case "MOVE_UP": {
const newIndex = activeIndex > 0 ? activeIndex - 1 : items.length - 1;
return {
activeItemKey: items[newIndex].key,
windowStart: calculateSlidingWindow(
state.windowStart,
newIndex,
items.length,
maxItemsToShow
)
};
}
case "MOVE_DOWN": {
const newIndex = activeIndex < items.length - 1 ? activeIndex + 1 : 0;
return {
activeItemKey: items[newIndex].key,
windowStart: calculateSlidingWindow(
state.windowStart,
newIndex,
items.length,
maxItemsToShow
)
};
}
default: {
return state;
}
}
};
}
function useSettingsNavigation({
items,
maxItemsToShow
}) {
const reducer = (0, import_react41.useMemo)(
() => createNavReducer(items, maxItemsToShow),
[items, maxItemsToShow]
);
const [state, dispatch] = (0, import_react41.useReducer)(reducer, {
activeItemKey: items[0]?.key ?? null,
windowStart: 0
});
const activeIndex = (0, import_react41.useMemo)(() => {
if (items.length === 0) return 0;
const idx = items.findIndex((i) => i.key === state.activeItemKey);
return idx !== -1 ? idx : 0;
}, [items, state.activeItemKey]);
const windowStart = (0, import_react41.useMemo)(
() => calculateSlidingWindow(
state.windowStart,
activeIndex,
items.length,
maxItemsToShow
),
[state.windowStart, activeIndex, items.length, maxItemsToShow]
);
const moveUp = (0, import_react41.useCallback)(() => dispatch({ type: "MOVE_UP" }), []);
const moveDown = (0, import_react41.useCallback)(() => dispatch({ type: "MOVE_DOWN" }), []);
return {
activeItemKey: state.activeItemKey,
activeIndex,
windowStart,
moveUp,
moveDown
};
}
// packages/cli/src/ui/hooks/useInlineEditBuffer.ts
var import_react42 = __toESM(require_react(), 1);
var initialState2 = {
editingKey: null,
buffer: "",
cursorPos: 0
};
function editBufferReducer(state, action) {
switch (action.type) {
case "START_EDIT":
return {
editingKey: action.key,
buffer: action.initialValue,
cursorPos: cpLen(action.initialValue)
};
case "COMMIT_EDIT":
return initialState2;
case "MOVE_LEFT":
return {
...state,
cursorPos: Math.max(0, state.cursorPos - 1)
};
case "MOVE_RIGHT":
return {
...state,
cursorPos: Math.min(cpLen(state.buffer), state.cursorPos + 1)
};
case "HOME":
return { ...state, cursorPos: 0 };
case "END":
return { ...state, cursorPos: cpLen(state.buffer) };
case "DELETE_LEFT": {
if (state.cursorPos === 0) return state;
const before = cpSlice(state.buffer, 0, state.cursorPos - 1);
const after = cpSlice(state.buffer, state.cursorPos);
return {
...state,
buffer: before + after,
cursorPos: state.cursorPos - 1
};
}
case "DELETE_RIGHT": {
if (state.cursorPos === cpLen(state.buffer)) return state;
const before = cpSlice(state.buffer, 0, state.cursorPos);
const after = cpSlice(state.buffer, state.cursorPos + 1);
return {
...state,
buffer: before + after
};
}
case "INSERT_CHAR": {
let ch = action.char;
let isValidChar = false;
if (action.isNumberType) {
isValidChar = /[0-9\-+.]/.test(ch);
} else {
isValidChar = ch.length === 1 && ch.charCodeAt(0) >= 32;
ch = stripUnsafeCharacters(ch);
}
if (!isValidChar || ch.length === 0) return state;
const before = cpSlice(state.buffer, 0, state.cursorPos);
const after = cpSlice(state.buffer, state.cursorPos);
return {
...state,
buffer: before + ch + after,
cursorPos: state.cursorPos + 1
};
}
default:
return state;
}
}
function useInlineEditBuffer({ onCommit }) {
const [state, dispatch] = (0, import_react42.useReducer)(editBufferReducer, initialState2);
const [cursorVisible, setCursorVisible] = (0, import_react42.useState)(true);
(0, import_react42.useEffect)(() => {
if (!state.editingKey) {
setCursorVisible(true);
return;
}
setCursorVisible(true);
const interval = setInterval(() => {
setCursorVisible((v) => !v);
}, 500);
return () => clearInterval(interval);
}, [state.editingKey, state.buffer, state.cursorPos]);
const startEditing = (0, import_react42.useCallback)((key, initialValue) => {
dispatch({ type: "START_EDIT", key, initialValue });
}, []);
const commitEdit = (0, import_react42.useCallback)(() => {
if (state.editingKey) {
onCommit(state.editingKey, state.buffer);
}
dispatch({ type: "COMMIT_EDIT" });
}, [state.editingKey, state.buffer, onCommit]);
return {
editState: state,
editDispatch: dispatch,
startEditing,
commitEdit,
cursorVisible
};
}
// packages/cli/src/ui/components/shared/BaseSettingsDialog.tsx
var import_jsx_runtime73 = __toESM(require_jsx_runtime(), 1);
function BaseSettingsDialog({
title,
borderColor,
searchEnabled = true,
searchPlaceholder = "Search to filter",
searchBuffer,
items,
showScopeSelector = true,
scopeItems: providedScopeItems,
selectedScope,
onScopeChange,
maxItemsToShow,
maxLabelWidth,
onItemToggle,
onEditCommit,
onItemClear,
onClose,
onKeyPress,
keyMatchers: customKeyMatchers,
availableHeight,
footer
}) {
const globalKeyMatchers = useKeyMatchers();
const keyMatchers = customKeyMatchers ?? globalKeyMatchers;
const scopeItems = (0, import_react43.useMemo)(
() => (providedScopeItems ?? getScopeItems()).map((item) => ({
...item,
key: item.value
})),
[providedScopeItems]
);
const showAvailableScopes = showScopeSelector && scopeItems.length > 1;
const { effectiveMaxItemsToShow, finalShowScopeSelector } = (0, import_react43.useMemo)(() => {
const initialShowScope = showAvailableScopes;
const initialMaxItems = maxItemsToShow;
if (!availableHeight) {
return {
effectiveMaxItemsToShow: initialMaxItems,
finalShowScopeSelector: initialShowScope
};
}
const DIALOG_PADDING3 = 4;
const SETTINGS_TITLE_HEIGHT = 1;
const SEARCH_SECTION_HEIGHT = searchEnabled ? 5 : 1;
const SCROLL_ARROWS_HEIGHT = 2;
const ITEMS_SPACING_AFTER = 1;
const SCOPE_SECTION_HEIGHT = 5;
const HELP_TEXT_HEIGHT = 1;
const FOOTER_CONTENT_HEIGHT = footer?.height ?? 0;
const ITEM_HEIGHT = 3;
const currentAvailableHeight = availableHeight - DIALOG_PADDING3;
const baseFixedHeight = SETTINGS_TITLE_HEIGHT + SEARCH_SECTION_HEIGHT + SCROLL_ARROWS_HEIGHT + ITEMS_SPACING_AFTER + HELP_TEXT_HEIGHT + FOOTER_CONTENT_HEIGHT;
const heightWithScope = baseFixedHeight + SCOPE_SECTION_HEIGHT;
const availableForItemsWithScope = currentAvailableHeight - heightWithScope;
const maxItemsWithScope = Math.max(
1,
Math.floor(availableForItemsWithScope / ITEM_HEIGHT)
);
const availableForItemsWithoutScope = currentAvailableHeight - baseFixedHeight;
const maxItemsWithoutScope = Math.max(
1,
Math.floor(availableForItemsWithoutScope / ITEM_HEIGHT)
);
let shouldShowScope = initialShowScope;
let maxItems = initialShowScope ? maxItemsWithScope : maxItemsWithoutScope;
if (initialShowScope && availableHeight < 25) {
if (maxItemsWithoutScope > maxItemsWithScope + 1) {
shouldShowScope = false;
maxItems = maxItemsWithoutScope;
}
}
return {
effectiveMaxItemsToShow: Math.min(maxItems, items.length),
finalShowScopeSelector: shouldShowScope
};
}, [
availableHeight,
maxItemsToShow,
items.length,
searchEnabled,
showAvailableScopes,
footer
]);
const { activeIndex, windowStart, moveUp, moveDown } = useSettingsNavigation({
items,
maxItemsToShow: effectiveMaxItemsToShow
});
const { editState, editDispatch, startEditing, commitEdit, cursorVisible } = useInlineEditBuffer({
onCommit: (key, value) => {
const itemToCommit = items.find((i) => i.key === key);
if (itemToCommit) {
onEditCommit(key, value, itemToCommit);
}
}
});
const {
editingKey,
buffer: editBuffer,
cursorPos: editCursorPos
} = editState;
const [focusSection, setFocusSection] = (0, import_react43.useState)(
"settings"
);
const effectiveFocusSection = !finalShowScopeSelector && focusSection === "scope" ? "settings" : focusSection;
const visibleItems = items.slice(
windowStart,
windowStart + effectiveMaxItemsToShow
);
const showScrollUp = items.length > effectiveMaxItemsToShow;
const showScrollDown = items.length > effectiveMaxItemsToShow;
const currentItem = items[activeIndex];
const handleScopeChange = (0, import_react43.useCallback)(
(scope) => {
onScopeChange?.(scope);
},
[onScopeChange]
);
useKeypress(
(key) => {
if (!editingKey && onKeyPress?.(key, currentItem)) {
return;
}
if (editingKey) {
const item = items.find((i) => i.key === editingKey);
const type = item?.type ?? "string";
if (keyMatchers["cursor.left" /* MOVE_LEFT */](key)) {
editDispatch({ type: "MOVE_LEFT" });
return;
}
if (keyMatchers["cursor.right" /* MOVE_RIGHT */](key)) {
editDispatch({ type: "MOVE_RIGHT" });
return;
}
if (keyMatchers["cursor.home" /* HOME */](key)) {
editDispatch({ type: "HOME" });
return;
}
if (keyMatchers["cursor.end" /* END */](key)) {
editDispatch({ type: "END" });
return;
}
if (keyMatchers["edit.deleteLeft" /* DELETE_CHAR_LEFT */](key)) {
editDispatch({ type: "DELETE_LEFT" });
return;
}
if (keyMatchers["edit.deleteRight" /* DELETE_CHAR_RIGHT */](key)) {
editDispatch({ type: "DELETE_RIGHT" });
return;
}
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
commitEdit();
return;
}
if (keyMatchers["basic.confirm" /* RETURN */](key)) {
commitEdit();
return;
}
if (keyMatchers["nav.dialog.up" /* DIALOG_NAVIGATION_UP */](key) && !key.insertable) {
commitEdit();
moveUp();
return;
}
if (keyMatchers["nav.dialog.down" /* DIALOG_NAVIGATION_DOWN */](key) && !key.insertable) {
commitEdit();
moveDown();
return;
}
if (key.sequence) {
editDispatch({
type: "INSERT_CHAR",
char: key.sequence,
isNumberType: type === "number"
});
}
return;
}
if (effectiveFocusSection === "settings") {
if (keyMatchers["nav.dialog.up" /* DIALOG_NAVIGATION_UP */](key)) {
moveUp();
return true;
}
if (keyMatchers["nav.dialog.down" /* DIALOG_NAVIGATION_DOWN */](key)) {
moveDown();
return true;
}
if (keyMatchers["basic.confirm" /* RETURN */](key) && currentItem) {
if (currentItem.type === "boolean" || currentItem.type === "enum") {
onItemToggle(currentItem.key, currentItem);
} else {
const rawVal = currentItem.rawValue;
const initialValue = currentItem.editValue ?? (rawVal !== void 0 ? String(rawVal) : "");
startEditing(currentItem.key, initialValue);
}
return true;
}
if (keyMatchers["app.clearScreen" /* CLEAR_SCREEN */](key) && currentItem) {
onItemClear(currentItem.key, currentItem);
return true;
}
if (currentItem?.type === "number" && /^[0-9]$/.test(key.sequence)) {
startEditing(currentItem.key, key.sequence);
return true;
}
}
if (key.name === "tab" && finalShowScopeSelector) {
setFocusSection((s) => s === "settings" ? "scope" : "settings");
return;
}
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
onClose();
return;
}
return;
},
{
isActive: true,
priority: effectiveFocusSection === "settings"
}
);
return /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: borderColor ?? theme.border.default,
flexDirection: "row",
padding: 1,
width: "100%",
maxHeight: availableHeight,
children: /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
Text,
{
bold: effectiveFocusSection === "settings" && !editingKey,
wrap: "truncate",
children: [
effectiveFocusSection === "settings" ? "> " : " ",
title,
" "
]
}
) }),
searchEnabled && searchBuffer && /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: editingKey ? theme.border.default : effectiveFocusSection === "settings" ? theme.ui.focus : theme.border.default,
paddingX: 1,
height: 3,
marginTop: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
TextInput,
{
focus: effectiveFocusSection === "settings" && !editingKey,
buffer: searchBuffer,
placeholder: searchPlaceholder
}
)
}
),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { height: 1 }),
visibleItems.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, height: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Text, { color: theme.text.secondary, children: "No matches found." }) }) : /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(import_jsx_runtime73.Fragment, { children: [
showScrollUp && /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Text, { color: theme.text.secondary, children: "\u25B2" }) }),
visibleItems.map((item, idx) => {
const globalIndex = idx + windowStart;
const isActive = effectiveFocusSection === "settings" && activeIndex === globalIndex;
let displayValue;
if (editingKey === item.key) {
if (cursorVisible && editCursorPos < cpLen(editBuffer)) {
const beforeCursor = cpSlice(editBuffer, 0, editCursorPos);
const atCursor = cpSlice(
editBuffer,
editCursorPos,
editCursorPos + 1
);
const afterCursor = cpSlice(editBuffer, editCursorPos + 1);
displayValue = beforeCursor + import_chalk2.default.inverse(atCursor) + afterCursor;
} else if (editCursorPos >= cpLen(editBuffer)) {
displayValue = editBuffer + (cursorVisible ? import_chalk2.default.inverse(" ") : " ");
} else {
displayValue = editBuffer;
}
} else {
displayValue = item.displayValue;
}
return /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(import_react43.default.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
Box_default,
{
marginX: 1,
flexDirection: "row",
alignItems: "flex-start",
backgroundColor: isActive ? theme.background.focus : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { minWidth: 2, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
Text,
{
color: isActive ? theme.ui.focus : theme.text.secondary,
children: isActive ? "\u25CF" : ""
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
Box_default,
{
flexDirection: "row",
flexGrow: 1,
minWidth: 0,
alignItems: "flex-start",
children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
Box_default,
{
flexDirection: "column",
width: maxLabelWidth,
minWidth: 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(
Text,
{
color: isActive ? theme.ui.focus : theme.text.primary,
children: [
item.label,
item.scopeMessage && /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(Text, { color: theme.text.secondary, children: [
" ",
item.scopeMessage
] })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Text, { color: theme.text.secondary, wrap: "truncate", children: item.description ?? "" })
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { minWidth: 3 }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
Text,
{
color: isActive ? theme.ui.focus : item.isGreyedOut ? theme.text.secondary : theme.text.primary,
terminalCursorFocus: editingKey === item.key && cursorVisible,
terminalCursorPosition: cpIndexToOffset(
editBuffer,
editCursorPos
),
children: displayValue
}
) })
]
}
)
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { height: 1 })
] }, item.key);
}),
showScrollDown && /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Text, { color: theme.text.secondary, children: "\u25BC" }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { height: 1 }),
finalShowScopeSelector && /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(Box_default, { marginX: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(Text, { bold: effectiveFocusSection === "scope", wrap: "truncate", children: [
effectiveFocusSection === "scope" ? "> " : " ",
"Apply To"
] }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(
RadioButtonSelect,
{
items: scopeItems,
initialIndex: scopeItems.findIndex(
(item) => item.value === selectedScope
),
onSelect: handleScopeChange,
onHighlight: handleScopeChange,
isFocused: effectiveFocusSection === "scope",
showNumbers: effectiveFocusSection === "scope",
priority: effectiveFocusSection === "scope"
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { height: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime73.jsxs)(Text, { color: theme.text.secondary, children: [
"(Use Enter to select, ",
formatCommand("app.clearScreen" /* CLEAR_SCREEN */),
" to reset",
finalShowScopeSelector ? ", Tab to change focus" : "",
", Esc to close)"
] }) }),
footer && /* @__PURE__ */ (0, import_jsx_runtime73.jsx)(Box_default, { marginX: 1, children: footer.content })
] })
}
);
}
// packages/cli/src/ui/components/SettingsDialog.tsx
var import_jsx_runtime74 = __toESM(require_jsx_runtime(), 1);
var MAX_ITEMS_TO_SHOW = 8;
var KEY_UP = new KeyBinding("up");
var KEY_CTRL_P = new KeyBinding("ctrl+p");
var KEY_DOWN = new KeyBinding("down");
var KEY_CTRL_N = new KeyBinding("ctrl+n");
function getActiveRestartRequiredSettings(settings) {
const snapshot = /* @__PURE__ */ new Map();
const scopes = [
["User", settings.user.settings],
["Workspace", settings.workspace.settings],
["System", settings.system.settings]
];
for (const key of getDialogRestartRequiredSettings()) {
const scopeMap = /* @__PURE__ */ new Map();
for (const [scopeName, scopeSettings] of scopes) {
const value = isInSettingsScope(key, scopeSettings) ? getEffectiveValue(key, scopeSettings) : void 0;
scopeMap.set(scopeName, JSON.stringify(value));
}
snapshot.set(key, scopeMap);
}
return snapshot;
}
function SettingsDialog({
onSelect,
onRestartRequest,
availableTerminalHeight
}) {
const { settings, setSetting } = useSettingsStore();
const [selectedScope, setSelectedScope] = (0, import_react44.useState)(
"User" /* User */
);
const editableScopeItems = (0, import_react44.useMemo)(
() => getScopeItems().filter((item) => {
const settingsFile = settings.forScope(item.value);
return settingsFile.readOnly !== true && (item.value !== "Workspace" /* Workspace */ || settingsFile.path !== void 0);
}),
[settings]
);
const writableSelectedScope = editableScopeItems.find((item) => item.value === selectedScope)?.value ?? editableScopeItems[0]?.value;
const effectiveSelectedScope = writableSelectedScope ?? "User" /* User */;
if (writableSelectedScope && selectedScope !== writableSelectedScope) {
setSelectedScope(writableSelectedScope);
}
const [activeRestartRequiredSettings] = (0, import_react44.useState)(
() => getActiveRestartRequiredSettings(settings)
);
const [searchQuery, setSearchQuery] = (0, import_react44.useState)("");
const [filteredKeys, setFilteredKeys] = (0, import_react44.useState)(
() => getDialogSettingKeys()
);
const { fzfInstance, searchMap } = (0, import_react44.useMemo)(() => {
const keys = getDialogSettingKeys();
const map = /* @__PURE__ */ new Map();
const searchItems = [];
keys.forEach((key) => {
const def = getSettingDefinition(key);
if (def?.label) {
searchItems.push(def.label);
map.set(def.label.toLowerCase(), key);
}
});
const fzf = new AsyncFzf(searchItems, {
fuzzy: "v2",
casing: "case-insensitive"
});
return { fzfInstance: fzf, searchMap: map };
}, []);
(0, import_react44.useEffect)(() => {
let active = true;
if (!searchQuery.trim() || !fzfInstance) {
setFilteredKeys(getDialogSettingKeys());
return;
}
const doSearch = async () => {
const results = await fzfInstance.find(searchQuery);
if (!active) return;
const matchedKeys = /* @__PURE__ */ new Set();
results.forEach((res) => {
const key = searchMap.get(res.item.toLowerCase());
if (key) matchedKeys.add(key);
});
setFilteredKeys(Array.from(matchedKeys));
};
doSearch();
return () => {
active = false;
};
}, [searchQuery, fzfInstance, searchMap]);
const pendingRestartRequiredSettings = (0, import_react44.useMemo)(() => {
const changed = /* @__PURE__ */ new Set();
const scopes = [
["User", settings.user.settings],
["Workspace", settings.workspace.settings],
["System", settings.system.settings]
];
for (const [key, initialScopeMap] of activeRestartRequiredSettings) {
for (const [scopeName, scopeSettings] of scopes) {
const currentValue = isInSettingsScope(key, scopeSettings) ? getEffectiveValue(key, scopeSettings) : void 0;
const initialJson = initialScopeMap.get(scopeName);
if (JSON.stringify(currentValue) !== initialJson) {
changed.add(key);
break;
}
}
}
return changed;
}, [settings, activeRestartRequiredSettings]);
const showRestartPrompt = pendingRestartRequiredSettings.size > 0;
const maxLabelOrDescriptionWidth = (0, import_react44.useMemo)(() => {
const allKeys = getDialogSettingKeys();
let max = 0;
for (const key of allKeys) {
const def = getSettingDefinition(key);
if (!def) continue;
const scopeMessage = getScopeMessageForSetting(
key,
effectiveSelectedScope,
settings
);
const label = def.label || key;
const labelFull = label + (scopeMessage ? ` ${scopeMessage}` : "");
const lWidth = getCachedStringWidth(labelFull);
const dWidth = def.description ? getCachedStringWidth(def.description) : 0;
max = Math.max(max, lWidth, dWidth);
}
return max;
}, [effectiveSelectedScope, settings]);
const searchBuffer = useSearchBuffer({
initialText: "",
onChange: setSearchQuery
});
const settingKeys = searchQuery ? filteredKeys : getDialogSettingKeys();
const items = (0, import_react44.useMemo)(() => {
const scopeSettings = settings.forScope(effectiveSelectedScope).settings;
const mergedSettings = settings.merged;
return settingKeys.map((key) => {
const definition = getSettingDefinition(key);
const type = definition?.type ?? "string";
const displayValue = getDisplayValue(key, scopeSettings, mergedSettings);
const scopeMessage = getScopeMessageForSetting(
key,
effectiveSelectedScope,
settings
);
const isGreyedOut = !isInSettingsScope(key, scopeSettings);
const rawValue = getEffectiveValue(key, scopeSettings);
const editValue = getEditValue(type, rawValue);
return {
key,
label: definition?.label || key,
description: definition?.description,
type,
displayValue,
isGreyedOut,
scopeMessage,
rawValue,
editValue
};
});
}, [settingKeys, effectiveSelectedScope, settings]);
const handleScopeChange = (0, import_react44.useCallback)((scope) => {
setSelectedScope(scope);
}, []);
const handleItemToggle = (0, import_react44.useCallback)(
(key, _item) => {
if (!writableSelectedScope) {
return;
}
const definition = getSettingDefinition(key);
if (!TOGGLE_TYPES.has(definition?.type)) {
return;
}
const scopeSettings = settings.forScope(writableSelectedScope).settings;
const currentValue = getEffectiveValue(key, scopeSettings);
let newValue;
if (definition?.type === "boolean") {
if (typeof currentValue !== "boolean") {
return;
}
newValue = !currentValue;
} else if (definition?.type === "enum" && definition.options) {
const options = definition.options;
if (options.length === 0) {
return;
}
const currentIndex = options?.findIndex(
(opt) => opt.value === currentValue
);
if (currentIndex !== -1 && currentIndex < options.length - 1) {
newValue = options[currentIndex + 1].value;
} else {
newValue = options[0].value;
}
} else {
return;
}
debugLogger.log(
`[DEBUG SettingsDialog] Saving ${key} immediately with value:`,
newValue
);
setSetting(writableSelectedScope, key, newValue);
},
[settings, writableSelectedScope, setSetting]
);
const handleEditCommit = (0, import_react44.useCallback)(
(key, newValue, _item) => {
if (!writableSelectedScope) {
return;
}
const definition = getSettingDefinition(key);
const type = definition?.type ?? "string";
const parsed = parseEditedValue(type, newValue);
if (parsed === null) {
return;
}
setSetting(writableSelectedScope, key, parsed);
},
[writableSelectedScope, setSetting]
);
const handleItemClear = (0, import_react44.useCallback)(
(key, _item) => {
if (!writableSelectedScope) {
return;
}
setSetting(writableSelectedScope, key, void 0);
},
[writableSelectedScope, setSetting]
);
const handleClose = (0, import_react44.useCallback)(() => {
onSelect(void 0, effectiveSelectedScope);
}, [onSelect, effectiveSelectedScope]);
const globalKeyMatchers = useKeyMatchers();
const settingsKeyMatchers = (0, import_react44.useMemo)(
() => ({
...globalKeyMatchers,
["nav.dialog.up" /* DIALOG_NAVIGATION_UP */]: (key) => KEY_UP.matches(key) || KEY_CTRL_P.matches(key),
["nav.dialog.down" /* DIALOG_NAVIGATION_DOWN */]: (key) => KEY_DOWN.matches(key) || KEY_CTRL_N.matches(key)
}),
[globalKeyMatchers]
);
const handleKeyPress = (0, import_react44.useCallback)(
(key, _currentItem) => {
if (showRestartPrompt && key.sequence === "r") {
if (onRestartRequest) onRestartRequest();
return true;
}
return false;
},
[showRestartPrompt, onRestartRequest]
);
const showSearch = !showRestartPrompt;
return /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(
BaseSettingsDialog,
{
title: "Settings",
borderColor: showRestartPrompt ? theme.status.warning : void 0,
searchEnabled: showSearch,
searchBuffer,
items,
showScopeSelector: editableScopeItems.length > 1,
scopeItems: editableScopeItems,
selectedScope: effectiveSelectedScope,
onScopeChange: handleScopeChange,
maxItemsToShow: MAX_ITEMS_TO_SHOW,
availableHeight: availableTerminalHeight,
maxLabelWidth: maxLabelOrDescriptionWidth,
onItemToggle: handleItemToggle,
onEditCommit: handleEditCommit,
onItemClear: handleItemClear,
onClose: handleClose,
onKeyPress: handleKeyPress,
keyMatchers: settingsKeyMatchers,
footer: showRestartPrompt ? {
content: /* @__PURE__ */ (0, import_jsx_runtime74.jsx)(Text, { color: theme.status.warning, children: "Changes that require a restart have been modified. Press r to exit and apply changes now." }),
height: 1
} : void 0
}
);
}
// packages/cli/src/ui/auth/AuthInProgress.tsx
var import_react45 = __toESM(require_react(), 1);
var import_jsx_runtime75 = __toESM(require_jsx_runtime(), 1);
function AuthInProgress({
onTimeout
}) {
const [timedOut, setTimedOut] = (0, import_react45.useState)(false);
useKeypress(
(key) => {
if (key.name === "escape" || key.ctrl && key.name === "c") {
onTimeout();
}
},
{ isActive: true }
);
(0, import_react45.useEffect)(() => {
const timer = setTimeout(() => {
setTimedOut(true);
onTimeout();
}, 18e4);
return () => clearTimeout(timer);
}, [onTimeout]);
return /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
padding: 1,
width: "100%",
children: timedOut ? /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Text, { color: theme.status.error, children: "Authentication timed out. Please try again." }) : /* @__PURE__ */ (0, import_jsx_runtime75.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime75.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime75.jsx)(CliSpinner, { type: "dots" }),
" Waiting for authentication... (Press Esc or Ctrl+C to cancel)"
] }) })
}
);
}
// packages/cli/src/ui/auth/AuthDialog.tsx
var import_react47 = __toESM(require_react(), 1);
// packages/cli/src/ui/auth/useAuth.ts
var import_react46 = __toESM(require_react(), 1);
function validateAuthMethodWithSettings(authType, settings) {
const enforcedType = settings.merged.security.auth.enforcedType;
if (enforcedType && enforcedType !== authType) {
return `Authentication is enforced to be ${enforcedType}, but you are currently using ${authType}.`;
}
if (settings.merged.security.auth.useExternal) {
return null;
}
if (authType === AuthType.USE_GEMINI) {
return null;
}
return validateAuthMethod(authType);
}
var useAuthCommand = (settings, config, initialAuthError = null, initialAccountSuspensionInfo = null) => {
const [authState, setAuthState] = (0, import_react46.useState)(
initialAuthError ? "updating" /* Updating */ : "unauthenticated" /* Unauthenticated */
);
const [authError, setAuthError] = (0, import_react46.useState)(initialAuthError);
const [accountSuspensionInfo, setAccountSuspensionInfo] = (0, import_react46.useState)(initialAccountSuspensionInfo);
const [apiKeyDefaultValue, setApiKeyDefaultValue] = (0, import_react46.useState)(void 0);
const onAuthError = (0, import_react46.useCallback)(
(error) => {
setAuthError(error);
if (error) {
setAuthState("updating" /* Updating */);
}
},
[setAuthError, setAuthState]
);
const reloadApiKey = (0, import_react46.useCallback)(async () => {
const envKey = process.env["GEMINI_API_KEY"];
if (envKey !== void 0) {
setApiKeyDefaultValue(envKey);
return envKey;
}
const storedKey = await loadApiKey() ?? "";
setApiKeyDefaultValue(storedKey);
return storedKey;
}, []);
(0, import_react46.useEffect)(() => {
if (authState === "awaiting_api_key_input" /* AwaitingApiKeyInput */) {
reloadApiKey();
}
}, [authState, reloadApiKey]);
(0, import_react46.useEffect)(() => {
(async () => {
if (authState !== "unauthenticated" /* Unauthenticated */) {
return;
}
const authType = settings.merged.security.auth.selectedType;
if (!authType) {
if (process.env["GEMINI_API_KEY"]) {
onAuthError(
'Existing API key detected (GEMINI_API_KEY). Select "Gemini API Key" option to use it.'
);
} else {
onAuthError("No authentication method selected.");
}
return;
}
if (authType === AuthType.USE_GEMINI) {
const key = await reloadApiKey();
if (!key) {
setAuthState("awaiting_api_key_input" /* AwaitingApiKeyInput */);
return;
}
}
const error = validateAuthMethodWithSettings(authType, settings);
if (error) {
onAuthError(error);
return;
}
const defaultAuthType = process.env["GEMINI_DEFAULT_AUTH_TYPE"];
if (defaultAuthType && // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
!Object.values(AuthType).includes(defaultAuthType)) {
onAuthError(
`Invalid value for GEMINI_DEFAULT_AUTH_TYPE: "${defaultAuthType}". Valid values are: ${Object.values(AuthType).join(", ")}.`
);
return;
}
try {
await config.refreshAuth(authType);
debugLogger.log(`Authenticated via "${authType}".`);
setAuthError(null);
setAuthState("authenticated" /* Authenticated */);
} catch (e) {
const suspendedError = isAccountSuspendedError(e);
if (suspendedError) {
setAccountSuspensionInfo({
message: suspendedError.message,
appealUrl: suspendedError.appealUrl,
appealLinkText: suspendedError.appealLinkText
});
} else if (e instanceof ProjectIdRequiredError) {
onAuthError(getErrorMessage(e));
} else {
onAuthError(`Failed to sign in. Message: ${getErrorMessage(e)}`);
}
}
})();
}, [
settings,
config,
authState,
setAuthState,
setAuthError,
onAuthError,
reloadApiKey
]);
return {
authState,
setAuthState,
authError,
onAuthError,
apiKeyDefaultValue,
reloadApiKey,
accountSuspensionInfo,
setAccountSuspensionInfo
};
};
// packages/cli/src/ui/auth/AuthDialog.tsx
var import_jsx_runtime76 = __toESM(require_jsx_runtime(), 1);
function AuthDialog({
config,
settings,
setAuthState,
authError,
onAuthError,
setAuthContext
}) {
const [exiting, setExiting] = (0, import_react47.useState)(false);
let items = [
{
label: "Sign in with Google",
value: AuthType.LOGIN_WITH_GOOGLE,
key: AuthType.LOGIN_WITH_GOOGLE
},
...process.env["CLOUD_SHELL"] === "true" ? [
{
label: "Use Cloud Shell user credentials",
value: AuthType.COMPUTE_ADC,
key: AuthType.COMPUTE_ADC
}
] : process.env["GEMINI_CLI_USE_COMPUTE_ADC"] === "true" ? [
{
label: "Use metadata server application default credentials",
value: AuthType.COMPUTE_ADC,
key: AuthType.COMPUTE_ADC
}
] : [],
{
label: "Use Gemini API Key",
value: AuthType.USE_GEMINI,
key: AuthType.USE_GEMINI
},
{
label: "Vertex AI",
value: AuthType.USE_VERTEX_AI,
key: AuthType.USE_VERTEX_AI
}
];
if (settings.merged.security.auth.enforcedType) {
items = items.filter(
(item) => item.value === settings.merged.security.auth.enforcedType
);
}
let defaultAuthType = null;
const defaultAuthTypeEnv = process.env["GEMINI_DEFAULT_AUTH_TYPE"];
if (defaultAuthTypeEnv && // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
Object.values(AuthType).includes(defaultAuthTypeEnv)) {
defaultAuthType = defaultAuthTypeEnv;
}
let initialAuthIndex = items.findIndex((item) => {
if (settings.merged.security.auth.selectedType) {
return item.value === settings.merged.security.auth.selectedType;
}
if (defaultAuthType) {
return item.value === defaultAuthType;
}
if (process.env["GEMINI_API_KEY"]) {
return item.value === AuthType.USE_GEMINI;
}
return item.value === AuthType.LOGIN_WITH_GOOGLE;
});
if (settings.merged.security.auth.enforcedType) {
initialAuthIndex = 0;
}
const onSelect = (0, import_react47.useCallback)(
async (authType, scope) => {
if (exiting) {
return;
}
if (authType) {
const needsRestart = authType === AuthType.LOGIN_WITH_GOOGLE || authType === AuthType.USE_VERTEX_AI && process.env["CLOUD_SHELL"] === "true";
if (needsRestart) {
setAuthContext({ requiresRestart: true });
} else {
setAuthContext({});
}
await clearCachedCredentialFile();
settings.setValue(scope, "security.auth.selectedType", authType);
if (authType === AuthType.LOGIN_WITH_GOOGLE && config.isBrowserLaunchSuppressed()) {
setExiting(true);
setTimeout(relaunchApp, 100);
return;
}
if (authType === AuthType.USE_GEMINI) {
setAuthState("awaiting_api_key_input" /* AwaitingApiKeyInput */);
return;
}
}
setAuthState("unauthenticated" /* Unauthenticated */);
},
[settings, config, setAuthState, exiting, setAuthContext]
);
const handleAuthSelect = (authMethod) => {
const error = validateAuthMethodWithSettings(authMethod, settings);
if (error) {
onAuthError(error);
} else {
onSelect(authMethod, "User" /* User */);
}
};
useKeypress(
(key) => {
if (key.name === "escape") {
if (authError) {
return true;
}
if (settings.merged.security.auth.selectedType === void 0) {
onAuthError(
"You must select an auth method to proceed. Press Ctrl+C twice to exit."
);
return true;
}
onSelect(void 0, "User" /* User */);
return true;
}
return false;
},
{ isActive: true }
);
if (exiting) {
return /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.ui.focus,
flexDirection: "row",
padding: 1,
width: "100%",
alignItems: "flex-start",
children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.primary, children: "Logging in with Google... Restarting Gemini CLI to continue." })
}
);
}
return /* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.ui.focus,
flexDirection: "row",
padding: 1,
width: "100%",
alignItems: "flex-start",
children: [
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.accent, children: "? " }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsxs)(Box_default, { flexDirection: "column", flexGrow: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { bold: true, color: theme.text.primary, children: "Get started" }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.primary, children: "How would you like to authenticate for this project?" }) }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(
RadioButtonSelect,
{
items,
initialIndex: initialAuthIndex,
onSelect: handleAuthSelect,
onHighlight: () => {
onAuthError(null);
}
}
) }),
authError && /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.status.error, children: authError }) }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.secondary, children: "(Use Enter to select)" }) }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.primary, children: "Terms of Services and Privacy Notice for Gemini CLI" }) }),
/* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime76.jsx)(Text, { color: theme.text.link, children: "https://geminicli.com/docs/resources/tos-privacy/" }) })
] })
]
}
);
}
// packages/cli/src/ui/auth/BannedAccountDialog.tsx
var import_react48 = __toESM(require_react(), 1);
var import_jsx_runtime77 = __toESM(require_jsx_runtime(), 1);
function BannedAccountDialog({
accountSuspensionInfo,
onExit,
onChangeAuth
}) {
const [errorMessage, setErrorMessage] = (0, import_react48.useState)(null);
const appealUrl = accountSuspensionInfo.appealUrl;
const appealLinkText = accountSuspensionInfo.appealLinkText ?? "Open the Google Form";
const items = (0, import_react48.useMemo)(() => {
const menuItems = [];
if (appealUrl) {
menuItems.push({
label: appealLinkText,
value: "open_form",
key: "open_form"
});
}
menuItems.push(
{
label: "Change authentication",
value: "change_auth",
key: "change_auth"
},
{
label: "Exit",
value: "exit",
key: "exit"
}
);
return menuItems;
}, [appealUrl, appealLinkText]);
useKeypress(
(key) => {
if (key.name === "escape") {
void handleExit();
return true;
}
return false;
},
{ isActive: true }
);
const handleExit = (0, import_react48.useCallback)(async () => {
await runExitCleanup();
onExit();
}, [onExit]);
const handleSelect = (0, import_react48.useCallback)(
async (choice) => {
if (choice === "open_form" && appealUrl) {
if (!shouldLaunchBrowser()) {
setErrorMessage(`Please open this URL in a browser: ${appealUrl}`);
return;
}
try {
await openBrowserSecurely(appealUrl);
} catch {
setErrorMessage(`Failed to open browser. Please visit: ${appealUrl}`);
}
} else if (choice === "change_auth") {
onChangeAuth();
} else {
await handleExit();
}
},
[handleExit, onChangeAuth, appealUrl]
);
return /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(Box_default, { flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Text, { bold: true, color: theme.status.error, children: "Error: Account Suspended" }),
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Text, { children: accountSuspensionInfo.message }) }),
appealUrl && /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(import_jsx_runtime77.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Text, { children: "Appeal URL:" }) }),
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime77.jsxs)(Text, { color: theme.text.link, children: [
"[",
appealUrl,
"]"
] }) })
] }),
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Text, { color: theme.status.error, children: errorMessage }) }),
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(
RadioButtonSelect,
{
items,
onSelect: (choice) => void handleSelect(choice)
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime77.jsx)(Text, { dimColor: true, children: "Escape to exit" }) })
] });
}
// packages/cli/src/ui/auth/ApiAuthDialog.tsx
var import_react49 = __toESM(require_react(), 1);
var import_jsx_runtime78 = __toESM(require_jsx_runtime(), 1);
function ApiAuthDialog({
onSubmit,
onCancel,
error,
defaultValue = ""
}) {
const keyMatchers = useKeyMatchers();
const { terminalWidth } = useUIState();
const viewportWidth = terminalWidth - 8;
const pendingPromise = (0, import_react49.useRef)(null);
(0, import_react49.useEffect)(
() => () => {
pendingPromise.current?.cancel();
},
[]
);
const initialApiKey = defaultValue;
const buffer = useTextBuffer({
initialText: initialApiKey || "",
initialCursorOffset: initialApiKey?.length || 0,
viewport: {
width: viewportWidth,
height: 4
},
inputFilter: (text) => text.replace(/[^a-zA-Z0-9_.-]/g, "").replace(/[\r\n]/g, ""),
singleLine: true
});
const handleSubmit = (value) => {
onSubmit(value);
};
const handleClear = () => {
pendingPromise.current?.cancel();
let isCancelled = false;
const wrappedPromise = new Promise((resolve3, reject) => {
clearApiKey().then(
() => !isCancelled && resolve3(),
(error2) => !isCancelled && reject(error2)
);
});
pendingPromise.current = {
cancel: () => {
isCancelled = true;
}
};
return wrappedPromise.then(() => {
buffer.setText("");
}).catch((err) => {
debugLogger.debug("Failed to clear API key:", err);
});
};
useKeypress(
(key) => {
if (keyMatchers["edit.clear" /* CLEAR_INPUT */](key)) {
void handleClear();
return true;
}
return false;
},
{ isActive: true }
);
return /* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.ui.focus,
flexDirection: "column",
padding: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Text, { bold: true, color: theme.text.primary, children: "Enter Gemini API Key" }),
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Text, { color: theme.text.primary, children: "Please enter your Gemini API key. It will be securely stored in your system keychain." }),
/* @__PURE__ */ (0, import_jsx_runtime78.jsxs)(Text, { color: theme.text.secondary, children: [
"You can get an API key from",
" ",
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Text, { color: theme.text.link, children: "https://aistudio.google.com/app/apikey" })
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Box_default, { marginTop: 1, flexDirection: "row", children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
paddingX: 1,
flexGrow: 1,
children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(
TextInput,
{
buffer,
onSubmit: handleSubmit,
onCancel,
placeholder: "Paste your API key here"
}
)
}
) }),
error && /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Text, { color: theme.status.error, children: error }) }),
/* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime78.jsx)(Text, { color: theme.text.secondary, children: "(Press Enter to submit, Esc to cancel, Ctrl+C to clear stored key)" }) })
]
}
);
}
// packages/cli/src/ui/components/EditorSettingsDialog.tsx
var import_react50 = __toESM(require_react(), 1);
// packages/cli/src/ui/editors/editorSettingsManager.ts
var EditorSettingsManager = class {
availableEditors;
constructor() {
const editorTypes = Object.keys(
EDITOR_DISPLAY_NAMES
).sort();
this.availableEditors = [
{
name: "None",
type: "not_set",
disabled: false
},
...editorTypes.map((type) => {
const hasEditor = hasValidEditorCommand(type);
const isAllowedInSandbox = allowEditorTypeInSandbox(type);
let labelSuffix = !isAllowedInSandbox ? " (Not available in sandbox)" : "";
labelSuffix = !hasEditor ? " (Not installed)" : labelSuffix;
return {
name: EDITOR_DISPLAY_NAMES[type] + labelSuffix,
type,
disabled: !hasEditor || !isAllowedInSandbox
};
})
];
}
getAvailableEditorDisplays() {
return this.availableEditors;
}
};
var editorSettingsManager = new EditorSettingsManager();
// packages/cli/src/ui/components/EditorSettingsDialog.tsx
var import_jsx_runtime79 = __toESM(require_jsx_runtime(), 1);
function EditorSettingsDialog({
onSelect,
settings,
onExit
}) {
const [selectedScope, setSelectedScope] = (0, import_react50.useState)(
"User" /* User */
);
const [focusedSection, setFocusedSection] = (0, import_react50.useState)(
"editor"
);
useKeypress(
(key) => {
if (key.name === "tab") {
setFocusedSection((prev) => prev === "editor" ? "scope" : "editor");
return true;
}
if (key.name === "escape") {
onExit();
return true;
}
return false;
},
{ isActive: true }
);
const editorItems = editorSettingsManager.getAvailableEditorDisplays();
const currentPreference = settings.forScope(selectedScope).settings.general?.preferredEditor;
let editorIndex = currentPreference ? editorItems.findIndex(
(item) => item.type === currentPreference
) : 0;
if (editorIndex === -1) {
coreEvents.emitFeedback(
"error",
`Editor is not supported: ${currentPreference}`
);
editorIndex = 0;
}
const scopeItems = [
{
label: "User Settings",
value: "User" /* User */,
key: "User" /* User */
},
{
label: "Workspace Settings",
value: "Workspace" /* Workspace */,
key: "Workspace" /* Workspace */
}
];
const handleEditorSelect = (editorType) => {
if (editorType === "not_set") {
onSelect(void 0, selectedScope);
return;
}
onSelect(editorType, selectedScope);
};
const handleScopeSelect = (scope) => {
setSelectedScope(scope);
setFocusedSection("editor");
};
let otherScopeModifiedMessage = "";
const otherScope = selectedScope === "User" /* User */ ? "Workspace" /* Workspace */ : "User" /* User */;
if (settings.forScope(otherScope).settings.general?.preferredEditor !== void 0) {
otherScopeModifiedMessage = settings.forScope(selectedScope).settings.general?.preferredEditor !== void 0 ? `(Also modified in ${otherScope})` : `(Modified in ${otherScope})`;
}
let mergedEditorName = "None";
if (settings.merged.general.preferredEditor && isEditorAvailable(settings.merged.general.preferredEditor)) {
mergedEditorName = EDITOR_DISPLAY_NAMES[
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
settings.merged.general.preferredEditor
];
}
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "row",
padding: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box_default, { flexDirection: "column", width: "45%", paddingRight: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Text, { bold: focusedSection === "editor", children: [
focusedSection === "editor" ? "> " : " ",
"Select Editor",
" ",
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Text, { color: theme.text.secondary, children: otherScopeModifiedMessage })
] }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
RadioButtonSelect,
{
items: editorItems.map((item) => ({
label: item.name,
value: item.type,
disabled: item.disabled,
key: item.type
})),
initialIndex: editorIndex,
onSelect: handleEditorSelect,
isFocused: focusedSection === "editor"
},
selectedScope
),
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box_default, { marginTop: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Text, { bold: focusedSection === "scope", children: [
focusedSection === "scope" ? "> " : " ",
"Apply To"
] }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
RadioButtonSelect,
{
items: scopeItems,
initialIndex: 0,
onSelect: handleScopeSelect,
isFocused: focusedSection === "scope"
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Text, { color: theme.text.secondary, children: "(Use Enter to select, Tab to change focus, Esc to close)" }) })
] }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box_default, { flexDirection: "column", width: "55%", paddingLeft: 2, children: [
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Text, { bold: true, color: theme.text.primary, children: "Editor Preference" }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Box_default, { flexDirection: "column", gap: 1, marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Text, { color: theme.text.secondary, children: "These editors are currently supported. Please note that some editors cannot be used in sandbox mode." }),
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(Text, { color: theme.text.secondary, children: [
"Your preferred editor is:",
" ",
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
Text,
{
color: mergedEditorName === "None" ? theme.status.error : theme.text.link,
bold: true,
children: mergedEditorName
}
),
"."
] })
] })
] })
]
}
);
}
// packages/cli/src/ui/privacy/GeminiPrivacyNotice.tsx
var import_jsx_runtime80 = __toESM(require_jsx_runtime(), 1);
var GeminiPrivacyNotice = ({ onExit }) => {
useKeypress(
(key) => {
if (key.name === "escape") {
onExit();
return true;
}
return false;
},
{ isActive: true }
);
return /* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { bold: true, color: theme.text.accent, children: "Gemini API Key Notice" }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Text, { color: theme.text.primary, children: [
"By using the Gemini API",
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.text.link, children: "[1]" }),
", Google AI Studio",
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.status.error, children: "[2]" }),
', and the other Google developer services that reference these terms (collectively, the "APIs" or "Services"), you are agreeing to Google APIs Terms of Service (the "API Terms")',
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.status.success, children: "[3]" }),
', and the Gemini API Additional Terms of Service (the "Additional Terms")',
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.text.accent, children: "[4]" }),
"."
] }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.text.link, children: "[1]" }),
" ",
"https://ai.google.dev/docs/gemini_api_overview"
] }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.status.error, children: "[2]" }),
" https://aistudio.google.com/"
] }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.status.success, children: "[3]" }),
" ",
"https://developers.google.com/terms"
] }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.text.accent, children: "[4]" }),
" ",
"https://ai.google.dev/gemini-api/terms"
] }),
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime80.jsx)(Text, { color: theme.text.secondary, children: "Press Esc to exit." })
] });
};
// packages/cli/src/ui/privacy/CloudPaidPrivacyNotice.tsx
var import_jsx_runtime81 = __toESM(require_jsx_runtime(), 1);
var CloudPaidPrivacyNotice = ({
onExit
}) => {
useKeypress(
(key) => {
if (key.name === "escape") {
onExit();
return true;
}
return false;
},
{ isActive: true }
);
return /* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { bold: true, color: theme.text.accent, children: "Vertex AI Notice" }),
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(Text, { color: theme.text.primary, children: [
"Service Specific Terms",
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { color: theme.text.link, children: "[1]" }),
" are incorporated into the agreement under which Google has agreed to provide Google Cloud Platform",
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { color: theme.status.success, children: "[2]" }),
" to Customer (the \u201CAgreement\u201D). If the Agreement authorizes the resale or supply of Google Cloud Platform under a Google Cloud partner or reseller program, then except for in the section entitled \u201CPartner-Specific Terms\u201D, all references to Customer in the Service Specific Terms mean Partner or Reseller (as applicable), and all references to Customer Data in the Service Specific Terms mean Partner Data. Capitalized terms used but not defined in the Service Specific Terms have the meaning given to them in the Agreement."
] }),
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { color: theme.text.link, children: "[1]" }),
" ",
"https://cloud.google.com/terms/service-terms"
] }),
/* @__PURE__ */ (0, import_jsx_runtime81.jsxs)(Text, { color: theme.text.primary, children: [
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { color: theme.status.success, children: "[2]" }),
" ",
"https://cloud.google.com/terms/services"
] }),
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime81.jsx)(Text, { color: theme.text.secondary, children: "Press Esc to exit." })
] });
};
// packages/cli/src/ui/hooks/usePrivacySettings.ts
var import_react51 = __toESM(require_react(), 1);
var usePrivacySettings = (config) => {
const [privacyState, setPrivacyState] = (0, import_react51.useState)({
isLoading: true
});
(0, import_react51.useEffect)(() => {
const fetchInitialState = async () => {
setPrivacyState({
isLoading: true
});
try {
const server = getCodeAssistServerOrFail(config);
const tier = server.userTier;
if (tier === void 0) {
throw new Error("Could not determine user tier.");
}
if (tier !== UserTierId.FREE) {
setPrivacyState({
isLoading: false,
isFreeTier: false
});
return;
}
const optIn = await getRemoteDataCollectionOptIn(server);
setPrivacyState({
isLoading: false,
isFreeTier: true,
dataCollectionOptIn: optIn
});
} catch (e) {
setPrivacyState({
isLoading: false,
error: e instanceof Error ? e.message : String(e)
});
}
};
fetchInitialState();
}, [config]);
const updateDataCollectionOptIn = (0, import_react51.useCallback)(
async (optIn) => {
try {
const server = getCodeAssistServerOrFail(config);
const updatedOptIn = await setRemoteDataCollectionOptIn(server, optIn);
setPrivacyState({
isLoading: false,
isFreeTier: true,
dataCollectionOptIn: updatedOptIn
});
} catch (e) {
setPrivacyState({
isLoading: false,
error: e instanceof Error ? e.message : String(e)
});
}
},
[config]
);
return {
privacyState,
updateDataCollectionOptIn
};
};
function getCodeAssistServerOrFail(config) {
const server = getCodeAssistServer(config);
if (server === void 0) {
throw new Error("Oauth not being used");
} else if (server.projectId === void 0) {
throw new Error("CodeAssist server is missing a project ID");
}
return server;
}
async function getRemoteDataCollectionOptIn(server) {
try {
const resp = await server.getCodeAssistGlobalUserSetting();
if (resp.freeTierDataCollectionOptin === void 0) {
debugLogger.warn(
"Warning: Code Assist API did not return freeTierDataCollectionOptin. Defaulting to true."
);
}
return resp.freeTierDataCollectionOptin ?? true;
} catch (error) {
if (error && typeof error === "object" && "response" in error) {
const gaxiosError = error;
if (gaxiosError.response?.status === 404) {
return true;
}
}
throw error;
}
}
async function setRemoteDataCollectionOptIn(server, optIn) {
const resp = await server.setCodeAssistGlobalUserSetting({
cloudaicompanionProject: server.projectId,
freeTierDataCollectionOptin: optIn
});
if (resp.freeTierDataCollectionOptin === void 0) {
debugLogger.warn(
`Warning: Code Assist API did not return freeTierDataCollectionOptin. Defaulting to ${optIn}.`
);
}
return resp.freeTierDataCollectionOptin ?? optIn;
}
// packages/cli/src/ui/privacy/CloudFreePrivacyNotice.tsx
var import_jsx_runtime82 = __toESM(require_jsx_runtime(), 1);
var CloudFreePrivacyNotice = ({
config,
onExit
}) => {
const { privacyState, updateDataCollectionOptIn } = usePrivacySettings(config);
useKeypress(
(key) => {
if ((privacyState.error || privacyState.isFreeTier === false) && key.name === "escape") {
onExit();
return true;
}
return false;
},
{ isActive: true }
);
if (privacyState.isLoading) {
return /* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.secondary, children: "Loading..." });
}
if (privacyState.error) {
return /* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Box_default, { flexDirection: "column", marginY: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Text, { color: theme.status.error, children: [
"Error loading Opt-in settings: ",
privacyState.error
] }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.secondary, children: "Press Esc to exit." })
] });
}
if (privacyState.isFreeTier === false) {
return /* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Box_default, { flexDirection: "column", marginY: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { bold: true, color: theme.text.accent, children: "Gemini Code Assist Privacy Notice" }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { children: "https://developers.google.com/gemini-code-assist/resources/privacy-notices" }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.secondary, children: "Press Esc to exit." })
] });
}
const items = [
{ label: "Yes", value: true, key: "true" },
{ label: "No", value: false, key: "false" }
];
return /* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Box_default, { flexDirection: "column", marginY: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { bold: true, color: theme.text.accent, children: "Gemini Code Assist for Individuals Privacy Notice" }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Text, { color: theme.text.primary, children: [
"This notice and our Privacy Policy",
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.link, children: "[1]" }),
" describe how Gemini Code Assist handles your data. Please read them carefully."
] }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.primary, children: "When you use Gemini Code Assist for individuals with Gemini CLI, Google collects your prompts, related code, generated output, code edits, related feature usage information, and your feedback to provide, improve, and develop Google products and services and machine learning technologies." }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.primary, children: "To help with quality and improve our products (such as generative machine-learning models), human reviewers may read, annotate, and process the data collected above. We take steps to protect your privacy as part of this process. This includes disconnecting the data from your Google Account before reviewers see or annotate it, and storing those disconnected copies for up to 18 months. Please don't submit confidential information or any data you wouldn't want a reviewer to see or Google to use to improve our products, services and machine-learning technologies." }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.primary, children: "Allow Google to use this data to develop and improve our products?" }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(
RadioButtonSelect,
{
items,
initialIndex: privacyState.dataCollectionOptIn ? 0 : 1,
onSelect: (value) => {
updateDataCollectionOptIn(value);
if (!privacyState.error) {
onExit();
}
}
}
)
] }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.link, children: "[1]" }),
" ",
"https://policies.google.com/privacy"
] }),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Newline, {}),
/* @__PURE__ */ (0, import_jsx_runtime82.jsx)(Text, { color: theme.text.secondary, children: "Press Enter to choose an option and exit." })
] });
};
// packages/cli/src/ui/privacy/PrivacyNotice.tsx
var import_jsx_runtime83 = __toESM(require_jsx_runtime(), 1);
var PrivacyNoticeText = ({
config,
onExit
}) => {
const authType = config.getContentGeneratorConfig()?.authType;
switch (authType) {
case AuthType.USE_GEMINI:
return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(GeminiPrivacyNotice, { onExit });
case AuthType.USE_VERTEX_AI:
return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(CloudPaidPrivacyNotice, { onExit });
case AuthType.LOGIN_WITH_GOOGLE:
default:
return /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(CloudFreePrivacyNotice, { config, onExit });
}
};
var PrivacyNotice = ({ onExit, config }) => /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(Box_default, { borderStyle: "round", padding: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(PrivacyNoticeText, { config, onExit }) });
// packages/cli/src/ui/components/ProQuotaDialog.tsx
var import_jsx_runtime84 = __toESM(require_jsx_runtime(), 1);
function ProQuotaDialog({
failedModel,
fallbackModel,
message,
isTerminalQuotaError,
isModelNotFoundError,
authType,
tierName,
onChoice
}) {
let items;
if (failedModel === fallbackModel) {
items = [
{
label: "Keep trying",
value: "retry_once",
key: "retry_once"
},
{
label: "Stop",
value: "retry_later",
key: "retry_later"
}
];
} else if (isModelNotFoundError || isTerminalQuotaError) {
const isUltra = isUltraTier(tierName);
items = [
{
label: `Switch to ${fallbackModel}`,
value: "retry_always",
key: "retry_always"
},
...authType === AuthType.LOGIN_WITH_GOOGLE && !isUltra ? [
{
label: "Upgrade for higher limits",
value: "upgrade",
key: "upgrade"
}
] : [],
{
label: `Stop`,
value: "retry_later",
key: "retry_later"
}
];
} else {
items = [
{
label: "Keep trying",
value: "retry_once",
key: "retry_once"
},
{
label: `Switch to ${fallbackModel}`,
value: "retry_always",
key: "retry_always"
},
{
label: "Stop",
value: "retry_later",
key: "retry_later"
}
];
}
const handleSelect = (choice) => {
onChoice(choice);
};
const renderMessage = (msg) => {
const parts = msg.split(/(\s+)/);
return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Text, { children: parts.map((part, index) => {
if (part.startsWith("/")) {
return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Text, { bold: true, color: theme.text.accent, children: part }, index);
}
return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Text, { children: part }, index);
}) });
};
return /* @__PURE__ */ (0, import_jsx_runtime84.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Box_default, { marginBottom: 1, children: renderMessage(message) }),
/* @__PURE__ */ (0, import_jsx_runtime84.jsx)(Box_default, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(RadioButtonSelect, { items, onSelect: handleSelect }) })
] });
}
// packages/cli/src/ui/components/ValidationDialog.tsx
var import_react52 = __toESM(require_react(), 1);
var import_jsx_runtime85 = __toESM(require_jsx_runtime(), 1);
function ValidationDialog({
validationLink,
learnMoreUrl,
onChoice
}) {
const keyMatchers = useKeyMatchers();
const [state, setState] = (0, import_react52.useState)("choosing");
const [errorMessage, setErrorMessage] = (0, import_react52.useState)("");
const items = [
{
label: "Verify your account",
value: "verify",
key: "verify"
},
{
label: "Change authentication",
value: "change_auth",
key: "change_auth"
}
];
useKeypress(
(key) => {
if (keyMatchers["basic.cancel" /* ESCAPE */](key) || keyMatchers["basic.quit" /* QUIT */](key)) {
onChoice("cancel");
return true;
} else if (state === "waiting" && keyMatchers["basic.confirm" /* RETURN */](key)) {
setState("complete");
return true;
}
return false;
},
{ isActive: state !== "complete" }
);
(0, import_react52.useEffect)(() => {
if (state === "complete") {
const timer = setTimeout(() => {
onChoice("verify");
}, 500);
return () => clearTimeout(timer);
}
return void 0;
}, [state, onChoice]);
const handleSelect = (0, import_react52.useCallback)(
async (choice) => {
if (choice === "verify") {
if (validationLink) {
if (!shouldLaunchBrowser()) {
setErrorMessage(
`Please open this URL in a browser: ${validationLink}`
);
setState("waiting");
return;
}
try {
await openBrowserSecurely(validationLink);
setState("waiting");
} catch (error) {
setErrorMessage(
error instanceof Error ? error.message : "Failed to open browser"
);
setState("error");
}
} else {
onChoice("verify");
}
} else {
onChoice(choice);
}
},
[validationLink, onChoice]
);
if (state === "error") {
return /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { color: theme.status.error, children: errorMessage || "Failed to open verification link. Please try again or change authentication." }),
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(
RadioButtonSelect,
{
items,
onSelect: (choice) => void handleSelect(choice)
}
) })
] });
}
if (state === "waiting") {
return /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(CliSpinner, {}),
/* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Text, { children: [
" ",
"Waiting for verification... (Press Esc or Ctrl+C to cancel)"
] })
] }),
errorMessage && /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { children: errorMessage }) }),
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { dimColor: true, children: "Press Enter when verification is complete." }) })
] });
}
if (state === "complete") {
return /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { color: theme.status.success, children: "Verification complete" }) });
}
return /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { children: "Further action is required to use this service." }) }),
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(
RadioButtonSelect,
{
items,
onSelect: (choice) => void handleSelect(choice)
}
) }),
learnMoreUrl && /* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime85.jsxs)(Text, { dimColor: true, children: [
"Learn more: ",
/* @__PURE__ */ (0, import_jsx_runtime85.jsx)(Text, { color: theme.text.accent, children: learnMoreUrl })
] }) })
] });
}
// packages/cli/src/ui/components/OverageMenuDialog.tsx
var import_jsx_runtime86 = __toESM(require_jsx_runtime(), 1);
function OverageMenuDialog({
failedModel,
fallbackModel,
resetTime,
creditBalance,
onChoice
}) {
const items = [
{
label: "Use AI Credits - Continue this request (Overage)",
value: "use_credits",
key: "use_credits"
},
{
label: "Manage - View balance and purchase more credits",
value: "manage",
key: "manage"
}
];
if (fallbackModel) {
items.push({
label: `Switch to ${fallbackModel}`,
value: "use_fallback",
key: "use_fallback"
});
}
items.push({
label: "Stop - Abort request",
value: "stop",
key: "stop"
});
return /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Box_default, { marginBottom: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { color: theme.status.warning, children: [
"Usage limit reached for ",
failedModel,
"."
] }),
resetTime && /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { children: [
"Access resets at ",
resetTime,
"."
] }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Text, { bold: true, color: theme.text.accent, children: "/stats" }),
" ",
"model for usage details"
] }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Text, { bold: true, color: theme.text.accent, children: "/model" }),
" ",
"to switch models."
] }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Text, { bold: true, color: theme.text.accent, children: "/auth" }),
" ",
"to switch to API key."
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime86.jsxs)(Text, { children: [
"You have",
" ",
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Text, { bold: true, color: theme.status.success, children: creditBalance }),
" ",
"AI Credits available."
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Text, { children: "How would you like to proceed?" }) }),
/* @__PURE__ */ (0, import_jsx_runtime86.jsx)(Box_default, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime86.jsx)(RadioButtonSelect, { items, onSelect: onChoice }) })
] });
}
// packages/cli/src/ui/components/EmptyWalletDialog.tsx
var import_jsx_runtime87 = __toESM(require_jsx_runtime(), 1);
function EmptyWalletDialog({
failedModel,
fallbackModel,
resetTime,
onGetCredits,
onChoice
}) {
const items = [
{
label: "Get AI Credits - Open browser to purchase credits",
value: "get_credits",
key: "get_credits"
}
];
if (fallbackModel) {
items.push({
label: `Switch to ${fallbackModel}`,
value: "use_fallback",
key: "use_fallback"
});
}
items.push({
label: "Stop - Abort request",
value: "stop",
key: "stop"
});
const handleSelect = (choice) => {
if (choice === "get_credits") {
onGetCredits?.();
}
onChoice(choice);
};
return /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Box_default, { borderStyle: "round", flexDirection: "column", padding: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Box_default, { marginBottom: 1, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Text, { color: theme.status.warning, children: [
"Usage limit reached for ",
failedModel,
"."
] }),
resetTime && /* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Text, { children: [
"Access resets at ",
resetTime,
"."
] }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { bold: true, color: theme.text.accent, children: "/stats" }),
" ",
"model for usage details"
] }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { bold: true, color: theme.text.accent, children: "/model" }),
" ",
"to switch models."
] }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { bold: true, color: theme.text.accent, children: "/auth" }),
" ",
"to switch to API key."
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { children: "To continue using this model now, purchase more AI Credits." }) }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { dimColor: true, children: "Newly purchased AI credits may take a few minutes to update." }) }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Text, { children: "How would you like to proceed?" }) }),
/* @__PURE__ */ (0, import_jsx_runtime87.jsx)(Box_default, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime87.jsx)(RadioButtonSelect, { items, onSelect: handleSelect }) })
] });
}
// packages/cli/src/ui/components/SessionBrowser.tsx
var import_react53 = __toESM(require_react(), 1);
// packages/cli/src/ui/colors.ts
var Colors = {
get type() {
return themeManager.getActiveTheme().colors.type;
},
get Foreground() {
return themeManager.getActiveTheme().colors.Foreground;
},
get Background() {
return themeManager.getColors().Background;
},
get LightBlue() {
return themeManager.getActiveTheme().colors.LightBlue;
},
get AccentBlue() {
return themeManager.getActiveTheme().colors.AccentBlue;
},
get AccentPurple() {
return themeManager.getActiveTheme().colors.AccentPurple;
},
get AccentCyan() {
return themeManager.getActiveTheme().colors.AccentCyan;
},
get AccentGreen() {
return themeManager.getActiveTheme().colors.AccentGreen;
},
get AccentYellow() {
return themeManager.getActiveTheme().colors.AccentYellow;
},
get AccentRed() {
return themeManager.getActiveTheme().colors.AccentRed;
},
get DiffAdded() {
return themeManager.getActiveTheme().colors.DiffAdded;
},
get DiffRemoved() {
return themeManager.getActiveTheme().colors.DiffRemoved;
},
get Comment() {
return themeManager.getActiveTheme().colors.Comment;
},
get Gray() {
return themeManager.getActiveTheme().colors.Gray;
},
get DarkGray() {
return themeManager.getColors().DarkGray;
},
get InputBackground() {
return themeManager.getColors().InputBackground;
},
get MessageBackground() {
return themeManager.getColors().MessageBackground;
},
get GradientColors() {
return themeManager.getActiveTheme().colors.GradientColors;
}
};
// packages/cli/src/ui/components/SessionBrowser.tsx
import path5 from "node:path";
// packages/cli/src/ui/components/SessionBrowser/SessionBrowserNav.tsx
var import_jsx_runtime88 = __toESM(require_jsx_runtime(), 1);
var Kbd = ({ name, shortcut }) => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(import_jsx_runtime88.Fragment, { children: [
name,
": ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Text, { bold: true, children: shortcut })
] });
var NavigationHelpDisplay = () => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Text, { color: Colors.Gray, children: [
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Navigate", shortcut: "\u2191/\u2193" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Resume", shortcut: "Enter" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Search", shortcut: "/" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Delete", shortcut: "x" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Quit", shortcut: "q" })
] }),
/* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Text, { color: Colors.Gray, children: [
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Sort", shortcut: "s" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "Reverse", shortcut: "r" }),
" ",
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Kbd, { name: "First/Last", shortcut: "g/G" })
] })
] });
var SearchModeDisplay = ({
state
}) => /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Box_default, { marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Text, { color: Colors.Gray, children: "Search: " }),
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Text, { color: Colors.AccentPurple, children: state.searchQuery }),
/* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Text, { color: Colors.Gray, children: " (Esc to cancel)" })
] });
var NoResultsDisplay = ({
state
}) => /* @__PURE__ */ (0, import_jsx_runtime88.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime88.jsxs)(Text, { color: Colors.Gray, dimColor: true, children: [
"No sessions found matching '",
state.searchQuery,
"'."
] }) });
// packages/cli/src/ui/components/SessionBrowser/SessionListHeader.tsx
var import_jsx_runtime89 = __toESM(require_jsx_runtime(), 1);
var SessionListHeader = ({
state
}) => /* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(Box_default, { flexDirection: "row", justifyContent: "space-between", children: [
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(Text, { color: Colors.AccentPurple, children: [
"Chat Sessions (",
state.totalSessions,
" total",
state.searchQuery ? `, filtered` : "",
")"
] }),
/* @__PURE__ */ (0, import_jsx_runtime89.jsxs)(Text, { color: Colors.Gray, children: [
"sorted by ",
state.sortOrder,
" ",
state.sortReverse ? "asc" : "desc"
] })
] });
// packages/cli/src/ui/components/SessionBrowser/SessionBrowserLoading.tsx
var import_jsx_runtime90 = __toESM(require_jsx_runtime(), 1);
var SessionBrowserLoading = () => /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(Box_default, { flexDirection: "column", paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime90.jsx)(Text, { color: Colors.Gray, children: "Loading sessions\u2026" }) });
// packages/cli/src/ui/components/SessionBrowser/SessionBrowserError.tsx
var import_jsx_runtime91 = __toESM(require_jsx_runtime(), 1);
var SessionBrowserError = ({
state
}) => /* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime91.jsxs)(Text, { color: Colors.AccentRed, children: [
"Error: ",
state.error
] }),
/* @__PURE__ */ (0, import_jsx_runtime91.jsx)(Text, { color: Colors.Gray, children: "Press q to exit" })
] });
// packages/cli/src/ui/components/SessionBrowser/SessionBrowserEmpty.tsx
var import_jsx_runtime92 = __toESM(require_jsx_runtime(), 1);
var SessionBrowserEmpty = () => /* @__PURE__ */ (0, import_jsx_runtime92.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Text, { color: Colors.Gray, children: "No auto-saved conversations found." }),
/* @__PURE__ */ (0, import_jsx_runtime92.jsx)(Text, { color: Colors.Gray, children: "Press q to exit" })
] });
// packages/cli/src/ui/components/SessionBrowser/utils.ts
var sortSessions = (sessions, sortBy, reverse) => {
const sorted = [...sessions].sort((a, b) => {
switch (sortBy) {
case "date":
return new Date(b.lastUpdated).getTime() - new Date(a.lastUpdated).getTime();
case "messages":
return b.messageCount - a.messageCount;
case "name":
return a.displayName.localeCompare(b.displayName);
default:
return 0;
}
});
return reverse ? sorted.reverse() : sorted;
};
var findTextMatches = (messages, query) => {
if (!query.trim()) return [];
const lowerQuery = query.toLowerCase();
const matches = [];
for (const message of messages) {
const m = cleanMessage(message.content);
const lowerContent = m.toLowerCase();
let startIndex = 0;
while (true) {
const matchIndex = lowerContent.indexOf(lowerQuery, startIndex);
if (matchIndex === -1) break;
const contextStart = Math.max(0, matchIndex - 10);
const contextEnd = Math.min(m.length, matchIndex + query.length + 10);
const snippet = m.slice(contextStart, contextEnd);
const relativeMatchStart = matchIndex - contextStart;
const relativeMatchEnd = relativeMatchStart + query.length;
let before = snippet.slice(0, relativeMatchStart);
const match = snippet.slice(relativeMatchStart, relativeMatchEnd);
let after = snippet.slice(relativeMatchEnd);
if (contextStart > 0) before = "\u2026" + before;
if (contextEnd < m.length) after = after + "\u2026";
matches.push({ before, match, after, role: message.role });
startIndex = matchIndex + 1;
}
}
return matches;
};
var filterSessions = (sessions, query) => {
if (!query.trim()) {
return sessions.map((session) => ({
...session,
matchSnippets: void 0,
matchCount: void 0
}));
}
const lowerQuery = query.toLowerCase();
return sessions.filter((session) => {
const titleMatch = session.displayName.toLowerCase().includes(lowerQuery) || session.id.toLowerCase().includes(lowerQuery) || session.firstUserMessage.toLowerCase().includes(lowerQuery);
const contentMatch = session.fullContent?.toLowerCase().includes(lowerQuery);
if (titleMatch || contentMatch) {
if (session.messages) {
session.matchSnippets = findTextMatches(session.messages, query);
session.matchCount = session.matchSnippets.length;
}
return true;
}
return false;
});
};
// packages/cli/src/ui/components/SessionBrowser.tsx
var import_jsx_runtime93 = __toESM(require_jsx_runtime(), 1);
var SESSIONS_PER_PAGE = 20;
var FIXED_SESSION_COLUMNS_WIDTH = 30;
var SessionTableHeader = ({
state
}) => /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Box_default, { flexDirection: "row", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { children: state.scrollOffset > 0 ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { children: "\u25B2 " }) : " " }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 5, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, bold: true, children: "Index" }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, children: " \u2502 " }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 4, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, bold: true, children: "Msgs" }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, children: " \u2502 " }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 4, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, bold: true, children: "Age" }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, children: " \u2502 " }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, bold: true, children: state.searchQuery ? "Match" : "Name" }) })
] });
var MatchSnippetDisplay = ({
session,
textColor
}) => {
if (!session.matchSnippets || session.matchSnippets.length === 0) {
return null;
}
const firstMatch = session.matchSnippets[0];
const rolePrefix = firstMatch.role === "user" ? "You: " : "Gemini:";
const roleColor = textColor(
firstMatch.role === "user" ? Colors.AccentGreen : Colors.AccentBlue
);
return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(import_jsx_runtime93.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: roleColor, bold: true, children: [
rolePrefix,
" "
] }),
firstMatch.before,
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(Colors.AccentRed), bold: true, children: firstMatch.match }),
firstMatch.after
] });
};
var SessionItem = ({
session,
state,
terminalWidth,
formatRelativeTime: formatRelativeTime2
}) => {
const originalIndex = state.startIndex + state.visibleSessions.indexOf(session);
const isActive = originalIndex === state.activeIndex;
const isDisabled = session.isCurrentSession;
const textColor = (c = Colors.Foreground) => {
if (isDisabled) {
return Colors.Gray;
}
return isActive ? theme.ui.focus : c;
};
const prefix = isActive ? "\u276F " : " ";
let additionalInfo = "";
let matchDisplay = null;
if (session.isCurrentSession) {
additionalInfo = " (current)";
}
if (state.searchQuery && session.matchSnippets && session.matchSnippets.length > 0) {
matchDisplay = /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(MatchSnippetDisplay, { session, textColor });
if (session.matchCount && session.matchCount > 1) {
additionalInfo += ` (+${session.matchCount - 1} more)`;
}
}
const reservedForMeta = additionalInfo ? additionalInfo.length + 1 : 0;
const availableMessageWidth = Math.max(
20,
terminalWidth - FIXED_SESSION_COLUMNS_WIDTH - reservedForMeta
);
const truncatedMessage = matchDisplay || (session.displayName.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(Colors.Gray), dimColor: true, children: "(No messages)" }) : session.displayName.length > availableMessageWidth ? session.displayName.slice(0, availableMessageWidth - 1) + "\u2026" : session.displayName);
return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(
Box_default,
{
flexDirection: "row",
backgroundColor: isActive ? theme.background.focus : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(), dimColor: isDisabled, children: prefix }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 5, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: textColor(), dimColor: isDisabled, children: [
"#",
originalIndex + 1
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [
" ",
"\u2502",
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 4, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(), dimColor: isDisabled, children: session.messageCount }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [
" ",
"\u2502",
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { width: 4, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(), dimColor: isDisabled, children: formatRelativeTime2(session.lastUpdated, "short") }) }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: textColor(Colors.Gray), dimColor: isDisabled, children: [
" ",
"\u2502",
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Text, { color: textColor(Colors.Comment), dimColor: isDisabled, children: [
truncatedMessage,
additionalInfo && /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: textColor(Colors.Gray), dimColor: true, bold: false, children: additionalInfo })
] }) })
]
}
);
};
var SessionList = ({
state,
formatRelativeTime: formatRelativeTime2
}) => /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Box_default, { flexDirection: "column", children: [
!state.isSearchMode && /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(NavigationHelpDisplay, {}),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionTableHeader, { state })
] }),
state.visibleSessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(
SessionItem,
{
session,
state,
terminalWidth: state.terminalWidth,
formatRelativeTime: formatRelativeTime2
},
session.id
)),
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { color: Colors.Gray, children: state.endIndex < state.totalSessions ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(import_jsx_runtime93.Fragment, { children: "\u25BC" }) : /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(Text, { dimColor: true, children: "\u25BC" }) })
] });
var useSessionBrowserState = (initialSessions = [], initialLoading = true, initialError = null) => {
const { columns: terminalWidth } = useTerminalSize();
const [sessions, setSessions] = (0, import_react53.useState)(initialSessions);
const [loading, setLoading] = (0, import_react53.useState)(initialLoading);
const [error, setError] = (0, import_react53.useState)(initialError);
const [activeIndex, setActiveIndex] = (0, import_react53.useState)(0);
const [scrollOffset, setScrollOffset] = (0, import_react53.useState)(0);
const [sortOrder, setSortOrder] = (0, import_react53.useState)(
"date"
);
const [sortReverse, setSortReverse] = (0, import_react53.useState)(false);
const [searchQuery, setSearchQuery] = (0, import_react53.useState)("");
const [isSearchMode, setIsSearchMode] = (0, import_react53.useState)(false);
const [hasLoadedFullContent, setHasLoadedFullContent] = (0, import_react53.useState)(false);
const loadingFullContentRef = (0, import_react53.useRef)(false);
const filteredAndSortedSessions = (0, import_react53.useMemo)(() => {
const filtered = filterSessions(sessions, searchQuery);
return sortSessions(filtered, sortOrder, sortReverse);
}, [sessions, searchQuery, sortOrder, sortReverse]);
(0, import_react53.useEffect)(() => {
if (!searchQuery) {
setHasLoadedFullContent(false);
loadingFullContentRef.current = false;
}
}, [searchQuery]);
const totalSessions = filteredAndSortedSessions.length;
const startIndex = scrollOffset;
const endIndex = Math.min(scrollOffset + SESSIONS_PER_PAGE, totalSessions);
const visibleSessions = filteredAndSortedSessions.slice(startIndex, endIndex);
const state = {
sessions,
setSessions,
loading,
setLoading,
error,
setError,
activeIndex,
setActiveIndex,
scrollOffset,
setScrollOffset,
searchQuery,
setSearchQuery,
isSearchMode,
setIsSearchMode,
hasLoadedFullContent,
setHasLoadedFullContent,
sortOrder,
setSortOrder,
sortReverse,
setSortReverse,
terminalWidth,
filteredAndSortedSessions,
totalSessions,
startIndex,
endIndex,
visibleSessions
};
return state;
};
var useLoadSessions = (config, state) => {
const {
setSessions,
setLoading,
setError,
isSearchMode,
hasLoadedFullContent,
setHasLoadedFullContent
} = state;
(0, import_react53.useEffect)(() => {
const loadSessions = async () => {
try {
const chatsDir = path5.join(config.storage.getProjectTempDir(), "chats");
const sessionData = await getSessionFiles(
chatsDir,
config.getSessionId()
);
setSessions(sessionData);
setLoading(false);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to load sessions"
);
setLoading(false);
}
};
loadSessions();
}, [config, setSessions, setLoading, setError]);
(0, import_react53.useEffect)(() => {
const loadFullContent = async () => {
if (isSearchMode && !hasLoadedFullContent) {
try {
const chatsDir = path5.join(
config.storage.getProjectTempDir(),
"chats"
);
const sessionData = await getSessionFiles(
chatsDir,
config.getSessionId(),
{ includeFullContent: true }
);
setSessions(sessionData);
setHasLoadedFullContent(true);
} catch (err) {
setError(
err instanceof Error ? err.message : "Failed to load full session content"
);
}
}
};
loadFullContent();
}, [
isSearchMode,
hasLoadedFullContent,
config,
setSessions,
setHasLoadedFullContent,
setError
]);
};
var useMoveSelection = (state) => {
const {
totalSessions,
activeIndex,
scrollOffset,
setActiveIndex,
setScrollOffset
} = state;
return (0, import_react53.useCallback)(
(delta) => {
const newIndex = Math.max(
0,
Math.min(totalSessions - 1, activeIndex + delta)
);
setActiveIndex(newIndex);
if (newIndex < scrollOffset) {
setScrollOffset(newIndex);
} else if (newIndex >= scrollOffset + SESSIONS_PER_PAGE) {
setScrollOffset(newIndex - SESSIONS_PER_PAGE + 1);
}
},
[totalSessions, activeIndex, scrollOffset, setActiveIndex, setScrollOffset]
);
};
var useCycleSortOrder = (state) => {
const { sortOrder, setSortOrder } = state;
return (0, import_react53.useCallback)(() => {
const orders = [
"date",
"messages",
"name"
];
const currentIndex = orders.indexOf(sortOrder);
const nextIndex = (currentIndex + 1) % orders.length;
setSortOrder(orders[nextIndex]);
}, [sortOrder, setSortOrder]);
};
var useSessionBrowserInput = (state, moveSelection, cycleSortOrder, onResumeSession, onDeleteSession, onExit) => {
useKeypress(
(key) => {
if (state.isSearchMode) {
if (key.name === "escape") {
state.setIsSearchMode(false);
state.setSearchQuery("");
state.setActiveIndex(0);
state.setScrollOffset(0);
return true;
} else if (key.name === "backspace") {
state.setSearchQuery((prev) => prev.slice(0, -1));
state.setActiveIndex(0);
state.setScrollOffset(0);
return true;
} else if (key.name === "enter") {
const selectedSession = state.filteredAndSortedSessions[state.activeIndex];
if (selectedSession && !selectedSession.isCurrentSession) {
onResumeSession(selectedSession);
}
return true;
} else if (key.sequence && key.sequence.length === 1 && !key.alt && !key.ctrl && !key.cmd) {
state.setSearchQuery((prev) => prev + key.sequence);
state.setActiveIndex(0);
state.setScrollOffset(0);
return true;
}
} else {
if (key.sequence === "g") {
state.setActiveIndex(0);
state.setScrollOffset(0);
return true;
} else if (key.sequence === "G") {
state.setActiveIndex(state.totalSessions - 1);
state.setScrollOffset(
Math.max(0, state.totalSessions - SESSIONS_PER_PAGE)
);
return true;
} else if (key.sequence === "s") {
cycleSortOrder();
return true;
} else if (key.sequence === "r") {
state.setSortReverse(!state.sortReverse);
return true;
} else if (key.sequence === "/") {
state.setIsSearchMode(true);
return true;
} else if (key.sequence === "q" || key.sequence === "Q" || key.name === "escape") {
onExit();
return true;
} else if (key.sequence === "x" || key.sequence === "X") {
const selectedSession = state.filteredAndSortedSessions[state.activeIndex];
if (selectedSession && !selectedSession.isCurrentSession) {
onDeleteSession(selectedSession).then(() => {
state.setSessions(
state.sessions.filter((s) => s.id !== selectedSession.id)
);
if (state.activeIndex >= state.filteredAndSortedSessions.length - 1) {
state.setActiveIndex(
Math.max(0, state.filteredAndSortedSessions.length - 2)
);
}
}).catch((error) => {
state.setError(
`Failed to delete session: ${error instanceof Error ? error.message : "Unknown error"}`
);
});
}
return true;
} else if (key.sequence === "u") {
moveSelection(-Math.round(SESSIONS_PER_PAGE / 2));
return true;
} else if (key.sequence === "d") {
moveSelection(Math.round(SESSIONS_PER_PAGE / 2));
return true;
}
}
if (key.name === "enter" && state.filteredAndSortedSessions[state.activeIndex]) {
const selectedSession = state.filteredAndSortedSessions[state.activeIndex];
if (!selectedSession.isCurrentSession) {
onResumeSession(selectedSession);
}
return true;
} else if (key.name === "up") {
moveSelection(-1);
return true;
} else if (key.name === "down") {
moveSelection(1);
return true;
} else if (key.name === "pageup") {
moveSelection(-SESSIONS_PER_PAGE);
return true;
} else if (key.name === "pagedown") {
moveSelection(SESSIONS_PER_PAGE);
return true;
}
return false;
},
{ isActive: true }
);
};
function SessionBrowserView({
state
}) {
if (state.loading) {
return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionBrowserLoading, {});
}
if (state.error) {
return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionBrowserError, { state });
}
if (state.sessions.length === 0) {
return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionBrowserEmpty, {});
}
return /* @__PURE__ */ (0, import_jsx_runtime93.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionListHeader, { state }),
state.isSearchMode && /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SearchModeDisplay, { state }),
state.totalSessions === 0 ? /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(NoResultsDisplay, { state }) : /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionList, { state, formatRelativeTime })
] });
}
function SessionBrowser({
config,
onResumeSession,
onDeleteSession,
onExit
}) {
const state = useSessionBrowserState();
useLoadSessions(config, state);
const moveSelection = useMoveSelection(state);
const cycleSortOrder = useCycleSortOrder(state);
useSessionBrowserInput(
state,
moveSelection,
cycleSortOrder,
onResumeSession,
onDeleteSession,
onExit
);
return /* @__PURE__ */ (0, import_jsx_runtime93.jsx)(SessionBrowserView, { state });
}
// packages/cli/src/ui/components/ModelDialog.tsx
var import_react55 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/ModelQuotaDisplay.tsx
var import_react54 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/ProgressBar.tsx
var import_jsx_runtime94 = __toESM(require_jsx_runtime(), 1);
var ProgressBar = ({
value,
width,
warningThreshold = 80
}) => {
const safeValue = Math.min(Math.max(value, 0), 100);
const activeChars = Math.ceil(safeValue / 100 * width);
const inactiveChars = width - activeChars;
let color = theme.status.success;
if (safeValue >= 100) {
color = theme.status.error;
} else if (safeValue >= warningThreshold) {
color = theme.status.warning;
}
return /* @__PURE__ */ (0, import_jsx_runtime94.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime94.jsx)(Text, { color, children: "\u25AC".repeat(activeChars) }),
/* @__PURE__ */ (0, import_jsx_runtime94.jsx)(Text, { color: theme.border.default, children: "\u25AC".repeat(inactiveChars) })
] });
};
// packages/cli/src/ui/components/ModelQuotaDisplay.tsx
var import_jsx_runtime95 = __toESM(require_jsx_runtime(), 1);
var ModelUsageRow = ({ row, availableWidth }) => {
const { terminalWidth } = useUIState();
const nameColumnWidth = 12;
const percentageWidth = 4;
const resetColumnWidth = 26;
const usedPercentage = row.usedPercentage;
const nameLabel = row.name;
const percentageLabel = `${usedPercentage.toFixed(0)}%`.padEnd(
percentageWidth
);
const resetLabel = row.resetTime ? formatResetTime(row.resetTime, "column").slice(0, resetColumnWidth).padEnd(resetColumnWidth) : "".padEnd(resetColumnWidth);
const nameColWidth = nameColumnWidth;
const percentColWidth = percentageWidth + 1;
const resetColWidth = resetColumnWidth + 1;
const fixedSiblingWidth = nameColWidth + percentColWidth + resetColWidth;
const calcWidth = availableWidth ?? terminalWidth;
const defaultPadding = availableWidth != null ? 0 : 4;
const buffer = 3;
const barWidth = Math.max(
0,
calcWidth - defaultPadding - fixedSiblingWidth - buffer
);
let percentageColor = theme.text.primary;
if (usedPercentage >= 100) {
percentageColor = theme.status.error;
} else if (usedPercentage >= 80) {
percentageColor = theme.status.warning;
}
return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(Box_default, { flexDirection: "row", width: "100%", children: [
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Box_default, { width: nameColumnWidth, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Text, { color: theme.text.primary, wrap: "truncate-end", children: nameLabel }) }),
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(ProgressBar, { value: usedPercentage, width: barWidth }) }),
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Box_default, { width: 4, marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Text, { color: percentageColor, children: percentageLabel }) }),
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Box_default, { width: resetColumnWidth, marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Text, { color: theme.text.secondary, children: resetLabel.trim() ? `Resets: ${resetLabel}` : "" }) })
] });
};
var ModelQuotaDisplay = ({
buckets,
availableWidth,
modelsToShow = ["all"],
title = "Model usage"
}) => {
const config = useConfig();
const modelsWithQuotas = (0, import_react54.useMemo)(() => {
if (!buckets) return [];
let filteredBuckets = buckets.filter(
(b) => b.modelId && b.remainingFraction != null
);
if (modelsToShow.includes("current")) {
const currentModel = config.getActiveModel?.() ?? config.getModel?.();
filteredBuckets = filteredBuckets.filter(
(b) => b.modelId === currentModel
);
} else if (!modelsToShow.includes("all")) {
filteredBuckets = filteredBuckets.filter(
(b) => b.modelId && modelsToShow.includes(b.modelId)
);
}
const groupedByTier = /* @__PURE__ */ new Map();
filteredBuckets.forEach((b) => {
const modelId = b.modelId;
const remainingFraction = b.remainingFraction;
if (!modelId || remainingFraction == null) return;
const tier = config?.modelConfigService?.getModelDefinition(modelId)?.tier;
const groupKey = tier ?? modelId;
const existing = groupedByTier.get(groupKey);
if (!existing || remainingFraction < existing.remainingFraction) {
const tierDisplayNames = {
pro: "Pro",
flash: "Flash",
"flash-lite": "Flash Lite"
};
const name = tier ? tierDisplayNames[tier] ?? tier : getDisplayString(modelId, config);
groupedByTier.set(groupKey, {
modelId,
remainingFraction,
resetTime: b.resetTime,
name
});
}
});
return Array.from(groupedByTier.entries()).map(([key, data]) => {
const usedFraction = 1 - data.remainingFraction;
const usedPercentage = usedFraction * 100;
return {
modelId: key,
name: data.name,
usedFraction,
usedPercentage,
resetTime: data.resetTime
};
});
}, [buckets, config, modelsToShow]);
if (modelsWithQuotas.length === 0) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
Box_default,
{
borderStyle: "single",
borderTop: true,
borderBottom: false,
borderLeft: false,
borderRight: false,
borderColor: theme.border.default
}
),
/* @__PURE__ */ (0, import_jsx_runtime95.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(Text, { bold: true, color: theme.text.primary, children: title }) }),
modelsWithQuotas.map((row) => /* @__PURE__ */ (0, import_jsx_runtime95.jsx)(
ModelUsageRow,
{
row,
availableWidth
},
row.modelId
))
] })
] });
};
// packages/cli/src/ui/components/shared/DescriptiveRadioButtonSelect.tsx
var import_jsx_runtime96 = __toESM(require_jsx_runtime(), 1);
function DescriptiveRadioButtonSelect({
items,
initialIndex = 0,
onSelect,
onHighlight,
isFocused = true,
showNumbers = false,
showScrollArrows = false,
maxItemsToShow = 10
}) {
return /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(
BaseSelectionList,
{
items,
initialIndex,
onSelect,
onHighlight,
isFocused,
showNumbers,
showScrollArrows,
maxItemsToShow,
renderItem: (item, { titleColor }) => /* @__PURE__ */ (0, import_jsx_runtime96.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime96.jsx)(Text, { color: titleColor, children: item.title }),
item.description && /* @__PURE__ */ (0, import_jsx_runtime96.jsx)(Text, { color: theme.text.secondary, children: item.description })
] }, item.key)
}
);
}
// packages/cli/src/ui/components/ModelDialog.tsx
var import_jsx_runtime97 = __toESM(require_jsx_runtime(), 1);
function ModelDialog({ onClose }) {
const config = (0, import_react55.useContext)(ConfigContext);
const settings = useSettings();
const { terminalWidth } = useUIState();
const [hasAccessToProModel, setHasAccessToProModel] = (0, import_react55.useState)(
() => !(config?.getProModelNoAccessSync() ?? false)
);
const [view, setView] = (0, import_react55.useState)(
() => config?.getProModelNoAccessSync() ? "manual" : "main"
);
const [persistMode, setPersistMode] = (0, import_react55.useState)(false);
(0, import_react55.useEffect)(() => {
async function checkAccess() {
if (!config) return;
const noAccess = await config.getProModelNoAccess();
setHasAccessToProModel(!noAccess);
if (noAccess) {
setView("manual");
}
}
void checkAccess();
}, [config]);
const preferredModel = config?.getModel() || DEFAULT_GEMINI_MODEL_AUTO;
const shouldShowPreviewModels = config?.getHasAccessToPreviewModel();
const useGemini31 = config?.getGemini31LaunchedSync?.() ?? false;
const useGemini31FlashLite = config?.getGemini31FlashLiteLaunchedSync?.() ?? false;
const selectedAuthType = settings.merged.security.auth.selectedType;
const useCustomToolModel = useGemini31 && selectedAuthType === AuthType.USE_GEMINI;
const manualModelSelected = (0, import_react55.useMemo)(() => {
if (config?.getExperimentalDynamicModelConfiguration?.() === true && config.getModelConfigService) {
const def = config.getModelConfigService().getModelDefinition(preferredModel);
return def && def.tier !== "auto" && def.isVisible === true ? preferredModel : "";
}
const manualModels = [
DEFAULT_GEMINI_MODEL,
DEFAULT_GEMINI_FLASH_MODEL,
DEFAULT_GEMINI_FLASH_LITE_MODEL,
PREVIEW_GEMINI_MODEL,
PREVIEW_GEMINI_3_1_MODEL,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
PREVIEW_GEMINI_FLASH_MODEL
];
if (manualModels.includes(preferredModel)) {
return preferredModel;
}
return "";
}, [preferredModel, config]);
useKeypress(
(key) => {
if (key.name === "escape") {
if (view === "manual" && hasAccessToProModel) {
setView("main");
} else {
onClose();
}
return true;
}
if (key.name === "tab") {
setPersistMode((prev) => !prev);
return true;
}
return false;
},
{ isActive: true }
);
const mainOptions = (0, import_react55.useMemo)(() => {
if (config?.getExperimentalDynamicModelConfiguration?.() === true && config.getModelConfigService) {
const allOptions = config.getModelConfigService().getAvailableModelOptions({
useGemini3_1: useGemini31,
useGemini3_1FlashLite: useGemini31FlashLite,
useCustomTools: useCustomToolModel,
hasAccessToPreview: shouldShowPreviewModels,
hasAccessToProModel
});
const list2 = allOptions.filter((o) => o.tier === "auto").map((o) => ({
value: o.modelId,
title: o.name,
description: o.description,
key: o.modelId
}));
list2.push({
value: "Manual",
title: manualModelSelected ? `Manual (${getDisplayString(manualModelSelected, config ?? void 0)})` : "Manual",
description: "Manually select a model",
key: "Manual"
});
return list2;
}
const list = [
{
value: DEFAULT_GEMINI_MODEL_AUTO,
title: getDisplayString(DEFAULT_GEMINI_MODEL_AUTO),
description: "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
key: DEFAULT_GEMINI_MODEL_AUTO
},
{
value: "Manual",
title: manualModelSelected ? `Manual (${getDisplayString(manualModelSelected)})` : "Manual",
description: "Manually select a model",
key: "Manual"
}
];
if (shouldShowPreviewModels) {
list.unshift({
value: PREVIEW_GEMINI_MODEL_AUTO,
title: getDisplayString(PREVIEW_GEMINI_MODEL_AUTO),
description: useGemini31 ? "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash" : "Let Gemini CLI decide the best model for the task: gemini-3-pro, gemini-3-flash",
key: PREVIEW_GEMINI_MODEL_AUTO
});
}
return list;
}, [
config,
shouldShowPreviewModels,
manualModelSelected,
useGemini31,
useGemini31FlashLite,
useCustomToolModel,
hasAccessToProModel
]);
const manualOptions = (0, import_react55.useMemo)(() => {
if (config?.getExperimentalDynamicModelConfiguration?.() === true && config.getModelConfigService) {
const allOptions = config.getModelConfigService().getAvailableModelOptions({
useGemini3_1: useGemini31,
useGemini3_1FlashLite: useGemini31FlashLite,
useCustomTools: useCustomToolModel,
hasAccessToPreview: shouldShowPreviewModels,
hasAccessToProModel
});
return allOptions.filter((o) => o.tier !== "auto").map((o) => ({
value: o.modelId,
title: o.name,
key: o.modelId
}));
}
const showGemmaModels = config?.getExperimentalGemma() ?? false;
const options2 = [
{
value: DEFAULT_GEMINI_MODEL,
title: getDisplayString(DEFAULT_GEMINI_MODEL),
key: DEFAULT_GEMINI_MODEL
},
{
value: DEFAULT_GEMINI_FLASH_MODEL,
title: getDisplayString(DEFAULT_GEMINI_FLASH_MODEL),
key: DEFAULT_GEMINI_FLASH_MODEL
},
{
value: DEFAULT_GEMINI_FLASH_LITE_MODEL,
title: getDisplayString(DEFAULT_GEMINI_FLASH_LITE_MODEL),
key: DEFAULT_GEMINI_FLASH_LITE_MODEL
}
];
if (showGemmaModels) {
options2.push(
{
value: GEMMA_4_31B_IT_MODEL,
title: getDisplayString(GEMMA_4_31B_IT_MODEL),
key: GEMMA_4_31B_IT_MODEL
},
{
value: GEMMA_4_26B_A4B_IT_MODEL,
title: getDisplayString(GEMMA_4_26B_A4B_IT_MODEL),
key: GEMMA_4_26B_A4B_IT_MODEL
}
);
}
if (shouldShowPreviewModels) {
const previewProModel = useGemini31 ? PREVIEW_GEMINI_3_1_MODEL : PREVIEW_GEMINI_MODEL;
const previewProValue = useCustomToolModel ? PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL : previewProModel;
const previewOptions = [
{
value: previewProValue,
title: getDisplayString(previewProModel),
key: previewProModel
},
{
value: PREVIEW_GEMINI_FLASH_MODEL,
title: getDisplayString(PREVIEW_GEMINI_FLASH_MODEL),
key: PREVIEW_GEMINI_FLASH_MODEL
}
];
if (useGemini31FlashLite) {
previewOptions.push({
value: PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL,
title: getDisplayString(PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL),
key: PREVIEW_GEMINI_3_1_FLASH_LITE_MODEL
});
}
options2.unshift(...previewOptions);
}
if (!hasAccessToProModel) {
return options2.filter((option) => !isProModel(option.value));
}
return options2;
}, [
shouldShowPreviewModels,
useGemini31,
useGemini31FlashLite,
useCustomToolModel,
hasAccessToProModel,
config
]);
const options = view === "main" ? mainOptions : manualOptions;
const initialIndex = (0, import_react55.useMemo)(() => {
const idx = options.findIndex((option) => option.value === preferredModel);
if (idx !== -1) {
return idx;
}
if (view === "main") {
const manualIdx = options.findIndex((o) => o.value === "Manual");
return manualIdx !== -1 ? manualIdx : 0;
}
return 0;
}, [preferredModel, options, view]);
const handleSelect = (0, import_react55.useCallback)(
(model) => {
if (model === "Manual") {
setView("manual");
return;
}
if (config) {
config.setModel(model, persistMode ? false : true);
const event = new ModelSlashCommandEvent(model);
logModelSlashCommand(config, event);
}
onClose();
},
[config, onClose, persistMode]
);
return /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
padding: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Text, { bold: true, children: "Select Model" }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
DescriptiveRadioButtonSelect,
{
items: options,
onSelect: handleSelect,
initialIndex,
showNumbers: true
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Box_default, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime97.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
"Remember model for future sessions:",
" "
] }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Text, { color: theme.status.success, children: persistMode ? "true" : "false" }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Text, { color: theme.text.secondary, children: " (Press Tab to toggle)" })
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Text, { color: theme.text.secondary, children: "> To use a specific Gemini model on startup, use the --model flag." }) }),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(
ModelQuotaDisplay,
{
buckets: config?.getLastRetrievedQuota()?.buckets,
availableWidth: terminalWidth - 2
}
),
/* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Box_default, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime97.jsx)(Text, { color: theme.text.secondary, children: "(Press Esc to close)" }) })
]
}
);
}
// packages/cli/src/ui/components/VoiceModelDialog.tsx
var import_react56 = __toESM(require_react(), 1);
var import_jsx_runtime98 = __toESM(require_jsx_runtime(), 1);
var WHISPER_MODELS = [
{
value: "ggml-tiny.en.bin",
label: "Tiny (EN)",
description: "Fastest, lower accuracy (~75MB)"
},
{
value: "ggml-base.en.bin",
label: "Base (EN)",
description: "Balanced speed and accuracy (~142MB)"
},
{
value: "ggml-large-v3-turbo-q5_0.bin",
label: "Large v3 Turbo (Q5_0)",
description: "High accuracy, quantized (~547MB)"
},
{
value: "ggml-large-v3-turbo-q8_0.bin",
label: "Large v3 Turbo (Q8_0)",
description: "Maximum accuracy, high memory (~834MB)"
}
];
function VoiceModelDialog({
onClose
}) {
const { settings, setSetting } = useSettingsStore();
const [view, setView] = (0, import_react56.useState)("backend");
const [downloadProgress, setDownloadProgress] = (0, import_react56.useState)(null);
const [error, setError] = (0, import_react56.useState)(null);
const whisperInstalled = (0, import_react56.useMemo)(
() => isBinaryAvailable("whisper-stream"),
[]
);
const modelManager = (0, import_react56.useMemo)(() => new WhisperModelManager(), []);
const currentBackend = settings.merged.experimental.voice?.backend ?? "gemini-live";
const currentWhisperModel = settings.merged.experimental.voice?.whisperModel ?? "ggml-base.en.bin";
const [highlightedBackend, setHighlightedBackend] = (0, import_react56.useState)(currentBackend);
const handleKeypress = (0, import_react56.useCallback)(
(key) => {
if (key.name === "escape") {
if (view === "whisper-models") {
setView("backend");
} else {
onClose();
}
return true;
}
return false;
},
[view, onClose]
);
useKeypress(handleKeypress, { isActive: true });
const handleBackendSelect = (0, import_react56.useCallback)(
(value) => {
if (value === "whisper") {
setView("whisper-models");
} else {
setSetting(
"User" /* User */,
"experimental.voice.backend",
"gemini-live"
);
onClose();
}
},
[setSetting, onClose]
);
const handleBackendHighlight = (0, import_react56.useCallback)((value) => {
setHighlightedBackend(value);
}, []);
const handleWhisperModelSelect = (0, import_react56.useCallback)(
async (modelName) => {
if (modelManager.isModelInstalled(modelName)) {
setSetting("User" /* User */, "experimental.voice.backend", "whisper");
setSetting(
"User" /* User */,
"experimental.voice.whisperModel",
modelName
);
onClose();
} else {
setError(null);
const onProgress = (p) => setDownloadProgress(p);
modelManager.on("progress", onProgress);
try {
await modelManager.downloadModel(modelName);
setSetting(
"User" /* User */,
"experimental.voice.backend",
"whisper"
);
setSetting(
"User" /* User */,
"experimental.voice.whisperModel",
modelName
);
onClose();
} catch (err) {
setError(
`Failed to download: ${err instanceof Error ? err.message : String(err)}`
);
} finally {
modelManager.off("progress", onProgress);
setDownloadProgress(null);
}
}
},
[modelManager, setSetting, onClose]
);
const backendOptions = (0, import_react56.useMemo)(
() => [
{
value: "gemini-live",
title: "Gemini Live API (Cloud)",
description: "Real-time cloud transcription via Gemini Live API.",
key: "gemini-live"
},
{
value: "whisper",
title: "Whisper (Local)",
description: whisperInstalled ? "Local transcription using whisper.cpp." : "Local transcription (Requires: brew install whisper-cpp)",
key: "whisper"
}
],
[whisperInstalled]
);
const whisperOptions = (0, import_react56.useMemo)(
() => WHISPER_MODELS.map((m) => ({
value: m.value,
title: `${m.label}${modelManager.isModelInstalled(m.value) ? " (Installed)" : " (Download)"}`,
description: m.description,
key: m.value
})),
[modelManager]
);
return /* @__PURE__ */ (0, import_jsx_runtime98.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.border.default,
flexDirection: "column",
padding: 1,
width: "100%",
children: [
/* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Text, { bold: true, children: view === "backend" ? "Select Voice Transcription Backend" : "Select Whisper Model" }),
error && /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Text, { color: theme.status.error, children: error }) }),
downloadProgress ? /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Box_default, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime98.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime98.jsxs)(Text, { children: [
"Downloading ",
downloadProgress.modelName,
"... "
] }),
/* @__PURE__ */ (0, import_jsx_runtime98.jsx)(CliSpinner, {}),
/* @__PURE__ */ (0, import_jsx_runtime98.jsxs)(Text, { children: [
" ",
Math.round(downloadProgress.percentage * 100),
"%"
] })
] }) }) : /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Box_default, { marginTop: 1, flexDirection: "column", children: view === "backend" ? /* @__PURE__ */ (0, import_jsx_runtime98.jsxs)(import_jsx_runtime98.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime98.jsx)(
DescriptiveRadioButtonSelect,
{
items: backendOptions,
onSelect: handleBackendSelect,
onHighlight: handleBackendHighlight,
initialIndex: currentBackend === "whisper" ? 1 : 0,
showNumbers: true
}
),
highlightedBackend === "gemini-live" && /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(WarningMessage, { text: "When using the Gemini Live backend, voice recordings are sent to Google Cloud for transcription. Enterprise users should verify this aligns with their data privacy and compliance requirements." }) })
] }) : /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(
DescriptiveRadioButtonSelect,
{
items: whisperOptions,
onSelect: handleWhisperModelSelect,
initialIndex: whisperOptions.findIndex(
(o) => o.value === currentWhisperModel
),
showNumbers: true
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Box_default, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime98.jsx)(Text, { color: theme.text.secondary, children: view === "whisper-models" ? "(Press Esc to go back)" : "(Press Esc to close)" }) })
]
}
);
}
// packages/cli/src/ui/components/DialogManager.tsx
import process5 from "node:process";
// packages/cli/src/ui/components/AdminSettingsChangedDialog.tsx
var import_jsx_runtime99 = __toESM(require_jsx_runtime(), 1);
var AdminSettingsChangedDialog = () => {
const keyMatchers = useKeyMatchers();
const { handleRestart } = useUIActions();
useKeypress(
(key) => {
if (keyMatchers["app.restart" /* RESTART_APP */](key)) {
handleRestart();
return true;
}
return false;
},
{ isActive: true }
);
const message = "Admin settings have changed. Please restart the session to apply new settings.";
return /* @__PURE__ */ (0, import_jsx_runtime99.jsx)(Box_default, { borderStyle: "round", borderColor: theme.status.warning, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime99.jsxs)(Text, { color: theme.status.warning, children: [
message,
" Press 'r' to restart, or 'Ctrl+C' twice to exit."
] }) });
};
// packages/cli/src/ui/components/IdeTrustChangeDialog.tsx
var import_jsx_runtime100 = __toESM(require_jsx_runtime(), 1);
var IdeTrustChangeDialog = ({ reason }) => {
useKeypress(
(key) => {
if (key.name === "r" || key.name === "R") {
relaunchApp();
return true;
}
return false;
},
{ isActive: true }
);
let message = "Workspace trust has changed.";
if (reason === "NONE") {
debugLogger.warn(
'IdeTrustChangeDialog rendered with unexpected reason "NONE"'
);
} else if (reason === "CONNECTION_CHANGE") {
message = "Workspace trust has changed due to a change in the IDE connection.";
} else if (reason === "TRUST_CHANGE") {
message = "Workspace trust has changed due to a change in the IDE trust.";
}
return /* @__PURE__ */ (0, import_jsx_runtime100.jsx)(Box_default, { borderStyle: "round", borderColor: theme.status.warning, paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime100.jsxs)(Text, { color: theme.status.warning, children: [
message,
" Press 'r' to restart Gemini to apply the changes."
] }) });
};
// packages/cli/src/ui/components/AgentConfigDialog.tsx
var import_react57 = __toESM(require_react(), 1);
var import_jsx_runtime101 = __toESM(require_jsx_runtime(), 1);
var AGENT_CONFIG_FIELDS = [
{
key: "enabled",
label: "Enabled",
description: "Enable or disable this agent",
type: "boolean",
path: ["enabled"],
defaultValue: true
},
{
key: "model",
label: "Model",
description: "Model to use (e.g., 'gemini-2.0-flash' or 'inherit')",
type: "string",
path: ["modelConfig", "model"],
defaultValue: "inherit"
},
{
key: "temperature",
label: "Temperature",
description: "Sampling temperature (0.0 to 2.0)",
type: "number",
path: ["modelConfig", "generateContentConfig", "temperature"],
defaultValue: void 0
},
{
key: "topP",
label: "Top P",
description: "Nucleus sampling parameter (0.0 to 1.0)",
type: "number",
path: ["modelConfig", "generateContentConfig", "topP"],
defaultValue: void 0
},
{
key: "topK",
label: "Top K",
description: "Top-K sampling parameter",
type: "number",
path: ["modelConfig", "generateContentConfig", "topK"],
defaultValue: void 0
},
{
key: "maxOutputTokens",
label: "Max Output Tokens",
description: "Maximum number of tokens to generate",
type: "number",
path: ["modelConfig", "generateContentConfig", "maxOutputTokens"],
defaultValue: void 0
},
{
key: "maxTimeMinutes",
label: "Max Time (minutes)",
description: "Maximum execution time in minutes",
type: "number",
path: ["runConfig", "maxTimeMinutes"],
defaultValue: void 0
},
{
key: "maxTurns",
label: "Max Turns",
description: "Maximum number of conversational turns",
type: "number",
path: ["runConfig", "maxTurns"],
defaultValue: void 0
}
];
function setNestedValue(obj, path14, value) {
if (!isRecord(obj)) return obj;
const result = { ...obj };
let current = result;
for (let i = 0; i < path14.length - 1; i++) {
const key = path14[i];
if (current[key] === void 0 || current[key] === null) {
current[key] = {};
} else if (isRecord(current[key])) {
current[key] = { ...current[key] };
}
const next = current[key];
if (isRecord(next)) {
current = next;
} else {
return result;
}
}
const finalKey = path14[path14.length - 1];
if (value === void 0) {
delete current[finalKey];
} else {
current[finalKey] = value;
}
return result;
}
function getFieldDefaultFromDefinition(field, definition) {
if (definition.kind !== "local") return field.defaultValue;
if (field.key === "enabled") {
return !definition.experimental;
}
if (field.key === "model") {
return definition.modelConfig?.model ?? "inherit";
}
if (field.key === "temperature") {
return definition.modelConfig?.generateContentConfig?.temperature;
}
if (field.key === "topP") {
return definition.modelConfig?.generateContentConfig?.topP;
}
if (field.key === "topK") {
return definition.modelConfig?.generateContentConfig?.topK;
}
if (field.key === "maxOutputTokens") {
return definition.modelConfig?.generateContentConfig?.maxOutputTokens;
}
if (field.key === "maxTimeMinutes") {
return definition.runConfig?.maxTimeMinutes;
}
if (field.key === "maxTurns") {
return definition.runConfig?.maxTurns;
}
return field.defaultValue;
}
function AgentConfigDialog({
agentName,
displayName,
definition,
settings,
onClose,
onSave,
availableTerminalHeight
}) {
const [selectedScope, setSelectedScope] = (0, import_react57.useState)(
"User" /* User */
);
const [pendingOverride, setPendingOverride] = (0, import_react57.useState)(() => {
const scopeSettings = settings.forScope(selectedScope).settings;
const existingOverride = scopeSettings.agents?.overrides?.[agentName];
return existingOverride ? structuredClone(existingOverride) : {};
});
const [modifiedFields, setModifiedFields] = (0, import_react57.useState)(/* @__PURE__ */ new Set());
(0, import_react57.useEffect)(() => {
const scopeSettings = settings.forScope(selectedScope).settings;
const existingOverride = scopeSettings.agents?.overrides?.[agentName];
setPendingOverride(
existingOverride ? structuredClone(existingOverride) : {}
);
setModifiedFields(/* @__PURE__ */ new Set());
}, [selectedScope, settings, agentName]);
const saveFieldValue = (0, import_react57.useCallback)(
(fieldKey, path14, value) => {
if (["__proto__", "constructor", "prototype"].includes(agentName)) {
return;
}
const settingsPath2 = ["agents", "overrides", agentName, ...path14].join(
"."
);
settings.setValue(selectedScope, settingsPath2, value);
onSave?.();
},
[settings, selectedScope, agentName, onSave]
);
const maxLabelWidth = (0, import_react57.useMemo)(() => {
let max = 0;
for (const field of AGENT_CONFIG_FIELDS) {
const lWidth = getCachedStringWidth(field.label);
const dWidth = getCachedStringWidth(field.description);
max = Math.max(max, lWidth, dWidth);
}
return max;
}, []);
const items = (0, import_react57.useMemo)(
() => AGENT_CONFIG_FIELDS.map((field) => {
const currentValue = getNestedValue(pendingOverride, field.path);
const defaultValue = getFieldDefaultFromDefinition(field, definition);
const effectiveValue = currentValue !== void 0 ? currentValue : defaultValue;
let displayValue;
if (field.type === "boolean") {
displayValue = effectiveValue ? "true" : "false";
} else if (effectiveValue !== void 0 && effectiveValue !== null) {
displayValue = String(effectiveValue);
} else {
displayValue = "(default)";
}
const isModified = modifiedFields.has(field.key) || currentValue !== void 0;
if (isModified && currentValue !== void 0) {
displayValue += "*";
}
const rawValue = currentValue !== void 0 ? currentValue : effectiveValue;
return {
key: field.key,
label: field.label,
description: field.description,
type: field.type,
displayValue,
isGreyedOut: currentValue === void 0,
scopeMessage: void 0,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
rawValue
};
}),
[pendingOverride, definition, modifiedFields]
);
const maxItemsToShow = 8;
const handleScopeChange = (0, import_react57.useCallback)((scope) => {
setSelectedScope(scope);
}, []);
const handleItemToggle = (0, import_react57.useCallback)(
(key, _item) => {
const field = AGENT_CONFIG_FIELDS.find((f) => f.key === key);
if (!field || field.type !== "boolean") return;
const currentValue = getNestedValue(pendingOverride, field.path);
const defaultValue = getFieldDefaultFromDefinition(field, definition);
const effectiveValue = currentValue !== void 0 ? currentValue : defaultValue;
const newValue = !effectiveValue;
const newOverride = setNestedValue(
pendingOverride,
field.path,
newValue
);
setPendingOverride(newOverride);
setModifiedFields((prev) => new Set(prev).add(key));
saveFieldValue(field.key, field.path, newValue);
},
[pendingOverride, definition, saveFieldValue]
);
const handleEditCommit = (0, import_react57.useCallback)(
(key, newValue, _item) => {
const field = AGENT_CONFIG_FIELDS.find((f) => f.key === key);
if (!field) return;
let parsed;
if (field.type === "number") {
if (newValue.trim() === "") {
parsed = void 0;
} else {
const numParsed = Number(newValue.trim());
if (Number.isNaN(numParsed)) {
return;
}
parsed = numParsed;
}
} else {
parsed = newValue.trim() === "" ? void 0 : newValue;
}
const newOverride = setNestedValue(
pendingOverride,
field.path,
parsed
);
setPendingOverride(newOverride);
setModifiedFields((prev) => new Set(prev).add(key));
saveFieldValue(field.key, field.path, parsed);
},
[pendingOverride, saveFieldValue]
);
const handleItemClear = (0, import_react57.useCallback)(
(key, _item) => {
const field = AGENT_CONFIG_FIELDS.find((f) => f.key === key);
if (!field) return;
const newOverride = setNestedValue(
pendingOverride,
field.path,
void 0
);
setPendingOverride(newOverride);
setModifiedFields((prev) => {
const updated = new Set(prev);
updated.delete(key);
return updated;
});
saveFieldValue(field.key, field.path, void 0);
},
[pendingOverride, saveFieldValue]
);
return /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(
BaseSettingsDialog,
{
title: `Configure: ${displayName}`,
searchEnabled: false,
items,
showScopeSelector: true,
selectedScope,
onScopeChange: handleScopeChange,
maxItemsToShow,
availableHeight: availableTerminalHeight,
maxLabelWidth,
onItemToggle: handleItemToggle,
onEditCommit: handleEditCommit,
onItemClear: handleItemClear,
onClose,
footer: modifiedFields.size > 0 ? {
content: /* @__PURE__ */ (0, import_jsx_runtime101.jsx)(Text, { color: theme.text.secondary, children: "Changes saved automatically." }),
height: 1
} : void 0
}
);
}
// packages/cli/src/ui/components/PolicyUpdateDialog.tsx
var import_react58 = __toESM(require_react(), 1);
var import_jsx_runtime102 = __toESM(require_jsx_runtime(), 1);
var PolicyUpdateDialog = ({
config,
request,
onClose
}) => {
const keyMatchers = useKeyMatchers();
const isProcessing = (0, import_react58.useRef)(false);
const handleSelect = (0, import_react58.useCallback)(
async (choice) => {
if (isProcessing.current) {
return;
}
isProcessing.current = true;
try {
if (choice === "accept" /* ACCEPT */) {
const integrityManager = new PolicyIntegrityManager();
await integrityManager.acceptIntegrity(
request.scope,
request.identifier,
request.newHash
);
await config.loadWorkspacePolicies(request.policyDir);
}
onClose();
} finally {
isProcessing.current = false;
}
},
[config, request, onClose]
);
useKeypress(
(key) => {
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
onClose();
return true;
}
return false;
},
{ isActive: true }
);
const options = [
{
label: "Accept and Load",
value: "accept" /* ACCEPT */,
key: "accept"
},
{
label: "Ignore (Use Default Policies)",
value: "ignore" /* IGNORE */,
key: "ignore"
}
];
return /* @__PURE__ */ (0, import_jsx_runtime102.jsx)(Box_default, { flexDirection: "column", width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime102.jsxs)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.status.warning,
padding: 1,
marginLeft: 1,
marginRight: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime102.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime102.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
"New or changed ",
request.scope,
" policies detected"
] }),
/* @__PURE__ */ (0, import_jsx_runtime102.jsxs)(Text, { color: theme.text.primary, children: [
"Location: ",
request.identifier
] }),
/* @__PURE__ */ (0, import_jsx_runtime102.jsx)(Text, { color: theme.text.primary, children: "Do you want to accept and load these policies?" })
] }),
/* @__PURE__ */ (0, import_jsx_runtime102.jsx)(
RadioButtonSelect,
{
items: options,
onSelect: handleSelect,
isFocused: true
}
)
]
}
) });
};
// packages/cli/src/ui/auth/LoginRestartDialog.tsx
var import_jsx_runtime103 = __toESM(require_jsx_runtime(), 1);
var LoginRestartDialog = ({
onDismiss,
config,
message
}) => {
useKeypress(
(key) => {
if (key.name === "escape") {
onDismiss();
return true;
} else if (key.name === "r" || key.name === "R") {
setTimeout(async () => {
if (process.send) {
const remoteSettings = config.getRemoteAdminSettings();
if (remoteSettings) {
process.send({
type: "admin-settings-update",
settings: remoteSettings
});
}
}
await relaunchApp();
}, 100);
return true;
}
return false;
},
{ isActive: true }
);
const displayMessage = message ?? "You've successfully signed in with Google. Gemini CLI needs to be restarted.";
return /* @__PURE__ */ (0, import_jsx_runtime103.jsxs)(
Box_default,
{
borderStyle: "round",
borderColor: theme.status.warning,
paddingX: 1,
flexDirection: "column",
children: [
/* @__PURE__ */ (0, import_jsx_runtime103.jsx)(Text, { color: theme.status.warning, children: displayMessage }),
/* @__PURE__ */ (0, import_jsx_runtime103.jsx)(Text, { color: theme.status.warning, children: "Press R to restart, or Esc to choose a different authentication method." })
]
}
);
};
// packages/cli/src/ui/components/DialogManager.tsx
var import_jsx_runtime104 = __toESM(require_jsx_runtime(), 1);
var DialogManager = ({
addItem,
terminalWidth
}) => {
const config = useConfig();
const settings = useSettings();
const uiState = useUIState();
const quotaState = useQuotaState();
const uiActions = useUIActions();
const {
constrainHeight,
terminalHeight,
staticExtraHeight,
terminalWidth: uiTerminalWidth
} = uiState;
if (uiState.adminSettingsChanged) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(AdminSettingsChangedDialog, {});
}
if (uiState.showIdeRestartPrompt) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(IdeTrustChangeDialog, { reason: uiState.ideTrustRestartReason });
}
if (uiState.newAgents) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
NewAgentsNotification,
{
agents: uiState.newAgents,
onSelect: uiActions.handleNewAgentsSelect
}
);
}
if (quotaState.proQuotaRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ProQuotaDialog,
{
failedModel: quotaState.proQuotaRequest.failedModel,
fallbackModel: quotaState.proQuotaRequest.fallbackModel,
message: quotaState.proQuotaRequest.message,
isTerminalQuotaError: quotaState.proQuotaRequest.isTerminalQuotaError,
isModelNotFoundError: !!quotaState.proQuotaRequest.isModelNotFoundError,
authType: quotaState.proQuotaRequest.authType,
tierName: config?.getUserTierName(),
onChoice: uiActions.handleProQuotaChoice
}
);
}
if (quotaState.validationRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ValidationDialog,
{
validationLink: quotaState.validationRequest.validationLink,
validationDescription: quotaState.validationRequest.validationDescription,
learnMoreUrl: quotaState.validationRequest.learnMoreUrl,
onChoice: uiActions.handleValidationChoice
}
);
}
if (quotaState.overageMenuRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
OverageMenuDialog,
{
failedModel: quotaState.overageMenuRequest.failedModel,
fallbackModel: quotaState.overageMenuRequest.fallbackModel,
resetTime: quotaState.overageMenuRequest.resetTime,
creditBalance: quotaState.overageMenuRequest.creditBalance,
onChoice: uiActions.handleOverageMenuChoice
}
);
}
if (quotaState.emptyWalletRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
EmptyWalletDialog,
{
failedModel: quotaState.emptyWalletRequest.failedModel,
fallbackModel: quotaState.emptyWalletRequest.fallbackModel,
resetTime: quotaState.emptyWalletRequest.resetTime,
onGetCredits: quotaState.emptyWalletRequest.onGetCredits,
onChoice: uiActions.handleEmptyWalletChoice
}
);
}
if (uiState.shouldShowIdePrompt) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
IdeIntegrationNudge,
{
ide: uiState.currentIDE,
onComplete: uiActions.handleIdePromptComplete
}
);
}
if (uiState.isFolderTrustDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
FolderTrustDialog,
{
onSelect: uiActions.handleFolderTrustSelect,
isRestarting: uiState.isRestarting,
discoveryResults: uiState.folderDiscoveryResults
}
);
}
if (uiState.isPolicyUpdateDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
PolicyUpdateDialog,
{
config,
request: uiState.policyUpdateConfirmationRequest,
onClose: () => uiActions.setIsPolicyUpdateDialogOpen(false)
}
);
}
if (uiState.loopDetectionConfirmationRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
LoopDetectionConfirmation,
{
onComplete: uiState.loopDetectionConfirmationRequest.onComplete
}
);
}
if (uiState.permissionConfirmationRequest) {
const files = uiState.permissionConfirmationRequest.files;
const filesList = files.map((f) => `- ${f}`).join("\n");
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ConsentPrompt,
{
prompt: `The following files are outside your workspace:
${filesList}
Do you want to allow this read?`,
onConfirm: (allowed) => {
uiState.permissionConfirmationRequest?.onComplete({ allowed });
},
terminalWidth
}
);
}
if (uiState.commandConfirmationRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ConsentPrompt,
{
prompt: uiState.commandConfirmationRequest.prompt,
onConfirm: uiState.commandConfirmationRequest.onConfirm,
terminalWidth
}
);
}
if (uiState.authConsentRequest) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ConsentPrompt,
{
prompt: uiState.authConsentRequest.prompt,
onConfirm: uiState.authConsentRequest.onConfirm,
terminalWidth
}
);
}
if (uiState.confirmUpdateExtensionRequests.length > 0) {
const request = uiState.confirmUpdateExtensionRequests[0];
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ConsentPrompt,
{
prompt: request.prompt,
onConfirm: request.onConfirm,
terminalWidth
}
);
}
if (uiState.isThemeDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsxs)(Box_default, { flexDirection: "column", children: [
uiState.themeError && /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Text, { color: theme.status.error, children: uiState.themeError }) }),
/* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ThemeDialog,
{
onSelect: uiActions.handleThemeSelect,
onCancel: uiActions.closeThemeDialog,
onHighlight: uiActions.handleThemeHighlight,
settings,
availableTerminalHeight: constrainHeight ? terminalHeight - staticExtraHeight : void 0,
terminalWidth: uiTerminalWidth
}
)
] });
}
if (uiState.isSettingsDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
SettingsDialog,
{
onSelect: () => uiActions.closeSettingsDialog(),
onRestartRequest: relaunchApp,
availableTerminalHeight: terminalHeight - staticExtraHeight
}
) });
}
if (uiState.isModelDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(ModelDialog, { onClose: uiActions.closeModelDialog });
}
if (uiState.isVoiceModelDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(VoiceModelDialog, { onClose: uiActions.closeVoiceModelDialog });
}
if (uiState.isAgentConfigDialogOpen && uiState.selectedAgentName && uiState.selectedAgentDisplayName && uiState.selectedAgentDefinition) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
AgentConfigDialog,
{
agentName: uiState.selectedAgentName,
displayName: uiState.selectedAgentDisplayName,
definition: uiState.selectedAgentDefinition,
settings,
availableTerminalHeight: terminalHeight - staticExtraHeight,
onClose: uiActions.closeAgentConfigDialog,
onSave: async () => {
const agentRegistry = config?.getAgentRegistry();
if (agentRegistry) {
await agentRegistry.reload();
}
}
}
) });
}
if (uiState.accountSuspensionInfo) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
BannedAccountDialog,
{
accountSuspensionInfo: uiState.accountSuspensionInfo,
onExit: () => {
process5.exit(1);
},
onChangeAuth: () => {
uiActions.clearAccountSuspension();
}
}
) });
}
if (uiState.isAuthenticating) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
AuthInProgress,
{
onTimeout: () => {
uiActions.onAuthError("Authentication cancelled.");
}
}
);
}
if (uiState.isAwaitingApiKeyInput) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
ApiAuthDialog,
{
onSubmit: uiActions.handleApiKeySubmit,
onCancel: uiActions.handleApiKeyCancel,
error: uiState.authError,
defaultValue: uiState.apiKeyDefaultValue
},
uiState.apiKeyDefaultValue
) });
}
if (uiState.isAwaitingLoginRestart) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
LoginRestartDialog,
{
onDismiss: uiActions.dismissLoginRestart,
config,
message: uiState.loginRestartMessage
}
) });
}
if (uiState.isAuthDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
AuthDialog,
{
config,
settings,
setAuthState: uiActions.setAuthState,
authError: uiState.authError,
onAuthError: uiActions.onAuthError,
setAuthContext: uiActions.setAuthContext
}
) });
}
if (uiState.isEditorDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsxs)(Box_default, { flexDirection: "column", children: [
uiState.editorError && /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(Text, { color: theme.status.error, children: uiState.editorError }) }),
/* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
EditorSettingsDialog,
{
onSelect: uiActions.handleEditorSelect,
settings,
onExit: uiActions.exitEditorDialog
}
)
] });
}
if (uiState.showPrivacyNotice) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
PrivacyNotice,
{
onExit: () => uiActions.exitPrivacyNotice(),
config
}
);
}
if (uiState.isSessionBrowserOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
SessionBrowser,
{
config,
onResumeSession: uiActions.handleResumeSession,
onDeleteSession: uiActions.handleDeleteSession,
onExit: uiActions.closeSessionBrowser
}
);
}
if (uiState.isPermissionsDialogOpen) {
return /* @__PURE__ */ (0, import_jsx_runtime104.jsx)(
PermissionsModifyTrustDialog,
{
onExit: uiActions.closePermissionsDialog,
addItem,
targetDirectory: uiState.permissionsDialogProps?.targetDirectory
}
);
}
return null;
};
// packages/cli/src/ui/components/Composer.tsx
var import_react83 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/ToastDisplay.tsx
var import_jsx_runtime105 = __toESM(require_jsx_runtime(), 1);
function shouldShowToast(uiState, inputState) {
return uiState.ctrlCPressedOnce || Boolean(uiState.transientMessage) || uiState.ctrlDPressedOnce || inputState.showEscapePrompt && (inputState.buffer.text.length > 0 || uiState.history.length > 0) || Boolean(uiState.queueErrorMessage) || uiState.showIsExpandableHint;
}
var ToastDisplay = () => {
const uiState = useUIState();
const inputState = useInputState();
if (uiState.ctrlCPressedOnce) {
return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(Text, { color: theme.status.warning, children: "Press Ctrl+C again to exit." });
}
if (uiState.transientMessage?.type === "warning" /* Warning */ && uiState.transientMessage.text) {
return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(Text, { color: theme.status.warning, children: uiState.transientMessage.text });
}
if (uiState.ctrlDPressedOnce) {
return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(Text, { color: theme.status.warning, children: "Press Ctrl+D again to exit." });
}
if (inputState.showEscapePrompt) {
const isPromptEmpty = inputState.buffer.text.length === 0;
const hasHistory = uiState.history.length > 0;
if (isPromptEmpty && !hasHistory) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(Text, { color: theme.text.secondary, children: [
"Press Esc again to ",
isPromptEmpty ? "rewind" : "clear prompt",
"."
] });
}
if (uiState.transientMessage?.type === "hint" /* Hint */ && uiState.transientMessage.text) {
return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(Text, { color: theme.text.secondary, children: uiState.transientMessage.text });
}
if (uiState.queueErrorMessage) {
return /* @__PURE__ */ (0, import_jsx_runtime105.jsx)(Text, { color: theme.status.error, children: uiState.queueErrorMessage });
}
if (uiState.showIsExpandableHint) {
const action = uiState.constrainHeight ? "show more" : "collapse";
return /* @__PURE__ */ (0, import_jsx_runtime105.jsxs)(Text, { color: theme.text.secondary, children: [
"Press Ctrl+O to ",
action,
" lines of the last response"
] });
}
return null;
};
// packages/cli/src/ui/components/DetailedMessagesDisplay.tsx
var import_react60 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useConsoleMessages.ts
var import_react59 = __toESM(require_react(), 1);
var MAX_CONSOLE_MESSAGES = 1e3;
var globalConsoleMessages = [];
var globalErrorCount = 0;
var listeners = /* @__PURE__ */ new Set();
var messageQueue = [];
var timeoutId = null;
function initializeConsoleStore() {
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
messageQueue = [];
globalConsoleMessages = [];
globalErrorCount = 0;
notifyListeners();
coreEvents.off(CoreEvent.ConsoleLog, handleConsoleLog);
coreEvents.off(CoreEvent.Output, handleOutput);
coreEvents.on(CoreEvent.ConsoleLog, handleConsoleLog);
coreEvents.on(CoreEvent.Output, handleOutput);
}
function notifyListeners() {
for (const listener of listeners) {
listener();
}
}
function processQueue() {
if (messageQueue.length === 0) return;
const newMessages = [...globalConsoleMessages];
for (const queuedMessage of messageQueue) {
if (queuedMessage.type === "error") {
globalErrorCount++;
}
const prev = newMessages[newMessages.length - 1];
if (prev && prev.type === queuedMessage.type && prev.content === queuedMessage.content) {
newMessages[newMessages.length - 1] = {
...prev,
count: prev.count + 1
};
} else {
newMessages.push({ ...queuedMessage, count: 1 });
}
}
globalConsoleMessages = newMessages.length > MAX_CONSOLE_MESSAGES ? newMessages.slice(-MAX_CONSOLE_MESSAGES) : newMessages;
messageQueue = [];
timeoutId = null;
notifyListeners();
}
function handleNewMessage(message) {
messageQueue.push(message);
if (!timeoutId) {
timeoutId = setTimeout(processQueue, 50);
}
}
function subscribe(listener) {
listeners.add(listener);
return () => {
listeners.delete(listener);
};
}
function getConsoleMessagesSnapshot() {
return globalConsoleMessages;
}
function getErrorCountSnapshot() {
return globalErrorCount;
}
var handleConsoleLog = (payload) => {
let content = payload.content;
const MAX_CONSOLE_MSG_LENGTH = 1e4;
if (content.length > MAX_CONSOLE_MSG_LENGTH) {
content = content.slice(0, MAX_CONSOLE_MSG_LENGTH) + `... [Truncated ${content.length - MAX_CONSOLE_MSG_LENGTH} characters]`;
}
handleNewMessage({
type: payload.type,
content,
count: 1
});
};
var handleOutput = (payload) => {
let content = typeof payload.chunk === "string" ? payload.chunk : new TextDecoder().decode(payload.chunk);
const MAX_OUTPUT_CHUNK_LENGTH = 1e4;
if (content.length > MAX_OUTPUT_CHUNK_LENGTH) {
content = content.slice(0, MAX_OUTPUT_CHUNK_LENGTH) + `... [Truncated ${content.length - MAX_OUTPUT_CHUNK_LENGTH} characters]`;
}
handleNewMessage({ type: "log", content, count: 1 });
};
function useConsoleMessages() {
return (0, import_react59.useSyncExternalStore)(subscribe, getConsoleMessagesSnapshot);
}
function useErrorCount() {
const errorCount = (0, import_react59.useSyncExternalStore)(subscribe, getErrorCountSnapshot);
const clearErrorCount = (0, import_react59.useCallback)(() => {
globalErrorCount = 0;
notifyListeners();
}, []);
return { errorCount, clearErrorCount };
}
// packages/cli/src/ui/components/DetailedMessagesDisplay.tsx
var import_jsx_runtime106 = __toESM(require_jsx_runtime(), 1);
var iconBoxWidth = 3;
var DetailedMessagesDisplay = ({ maxHeight, width, hasFocus }) => {
const scrollableListRef = (0, import_react60.useRef)(null);
const consoleMessages = useConsoleMessages();
const config = useConfig();
const messages = (0, import_react60.useMemo)(() => {
if (config.getDebugMode()) {
return consoleMessages;
}
return consoleMessages.filter((msg) => msg.type !== "debug");
}, [consoleMessages, config]);
const borderAndPadding = 3;
const estimatedItemHeight = (0, import_react60.useCallback)(
(index) => {
const msg = messages[index];
if (!msg) {
return 1;
}
const textWidth = width - borderAndPadding - iconBoxWidth;
if (textWidth <= 0) {
return 1;
}
const lines = Math.ceil((msg.content?.length || 1) / textWidth);
return Math.max(1, lines);
},
[width, messages]
);
if (messages.length === 0) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(
Box_default,
{
flexDirection: "column",
marginTop: 1,
borderStyle: "round",
borderColor: theme.border.default,
paddingLeft: 1,
width,
height: maxHeight,
flexShrink: 0,
flexGrow: 0,
overflow: "hidden",
children: [
/* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Box_default, { marginBottom: 1, children: /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(Text, { bold: true, color: theme.text.primary, children: [
"Debug Console ",
/* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Text, { color: theme.text.secondary, children: "(F12 to close)" })
] }) }),
/* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Box_default, { height: maxHeight, width: width - borderAndPadding, children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(
ScrollableListWithForwardRef,
{
ref: scrollableListRef,
data: messages,
renderItem: ({ item: msg }) => {
let textColor = theme.text.primary;
let icon = "\u2139";
switch (msg.type) {
case "warn":
textColor = theme.status.warning;
icon = "\u26A0";
break;
case "error":
textColor = theme.status.error;
icon = "\u2716";
break;
case "debug":
textColor = theme.text.secondary;
icon = "\u{1F50D}";
break;
case "log":
default:
break;
}
return /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Box_default, { minWidth: iconBoxWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime106.jsx)(Text, { color: textColor, children: icon }) }),
/* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(Text, { color: textColor, wrap: "wrap", children: [
msg.content,
msg.count && msg.count > 1 && /* @__PURE__ */ (0, import_jsx_runtime106.jsxs)(Text, { color: theme.text.secondary, children: [
" (x",
msg.count,
")"
] })
] })
] });
},
keyExtractor: (item, index) => `${item.content}-${index}`,
estimatedItemHeight,
hasFocus,
initialScrollIndex: Number.MAX_SAFE_INTEGER
}
) })
]
}
);
};
// packages/cli/src/ui/components/shared/SectionHeader.tsx
var import_jsx_runtime107 = __toESM(require_jsx_runtime(), 1);
var SectionHeader = ({
title,
subtitle
}) => /* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(Box_default, { width: "100%", flexDirection: "column", overflow: "hidden", children: [
/* @__PURE__ */ (0, import_jsx_runtime107.jsx)(
Box_default,
{
width: "100%",
borderStyle: "single",
borderTop: true,
borderBottom: false,
borderLeft: false,
borderRight: false,
borderColor: theme.text.secondary
}
),
/* @__PURE__ */ (0, import_jsx_runtime107.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime107.jsx)(Text, { color: theme.text.primary, bold: true, wrap: "truncate-end", children: title }),
subtitle && /* @__PURE__ */ (0, import_jsx_runtime107.jsx)(Text, { color: theme.text.secondary, wrap: "truncate-end", children: subtitle })
] })
] });
// packages/cli/src/ui/components/ShortcutsHelp.tsx
var import_jsx_runtime108 = __toESM(require_jsx_runtime(), 1);
var buildShortcutItems = () => [
{ key: "!", description: "shell mode" },
{ key: "@", description: "select file or folder" },
{ key: "Double Esc", description: "clear & rewind" },
{ key: formatCommand("app.focusShellInput" /* FOCUS_SHELL_INPUT */), description: "focus UI" },
{ key: formatCommand("app.toggleYolo" /* TOGGLE_YOLO */), description: "YOLO mode" },
{
key: formatCommand("app.cycleApprovalMode" /* CYCLE_APPROVAL_MODE */),
description: "cycle mode"
},
{
key: formatCommand("input.paste" /* PASTE_CLIPBOARD */),
description: "paste images"
},
{
key: formatCommand("app.toggleMarkdown" /* TOGGLE_MARKDOWN */),
description: "raw markdown mode"
},
{
key: formatCommand("history.search.start" /* REVERSE_SEARCH */),
description: "reverse-search history"
},
{
key: formatCommand("input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */),
description: "open external editor"
}
];
var Shortcut = ({ item }) => /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(Box_default, { flexDirection: "row", children: [
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Box_default, { flexShrink: 0, marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Text, { color: theme.text.accent, children: item.key }) }),
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Box_default, { flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Text, { color: theme.text.primary, children: item.description }) })
] });
var ShortcutsHelp = () => {
const { terminalWidth } = useUIState();
const isNarrow = isNarrowWidth(terminalWidth);
const items = buildShortcutItems();
const itemsForDisplay = isNarrow ? items : [
// Keep first column stable: !, @, Esc Esc, Tab Tab.
items[0],
items[5],
items[6],
items[1],
items[4],
items[7],
items[2],
items[8],
items[9],
items[3]
];
return /* @__PURE__ */ (0, import_jsx_runtime108.jsxs)(Box_default, { flexDirection: "column", width: "100%", children: [
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(SectionHeader, { title: " Shortcuts", subtitle: " See /help for more" }),
/* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Box_default, { flexDirection: "row", flexWrap: "wrap", paddingLeft: 1, paddingRight: 2, children: itemsForDisplay.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(
Box_default,
{
width: isNarrow ? "100%" : "33%",
paddingRight: isNarrow ? 0 : 2,
children: /* @__PURE__ */ (0, import_jsx_runtime108.jsx)(Shortcut, { item })
},
`${item.key}-${index}`
)) })
] });
};
// packages/cli/src/ui/components/InputPrompt.tsx
var import_react75 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/SuggestionsDisplay.tsx
var import_jsx_runtime109 = __toESM(require_jsx_runtime(), 1);
var MAX_SUGGESTIONS_TO_SHOW = 8;
function SuggestionsDisplay({
suggestions,
activeIndex,
isLoading,
width,
scrollOffset,
userInput,
mode,
expandedIndex
}) {
if (isLoading) {
return /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Box_default, { paddingX: 1, width, children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: "gray", children: "Loading suggestions..." }) });
}
if (suggestions.length === 0) {
return null;
}
const startIndex = scrollOffset;
const endIndex = Math.min(
scrollOffset + MAX_SUGGESTIONS_TO_SHOW,
suggestions.length
);
const visibleSuggestions = suggestions.slice(startIndex, endIndex);
const COMMAND_KIND_SUFFIX = {
["mcp-prompt" /* MCP_PROMPT */]: " [MCP]",
["agent" /* AGENT */]: " [Agent]"
};
const getFullLabel = (s) => s.label + (s.commandKind ? COMMAND_KIND_SUFFIX[s.commandKind] ?? "" : "");
const maxLabelLength = Math.max(
...suggestions.map((s) => getFullLabel(s).length)
);
const commandColumnWidth = mode === "slash" ? Math.min(maxLabelLength, Math.floor(width * 0.5)) : 0;
return /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(Box_default, { flexDirection: "column", paddingX: 1, width, children: [
scrollOffset > 0 && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: theme.text.primary, children: "\u25B2" }),
visibleSuggestions.map((suggestion, index) => {
const originalIndex = startIndex + index;
const isActive = originalIndex === activeIndex;
const isExpanded = originalIndex === expandedIndex;
const textColor = isActive ? theme.ui.focus : theme.text.secondary;
const isLong = suggestion.value.length >= MAX_WIDTH;
const previousSectionTitle = suggestions[originalIndex - 1]?.sectionTitle;
const shouldRenderSectionHeader = mode === "slash" && !!suggestion.sectionTitle && suggestion.sectionTitle !== previousSectionTitle;
const labelElement = /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
ExpandableText,
{
label: suggestion.value,
matchedIndex: suggestion.matchedIndex,
userInput,
textColor,
isExpanded
}
);
return /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
Box_default,
{
flexDirection: "column",
children: [
shouldRenderSectionHeader && /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(Text, { color: theme.text.secondary, children: [
"-- ",
suggestion.sectionTitle,
" --"
] }),
/* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(
Box_default,
{
flexDirection: "row",
backgroundColor: isActive ? theme.background.focus : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime109.jsx)(
Box_default,
{
...mode === "slash" ? { width: commandColumnWidth, flexShrink: 0 } : { flexShrink: 1 },
children: /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(Box_default, { children: [
labelElement,
suggestion.commandKind && COMMAND_KIND_SUFFIX[suggestion.commandKind] && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: textColor, children: COMMAND_KIND_SUFFIX[suggestion.commandKind] })
] })
}
),
suggestion.description && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Box_default, { flexGrow: 1, paddingLeft: 3, children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: textColor, wrap: "truncate", children: sanitizeForDisplay(suggestion.description, 100) }) }),
isActive && isLong && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Box_default, { width: 3, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: Colors.Gray, children: isExpanded ? " \u2190 " : " \u2192 " }) })
]
}
)
]
},
`${suggestion.value}-${originalIndex}`
);
}),
endIndex < suggestions.length && /* @__PURE__ */ (0, import_jsx_runtime109.jsx)(Text, { color: "gray", children: "\u25BC" }),
suggestions.length > MAX_SUGGESTIONS_TO_SHOW && /* @__PURE__ */ (0, import_jsx_runtime109.jsxs)(Text, { color: "gray", children: [
"(",
activeIndex + 1,
"/",
suggestions.length,
")"
] })
] });
}
// packages/cli/src/ui/hooks/useInputHistory.ts
var import_react61 = __toESM(require_react(), 1);
function useInputHistory({
userMessages,
onSubmit,
isActive,
currentQuery,
currentCursorOffset,
onChange
}) {
const [historyIndex, setHistoryIndex] = (0, import_react61.useState)(-1);
const previousHistoryIndexRef = (0, import_react61.useRef)(void 0);
const historyCacheRef = (0, import_react61.useRef)({});
const resetHistoryNav = (0, import_react61.useCallback)(() => {
setHistoryIndex(-1);
previousHistoryIndexRef.current = void 0;
historyCacheRef.current = {};
}, []);
const handleSubmit = (0, import_react61.useCallback)(
(value) => {
const trimmedValue = value.trim();
if (trimmedValue) {
onSubmit(trimmedValue);
}
resetHistoryNav();
},
[onSubmit, resetHistoryNav]
);
const navigateTo = (0, import_react61.useCallback)(
(nextIndex, defaultCursor) => {
const prevIndexBeforeMove = historyIndex;
historyCacheRef.current[prevIndexBeforeMove] = {
text: currentQuery,
offset: currentCursorOffset
};
setHistoryIndex(nextIndex);
const saved = historyCacheRef.current[nextIndex];
const isReturningToPrevious = nextIndex === -1 || nextIndex === previousHistoryIndexRef.current;
if (isReturningToPrevious && saved && saved.offset > 0 && saved.offset < cpLen(saved.text)) {
onChange(saved.text, saved.offset);
} else if (nextIndex === -1) {
onChange(saved ? saved.text : "", defaultCursor);
} else {
if (saved) {
onChange(saved.text, defaultCursor);
} else {
const newValue = userMessages[userMessages.length - 1 - nextIndex];
onChange(newValue, defaultCursor);
}
}
previousHistoryIndexRef.current = prevIndexBeforeMove;
},
[historyIndex, currentQuery, currentCursorOffset, userMessages, onChange]
);
const navigateUp = (0, import_react61.useCallback)(() => {
if (!isActive) return false;
if (userMessages.length === 0) return false;
if (historyIndex < userMessages.length - 1) {
navigateTo(historyIndex + 1, "start");
return true;
}
return false;
}, [historyIndex, userMessages, isActive, navigateTo]);
const navigateDown = (0, import_react61.useCallback)(() => {
if (!isActive) return false;
if (historyIndex === -1) return false;
navigateTo(historyIndex - 1, "end");
return true;
}, [historyIndex, isActive, navigateTo]);
return {
handleSubmit,
navigateUp,
navigateDown
};
}
// packages/cli/src/ui/components/ListeningIndicator.tsx
var import_react62 = __toESM(require_react(), 1);
var import_jsx_runtime110 = __toESM(require_jsx_runtime(), 1);
var WAVE_CHARS = [" ", "\u2582", "\u2583", "\u2584", "\u2585", "\u2586", "\u2587", "\u2588"];
var ANIMATION_SPEED = 0.4;
var BAR_PHASE_OFFSET = 1.2;
var MAX_HEIGHT_MULTIPLIER = 3.5;
var FRAME_INTERVAL_MS = 40;
var ListeningIndicator = ({
color
}) => {
const [tick, setTick] = (0, import_react62.useState)(0);
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
(0, import_react62.useEffect)(() => {
if (isScreenReaderEnabled) return;
const timer = setInterval(() => setTick((t) => t + 1), FRAME_INTERVAL_MS);
return () => clearInterval(timer);
}, [isScreenReaderEnabled]);
if (isScreenReaderEnabled) {
return /* @__PURE__ */ (0, import_jsx_runtime110.jsx)(Text, { color, children: "Listening... " });
}
const bars = Array.from({ length: 3 }).map((_, i) => {
const phase = tick * ANIMATION_SPEED + i * BAR_PHASE_OFFSET;
const height = Math.floor((Math.sin(phase) + 1) * MAX_HEIGHT_MULTIPLIER);
return WAVE_CHARS[Math.max(0, Math.min(7, height))] ?? " ";
});
return /* @__PURE__ */ (0, import_jsx_runtime110.jsxs)(Text, { color, children: [
bars.join(""),
" "
] });
};
// packages/cli/src/ui/components/InputPrompt.tsx
var import_chalk3 = __toESM(require_source(), 1);
// packages/cli/src/ui/hooks/useShellHistory.ts
var import_react63 = __toESM(require_react(), 1);
import * as fs2 from "node:fs/promises";
import * as path6 from "node:path";
var MAX_HISTORY_LENGTH = 100;
async function getHistoryFilePath(projectRoot, configStorage) {
const storage = configStorage ?? new Storage(projectRoot);
await storage.initialize();
return storage.getHistoryFilePath();
}
async function readHistoryFile(filePath) {
try {
const text = await fs2.readFile(filePath, "utf-8");
const result = [];
let cur = "";
for (const raw of text.split(/\r?\n/)) {
if (!raw.trim()) continue;
const line = raw;
const m = cur.match(/(\\+)$/);
if (m && m[1].length % 2) {
cur = cur.slice(0, -1) + " " + line;
} else {
if (cur) result.push(cur);
cur = line;
}
}
if (cur) result.push(cur);
return result;
} catch (err) {
if (isNodeError(err) && err.code === "ENOENT") return [];
debugLogger.error("Error reading history:", err);
return [];
}
}
async function writeHistoryFile(filePath, history) {
try {
await fs2.mkdir(path6.dirname(filePath), { recursive: true });
await fs2.writeFile(filePath, history.join("\n"));
} catch (error) {
debugLogger.error("Error writing shell history:", error);
}
}
function useShellHistory(projectRoot, storage) {
const [history, setHistory] = (0, import_react63.useState)([]);
const [historyIndex, setHistoryIndex] = (0, import_react63.useState)(-1);
const [historyFilePath, setHistoryFilePath] = (0, import_react63.useState)(null);
(0, import_react63.useEffect)(() => {
async function loadHistory() {
const filePath = await getHistoryFilePath(projectRoot, storage);
setHistoryFilePath(filePath);
const loadedHistory = await readHistoryFile(filePath);
setHistory(loadedHistory.reverse());
}
loadHistory();
}, [projectRoot, storage]);
const addCommandToHistory = (0, import_react63.useCallback)(
(command) => {
if (!command.trim() || !historyFilePath) {
return;
}
const newHistory = [command, ...history.filter((c) => c !== command)].slice(0, MAX_HISTORY_LENGTH).filter(Boolean);
setHistory(newHistory);
writeHistoryFile(historyFilePath, [...newHistory].reverse());
setHistoryIndex(-1);
},
[history, historyFilePath]
);
const getPreviousCommand = (0, import_react63.useCallback)(() => {
if (history.length === 0) {
return null;
}
const newIndex = Math.min(historyIndex + 1, history.length - 1);
setHistoryIndex(newIndex);
return history[newIndex] ?? null;
}, [history, historyIndex]);
const getNextCommand = (0, import_react63.useCallback)(() => {
if (historyIndex < 0) {
return null;
}
const newIndex = historyIndex - 1;
setHistoryIndex(newIndex);
if (newIndex < 0) {
return "";
}
return history[newIndex] ?? null;
}, [history, historyIndex]);
const resetHistoryPosition = (0, import_react63.useCallback)(() => {
setHistoryIndex(-1);
}, []);
return {
history,
addCommandToHistory,
getPreviousCommand,
getNextCommand,
resetHistoryPosition
};
}
// packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx
var import_react65 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useCompletion.ts
var import_react64 = __toESM(require_react(), 1);
function useCompletion() {
const [suggestions, setSuggestions] = (0, import_react64.useState)([]);
const [activeSuggestionIndex, setActiveSuggestionIndex] = (0, import_react64.useState)(-1);
const [visibleStartIndex, setVisibleStartIndex] = (0, import_react64.useState)(0);
const [isLoadingSuggestions, setIsLoadingSuggestions] = (0, import_react64.useState)(false);
const [isPerfectMatch, setIsPerfectMatch] = (0, import_react64.useState)(false);
const resetCompletionState = (0, import_react64.useCallback)(() => {
setSuggestions([]);
setActiveSuggestionIndex(-1);
setVisibleStartIndex(0);
setIsLoadingSuggestions(false);
setIsPerfectMatch(false);
}, []);
const navigateUp = (0, import_react64.useCallback)(() => {
if (suggestions.length === 0) return;
setActiveSuggestionIndex((prevActiveIndex) => {
const newActiveIndex = prevActiveIndex <= 0 ? suggestions.length - 1 : prevActiveIndex - 1;
setVisibleStartIndex((prevVisibleStart) => {
if (newActiveIndex === suggestions.length - 1 && suggestions.length > MAX_SUGGESTIONS_TO_SHOW) {
return Math.max(0, suggestions.length - MAX_SUGGESTIONS_TO_SHOW);
}
if (newActiveIndex < prevVisibleStart) {
return newActiveIndex;
}
return prevVisibleStart;
});
return newActiveIndex;
});
}, [suggestions.length]);
const navigateDown = (0, import_react64.useCallback)(() => {
if (suggestions.length === 0) return;
setActiveSuggestionIndex((prevActiveIndex) => {
const newActiveIndex = prevActiveIndex >= suggestions.length - 1 ? 0 : prevActiveIndex + 1;
setVisibleStartIndex((prevVisibleStart) => {
if (newActiveIndex === 0 && suggestions.length > MAX_SUGGESTIONS_TO_SHOW) {
return 0;
}
const visibleEndIndex = prevVisibleStart + MAX_SUGGESTIONS_TO_SHOW;
if (newActiveIndex >= visibleEndIndex) {
return newActiveIndex - MAX_SUGGESTIONS_TO_SHOW + 1;
}
return prevVisibleStart;
});
return newActiveIndex;
});
}, [suggestions.length]);
return {
suggestions,
activeSuggestionIndex,
visibleStartIndex,
isLoadingSuggestions,
isPerfectMatch,
setSuggestions,
setActiveSuggestionIndex,
setVisibleStartIndex,
setIsLoadingSuggestions,
setIsPerfectMatch,
resetCompletionState,
navigateUp,
navigateDown
};
}
// packages/cli/src/ui/hooks/useReverseSearchCompletion.tsx
function useDebouncedValue(value, delay2 = 200) {
const [debounced, setDebounced] = (0, import_react65.useState)(value);
(0, import_react65.useEffect)(() => {
const handle = setTimeout(() => setDebounced(value), delay2);
return () => clearTimeout(handle);
}, [value, delay2]);
return debounced;
}
function useReverseSearchCompletion(buffer, history, reverseSearchActive) {
const {
suggestions,
activeSuggestionIndex,
visibleStartIndex,
isLoadingSuggestions,
setSuggestions,
setActiveSuggestionIndex,
resetCompletionState,
navigateUp,
navigateDown,
setVisibleStartIndex
} = useCompletion();
const debouncedQuery = useDebouncedValue(buffer.text, 100);
const prevQueryRef = (0, import_react65.useRef)("");
const prevMatchesRef = (0, import_react65.useRef)([]);
(0, import_react65.useEffect)(() => {
if (reverseSearchActive) {
prevQueryRef.current = "";
prevMatchesRef.current = [];
}
}, [reverseSearchActive]);
(0, import_react65.useEffect)(() => {
prevQueryRef.current = "";
prevMatchesRef.current = [];
}, [history]);
const searchHistory = (0, import_react65.useCallback)(
(query, items) => {
const out = [];
for (let i = 0; i < items.length; i++) {
const cmd = items[i];
const idx = cmd.toLowerCase().indexOf(query);
if (idx !== -1) {
out.push({ label: cmd, value: cmd, matchedIndex: idx });
}
}
return out;
},
[]
);
const matches = (0, import_react65.useMemo)(() => {
if (!reverseSearchActive) return [];
if (debouncedQuery.length === 0)
return history.map((cmd) => ({
label: cmd,
value: cmd,
matchedIndex: -1
}));
const query = debouncedQuery.toLowerCase();
const canUseCache = prevQueryRef.current && query.startsWith(prevQueryRef.current) && prevMatchesRef.current.length > 0;
const source = canUseCache ? prevMatchesRef.current.map((m) => m.value) : history;
return searchHistory(query, source);
}, [debouncedQuery, history, reverseSearchActive, searchHistory]);
(0, import_react65.useEffect)(() => {
if (!reverseSearchActive) {
resetCompletionState();
return;
}
setSuggestions(matches);
const hasAny = matches.length > 0;
setActiveSuggestionIndex(hasAny ? 0 : -1);
setVisibleStartIndex(0);
prevQueryRef.current = debouncedQuery.toLowerCase();
prevMatchesRef.current = matches;
}, [
debouncedQuery,
matches,
reverseSearchActive,
setSuggestions,
setActiveSuggestionIndex,
setVisibleStartIndex,
resetCompletionState
]);
const showSuggestions = reverseSearchActive && (isLoadingSuggestions || suggestions.length > 0);
const handleAutocomplete = (0, import_react65.useCallback)(
(i) => {
if (i < 0 || i >= suggestions.length) return;
buffer.setText(suggestions[i].value);
resetCompletionState();
},
[buffer, suggestions, resetCompletionState]
);
return {
suggestions,
activeSuggestionIndex,
visibleStartIndex,
showSuggestions,
isLoadingSuggestions,
navigateUp,
navigateDown,
handleAutocomplete,
resetCompletionState
};
}
// packages/cli/src/ui/hooks/useCommandCompletion.tsx
var import_react70 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useAtCompletion.ts
var import_react66 = __toESM(require_react(), 1);
import { setTimeout as setTimeoutPromise } from "node:timers/promises";
import * as path7 from "node:path";
var DEFAULT_SEARCH_TIMEOUT_MS = 5e3;
var initialState3 = {
status: "idle" /* IDLE */,
suggestions: [],
isLoading: false,
pattern: null
};
function atCompletionReducer(state, action) {
switch (action.type) {
case "INITIALIZE":
return {
...state,
status: "initializing" /* INITIALIZING */,
isLoading: true
};
case "INITIALIZE_SUCCESS":
return { ...state, status: "ready" /* READY */, isLoading: false };
case "SEARCH":
return {
...state,
status: "searching" /* SEARCHING */,
pattern: action.payload
};
case "SEARCH_SUCCESS":
return {
...state,
status: "ready" /* READY */,
suggestions: action.payload,
isLoading: false
};
case "SET_LOADING":
if (state.status === "searching" /* SEARCHING */) {
return { ...state, isLoading: action.payload, suggestions: [] };
}
return state;
case "ERROR":
return {
...state,
status: "error" /* ERROR */,
isLoading: false,
suggestions: []
};
case "RESET":
return initialState3;
default:
return state;
}
}
function buildResourceCandidates(config) {
const registry = config?.getResourceRegistry?.();
if (!registry) {
return [];
}
const resources = registry.getAllResources().map((resource) => {
const prefixedUri = `${resource.serverName}:${resource.uri}`;
return {
// Include prefixedUri in searchKey so users can search by the displayed format
searchKey: `${prefixedUri} ${resource.name ?? ""}`.toLowerCase(),
suggestion: {
label: prefixedUri,
value: prefixedUri
}
};
});
return resources;
}
function buildAgentCandidates(config) {
const registry = config?.getAgentRegistry?.();
if (!registry) {
return [];
}
return registry.getAllDefinitions().map((def) => ({
label: def.name,
value: def.name,
commandKind: "agent" /* AGENT */
}));
}
async function searchResourceCandidates(pattern, candidates) {
if (candidates.length === 0) {
return [];
}
const normalizedPattern = pattern.toLowerCase();
if (!normalizedPattern) {
return candidates.slice(0, MAX_SUGGESTIONS_TO_SHOW).map((candidate) => candidate.suggestion);
}
const fzf = new AsyncFzf(candidates, {
selector: (candidate) => candidate.searchKey
});
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW * 3
});
return results.map(
(result) => result.item.suggestion
);
}
async function searchAgentCandidates(pattern, candidates) {
if (candidates.length === 0) {
return [];
}
const normalizedPattern = pattern.toLowerCase();
if (!normalizedPattern) {
return candidates.slice(0, MAX_SUGGESTIONS_TO_SHOW);
}
const fzf = new AsyncFzf(candidates, {
selector: (s) => s.label
});
const results = await fzf.find(normalizedPattern, {
limit: MAX_SUGGESTIONS_TO_SHOW
});
return results.map((r) => r.item);
}
function useAtCompletion(props) {
const {
enabled,
pattern,
config,
cwd: cwd5,
setSuggestions,
setIsLoadingSuggestions
} = props;
const [state, dispatch] = (0, import_react66.useReducer)(atCompletionReducer, initialState3);
const fileSearchMap = (0, import_react66.useRef)(/* @__PURE__ */ new Map());
const initEpoch = (0, import_react66.useRef)(0);
const searchAbortController = (0, import_react66.useRef)(null);
const slowSearchTimer = (0, import_react66.useRef)(null);
(0, import_react66.useEffect)(() => {
setSuggestions(state.suggestions);
}, [state.suggestions, setSuggestions]);
(0, import_react66.useEffect)(() => {
setIsLoadingSuggestions(state.isLoading);
}, [state.isLoading, setIsLoadingSuggestions]);
const disposeFileSearchers = (0, import_react66.useCallback)(async () => {
const searchers = [...fileSearchMap.current.values()];
fileSearchMap.current.clear();
initEpoch.current += 1;
const closePromises = [];
for (const searcher of searchers) {
if (searcher.close) {
closePromises.push(searcher.close());
}
}
await Promise.all(closePromises);
}, []);
const resetFileSearchState = (0, import_react66.useCallback)(() => {
void disposeFileSearchers();
dispatch({ type: "RESET" });
}, [disposeFileSearchers]);
(0, import_react66.useEffect)(() => {
resetFileSearchState();
}, [cwd5, config, resetFileSearchState]);
(0, import_react66.useEffect)(() => {
const workspaceContext = config?.getWorkspaceContext?.();
if (!workspaceContext) return;
const unsubscribe = workspaceContext.onDirectoriesChanged(resetFileSearchState);
return unsubscribe;
}, [config, resetFileSearchState]);
(0, import_react66.useEffect)(
() => () => {
void disposeFileSearchers();
searchAbortController.current?.abort();
if (slowSearchTimer.current) {
clearTimeout(slowSearchTimer.current);
}
},
[disposeFileSearchers]
);
(0, import_react66.useEffect)(() => {
if (!enabled) {
if (state.status === "ready" /* READY */ || state.status === "error" /* ERROR */) {
dispatch({ type: "RESET" });
}
return;
}
if (pattern === null) {
dispatch({ type: "RESET" });
return;
}
if (state.status === "idle" /* IDLE */) {
dispatch({ type: "INITIALIZE" });
} else if ((state.status === "ready" /* READY */ || state.status === "searching" /* SEARCHING */) && pattern.toLowerCase() !== state.pattern) {
dispatch({ type: "SEARCH", payload: pattern.toLowerCase() });
}
}, [enabled, pattern, state.status, state.pattern]);
(0, import_react66.useEffect)(() => {
const initialize = async () => {
const currentEpoch = initEpoch.current;
try {
const directories = config?.getWorkspaceContext?.()?.getDirectories() ?? [cwd5];
const initPromises = [];
for (const dir of directories) {
if (fileSearchMap.current.has(dir)) continue;
const searcher = FileSearchFactory.create({
projectRoot: dir,
ignoreDirs: [],
fileDiscoveryService: new FileDiscoveryService(
dir,
config?.getFileFilteringOptions()
),
cache: true,
cacheTtl: 30,
enableFileWatcher: config?.getFileFilteringOptions()?.enableFileWatcher ?? false,
enableRecursiveFileSearch: config?.getEnableRecursiveFileSearch() ?? true,
enableFuzzySearch: config?.getFileFilteringEnableFuzzySearch() ?? true,
maxFiles: config?.getFileFilteringOptions()?.maxFileCount
});
initPromises.push(
searcher.initialize().then(() => {
if (initEpoch.current === currentEpoch) {
fileSearchMap.current.set(dir, searcher);
}
})
);
}
await Promise.all(initPromises);
if (initEpoch.current !== currentEpoch) return;
dispatch({ type: "INITIALIZE_SUCCESS" });
if (state.pattern !== null) {
dispatch({ type: "SEARCH", payload: state.pattern });
}
} catch {
if (initEpoch.current === currentEpoch) {
dispatch({ type: "ERROR" });
}
}
};
const search = async () => {
if (fileSearchMap.current.size === 0 || state.pattern === null) {
return;
}
const currentPattern = state.pattern;
if (slowSearchTimer.current) {
clearTimeout(slowSearchTimer.current);
}
const controller = new AbortController();
searchAbortController.current = controller;
slowSearchTimer.current = setTimeout(() => {
dispatch({ type: "SET_LOADING", payload: true });
}, 200);
const timeoutMs = config?.getFileFilteringOptions()?.searchTimeout ?? DEFAULT_SEARCH_TIMEOUT_MS;
(async () => {
try {
await setTimeoutPromise(timeoutMs, void 0, {
signal: controller.signal
});
controller.abort();
} catch {
}
})();
try {
const directories = config?.getWorkspaceContext?.()?.getDirectories() ?? [cwd5];
const cwdRealpath = directories[0];
const allSearchPromises = [...fileSearchMap.current.entries()].map(
async ([dir, searcher]) => {
const results = await searcher.search(currentPattern, {
signal: controller.signal,
maxResults: MAX_SUGGESTIONS_TO_SHOW * 3
});
if (dir !== cwdRealpath) {
return results.map((p) => path7.join(dir, p));
}
return results;
}
);
const allResults = await Promise.all(allSearchPromises);
if (slowSearchTimer.current) {
clearTimeout(slowSearchTimer.current);
}
if (controller.signal.aborted) {
return;
}
const mergedResults = allResults.flat();
const fileSuggestions = mergedResults.map((p) => ({
label: p,
value: escapePath(p)
}));
const resourceCandidates = buildResourceCandidates(config);
const resourceSuggestions = (await searchResourceCandidates(
currentPattern ?? "",
resourceCandidates
)).map((suggestion) => ({
...suggestion,
label: suggestion.label.replace(/^@/, ""),
value: suggestion.value.replace(/^@/, "")
}));
const agentCandidates = buildAgentCandidates(config);
const agentSuggestions = await searchAgentCandidates(
currentPattern ?? "",
agentCandidates
);
if (controller.signal.aborted) {
return;
}
const combinedSuggestions = [
...agentSuggestions,
...fileSuggestions,
...resourceSuggestions
];
dispatch({ type: "SEARCH_SUCCESS", payload: combinedSuggestions });
} catch (error) {
if (!(error instanceof Error && error.name === "AbortError")) {
dispatch({ type: "ERROR" });
}
} finally {
controller.abort();
}
};
if (state.status === "initializing" /* INITIALIZING */) {
initialize();
} else if (state.status === "searching" /* SEARCHING */) {
search();
}
return () => {
searchAbortController.current?.abort();
if (slowSearchTimer.current) {
clearTimeout(slowSearchTimer.current);
}
};
}, [state.status, state.pattern, config, cwd5]);
}
// packages/cli/src/ui/hooks/useSlashCompletion.ts
var import_react67 = __toESM(require_react(), 1);
function logErrorSafely(error, context) {
if (error instanceof Error) {
debugLogger.warn(`[${context}]`, error);
} else {
debugLogger.warn(`[${context}] Non-error thrown:`, error);
}
}
function matchesCommand(cmd, query) {
return cmd.name.toLowerCase() === query.toLowerCase() || cmd.altNames?.some((alt) => alt.toLowerCase() === query.toLowerCase()) || false;
}
function useCommandParser(query, slashCommands) {
return (0, import_react67.useMemo)(() => {
if (!query) {
return {
hasTrailingSpace: false,
commandPathParts: [],
partial: "",
currentLevel: slashCommands,
leafCommand: null,
isArgumentCompletion: false
};
}
const fullPath = query.substring(1) || "";
const hasTrailingSpace = !!query.endsWith(" ");
const rawParts = fullPath.split(/\s+/).filter((p) => p);
let commandPathParts = rawParts;
let partial = "";
if (!hasTrailingSpace && rawParts.length > 0) {
partial = rawParts[rawParts.length - 1];
commandPathParts = rawParts.slice(0, -1);
}
let currentLevel = slashCommands;
let leafCommand = null;
for (const part of commandPathParts) {
if (!currentLevel) {
leafCommand = null;
currentLevel = [];
break;
}
const found = currentLevel.find(
(cmd) => matchesCommand(cmd, part)
);
if (found) {
leafCommand = found;
currentLevel = found.subCommands;
if (found.kind === "mcp-prompt" /* MCP_PROMPT */) {
break;
}
} else {
leafCommand = null;
currentLevel = [];
break;
}
}
const depth = commandPathParts.length;
const isArgumentCompletion = !!(leafCommand?.completion && (hasTrailingSpace || rawParts.length > depth && depth > 0 && partial !== ""));
return {
hasTrailingSpace,
commandPathParts,
partial,
currentLevel,
leafCommand,
isArgumentCompletion
};
}, [query, slashCommands]);
}
function useCommandSuggestions(query, parserResult, commandContext, getFzfForCommands, getPrefixSuggestions) {
const [suggestions, setSuggestions] = (0, import_react67.useState)([]);
const [isLoading, setIsLoading] = (0, import_react67.useState)(false);
(0, import_react67.useEffect)(() => {
const abortController = new AbortController();
const { signal } = abortController;
const {
isArgumentCompletion,
leafCommand,
commandPathParts,
partial,
currentLevel
} = parserResult;
if (isArgumentCompletion) {
const fetchAndSetSuggestions = async () => {
if (signal.aborted) return;
if (!leafCommand?.completion) {
debugLogger.warn(
"Attempted argument completion without completion function"
);
return;
}
const showLoading = leafCommand.showCompletionLoading !== false;
if (showLoading) {
setIsLoading(true);
}
try {
const rawParts = [...commandPathParts];
if (partial) rawParts.push(partial);
const depth = commandPathParts.length;
const argString = rawParts.slice(depth).join(" ");
const results = await leafCommand.completion(
{
...commandContext,
invocation: {
raw: query || `/${rawParts.join(" ")}`,
name: leafCommand.name,
args: argString
}
},
argString
) || [];
if (!signal.aborted) {
const finalSuggestions = results.map((s) => ({
label: s,
value: s
}));
setSuggestions(finalSuggestions);
setIsLoading(false);
}
} catch (error) {
if (!signal.aborted) {
logErrorSafely(error, "Argument completion");
setSuggestions([]);
setIsLoading(false);
}
}
};
fetchAndSetSuggestions();
return () => abortController.abort();
}
const commandsToSearch = currentLevel || [];
if (commandsToSearch.length > 0) {
const performFuzzySearch = async () => {
if (signal.aborted) return;
let potentialSuggestions = [];
if (partial === "") {
potentialSuggestions = commandsToSearch.filter(
(cmd) => cmd.description && !cmd.hidden
);
} else {
const fzfInstance = getFzfForCommands(commandsToSearch);
if (fzfInstance) {
try {
const fzfResults = await fzfInstance.fzf.find(partial);
if (signal.aborted) return;
const uniqueCommands = /* @__PURE__ */ new Set();
fzfResults.forEach((result) => {
const cmd = fzfInstance.commandMap.get(result.item);
if (cmd && cmd.description) {
uniqueCommands.add(cmd);
}
});
potentialSuggestions = Array.from(uniqueCommands);
} catch (error) {
logErrorSafely(
error,
"Fuzzy search - falling back to prefix matching"
);
potentialSuggestions = getPrefixSuggestions(
commandsToSearch,
partial
);
}
} else {
potentialSuggestions = getPrefixSuggestions(
commandsToSearch,
partial
);
}
}
if (!signal.aborted) {
const lowerPartial = partial.toLowerCase();
const sortedSuggestions = [...potentialSuggestions].sort((a, b) => {
const aNameExact = a.name.toLowerCase() === lowerPartial;
const bNameExact = b.name.toLowerCase() === lowerPartial;
if (aNameExact && !bNameExact) return -1;
if (!aNameExact && bNameExact) return 1;
const aAltExact = a.altNames?.some((alt) => alt.toLowerCase() === lowerPartial) || false;
const bAltExact = b.altNames?.some((alt) => alt.toLowerCase() === lowerPartial) || false;
if (aAltExact && !bAltExact) return -1;
if (!aAltExact && bAltExact) return 1;
const aNamePrefix = a.name.toLowerCase().startsWith(lowerPartial);
const bNamePrefix = b.name.toLowerCase().startsWith(lowerPartial);
if (aNamePrefix && !bNamePrefix) return -1;
if (!aNamePrefix && bNamePrefix) return 1;
const aAltPrefix = a.altNames?.some(
(alt) => alt.toLowerCase().startsWith(lowerPartial)
) || false;
const bAltPrefix = b.altNames?.some(
(alt) => alt.toLowerCase().startsWith(lowerPartial)
) || false;
if (aAltPrefix && !bAltPrefix) return -1;
if (!aAltPrefix && bAltPrefix) return 1;
return 0;
});
const finalSuggestions = sortedSuggestions.map((cmd) => {
const suggestion = {
label: cmd.name,
value: cmd.name,
description: cmd.description,
commandKind: cmd.kind
};
if (cmd.suggestionGroup) {
suggestion.sectionTitle = cmd.suggestionGroup;
}
return suggestion;
});
const isTopLevelChatOrResumeContext = !!(leafCommand && (leafCommand.name === "chat" || leafCommand.name === "resume") && (commandPathParts.length === 0 || commandPathParts.length === 1 && matchesCommand(leafCommand, commandPathParts[0])));
if (isTopLevelChatOrResumeContext) {
const canonicalParentName = leafCommand.name;
const autoLabel = "list";
if (partial === "" || autoLabel.toLowerCase().startsWith(partial.toLowerCase())) {
const autoSectionSuggestion = {
label: autoLabel,
value: autoLabel,
insertValue: canonicalParentName,
description: "Browse auto-saved chats",
commandKind: "built-in" /* BUILT_IN */,
sectionTitle: "auto",
submitValue: `/${canonicalParentName}`
};
setSuggestions([autoSectionSuggestion, ...finalSuggestions]);
return;
}
}
setSuggestions(finalSuggestions);
}
};
performFuzzySearch().catch((error) => {
logErrorSafely(error, "Unexpected fuzzy search error");
if (!signal.aborted) {
setSuggestions([]);
}
});
return () => abortController.abort();
}
setSuggestions([]);
return () => abortController.abort();
}, [
query,
parserResult,
commandContext,
getFzfForCommands,
getPrefixSuggestions
]);
return { suggestions, isLoading };
}
function useCompletionPositions(query, parserResult) {
return (0, import_react67.useMemo)(() => {
if (!query) {
return { start: -1, end: -1 };
}
const { hasTrailingSpace, partial } = parserResult;
if (hasTrailingSpace) {
return { start: query.length, end: query.length };
} else if (partial) {
if (parserResult.isArgumentCompletion) {
const commandSoFar = `/${parserResult.commandPathParts.join(" ")}`;
const argStartIndex = commandSoFar.length + (parserResult.commandPathParts.length > 0 ? 1 : 0);
return { start: argStartIndex, end: query.length };
} else {
return { start: query.length - partial.length, end: query.length };
}
} else {
return { start: 1, end: query.length };
}
}, [query, parserResult]);
}
function usePerfectMatch(parserResult) {
return (0, import_react67.useMemo)(() => {
const { hasTrailingSpace, partial, leafCommand, currentLevel } = parserResult;
if (hasTrailingSpace) {
return { isPerfectMatch: false };
}
if (leafCommand && partial === "" && leafCommand.action) {
return { isPerfectMatch: true };
}
if (currentLevel) {
const perfectMatch = currentLevel.find(
(cmd) => matchesCommand(cmd, partial) && cmd.action
);
if (perfectMatch) {
return { isPerfectMatch: true };
}
}
return { isPerfectMatch: false };
}, [parserResult]);
}
function getCommandFromSuggestion(suggestion, parserResult) {
const { currentLevel } = parserResult;
if (!currentLevel) {
return void 0;
}
const command = currentLevel.find(
(cmd) => matchesCommand(cmd, suggestion.value)
);
return command;
}
function useSlashCompletion(props) {
const {
enabled,
query,
slashCommands,
commandContext,
setSuggestions,
setIsLoadingSuggestions,
setIsPerfectMatch
} = props;
const [completionStart, setCompletionStart] = (0, import_react67.useState)(-1);
const [completionEnd, setCompletionEnd] = (0, import_react67.useState)(-1);
const fzfInstanceCache = (0, import_react67.useMemo)(
() => /* @__PURE__ */ new WeakMap(),
[]
);
const getFzfForCommands = (0, import_react67.useMemo)(
() => (commands) => {
if (!commands || commands.length === 0) {
return null;
}
const cached = fzfInstanceCache.get(commands);
if (cached) {
return cached;
}
const commandItems = [];
const commandMap = /* @__PURE__ */ new Map();
commands.forEach((cmd) => {
if (cmd.description && !cmd.hidden) {
commandItems.push(cmd.name);
commandMap.set(cmd.name, cmd);
if (cmd.altNames) {
cmd.altNames.forEach((alt) => {
commandItems.push(alt);
commandMap.set(alt, cmd);
});
}
}
});
if (commandItems.length === 0) {
return null;
}
try {
const instance = {
fzf: new AsyncFzf(commandItems, {
fuzzy: "v2",
casing: "case-insensitive"
// Explicitly enforce case-insensitivity
}),
commandMap
};
fzfInstanceCache.set(commands, instance);
return instance;
} catch (error) {
logErrorSafely(error, "FZF instance creation");
return null;
}
},
[fzfInstanceCache]
);
const getPrefixSuggestions = (0, import_react67.useMemo)(
() => (commands, partial) => commands.filter(
(cmd) => cmd.description && !cmd.hidden && (cmd.name.toLowerCase().startsWith(partial.toLowerCase()) || cmd.altNames?.some(
(alt) => alt.toLowerCase().startsWith(partial.toLowerCase())
))
),
[]
);
const parserResult = useCommandParser(query, slashCommands);
const { suggestions: hookSuggestions, isLoading } = useCommandSuggestions(
query,
parserResult,
commandContext,
getFzfForCommands,
getPrefixSuggestions
);
const { start: calculatedStart, end: calculatedEnd } = useCompletionPositions(
query,
parserResult
);
const { isPerfectMatch } = usePerfectMatch(parserResult);
(0, import_react67.useEffect)(() => {
if (!enabled) {
setSuggestions([]);
setIsLoadingSuggestions(false);
setIsPerfectMatch(false);
setCompletionStart(-1);
setCompletionEnd(-1);
}
}, [enabled, setSuggestions, setIsLoadingSuggestions, setIsPerfectMatch]);
(0, import_react67.useEffect)(() => {
if (!enabled || query === null) {
return;
}
setSuggestions(hookSuggestions);
setIsLoadingSuggestions(isLoading);
setIsPerfectMatch(isPerfectMatch);
setCompletionStart(calculatedStart);
setCompletionEnd(calculatedEnd);
}, [
enabled,
query,
hookSuggestions,
isLoading,
isPerfectMatch,
calculatedStart,
calculatedEnd,
setSuggestions,
setIsLoadingSuggestions,
setIsPerfectMatch
]);
return {
completionStart,
completionEnd,
getCommandFromSuggestion: (suggestion) => getCommandFromSuggestion(suggestion, parserResult),
isArgumentCompletion: parserResult.isArgumentCompletion,
leafCommand: parserResult.leafCommand
};
}
// packages/cli/src/ui/hooks/useShellCompletion.ts
var import_react68 = __toESM(require_react(), 1);
import * as fs4 from "node:fs/promises";
import * as path9 from "node:path";
import * as os from "node:os";
// packages/cli/src/ui/hooks/shell-completions/gitProvider.ts
import { execFile } from "node:child_process";
import { promisify } from "node:util";
var execFileAsync = promisify(execFile);
var GIT_SUBCOMMANDS = [
"add",
"branch",
"checkout",
"commit",
"diff",
"merge",
"pull",
"push",
"rebase",
"status",
"switch"
];
var gitProvider = {
command: "git",
async getCompletions(tokens, cursorIndex, cwd5, signal) {
if (cursorIndex === 1) {
const partial = tokens[1] || "";
return {
suggestions: GIT_SUBCOMMANDS.filter(
(cmd) => cmd.startsWith(partial)
).map((cmd) => ({
label: cmd,
value: cmd,
description: "git command"
})),
exclusive: true
};
}
if (cursorIndex === 2) {
const subcommand = tokens[1];
if (subcommand === "checkout" || subcommand === "switch" || subcommand === "merge" || subcommand === "branch") {
const partial = tokens[2] || "";
try {
const { stdout } = await execFileAsync(
"git",
["branch", "--format=%(refname:short)"],
{ cwd: cwd5, signal }
);
const branches = stdout.split("\n").map((b) => b.trim()).filter(Boolean);
return {
suggestions: branches.filter((b) => b.startsWith(partial)).map((b) => ({
label: b,
value: escapeShellPath(b),
description: "branch"
})),
exclusive: true
};
} catch {
return { suggestions: [], exclusive: true };
}
}
}
return { suggestions: [], exclusive: false };
}
};
// packages/cli/src/ui/hooks/shell-completions/npmProvider.ts
import * as fs3 from "node:fs/promises";
import * as path8 from "node:path";
var NPM_SUBCOMMANDS = [
"build",
"ci",
"dev",
"install",
"publish",
"run",
"start",
"test"
];
var npmProvider = {
command: "npm",
async getCompletions(tokens, cursorIndex, cwd5, signal) {
if (cursorIndex === 1) {
const partial = tokens[1] || "";
return {
suggestions: NPM_SUBCOMMANDS.filter(
(cmd) => cmd.startsWith(partial)
).map((cmd) => ({
label: cmd,
value: cmd,
description: "npm command"
})),
exclusive: true
};
}
if (cursorIndex === 2 && tokens[1] === "run") {
const partial = tokens[2] || "";
try {
if (signal?.aborted) return { suggestions: [], exclusive: true };
const pkgJsonPath = path8.join(cwd5, "package.json");
const content = await fs3.readFile(pkgJsonPath, "utf8");
const pkg = JSON.parse(content);
const scripts = pkg && typeof pkg === "object" && "scripts" in pkg && pkg.scripts && typeof pkg.scripts === "object" ? Object.keys(pkg.scripts) : [];
return {
suggestions: scripts.filter((s) => s.startsWith(partial)).map((s) => ({
label: s,
value: escapeShellPath(s),
description: "npm script"
})),
exclusive: true
};
} catch {
return { suggestions: [], exclusive: true };
}
}
return { suggestions: [], exclusive: false };
}
};
// packages/cli/src/ui/hooks/shell-completions/index.ts
var providers = [gitProvider, npmProvider];
async function getArgumentCompletions(commandToken, tokens, cursorIndex, cwd5, signal) {
const provider = providers.find((p) => p.command === commandToken);
if (!provider) {
return null;
}
return provider.getCompletions(tokens, cursorIndex, cwd5, signal);
}
// packages/cli/src/ui/hooks/useShellCompletion.ts
var MAX_SHELL_SUGGESTIONS = 100;
var FS_COMPLETION_DEBOUNCE_MS = 50;
var UNIX_SHELL_SPECIAL_CHARS = /[ \t\n\r'"()&|;<>!#$`{}[\]*?\\]/g;
function escapeShellPath(segment) {
if (process.platform === "win32") {
return segment;
}
return segment.replace(UNIX_SHELL_SPECIAL_CHARS, "\\$&");
}
function getTokenAtCursor(line, cursorCol) {
const tokensInfo = [];
let i = 0;
while (i < line.length) {
if (line[i] === " " || line[i] === " ") {
i++;
continue;
}
const tokenStart = i;
let token = "";
while (i < line.length) {
const ch = line[i];
if (ch === "\\" && i + 1 < line.length) {
token += line[i + 1];
i += 2;
continue;
}
if (ch === "'") {
i++;
while (i < line.length && line[i] !== "'") {
token += line[i];
i++;
}
if (i < line.length) i++;
continue;
}
if (ch === '"') {
i++;
while (i < line.length && line[i] !== '"') {
if (line[i] === "\\" && i + 1 < line.length) {
token += line[i + 1];
i += 2;
} else {
token += line[i];
i++;
}
}
if (i < line.length) i++;
continue;
}
if (ch === " " || ch === " ") {
break;
}
token += ch;
i++;
}
tokensInfo.push({ token, start: tokenStart, end: i });
}
const rawTokens = tokensInfo.map((t) => t.token);
const commandToken = rawTokens.length > 0 ? rawTokens[0] : "";
if (tokensInfo.length === 0) {
return {
token: "",
start: cursorCol,
end: cursorCol,
isFirstToken: true,
tokens: [""],
cursorIndex: 0,
commandToken: ""
};
}
for (let idx = 0; idx < tokensInfo.length; idx++) {
const t = tokensInfo[idx];
if (cursorCol >= t.start && cursorCol <= t.end) {
return {
token: t.token,
start: t.start,
end: t.end,
isFirstToken: idx === 0,
tokens: rawTokens,
cursorIndex: idx,
commandToken
};
}
}
let insertIndex = tokensInfo.length;
for (let idx = 0; idx < tokensInfo.length; idx++) {
if (cursorCol < tokensInfo[idx].start) {
insertIndex = idx;
break;
}
}
const newTokens = [
...rawTokens.slice(0, insertIndex),
"",
...rawTokens.slice(insertIndex)
];
return {
token: "",
start: cursorCol,
end: cursorCol,
isFirstToken: insertIndex === 0,
tokens: newTokens,
cursorIndex: insertIndex,
commandToken: newTokens.length > 0 ? newTokens[0] : ""
};
}
async function scanPathExecutables(signal) {
const pathEnv = process.env["PATH"] ?? "";
const dirs = pathEnv.split(path9.delimiter).filter(Boolean);
const isWindows3 = process.platform === "win32";
const pathExtList = isWindows3 ? (process.env["PATHEXT"] ?? ".EXE;.CMD;.BAT;.COM").split(";").filter(Boolean).map((e) => e.toLowerCase()) : [];
const seen = /* @__PURE__ */ new Set();
const executables = [];
if (isWindows3) {
const builtins = [
"assoc",
"break",
"call",
"cd",
"chcp",
"chdir",
"cls",
"color",
"copy",
"date",
"del",
"dir",
"echo",
"endlocal",
"erase",
"exit",
"for",
"ftype",
"goto",
"if",
"md",
"mkdir",
"mklink",
"move",
"path",
"pause",
"popd",
"prompt",
"pushd",
"rd",
"rem",
"ren",
"rename",
"rmdir",
"set",
"setlocal",
"shift",
"start",
"time",
"title",
"type",
"ver",
"verify",
"vol"
];
for (const builtin of builtins) {
seen.add(builtin);
executables.push(builtin);
}
}
const dirResults = await Promise.all(
dirs.map(async (dir) => {
if (signal?.aborted) return [];
try {
const entries = await fs4.readdir(dir, { withFileTypes: true });
const validEntries = [];
await Promise.all(
entries.map(async (entry) => {
if (signal?.aborted) return;
if (!entry.isFile() && !entry.isSymbolicLink()) return;
const name = entry.name;
if (isWindows3) {
const ext = path9.extname(name).toLowerCase();
if (pathExtList.length > 0 && !pathExtList.includes(ext)) return;
}
try {
await fs4.access(
path9.join(dir, name),
fs4.constants.R_OK | fs4.constants.X_OK
);
validEntries.push(name);
} catch {
}
})
);
return validEntries;
} catch {
return [];
}
})
);
for (const names of dirResults) {
for (const name of names) {
if (!seen.has(name)) {
seen.add(name);
executables.push(name);
}
}
}
executables.sort();
return executables;
}
function expandTilde(inputPath) {
if (inputPath === "~" || inputPath.startsWith("~/") || inputPath.startsWith("~" + path9.sep)) {
return [path9.join(os.homedir(), inputPath.slice(1)), true];
}
return [inputPath, false];
}
async function resolvePathCompletions(partial, cwd5, signal) {
if (partial == null) return [];
let strippedPartial = partial;
if (strippedPartial.startsWith('"') || strippedPartial.startsWith("'")) {
strippedPartial = strippedPartial.slice(1);
}
if (strippedPartial.endsWith('"') || strippedPartial.endsWith("'")) {
strippedPartial = strippedPartial.slice(0, -1);
}
const normalizedPartial = strippedPartial.replace(/\\/g, "/");
const [expandedPartial, didExpandTilde] = expandTilde(normalizedPartial);
const endsWithSep = normalizedPartial.endsWith("/") || normalizedPartial === "";
const dirToRead = endsWithSep ? path9.resolve(cwd5, expandedPartial) : path9.resolve(cwd5, path9.dirname(expandedPartial));
const prefix = endsWithSep ? "" : path9.basename(expandedPartial);
const prefixLower = prefix.toLowerCase();
const showDotfiles = prefix.startsWith(".");
let entries;
try {
if (signal?.aborted) return [];
entries = await fs4.readdir(dirToRead, { withFileTypes: true });
} catch {
return [];
}
if (signal?.aborted) return [];
const suggestions = [];
for (const entry of entries) {
if (signal?.aborted) break;
const name = entry.name;
if (name.startsWith(".") && !showDotfiles) continue;
if (!name.toLowerCase().startsWith(prefixLower)) continue;
const isDir = entry.isDirectory();
const displayName = isDir ? name + "/" : name;
let completionValue;
if (endsWithSep) {
completionValue = normalizedPartial + displayName;
} else {
const parentPart = normalizedPartial.slice(
0,
normalizedPartial.length - path9.basename(normalizedPartial).length
);
completionValue = parentPart + displayName;
}
if (didExpandTilde) {
const homeDir = os.homedir().replace(/\\/g, "/");
if (completionValue.startsWith(homeDir)) {
completionValue = "~" + completionValue.slice(homeDir.length);
}
}
const escapedValue = escapeShellPath(completionValue);
suggestions.push({
label: displayName,
value: escapedValue,
description: isDir ? "directory" : "file"
});
if (suggestions.length >= MAX_SHELL_SUGGESTIONS) break;
}
suggestions.sort((a, b) => {
const aIsDir = a.description === "directory";
const bIsDir = b.description === "directory";
if (aIsDir !== bIsDir) return aIsDir ? -1 : 1;
return a.label.localeCompare(b.label);
});
return suggestions;
}
var EMPTY_TOKENS = [];
function useShellCompletion({
enabled,
line,
cursorCol,
cwd: cwd5,
setSuggestions,
setIsLoadingSuggestions
}) {
const pathCachePromiseRef = (0, import_react68.useRef)(null);
const pathEnvRef = (0, import_react68.useRef)(process.env["PATH"] ?? "");
const abortRef = (0, import_react68.useRef)(null);
const debounceRef = (0, import_react68.useRef)(null);
const [activeStart, setActiveStart] = (0, import_react68.useState)(-1);
const tokenInfo = (0, import_react68.useMemo)(
() => enabled ? getTokenAtCursor(line, cursorCol) : null,
[enabled, line, cursorCol]
);
const {
token: query = "",
start: completionStart = -1,
end: completionEnd = -1,
isFirstToken: isCommandPosition = false,
tokens = EMPTY_TOKENS,
cursorIndex = -1,
commandToken = ""
} = tokenInfo || {};
if (enabled && activeStart !== -1 && completionStart !== activeStart) {
setSuggestions([]);
setActiveStart(-1);
}
(0, import_react68.useEffect)(() => {
const currentPath = process.env["PATH"] ?? "";
if (currentPath !== pathEnvRef.current) {
pathCachePromiseRef.current = null;
pathEnvRef.current = currentPath;
}
});
const performCompletion = (0, import_react68.useCallback)(async () => {
if (!enabled || !tokenInfo) {
setSuggestions([]);
return;
}
if (query.startsWith("-")) {
setSuggestions([]);
return;
}
if (abortRef.current) {
abortRef.current.abort();
}
const controller = new AbortController();
abortRef.current = controller;
const { signal } = controller;
try {
let results;
if (isCommandPosition) {
setIsLoadingSuggestions(true);
if (!pathCachePromiseRef.current) {
pathCachePromiseRef.current = scanPathExecutables();
}
const executables = await pathCachePromiseRef.current;
if (signal.aborted) return;
const queryLower = query.toLowerCase();
results = executables.filter((cmd) => cmd.toLowerCase().startsWith(queryLower)).sort((a, b) => {
if (a.length !== b.length) {
return a.length - b.length;
}
return a.localeCompare(b);
}).slice(0, MAX_SHELL_SUGGESTIONS).map((cmd) => ({
label: cmd,
value: escapeShellPath(cmd),
description: "command"
}));
} else {
const argumentCompletions = await getArgumentCompletions(
commandToken,
tokens,
cursorIndex,
cwd5,
signal
);
if (signal.aborted) return;
if (argumentCompletions?.exclusive) {
results = argumentCompletions.suggestions;
} else {
const pathSuggestions = await resolvePathCompletions(
query,
cwd5,
signal
);
if (signal.aborted) return;
results = [
...argumentCompletions?.suggestions ?? [],
...pathSuggestions
].slice(0, MAX_SHELL_SUGGESTIONS);
}
}
if (signal.aborted) return;
setSuggestions(results);
setActiveStart(completionStart);
} catch (error) {
if (!(signal.aborted || error instanceof Error && error.name === "AbortError")) {
debugLogger.warn(
`[WARN] shell completion failed: ${error instanceof Error ? error.message : String(error)}`
);
}
if (!signal.aborted) {
setSuggestions([]);
setActiveStart(completionStart);
}
} finally {
if (!signal.aborted) {
setIsLoadingSuggestions(false);
}
}
}, [
enabled,
tokenInfo,
query,
isCommandPosition,
tokens,
cursorIndex,
commandToken,
cwd5,
completionStart,
setSuggestions,
setIsLoadingSuggestions
]);
(0, import_react68.useEffect)(() => {
if (!enabled) {
abortRef.current?.abort();
setSuggestions([]);
setActiveStart(-1);
setIsLoadingSuggestions(false);
}
}, [enabled, setSuggestions, setIsLoadingSuggestions]);
(0, import_react68.useEffect)(() => {
if (!enabled) return;
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
debounceRef.current = setTimeout(() => {
performCompletion();
}, FS_COMPLETION_DEBOUNCE_MS);
return () => {
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
};
}, [enabled, performCompletion]);
(0, import_react68.useEffect)(
() => () => {
abortRef.current?.abort();
if (debounceRef.current) {
clearTimeout(debounceRef.current);
}
},
[]
);
return {
completionStart,
completionEnd,
query,
activeStart
};
}
// packages/cli/src/ui/hooks/usePromptCompletion.ts
var import_react69 = __toESM(require_react(), 1);
var PROMPT_COMPLETION_MIN_LENGTH = 5;
var PROMPT_COMPLETION_DEBOUNCE_MS = 250;
function usePromptCompletion({
buffer,
config
}) {
const [ghostText, setGhostText] = (0, import_react69.useState)("");
const [isLoadingGhostText, setIsLoadingGhostText] = (0, import_react69.useState)(false);
const abortControllerRef = (0, import_react69.useRef)(null);
const [justSelectedSuggestion, setJustSelectedSuggestion] = (0, import_react69.useState)(false);
const lastSelectedTextRef = (0, import_react69.useRef)("");
const lastRequestedTextRef = (0, import_react69.useRef)("");
const isPromptCompletionEnabled = false;
const clearGhostText = (0, import_react69.useCallback)(() => {
setGhostText("");
setIsLoadingGhostText(false);
}, []);
const acceptGhostText = (0, import_react69.useCallback)(() => {
if (ghostText && ghostText.length > buffer.text.length) {
buffer.setText(ghostText);
setGhostText("");
setJustSelectedSuggestion(true);
lastSelectedTextRef.current = ghostText;
}
}, [ghostText, buffer]);
const markSuggestionSelected = (0, import_react69.useCallback)((selectedText) => {
setJustSelectedSuggestion(true);
lastSelectedTextRef.current = selectedText;
}, []);
const generatePromptSuggestions = (0, import_react69.useCallback)(async () => {
const trimmedText = buffer.text.trim();
const geminiClient = config?.getGeminiClient();
if (trimmedText === lastRequestedTextRef.current) {
return;
}
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
if (trimmedText.length < PROMPT_COMPLETION_MIN_LENGTH || !geminiClient || isSlashCommand(trimmedText) || trimmedText.includes("@") || !isPromptCompletionEnabled) {
clearGhostText();
lastRequestedTextRef.current = "";
return;
}
lastRequestedTextRef.current = trimmedText;
setIsLoadingGhostText(true);
abortControllerRef.current = new AbortController();
const signal = abortControllerRef.current.signal;
try {
const contents = [
{
role: "user",
parts: [
{
text: `You are a professional prompt engineering assistant. Complete the user's partial prompt with expert precision and clarity. User's input: "${trimmedText}" Continue this prompt by adding specific, actionable details that align with the user's intent. Focus on: clear, precise language; structured requirements; professional terminology; measurable outcomes. Length Guidelines: Keep suggestions concise (ideally 10-20 characters); prioritize brevity while maintaining clarity; use essential keywords only; avoid redundant phrases. Start your response with the exact user text ("${trimmedText}") followed by your completion. Provide practical, implementation-focused suggestions rather than creative interpretations. Format: Plain text only. Single completion. Match the user's language. Emphasize conciseness over elaboration.`
}
]
}
];
const response = await geminiClient.generateContent(
{ model: "prompt-completion" },
contents,
signal,
LlmRole.UTILITY_AUTOCOMPLETE
);
if (signal.aborted) {
return;
}
if (response) {
const responseText = getResponseText(response);
if (responseText) {
const suggestionText = responseText.trim();
if (suggestionText.length > 0 && suggestionText.startsWith(trimmedText)) {
setGhostText(suggestionText);
} else {
clearGhostText();
}
}
}
} catch (error) {
if (!(signal.aborted || error instanceof Error && error.name === "AbortError")) {
debugLogger.warn(
`[WARN] prompt completion failed: : (${error instanceof Error ? error.message : String(error)})`
);
}
clearGhostText();
} finally {
if (!signal.aborted) {
setIsLoadingGhostText(false);
}
}
}, [buffer.text, config, clearGhostText, isPromptCompletionEnabled]);
const isCursorAtEnd = (0, import_react69.useCallback)(() => {
const [cursorRow, cursorCol] = buffer.cursor;
const totalLines = buffer.lines.length;
if (cursorRow !== totalLines - 1) {
return false;
}
const lastLine = buffer.lines[cursorRow] || "";
return cursorCol === lastLine.length;
}, [buffer.cursor, buffer.lines]);
const handlePromptCompletion = (0, import_react69.useCallback)(() => {
if (!isCursorAtEnd()) {
clearGhostText();
return;
}
const trimmedText = buffer.text.trim();
if (justSelectedSuggestion && trimmedText === lastSelectedTextRef.current) {
return;
}
if (trimmedText !== lastSelectedTextRef.current) {
setJustSelectedSuggestion(false);
lastSelectedTextRef.current = "";
}
generatePromptSuggestions();
}, [
buffer.text,
generatePromptSuggestions,
justSelectedSuggestion,
isCursorAtEnd,
clearGhostText
]);
(0, import_react69.useEffect)(() => {
const timeoutId2 = setTimeout(
handlePromptCompletion,
PROMPT_COMPLETION_DEBOUNCE_MS
);
return () => clearTimeout(timeoutId2);
}, [buffer.text, buffer.cursor, handlePromptCompletion]);
(0, import_react69.useEffect)(() => {
const currentText = buffer.text.trim();
if (ghostText && !isCursorAtEnd()) {
clearGhostText();
return;
}
if (ghostText && currentText.length > 0 && !ghostText.startsWith(currentText)) {
clearGhostText();
}
}, [buffer.text, buffer.cursor, ghostText, clearGhostText, isCursorAtEnd]);
(0, import_react69.useEffect)(() => () => abortControllerRef.current?.abort(), []);
const isActive = (0, import_react69.useMemo)(() => {
if (!isPromptCompletionEnabled) return false;
if (!isCursorAtEnd()) return false;
const trimmedText = buffer.text.trim();
return trimmedText.length >= PROMPT_COMPLETION_MIN_LENGTH && !isSlashCommand(trimmedText) && !trimmedText.includes("@");
}, [buffer.text, isPromptCompletionEnabled, isCursorAtEnd]);
return {
text: ghostText,
isLoading: isLoadingGhostText,
isActive,
accept: acceptGhostText,
clear: clearGhostText,
markSelected: markSuggestionSelected
};
}
// packages/cli/src/ui/hooks/useCommandCompletion.tsx
function useCommandCompletion({
buffer,
cwd: cwd5,
slashCommands,
commandContext,
reverseSearchActive = false,
shellModeActive,
config,
active
}) {
const [forceShowShellSuggestions, setForceShowShellSuggestions] = (0, import_react70.useState)(false);
const {
suggestions,
activeSuggestionIndex,
visibleStartIndex,
isLoadingSuggestions,
isPerfectMatch,
setSuggestions,
setActiveSuggestionIndex,
setIsLoadingSuggestions,
setIsPerfectMatch,
setVisibleStartIndex,
resetCompletionState: baseResetCompletionState,
navigateUp,
navigateDown
} = useCompletion();
const resetCompletionState = (0, import_react70.useCallback)(() => {
baseResetCompletionState();
setForceShowShellSuggestions(false);
}, [baseResetCompletionState]);
const cursorRow = buffer.cursor[0];
const cursorCol = buffer.cursor[1];
const {
completionMode,
query: memoQuery,
completionStart,
completionEnd
} = (0, import_react70.useMemo)(() => {
const currentLine = buffer.lines[cursorRow] || "";
const codePoints = toCodePoints(currentLine);
if (shellModeActive) {
return {
completionMode: currentLine.trim().length === 0 ? "IDLE" /* IDLE */ : "SHELL" /* SHELL */,
query: "",
completionStart: -1,
completionEnd: -1
};
}
for (let i = cursorCol - 1; i >= 0; i--) {
const char = codePoints[i];
if (char === " ") {
let backslashCount = 0;
for (let j = i - 1; j >= 0 && codePoints[j] === "\\"; j--) {
backslashCount++;
}
if (backslashCount % 2 === 0) {
break;
}
} else if (char === "@") {
let end = codePoints.length;
for (let i2 = cursorCol; i2 < codePoints.length; i2++) {
if (codePoints[i2] === " ") {
let backslashCount = 0;
for (let j = i2 - 1; j >= 0 && codePoints[j] === "\\"; j--) {
backslashCount++;
}
if (backslashCount % 2 === 0) {
end = i2;
break;
}
}
}
const pathStart = i + 1;
const partialPath = currentLine.substring(pathStart, end);
return {
completionMode: "AT" /* AT */,
query: partialPath,
completionStart: pathStart,
completionEnd: end
};
}
}
if (cursorRow === 0 && isSlashCommand(currentLine.trim())) {
return {
completionMode: "SLASH" /* SLASH */,
query: currentLine,
completionStart: 0,
completionEnd: currentLine.length
};
}
const trimmedText = buffer.text.trim();
const isPromptCompletionEnabled = false;
if (isPromptCompletionEnabled && trimmedText.length >= PROMPT_COMPLETION_MIN_LENGTH && !isSlashCommand(trimmedText) && !trimmedText.includes("@")) {
return {
completionMode: "PROMPT" /* PROMPT */,
query: trimmedText,
completionStart: 0,
completionEnd: trimmedText.length
};
}
return {
completionMode: "IDLE" /* IDLE */,
query: null,
completionStart: -1,
completionEnd: -1
};
}, [cursorRow, cursorCol, buffer.lines, buffer.text, shellModeActive]);
useAtCompletion({
enabled: active && completionMode === "AT" /* AT */,
pattern: memoQuery || "",
config,
cwd: cwd5,
setSuggestions,
setIsLoadingSuggestions
});
const slashCompletionRange = useSlashCompletion({
enabled: active && completionMode === "SLASH" /* SLASH */ && !shellModeActive,
query: memoQuery,
slashCommands,
commandContext,
setSuggestions,
setIsLoadingSuggestions,
setIsPerfectMatch
});
const shellCompletionRange = useShellCompletion({
enabled: active && completionMode === "SHELL" /* SHELL */,
line: buffer.lines[cursorRow] || "",
cursorCol,
cwd: cwd5,
setSuggestions,
setIsLoadingSuggestions
});
const query = completionMode === "SHELL" /* SHELL */ ? shellCompletionRange.query : memoQuery;
const basePromptCompletion = usePromptCompletion({
buffer
});
const isShellSuggestionsVisible = completionMode !== "SHELL" /* SHELL */ || forceShowShellSuggestions;
const promptCompletion = (0, import_react70.useMemo)(() => {
if (completionMode === "SHELL" /* SHELL */ && suggestions.length === 1 && query != null && shellCompletionRange.completionStart === shellCompletionRange.activeStart) {
const suggestion = suggestions[0];
const textToInsertBase = suggestion.value;
if (textToInsertBase.startsWith(query) && textToInsertBase.length > query.length) {
const currentLine = buffer.lines[cursorRow] || "";
const start = shellCompletionRange.completionStart;
const end = shellCompletionRange.completionEnd;
let textToInsert = textToInsertBase;
const charAfterCompletion = currentLine[end];
if (charAfterCompletion !== " " && !textToInsert.endsWith("/") && !textToInsert.endsWith("\\")) {
textToInsert += " ";
}
const newText = currentLine.substring(0, start) + textToInsert + currentLine.substring(end);
return {
text: newText,
isActive: true,
isLoading: false,
accept: () => {
buffer.replaceRangeByOffset(
logicalPosToOffset(buffer.lines, cursorRow, start),
logicalPosToOffset(buffer.lines, cursorRow, end),
textToInsert
);
},
clear: () => {
},
markSelected: () => {
}
};
}
}
return basePromptCompletion;
}, [
completionMode,
suggestions,
query,
basePromptCompletion,
buffer,
cursorRow,
shellCompletionRange
]);
(0, import_react70.useEffect)(() => {
setActiveSuggestionIndex(suggestions.length > 0 ? 0 : -1);
setVisibleStartIndex(0);
if (completionMode !== "SLASH" /* SLASH */) {
if (suggestions.length > 0) {
const firstSuggestion = suggestions[0];
setIsPerfectMatch(firstSuggestion.value === query);
} else {
setIsPerfectMatch(false);
}
}
}, [
suggestions,
setActiveSuggestionIndex,
setVisibleStartIndex,
completionMode,
query,
setIsPerfectMatch
]);
(0, import_react70.useEffect)(() => {
if (!active || completionMode === "IDLE" /* IDLE */ || reverseSearchActive) {
resetCompletionState();
}
}, [active, completionMode, reverseSearchActive, resetCompletionState]);
const showSuggestions = active && completionMode !== "IDLE" /* IDLE */ && !reverseSearchActive && isShellSuggestionsVisible && (isLoadingSuggestions || suggestions.length > 0);
const getCompletedText = (0, import_react70.useCallback)(
(suggestion) => {
const currentLine = buffer.lines[cursorRow] || "";
let start = completionStart;
let end = completionEnd;
if (completionMode === "SLASH" /* SLASH */) {
start = slashCompletionRange.completionStart;
end = slashCompletionRange.completionEnd;
} else if (completionMode === "SHELL" /* SHELL */) {
start = shellCompletionRange.completionStart;
end = shellCompletionRange.completionEnd;
}
if (start === -1 || end === -1) {
return null;
}
let suggestionText = suggestion.insertValue ?? suggestion.value;
if (completionMode === "SLASH" /* SLASH */) {
if (start === end && start > 1 && currentLine[start - 1] !== " ") {
suggestionText = " " + suggestionText;
}
}
return currentLine.substring(0, start) + suggestionText + currentLine.substring(end);
},
[
cursorRow,
buffer.lines,
completionMode,
completionStart,
completionEnd,
slashCompletionRange,
shellCompletionRange
]
);
const handleAutocomplete = (0, import_react70.useCallback)(
(indexToUse) => {
if (indexToUse < 0 || indexToUse >= suggestions.length) {
return;
}
const suggestion = suggestions[indexToUse];
const completedText = getCompletedText(suggestion);
if (completedText === null) {
return;
}
let start = completionStart;
let end = completionEnd;
if (completionMode === "SLASH" /* SLASH */) {
start = slashCompletionRange.completionStart;
end = slashCompletionRange.completionEnd;
} else if (completionMode === "SHELL" /* SHELL */) {
start = shellCompletionRange.completionStart;
end = shellCompletionRange.completionEnd;
}
let suggestionText = suggestion.insertValue ?? suggestion.value;
if (completionMode === "SLASH" /* SLASH */) {
if (start === end && start > 1 && (buffer.lines[cursorRow] || "")[start - 1] !== " ") {
suggestionText = " " + suggestionText;
}
}
const lineCodePoints = toCodePoints(buffer.lines[cursorRow] || "");
const charAfterCompletion = lineCodePoints[end];
let shouldAddSpace = true;
if (completionMode === "SLASH" /* SLASH */) {
const command = slashCompletionRange.getCommandFromSuggestion(suggestion);
const isExecutableCommand = !!(command && command.action);
const requiresArguments = !!(command && command.completion);
shouldAddSpace = !isExecutableCommand || requiresArguments;
}
if (charAfterCompletion !== " " && !suggestionText.endsWith("/") && !suggestionText.endsWith("\\") && shouldAddSpace) {
suggestionText += " ";
}
buffer.replaceRangeByOffset(
logicalPosToOffset(buffer.lines, cursorRow, start),
logicalPosToOffset(buffer.lines, cursorRow, end),
suggestionText
);
},
[
cursorRow,
buffer,
suggestions,
completionMode,
completionStart,
completionEnd,
slashCompletionRange,
shellCompletionRange,
getCompletedText
]
);
return {
suggestions,
activeSuggestionIndex,
visibleStartIndex,
showSuggestions,
isLoadingSuggestions,
isPerfectMatch,
forceShowShellSuggestions,
setForceShowShellSuggestions,
isShellSuggestionsVisible,
setActiveSuggestionIndex,
resetCompletionState,
navigateUp,
navigateDown,
handleAutocomplete,
promptCompletion,
getCommandFromSuggestion: slashCompletionRange.getCommandFromSuggestion,
slashCompletionRange,
getCompletedText,
completionMode
};
}
// packages/cli/src/ui/hooks/useVoiceMode.ts
var import_react71 = __toESM(require_react(), 1);
var HOLD_DELAY_MS = 600;
var RELEASE_DELAY_MS = 300;
function useVoiceMode({
buffer,
config,
settings,
setQueueErrorMessage,
isVoiceModeEnabled,
setVoiceModeEnabled,
keyMatchers
}) {
const [isRecording, setIsRecording] = (0, import_react71.useState)(false);
const [isConnecting, setIsConnecting] = (0, import_react71.useState)(false);
const liveTranscriptionRef = (0, import_react71.useRef)("");
const stopRequestedRef = (0, import_react71.useRef)(false);
const isRecordingRef = (0, import_react71.useRef)(false);
const lastFailureTimeRef = (0, import_react71.useRef)(0);
const recordingInProgressRef = (0, import_react71.useRef)(false);
const voiceTimeoutRef = (0, import_react71.useRef)(null);
const recorderRef = (0, import_react71.useRef)(null);
const transcriptionServiceRef = (0, import_react71.useRef)(null);
const turnBaselineRef = (0, import_react71.useRef)(null);
const turnBaselineCursorOffsetRef = (0, import_react71.useRef)(0);
const pttStateRef = (0, import_react71.useRef)("idle");
const pttTimerRef = (0, import_react71.useRef)(null);
const disconnectTimerRef = (0, import_react71.useRef)(null);
const bufferRef = (0, import_react71.useRef)(buffer);
bufferRef.current = buffer;
const stopVoiceRecording = (0, import_react71.useCallback)(() => {
if (stopRequestedRef.current) return;
debugLogger.debug("[Voice] Stop requested");
stopRequestedRef.current = true;
setIsRecording(false);
isRecordingRef.current = false;
setIsConnecting(false);
if (recorderRef.current) {
recorderRef.current.stop();
recorderRef.current = null;
}
const serviceToDisconnect = transcriptionServiceRef.current;
if (serviceToDisconnect) {
const gracePeriodMs = settings.experimental.voice.stopGracePeriodMs;
debugLogger.debug(
`[Voice] Draining transcription for ${gracePeriodMs}ms`
);
if (disconnectTimerRef.current) clearTimeout(disconnectTimerRef.current);
disconnectTimerRef.current = setTimeout(() => {
debugLogger.debug("[Voice] Grace period ended, disconnecting service");
serviceToDisconnect.disconnect();
if (transcriptionServiceRef.current === serviceToDisconnect) {
transcriptionServiceRef.current = null;
}
disconnectTimerRef.current = null;
liveTranscriptionRef.current = "";
}, gracePeriodMs);
} else {
liveTranscriptionRef.current = "";
}
pttStateRef.current = "idle";
}, [settings.experimental.voice]);
const startVoiceRecording = (0, import_react71.useCallback)(() => {
if (isRecordingRef.current || Date.now() - lastFailureTimeRef.current < 2e3) {
return;
}
if (disconnectTimerRef.current) {
clearTimeout(disconnectTimerRef.current);
disconnectTimerRef.current = null;
}
recordingInProgressRef.current = true;
turnBaselineRef.current = bufferRef.current.text;
turnBaselineCursorOffsetRef.current = bufferRef.current.getOffset();
setIsConnecting(true);
setIsRecording(true);
isRecordingRef.current = true;
liveTranscriptionRef.current = "";
stopRequestedRef.current = false;
const apiKey = config.getContentGeneratorConfig()?.apiKey || process.env["GEMINI_API_KEY"] || "";
const startAsync = async () => {
if (disconnectTimerRef.current) {
clearTimeout(disconnectTimerRef.current);
disconnectTimerRef.current = null;
}
if (transcriptionServiceRef.current) {
transcriptionServiceRef.current.disconnect();
transcriptionServiceRef.current = null;
}
const cleanupIfStopped = () => {
if (stopRequestedRef.current) {
if (recorderRef.current) {
recorderRef.current.stop();
recorderRef.current = null;
}
if (transcriptionServiceRef.current) {
transcriptionServiceRef.current.disconnect();
transcriptionServiceRef.current = null;
}
setIsRecording(false);
isRecordingRef.current = false;
setIsConnecting(false);
recordingInProgressRef.current = false;
return true;
}
return false;
};
if (cleanupIfStopped()) return;
const voiceBackend = settings.experimental.voice?.backend ?? "gemini-live";
if (!apiKey && voiceBackend === "gemini-live") {
setQueueErrorMessage(
"Cloud voice mode requires a GEMINI_API_KEY. Please set it in your environment or ~/.gemini/.env."
);
setIsRecording(false);
isRecordingRef.current = false;
setIsConnecting(false);
recordingInProgressRef.current = false;
lastFailureTimeRef.current = Date.now();
return;
}
if (voiceBackend === "gemini-live") {
recorderRef.current = new AudioRecorder();
}
const currentService = TranscriptionFactory.createProvider(
settings.experimental.voice,
apiKey
);
transcriptionServiceRef.current = currentService;
currentService.on("transcription", (text) => {
if (transcriptionServiceRef.current !== currentService && stopRequestedRef.current) {
return;
}
if (text) {
const baseline = turnBaselineRef.current ?? "";
const insertOffset = turnBaselineCursorOffsetRef.current;
const textBefore = baseline.slice(0, insertOffset);
const textAfter = baseline.slice(insertOffset);
const prefix = textBefore.length > 0 && !/\s$/.test(textBefore) ? textBefore + " " : textBefore;
const suffix = text.length > 0 && textAfter.length > 0 && !/^\s/.test(textAfter) ? " " : "";
const newTotalText = prefix + text + suffix + textAfter;
bufferRef.current.setText(newTotalText, prefix.length + text.length);
}
liveTranscriptionRef.current = text;
});
currentService.on("turnComplete", () => {
if (transcriptionServiceRef.current !== currentService && stopRequestedRef.current)
return;
turnBaselineRef.current = bufferRef.current.text;
turnBaselineCursorOffsetRef.current = bufferRef.current.getOffset();
liveTranscriptionRef.current = "";
});
currentService.on("error", (err) => {
if (transcriptionServiceRef.current !== currentService) return;
debugLogger.error("[Voice] Transcription error:", err);
lastFailureTimeRef.current = Date.now();
recordingInProgressRef.current = false;
});
currentService.on("close", () => {
if (transcriptionServiceRef.current !== currentService) return;
if (!stopRequestedRef.current) {
setIsRecording(false);
isRecordingRef.current = false;
setIsConnecting(false);
recordingInProgressRef.current = false;
lastFailureTimeRef.current = Date.now();
}
});
try {
await currentService.connect();
if (cleanupIfStopped()) return;
await recorderRef.current?.start();
if (cleanupIfStopped()) return;
setIsConnecting(false);
const currentVoiceBackend = settings.experimental.voice?.backend ?? "gemini-live";
recorderRef.current?.on("data", (chunk) => {
if (currentVoiceBackend === "gemini-live") {
currentService.sendAudioChunk(chunk);
}
});
recorderRef.current?.on("error", (err) => {
debugLogger.error("[Voice] Recorder error:", err);
stopVoiceRecording();
lastFailureTimeRef.current = Date.now();
});
} catch (err) {
if (transcriptionServiceRef.current !== currentService) return;
const message = err instanceof Error ? err.message : String(err);
setQueueErrorMessage(`Voice mode failure: ${message}`);
setIsRecording(false);
isRecordingRef.current = false;
setIsConnecting(false);
recordingInProgressRef.current = false;
lastFailureTimeRef.current = Date.now();
if (recorderRef.current) {
recorderRef.current.stop();
recorderRef.current = null;
}
if (transcriptionServiceRef.current) {
transcriptionServiceRef.current.disconnect();
transcriptionServiceRef.current = null;
}
}
};
void startAsync();
}, [
config,
settings.experimental.voice,
setQueueErrorMessage,
stopVoiceRecording
]);
(0, import_react71.useEffect)(
() => () => {
if (voiceTimeoutRef.current) clearTimeout(voiceTimeoutRef.current);
if (recorderRef.current) {
recorderRef.current.stop();
recorderRef.current = null;
}
if (transcriptionServiceRef.current) {
transcriptionServiceRef.current.disconnect();
transcriptionServiceRef.current = null;
}
if (pttTimerRef.current) clearTimeout(pttTimerRef.current);
if (disconnectTimerRef.current) clearTimeout(disconnectTimerRef.current);
},
[]
);
const handleVoiceInput = (0, import_react71.useCallback)(
(key) => {
const activeRecording = isRecording || isRecordingRef.current;
if (activeRecording) {
const activationMode = settings.experimental.voice?.activationMode ?? "push-to-talk";
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
stopVoiceRecording();
return true;
}
if (keyMatchers["app.voiceModePTT" /* VOICE_MODE_PTT */](key)) {
if (activationMode === "push-to-talk") {
if (pttTimerRef.current) {
clearTimeout(pttTimerRef.current);
}
pttTimerRef.current = setTimeout(() => {
stopVoiceRecording();
pttTimerRef.current = null;
}, RELEASE_DELAY_MS);
return true;
} else {
stopVoiceRecording();
return true;
}
}
return true;
}
if (isVoiceModeEnabled) {
const activationMode = settings.experimental.voice?.activationMode ?? "push-to-talk";
if (keyMatchers["basic.cancel" /* ESCAPE */](key) && buffer.text === "") {
setVoiceModeEnabled(false);
return true;
}
if (keyMatchers["app.voiceModePTT" /* VOICE_MODE_PTT */](key)) {
if (key.name === "space" && !key.ctrl && !key.alt && !key.shift && !key.cmd) {
if (activationMode === "toggle") {
startVoiceRecording();
return true;
} else {
if (pttStateRef.current === "idle") {
buffer.insert(" ");
pttStateRef.current = "possible-hold";
if (pttTimerRef.current) clearTimeout(pttTimerRef.current);
pttTimerRef.current = setTimeout(() => {
pttStateRef.current = "idle";
pttTimerRef.current = null;
}, HOLD_DELAY_MS);
return true;
} else if (pttStateRef.current === "possible-hold") {
if (pttTimerRef.current) clearTimeout(pttTimerRef.current);
buffer.backspace();
pttStateRef.current = "recording";
startVoiceRecording();
pttTimerRef.current = setTimeout(() => {
stopVoiceRecording();
pttTimerRef.current = null;
}, RELEASE_DELAY_MS);
return true;
}
}
}
}
if (pttStateRef.current === "possible-hold") {
pttStateRef.current = "idle";
if (pttTimerRef.current) {
clearTimeout(pttTimerRef.current);
pttTimerRef.current = null;
}
}
}
return false;
},
[
isRecording,
isVoiceModeEnabled,
settings.experimental.voice,
keyMatchers,
stopVoiceRecording,
startVoiceRecording,
buffer,
setVoiceModeEnabled
]
);
return {
isRecording,
isConnecting,
startVoiceRecording,
stopVoiceRecording,
handleVoiceInput,
resetTurnBaseline: () => {
turnBaselineRef.current = null;
}
};
}
// packages/cli/src/ui/utils/highlight.ts
var HIGHLIGHT_REGEX = new RegExp(
`(^/[a-zA-Z0-9_-]+|(?<!\\\\)@${AT_COMMAND_PATH_REGEX_SOURCE}|${PASTED_TEXT_PLACEHOLDER_REGEX.source})`,
"g"
);
var highlightCache = new import_lru_cache.default(
LRU_BUFFER_PERF_CACHE_LIMIT
);
function parseInputForHighlighting(text, index, transformations = [], cursorCol) {
let isCursorInsideTransform = false;
if (cursorCol !== void 0) {
for (const transform of transformations) {
if (cursorCol >= transform.logStart && cursorCol <= transform.logEnd) {
isCursorInsideTransform = true;
break;
}
}
}
const cacheKey = `${index === 0 ? "F" : "N"}:${isCursorInsideTransform ? cursorCol : "NC"}:${text}`;
const cached = highlightCache.get(cacheKey);
if (cached !== void 0) return cached;
HIGHLIGHT_REGEX.lastIndex = 0;
if (!text) {
return [{ text: "", type: "default" }];
}
const parseUntransformedInput = (text2) => {
const tokens2 = [];
if (!text2) return tokens2;
HIGHLIGHT_REGEX.lastIndex = 0;
let last = 0;
let match;
while ((match = HIGHLIGHT_REGEX.exec(text2)) !== null) {
const [fullMatch] = match;
const matchIndex = match.index;
if (matchIndex > last) {
tokens2.push({ text: text2.slice(last, matchIndex), type: "default" });
}
const type = fullMatch.startsWith("/") ? "command" : fullMatch.startsWith("@") ? "file" : "paste";
if (type === "command" && index !== 0) {
tokens2.push({ text: fullMatch, type: "default" });
} else {
tokens2.push({ text: fullMatch, type });
}
last = matchIndex + fullMatch.length;
}
if (last < text2.length) {
tokens2.push({ text: text2.slice(last), type: "default" });
}
return tokens2;
};
const tokens = [];
let column = 0;
const sortedTransformations = (transformations ?? []).slice().sort((a, b) => a.logStart - b.logStart);
for (const transformation of sortedTransformations) {
const textBeforeTransformation = cpSlice(
text,
column,
transformation.logStart
);
tokens.push(...parseUntransformedInput(textBeforeTransformation));
const isCursorInside = cursorCol !== void 0 && cursorCol >= transformation.logStart && cursorCol <= transformation.logEnd;
const transformationText = isCursorInside ? transformation.logicalText : transformation.collapsedText;
tokens.push({ text: transformationText, type: "file" });
column = transformation.logEnd;
}
const textAfterFinalTransformation = cpSlice(text, column);
tokens.push(...parseUntransformedInput(textAfterFinalTransformation));
highlightCache.set(cacheKey, tokens);
return tokens;
}
function parseSegmentsFromTokens(tokens, sliceStart, sliceEnd) {
if (sliceStart >= sliceEnd) return [];
const segments = [];
let tokenCpStart = 0;
for (const token of tokens) {
const tokenLen = cpLen(token.text);
const tokenStart = tokenCpStart;
const tokenEnd = tokenStart + tokenLen;
const overlapStart = Math.max(tokenStart, sliceStart);
const overlapEnd = Math.min(tokenEnd, sliceEnd);
if (overlapStart < overlapEnd) {
const sliceStartInToken = overlapStart - tokenStart;
const sliceEndInToken = overlapEnd - tokenStart;
const rawSlice = cpSlice(token.text, sliceStartInToken, sliceEndInToken);
const last = segments[segments.length - 1];
if (last && last.type === token.type) {
last.text += rawSlice;
} else {
segments.push({ type: token.type, text: rawSlice });
}
}
tokenCpStart += tokenLen;
}
return segments;
}
// packages/cli/src/ui/hooks/useKittyKeyboardProtocol.ts
var import_react72 = __toESM(require_react(), 1);
function useKittyKeyboardProtocol() {
const [status] = (0, import_react72.useState)({
enabled: terminalCapabilityManager.isKittyProtocolEnabled(),
checking: false
});
return status;
}
// packages/cli/src/ui/components/InputPrompt.tsx
import * as path10 from "node:path";
// packages/cli/src/ui/contexts/ShellFocusContext.tsx
var import_react73 = __toESM(require_react(), 1);
var ShellFocusContext = (0, import_react73.createContext)(true);
var useShellFocusState = () => (0, import_react73.useContext)(ShellFocusContext);
// packages/cli/src/ui/utils/shortcutsHelp.ts
function useIsHelpDismissKey() {
const keyMatchers = useKeyMatchers();
return (key) => Object.values(Command).some((command) => keyMatchers[command](key));
}
// packages/cli/src/ui/hooks/useRepeatedKeyPress.ts
var import_react74 = __toESM(require_react(), 1);
function useRepeatedKeyPress(options) {
const [pressCount, setPressCount] = (0, import_react74.useState)(0);
const pressCountRef = (0, import_react74.useRef)(0);
const timerRef = (0, import_react74.useRef)(null);
const optionsRef = (0, import_react74.useRef)(options);
(0, import_react74.useEffect)(() => {
optionsRef.current = options;
}, [options]);
const resetCount = (0, import_react74.useCallback)(() => {
if (timerRef.current) {
clearTimeout(timerRef.current);
timerRef.current = null;
}
if (pressCountRef.current > 0) {
pressCountRef.current = 0;
setPressCount(0);
optionsRef.current.onReset?.();
}
}, []);
const handlePress = (0, import_react74.useCallback)(() => {
const newCount = pressCountRef.current + 1;
pressCountRef.current = newCount;
setPressCount(newCount);
if (timerRef.current) {
clearTimeout(timerRef.current);
}
timerRef.current = setTimeout(() => {
pressCountRef.current = 0;
setPressCount(0);
timerRef.current = null;
optionsRef.current.onReset?.();
}, optionsRef.current.windowMs);
optionsRef.current.onRepeat?.(newCount);
return newCount;
}, []);
(0, import_react74.useEffect)(
() => () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
},
[]
);
return { pressCount, handlePress, resetCount };
}
// packages/cli/src/ui/components/InputPrompt.tsx
var import_jsx_runtime111 = __toESM(require_jsx_runtime(), 1);
var SCROLLBAR_GUTTER_WIDTH = 1;
function isTerminalPasteTrusted(kittyProtocolSupported) {
return kittyProtocolSupported;
}
var calculatePromptWidths = (mainContentWidth) => {
const FRAME_PADDING_AND_BORDER = 4;
const PROMPT_PREFIX_WIDTH = 2;
const FRAME_OVERHEAD = FRAME_PADDING_AND_BORDER + PROMPT_PREFIX_WIDTH;
const suggestionsWidth = Math.max(20, mainContentWidth);
return {
inputWidth: Math.max(mainContentWidth - FRAME_OVERHEAD, 1),
containerWidth: mainContentWidth,
suggestionsWidth,
frameOverhead: FRAME_OVERHEAD
};
};
function isLargePaste(text) {
const pasteLineCount = text.split("\n").length;
return pasteLineCount > LARGE_PASTE_LINE_THRESHOLD || text.length > LARGE_PASTE_CHAR_THRESHOLD;
}
var DOUBLE_TAB_CLEAN_UI_TOGGLE_WINDOW_MS = 350;
function tryTogglePasteExpansion(buffer) {
if (!buffer.pastedContent || Object.keys(buffer.pastedContent).length === 0) {
return false;
}
const [row, col] = buffer.cursor;
const transform = getTransformUnderCursor(
row,
col,
buffer.transformationsByLine,
{ includeEdge: true }
);
if (transform?.type === "paste" && transform.id) {
buffer.togglePasteExpansion(transform.id, row, col);
return true;
}
const expandedId = buffer.getExpandedPasteAtLine(row);
if (expandedId) {
buffer.togglePasteExpansion(expandedId, row, col);
return true;
}
appEvents.emit("transient-message" /* TransientMessage */, {
message: "Move cursor within placeholder to expand",
type: "hint" /* Hint */
});
return true;
}
var InputPrompt = ({
onSubmit,
onClearScreen,
config,
slashCommands,
commandContext,
placeholder = " Type your message or @path/to/file",
focus = true,
setShellModeActive,
approvalMode,
onEscapePromptChange,
onSuggestionsVisibilityChange,
vimHandleInput,
isEmbeddedShellFocused,
setQueueErrorMessage,
streamingState,
popAllMessages,
onQueueMessage,
suggestionsPosition = "below",
setBannerVisible
}) => {
const inputState = useInputState();
const {
buffer,
userMessages,
shellModeActive,
copyModeEnabled,
inputWidth,
suggestionsWidth
} = inputState;
const isHelpDismissKey = useIsHelpDismissKey();
const keyMatchers = useKeyMatchers();
const { stdout } = use_stdout_default();
const { merged: settings } = useSettings();
const kittyProtocol = useKittyKeyboardProtocol();
const isShellFocused = useShellFocusState();
const {
setEmbeddedShellFocused,
setShortcutsHelpVisible,
toggleCleanUiDetailsVisible,
setVoiceModeEnabled
} = useUIActions();
const {
terminalWidth,
activePtyId,
history,
backgroundTasks,
backgroundTaskHeight,
shortcutsHelpVisible,
isVoiceModeEnabled
} = useUIState();
const [suppressCompletion, setSuppressCompletion] = (0, import_react75.useState)(false);
const { handlePress: registerPlainTabPress, resetCount: resetPlainTabPress } = useRepeatedKeyPress({
windowMs: DOUBLE_TAB_CLEAN_UI_TOGGLE_WINDOW_MS
});
const [showEscapePrompt, setShowEscapePrompt] = (0, import_react75.useState)(false);
const { handlePress: handleEscPress, resetCount: resetEscapeState } = useRepeatedKeyPress({
windowMs: 500,
onRepeat: (count) => {
if (count === 1) {
setShowEscapePrompt(true);
} else if (count === 2) {
resetEscapeState();
if (buffer.text.length > 0) {
buffer.setText("");
resetTurnBaseline();
resetCompletionState();
} else if (history.length > 0) {
onSubmit("/rewind");
} else {
coreEvents.emitFeedback("info", "Nothing to rewind to");
}
}
},
onReset: () => setShowEscapePrompt(false)
});
const [recentUnsafePasteTime, setRecentUnsafePasteTime] = (0, import_react75.useState)(null);
const pasteTimeoutRef = (0, import_react75.useRef)(null);
const innerBoxRef = (0, import_react75.useRef)(null);
const hasUserNavigatedSuggestions = (0, import_react75.useRef)(false);
const listRef = (0, import_react75.useRef)(null);
const { isRecording, handleVoiceInput, resetTurnBaseline } = useVoiceMode({
buffer,
config,
settings,
setQueueErrorMessage,
isVoiceModeEnabled,
setVoiceModeEnabled,
keyMatchers
});
const [reverseSearchActive, setReverseSearchActive] = (0, import_react75.useState)(false);
const [commandSearchActive, setCommandSearchActive] = (0, import_react75.useState)(false);
const [textBeforeReverseSearch, setTextBeforeReverseSearch] = (0, import_react75.useState)("");
const [cursorPosition, setCursorPosition] = (0, import_react75.useState)([
0,
0
]);
const [expandedSuggestionIndex, setExpandedSuggestionIndex] = (0, import_react75.useState)(-1);
const shellHistory = useShellHistory(config.getProjectRoot(), config.storage);
const shellHistoryData = shellHistory.history;
const completion = useCommandCompletion({
buffer,
cwd: config.getTargetDir(),
slashCommands,
commandContext,
reverseSearchActive,
shellModeActive,
config,
active: !suppressCompletion
});
const reverseSearchCompletion = useReverseSearchCompletion(
buffer,
shellHistoryData,
reverseSearchActive
);
const reversedUserMessages = (0, import_react75.useMemo)(
() => [...userMessages].reverse(),
[userMessages]
);
const commandSearchCompletion = useReverseSearchCompletion(
buffer,
reversedUserMessages,
commandSearchActive
);
const resetCompletionState = completion.resetCompletionState;
const resetReverseSearchCompletionState = reverseSearchCompletion.resetCompletionState;
const resetCommandSearchCompletionState = commandSearchCompletion.resetCompletionState;
const getActiveCompletion = (0, import_react75.useCallback)(() => {
if (commandSearchActive) return commandSearchCompletion;
if (reverseSearchActive) return reverseSearchCompletion;
return completion;
}, [
commandSearchActive,
commandSearchCompletion,
reverseSearchActive,
reverseSearchCompletion,
completion
]);
const activeCompletion = getActiveCompletion();
const shouldShowSuggestions = activeCompletion.showSuggestions;
const {
forceShowShellSuggestions,
setForceShowShellSuggestions,
isShellSuggestionsVisible
} = completion;
const effectivePlaceholder = (0, import_react75.useMemo)(() => {
if (!isVoiceModeEnabled) return placeholder;
const voiceAction = (settings.experimental.voice?.activationMode ?? "push-to-talk") === "push-to-talk" ? "hold space to talk" : "space to talk";
return ` Type your message or ${voiceAction} (Esc to exit)`;
}, [
isVoiceModeEnabled,
placeholder,
settings.experimental.voice?.activationMode
]);
const showCursor = focus && isShellFocused && !isEmbeddedShellFocused && !copyModeEnabled;
(0, import_react75.useEffect)(() => {
appEvents.emit("scroll-to-bottom" /* ScrollToBottom */);
}, [buffer.text, buffer.cursor]);
(0, import_react75.useEffect)(() => {
if (onEscapePromptChange) {
onEscapePromptChange(showEscapePrompt);
}
}, [showEscapePrompt, onEscapePromptChange]);
(0, import_react75.useEffect)(
() => () => {
if (pasteTimeoutRef.current) {
clearTimeout(pasteTimeoutRef.current);
}
},
[]
);
const handleSubmitAndClear = (0, import_react75.useCallback)(
(submittedValue) => {
let processedValue = submittedValue;
if (buffer.pastedContent) {
processedValue = expandPastePlaceholders(
processedValue,
buffer.pastedContent
);
}
if (shellModeActive) {
shellHistory.addCommandToHistory(processedValue);
}
buffer.setText("");
resetTurnBaseline();
onSubmit(processedValue);
resetCompletionState();
resetReverseSearchCompletionState();
},
[
buffer,
onSubmit,
resetCompletionState,
shellModeActive,
shellHistory,
resetReverseSearchCompletionState,
resetTurnBaseline
]
);
const customSetTextAndResetCompletionSignal = (0, import_react75.useCallback)(
(newText, cursorPosition2) => {
buffer.setText(newText, cursorPosition2);
setSuppressCompletion(true);
},
[buffer, setSuppressCompletion]
);
const inputHistory = useInputHistory({
userMessages,
onSubmit: handleSubmitAndClear,
isActive: (!(completion.showSuggestions && isShellSuggestionsVisible) || completion.suggestions.length === 1) && !shellModeActive,
currentQuery: buffer.text,
currentCursorOffset: buffer.getOffset(),
onChange: customSetTextAndResetCompletionSignal
});
const handleSubmit = (0, import_react75.useCallback)(
(submittedValue) => {
const trimmedMessage = submittedValue.trim();
const isSlash = isSlashCommand(trimmedMessage);
const isShell = shellModeActive;
if ((isSlash || isShell) && streamingState === "responding" /* Responding */) {
if (isSlash) {
const { commandToExecute } = parseSlashCommand(
trimmedMessage,
slashCommands
);
if (commandToExecute?.isSafeConcurrent) {
handleSubmitAndClear(trimmedMessage);
return;
}
}
setQueueErrorMessage(
`${isShell ? "Shell" : "Slash"} commands cannot be queued`
);
return;
}
inputHistory.handleSubmit(trimmedMessage);
},
[
inputHistory,
shellModeActive,
streamingState,
setQueueErrorMessage,
slashCommands,
handleSubmitAndClear
]
);
(0, import_react75.useEffect)(() => {
if (suppressCompletion) {
resetCompletionState();
resetReverseSearchCompletionState();
resetCommandSearchCompletionState();
setExpandedSuggestionIndex(-1);
}
}, [
suppressCompletion,
buffer.text,
resetCompletionState,
setSuppressCompletion,
resetReverseSearchCompletionState,
resetCommandSearchCompletionState,
setExpandedSuggestionIndex
]);
const tryLoadQueuedMessages = (0, import_react75.useCallback)(() => {
if (buffer.text.trim() === "" && popAllMessages) {
const allMessages = popAllMessages();
if (allMessages) {
buffer.setText(allMessages);
return true;
} else {
inputHistory.navigateUp();
}
return true;
}
return false;
}, [buffer, popAllMessages, inputHistory]);
const handleClipboardPaste = (0, import_react75.useCallback)(async () => {
if (shortcutsHelpVisible) {
setShortcutsHelpVisible(false);
}
try {
if (await clipboardHasImage()) {
const imagePath = await saveClipboardImage(config.getTargetDir());
if (imagePath) {
cleanupOldClipboardImages(config.getTargetDir()).catch(() => {
});
const relativePath = path10.relative(config.getTargetDir(), imagePath);
const insertText = `@${relativePath}`;
const currentText = buffer.text;
const offset = buffer.getOffset();
let textToInsert = insertText;
const charBefore = offset > 0 ? currentText[offset - 1] : "";
const charAfter = offset < currentText.length ? currentText[offset] : "";
if (charBefore && charBefore !== " " && charBefore !== "\n") {
textToInsert = " " + textToInsert;
}
if (!charAfter || charAfter !== " " && charAfter !== "\n") {
textToInsert = textToInsert + " ";
}
buffer.replaceRangeByOffset(offset, offset, textToInsert);
}
}
if (settings.experimental?.useOSC52Paste) {
stdout.write("\x1B]52;c;?\x07");
} else {
const textToInsert = await clipboardy_default.read();
const escapedText = settings.ui?.escapePastedAtSymbols ? escapeAtSymbols(textToInsert) : textToInsert;
buffer.insert(escapedText, { paste: true });
if (isLargePaste(textToInsert)) {
appEvents.emit("transient-message" /* TransientMessage */, {
message: `Press ${formatCommand("app.expandPaste" /* EXPAND_PASTE */)} to expand pasted text`,
type: "hint" /* Hint */
});
}
}
} catch (error) {
debugLogger.error("Error handling paste:", error);
}
}, [
buffer,
config,
stdout,
settings,
shortcutsHelpVisible,
setShortcutsHelpVisible
]);
useMouseClick(
innerBoxRef,
(_event, relX, relY) => {
setSuppressCompletion(true);
if (isEmbeddedShellFocused) {
setEmbeddedShellFocused(false);
}
const currentScrollTop = Math.round(
listRef.current?.getScrollState().scrollTop ?? buffer.visualScrollRow
);
const visualRow = currentScrollTop + relY;
buffer.moveToVisualPosition(visualRow, relX);
},
{ isActive: focus }
);
const isAlternateBuffer = useAlternateBuffer();
useMouseClick(
innerBoxRef,
(_event, relX, relY) => {
if (!isAlternateBuffer) return;
const currentScrollTop = Math.round(
listRef.current?.getScrollState().scrollTop ?? buffer.visualScrollRow
);
const visualLine = buffer.allVisualLines[currentScrollTop + relY];
if (!visualLine) return;
const isPastEndOfLine = relX >= stringWidth(visualLine);
const logicalPos = isPastEndOfLine ? null : buffer.getLogicalPositionFromVisual(currentScrollTop + relY, relX);
if (logicalPos) {
const transform = getTransformUnderCursor(
logicalPos.row,
logicalPos.col,
buffer.transformationsByLine,
{ includeEdge: true }
);
if (transform?.type === "paste" && transform.id) {
buffer.togglePasteExpansion(
transform.id,
logicalPos.row,
logicalPos.col
);
return;
}
}
const visualRow = currentScrollTop + relY;
const mapEntry = buffer.visualToLogicalMap[visualRow];
const row = mapEntry ? mapEntry[0] : visualRow;
const expandedId = buffer.getExpandedPasteAtLine(row);
if (expandedId) {
buffer.togglePasteExpansion(
expandedId,
row,
logicalPos?.col ?? relX
// Fallback to relX if past end of line
);
}
},
{ isActive: focus, name: "double-click" }
);
useMouse(
(event) => {
if (event.name === "right-release") {
setSuppressCompletion(false);
handleClipboardPaste();
}
},
{ isActive: focus }
);
const handleInput = (0, import_react75.useCallback)(
(key) => {
if (handleVoiceInput(key)) return true;
const isHistoryUp = !shellModeActive && (keyMatchers["history.previous" /* HISTORY_UP */](key) || keyMatchers["nav.up" /* NAVIGATION_UP */](key) && (buffer.allVisualLines.length === 1 || buffer.visualCursor[0] === 0 && buffer.visualScrollRow === 0));
const isHistoryDown = !shellModeActive && (keyMatchers["history.next" /* HISTORY_DOWN */](key) || keyMatchers["nav.down" /* NAVIGATION_DOWN */](key) && (buffer.allVisualLines.length === 1 || buffer.visualCursor[0] === buffer.allVisualLines.length - 1));
const isHistoryNav = isHistoryUp || isHistoryDown;
const isCursorMovement = keyMatchers["cursor.left" /* MOVE_LEFT */](key) || keyMatchers["cursor.right" /* MOVE_RIGHT */](key) || keyMatchers["cursor.up" /* MOVE_UP */](key) || keyMatchers["cursor.down" /* MOVE_DOWN */](key) || keyMatchers["cursor.wordLeft" /* MOVE_WORD_LEFT */](key) || keyMatchers["cursor.wordRight" /* MOVE_WORD_RIGHT */](key) || keyMatchers["cursor.home" /* HOME */](key) || keyMatchers["cursor.end" /* END */](key);
const isSuggestionsNav = shouldShowSuggestions && (keyMatchers["suggest.focusPrevious" /* COMPLETION_UP */](key) || keyMatchers["suggest.focusNext" /* COMPLETION_DOWN */](key) || keyMatchers["suggest.expand" /* EXPAND_SUGGESTION */](key) || keyMatchers["suggest.collapse" /* COLLAPSE_SUGGESTION */](key) || keyMatchers["suggest.accept" /* ACCEPT_SUGGESTION */](key));
if (!isSuggestionsNav) {
setSuppressCompletion(
isHistoryNav || isCursorMovement || keyMatchers["basic.cancel" /* ESCAPE */](key)
);
hasUserNavigatedSuggestions.current = false;
if (key.name !== "tab") {
setForceShowShellSuggestions(false);
}
}
if (!focus && key.name !== "paste") {
return false;
}
if (shortcutsHelpVisible && key.name === "escape") {
setShortcutsHelpVisible(false);
return true;
}
const isGenerating = streamingState === "responding" /* Responding */ || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */;
const isQueueMessageKey = keyMatchers["input.queueMessage" /* QUEUE_MESSAGE */](key);
const isPlainTab = key.name === "tab" && !key.shift && !key.alt && !key.ctrl && !key.cmd;
const hasTabCompletionInteraction = completion.showSuggestions && isShellSuggestionsVisible || Boolean(completion.promptCompletion.text) || reverseSearchActive || commandSearchActive;
if (isGenerating && isQueueMessageKey && !hasTabCompletionInteraction && buffer.text.trim().length > 0) {
const trimmedMessage = buffer.text.trim();
const isSlash = isSlashCommand(trimmedMessage);
if (isSlash || shellModeActive) {
setQueueErrorMessage(
`${shellModeActive ? "Shell" : "Slash"} commands cannot be queued`
);
} else if (onQueueMessage) {
onQueueMessage(buffer.text);
buffer.setText("");
resetCompletionState();
resetReverseSearchCompletionState();
}
resetPlainTabPress();
return true;
}
if (isPlainTab && shellModeActive) {
resetPlainTabPress();
if (!shouldShowSuggestions) {
setSuppressCompletion(false);
if (completion.promptCompletion.text) {
completion.promptCompletion.accept();
return true;
} else if (completion.suggestions.length > 0 && !forceShowShellSuggestions) {
setForceShowShellSuggestions(true);
return true;
}
}
} else if (isPlainTab) {
if (!hasTabCompletionInteraction) {
if (registerPlainTabPress() === 2) {
toggleCleanUiDetailsVisible();
resetPlainTabPress();
return true;
}
} else {
resetPlainTabPress();
}
} else {
resetPlainTabPress();
}
if (key.name === "paste") {
if (shortcutsHelpVisible) {
setShortcutsHelpVisible(false);
}
if (!isTerminalPasteTrusted(kittyProtocol.enabled)) {
setRecentUnsafePasteTime(Date.now());
if (pasteTimeoutRef.current) {
clearTimeout(pasteTimeoutRef.current);
}
pasteTimeoutRef.current = setTimeout(() => {
setRecentUnsafePasteTime(null);
pasteTimeoutRef.current = null;
}, 40);
}
if (settings.ui?.escapePastedAtSymbols) {
buffer.handleInput({
...key,
sequence: escapeAtSymbols(key.sequence || "")
});
} else {
buffer.handleInput(key);
}
if (key.sequence && isLargePaste(key.sequence)) {
appEvents.emit("transient-message" /* TransientMessage */, {
message: `Press ${formatCommand("app.expandPaste" /* EXPAND_PASTE */)} to expand pasted text`,
type: "hint" /* Hint */
});
}
return true;
}
if (shortcutsHelpVisible && isHelpDismissKey(key)) {
setShortcutsHelpVisible(false);
}
if (shortcutsHelpVisible) {
if (key.sequence === "?" && key.insertable) {
setShortcutsHelpVisible(false);
buffer.handleInput(key);
return true;
}
if (key.name === "backspace" || key.sequence === "\b") {
setShortcutsHelpVisible(false);
return true;
}
if (key.insertable) {
setShortcutsHelpVisible(false);
}
}
if (key.sequence === "?" && key.insertable && !shortcutsHelpVisible && buffer.text.length === 0) {
setShortcutsHelpVisible(true);
return true;
}
if (vimHandleInput && vimHandleInput(key)) {
return true;
}
if (key.name !== "escape") {
resetEscapeState();
}
if (keyMatchers["app.expandPaste" /* EXPAND_PASTE */](key)) {
const handled2 = tryTogglePasteExpansion(buffer);
if (handled2) return true;
}
if (key.sequence === "!" && buffer.text === "" && !(completion.showSuggestions && isShellSuggestionsVisible)) {
setShellModeActive(!shellModeActive);
buffer.setText("");
resetTurnBaseline();
return true;
}
if (keyMatchers["basic.cancel" /* ESCAPE */](key)) {
const cancelSearch = (setActive, resetCompletion) => {
setActive(false);
resetCompletion();
buffer.setText(textBeforeReverseSearch);
const offset = logicalPosToOffset(
buffer.lines,
cursorPosition[0],
cursorPosition[1]
);
buffer.moveToOffset(offset);
setExpandedSuggestionIndex(-1);
};
if (reverseSearchActive) {
cancelSearch(
setReverseSearchActive,
reverseSearchCompletion.resetCompletionState
);
return true;
}
if (commandSearchActive) {
cancelSearch(
setCommandSearchActive,
commandSearchCompletion.resetCompletionState
);
return true;
}
if (completion.showSuggestions && isShellSuggestionsVisible) {
completion.resetCompletionState();
setExpandedSuggestionIndex(-1);
resetEscapeState();
return true;
}
if (shellModeActive) {
setShellModeActive(false);
resetEscapeState();
return true;
}
if (isGenerating) {
return false;
}
handleEscPress();
return true;
}
if (keyMatchers["app.clearScreen" /* CLEAR_SCREEN */](key)) {
setBannerVisible(false);
onClearScreen();
return true;
}
if (shellModeActive && keyMatchers["history.search.start" /* REVERSE_SEARCH */](key)) {
setReverseSearchActive(true);
setTextBeforeReverseSearch(buffer.text);
setCursorPosition(buffer.cursor);
return true;
}
if (reverseSearchActive || commandSearchActive) {
const isCommandSearch = commandSearchActive;
const sc = isCommandSearch ? commandSearchCompletion : reverseSearchCompletion;
const {
activeSuggestionIndex,
navigateUp,
navigateDown,
showSuggestions,
suggestions
} = sc;
const setActive = isCommandSearch ? setCommandSearchActive : setReverseSearchActive;
const resetState = sc.resetCompletionState;
if (showSuggestions) {
if (keyMatchers["nav.up" /* NAVIGATION_UP */](key)) {
navigateUp();
return true;
}
if (keyMatchers["nav.down" /* NAVIGATION_DOWN */](key)) {
navigateDown();
return true;
}
if (keyMatchers["suggest.collapse" /* COLLAPSE_SUGGESTION */](key)) {
if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
setExpandedSuggestionIndex(-1);
return true;
}
}
if (keyMatchers["suggest.expand" /* EXPAND_SUGGESTION */](key)) {
if (suggestions[activeSuggestionIndex].value.length >= MAX_WIDTH) {
setExpandedSuggestionIndex(activeSuggestionIndex);
return true;
}
}
if (keyMatchers["history.search.accept" /* ACCEPT_SUGGESTION_REVERSE_SEARCH */](key)) {
sc.handleAutocomplete(activeSuggestionIndex);
resetState();
setActive(false);
return true;
}
}
if (keyMatchers["history.search.submit" /* SUBMIT_REVERSE_SEARCH */](key)) {
const textToSubmit = showSuggestions && activeSuggestionIndex > -1 ? suggestions[activeSuggestionIndex].value : buffer.text;
handleSubmit(textToSubmit);
resetState();
setActive(false);
return true;
}
if (keyMatchers["nav.up" /* NAVIGATION_UP */](key) || keyMatchers["nav.down" /* NAVIGATION_DOWN */](key)) {
return true;
}
}
if (completion.isPerfectMatch && keyMatchers["input.submit" /* SUBMIT */](key) && recentUnsafePasteTime === null && (!(completion.showSuggestions && isShellSuggestionsVisible) || completion.activeSuggestionIndex <= 0 && !hasUserNavigatedSuggestions.current)) {
handleSubmit(buffer.text);
return true;
}
if (keyMatchers["input.newline" /* NEWLINE */](key)) {
buffer.newline();
return true;
}
if (completion.showSuggestions && isShellSuggestionsVisible) {
if (completion.suggestions.length > 1) {
if (keyMatchers["suggest.focusPrevious" /* COMPLETION_UP */](key)) {
completion.navigateUp();
hasUserNavigatedSuggestions.current = true;
setExpandedSuggestionIndex(-1);
return true;
}
if (keyMatchers["suggest.focusNext" /* COMPLETION_DOWN */](key)) {
completion.navigateDown();
hasUserNavigatedSuggestions.current = true;
setExpandedSuggestionIndex(-1);
return true;
}
}
if (keyMatchers["suggest.accept" /* ACCEPT_SUGGESTION */](key)) {
if (completion.suggestions.length > 0) {
const targetIndex = completion.activeSuggestionIndex === -1 ? 0 : completion.activeSuggestionIndex;
if (targetIndex < completion.suggestions.length) {
const suggestion = completion.suggestions[targetIndex];
const isEnterKey = key.name === "enter" && !key.ctrl;
if (isEnterKey && shellModeActive) {
if (hasUserNavigatedSuggestions.current) {
completion.handleAutocomplete(
completion.activeSuggestionIndex
);
setExpandedSuggestionIndex(-1);
hasUserNavigatedSuggestions.current = false;
return true;
}
completion.resetCompletionState();
setExpandedSuggestionIndex(-1);
hasUserNavigatedSuggestions.current = false;
if (buffer.text.trim()) {
handleSubmit(buffer.text);
}
return true;
}
if (isEnterKey && buffer.text.startsWith("/")) {
if (suggestion.submitValue) {
setExpandedSuggestionIndex(-1);
handleSubmit(suggestion.submitValue.trim());
return true;
}
const { isArgumentCompletion, leafCommand } = completion.slashCompletionRange;
if (isArgumentCompletion && isAutoExecutableCommand(leafCommand)) {
const completedText = completion.getCompletedText(suggestion);
if (completedText) {
setExpandedSuggestionIndex(-1);
handleSubmit(completedText.trim());
return true;
}
} else if (!isArgumentCompletion) {
const command = completion.getCommandFromSuggestion(suggestion);
if (command && isAutoExecutableCommand(command) && !command.completion) {
const completedText = completion.getCompletedText(suggestion);
if (completedText) {
setExpandedSuggestionIndex(-1);
handleSubmit(completedText.trim());
return true;
}
}
}
}
completion.handleAutocomplete(targetIndex);
setExpandedSuggestionIndex(-1);
}
}
return true;
}
}
if (key.name === "tab" && !key.shift && !(completion.showSuggestions && isShellSuggestionsVisible) && completion.promptCompletion.text) {
completion.promptCompletion.accept();
return true;
}
if (!shellModeActive) {
if (keyMatchers["history.search.start" /* REVERSE_SEARCH */](key)) {
setCommandSearchActive(true);
setTextBeforeReverseSearch(buffer.text);
setCursorPosition(buffer.cursor);
return true;
}
if (isHistoryUp) {
if (keyMatchers["nav.up" /* NAVIGATION_UP */](key) && buffer.visualCursor[1] > 0) {
buffer.move("home");
return true;
}
if (tryLoadQueuedMessages()) {
return true;
}
inputHistory.navigateUp();
return true;
}
if (isHistoryDown) {
if (keyMatchers["nav.down" /* NAVIGATION_DOWN */](key) && buffer.visualCursor[1] < cpLen(buffer.allVisualLines[buffer.visualCursor[0]] || "")) {
buffer.move("end");
return true;
}
inputHistory.navigateDown();
return true;
}
} else {
if (keyMatchers["nav.up" /* NAVIGATION_UP */](key)) {
if ((buffer.allVisualLines.length === 1 || buffer.visualCursor[0] === 0 && buffer.visualScrollRow === 0) && buffer.visualCursor[1] > 0) {
buffer.move("home");
return true;
}
const prevCommand = shellHistory.getPreviousCommand();
if (prevCommand !== null) buffer.setText(prevCommand);
return true;
}
if (keyMatchers["nav.down" /* NAVIGATION_DOWN */](key)) {
if ((buffer.allVisualLines.length === 1 || buffer.visualCursor[0] === buffer.allVisualLines.length - 1) && buffer.visualCursor[1] < cpLen(buffer.allVisualLines[buffer.visualCursor[0]] || "")) {
buffer.move("end");
return true;
}
const nextCommand = shellHistory.getNextCommand();
if (nextCommand !== null) buffer.setText(nextCommand);
return true;
}
}
if (keyMatchers["input.submit" /* SUBMIT */](key)) {
if (buffer.text.trim()) {
if (recentUnsafePasteTime !== null) {
buffer.newline();
return true;
}
const [row, col] = buffer.cursor;
const line = buffer.lines[row];
const charBefore = col > 0 ? cpSlice(line, col - 1, col) : "";
if (charBefore === "\\") {
buffer.backspace();
buffer.newline();
} else {
handleSubmit(buffer.text);
}
}
return true;
}
if (keyMatchers["cursor.home" /* HOME */](key)) {
buffer.move("home");
return true;
}
if (keyMatchers["cursor.end" /* END */](key)) {
buffer.move("end");
return true;
}
if (keyMatchers["edit.deleteRightAll" /* KILL_LINE_RIGHT */](key)) {
buffer.killLineRight();
return true;
}
if (keyMatchers["edit.deleteLeftAll" /* KILL_LINE_LEFT */](key)) {
buffer.killLineLeft();
return true;
}
if (keyMatchers["edit.deleteWordLeft" /* DELETE_WORD_BACKWARD */](key)) {
buffer.deleteWordLeft();
return true;
}
if (keyMatchers["input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */](key)) {
buffer.openInExternalEditor();
return true;
}
if (keyMatchers["input.deprecatedOpenExternalEditor" /* DEPRECATED_OPEN_EXTERNAL_EDITOR */](key)) {
const cmdKey = formatCommand("input.openExternalEditor" /* OPEN_EXTERNAL_EDITOR */);
appEvents.emit("transient-message" /* TransientMessage */, {
message: `Use ${cmdKey} to open the external editor.`,
type: "hint" /* Hint */
});
return true;
}
if (keyMatchers["input.paste" /* PASTE_CLIPBOARD */](key)) {
handleClipboardPaste();
return true;
}
if (keyMatchers["background.toggle" /* TOGGLE_BACKGROUND_SHELL */](key)) {
return false;
}
if (keyMatchers["app.focusShellInput" /* FOCUS_SHELL_INPUT */](key)) {
if (activePtyId || backgroundTasks.size > 0 && backgroundTaskHeight > 0) {
setEmbeddedShellFocused(true);
return true;
}
return false;
}
const handled = buffer.handleInput(key);
if (handled) {
if (keyMatchers["edit.clear" /* CLEAR_INPUT */](key)) {
resetCompletionState();
}
if (completion.promptCompletion.text && key.sequence && key.sequence.length === 1 && !key.alt && !key.ctrl && !key.cmd) {
completion.promptCompletion.clear();
setExpandedSuggestionIndex(-1);
}
}
return handled;
},
[
focus,
buffer,
completion,
setForceShowShellSuggestions,
shellModeActive,
setShellModeActive,
onClearScreen,
inputHistory,
handleSubmit,
shellHistory,
reverseSearchCompletion,
handleClipboardPaste,
resetCompletionState,
resetEscapeState,
vimHandleInput,
reverseSearchActive,
textBeforeReverseSearch,
cursorPosition,
recentUnsafePasteTime,
commandSearchActive,
commandSearchCompletion,
kittyProtocol.enabled,
shortcutsHelpVisible,
setShortcutsHelpVisible,
tryLoadQueuedMessages,
onQueueMessage,
setQueueErrorMessage,
resetReverseSearchCompletionState,
setBannerVisible,
activePtyId,
setEmbeddedShellFocused,
backgroundTasks.size,
backgroundTaskHeight,
streamingState,
handleEscPress,
resetTurnBaseline,
registerPlainTabPress,
resetPlainTabPress,
toggleCleanUiDetailsVisible,
shouldShowSuggestions,
isShellSuggestionsVisible,
forceShowShellSuggestions,
keyMatchers,
isHelpDismissKey,
settings,
handleVoiceInput
]
);
useKeypress(handleInput, {
isActive: !isEmbeddedShellFocused && !copyModeEnabled,
priority: true
});
const [cursorVisualRowAbsolute, cursorVisualColAbsolute] = buffer.visualCursor;
const getGhostTextLines = (0, import_react75.useCallback)(() => {
if (!completion.promptCompletion.text || !buffer.text || !completion.promptCompletion.text.startsWith(buffer.text)) {
return { inlineGhost: "", additionalLines: [] };
}
const ghostSuffix = completion.promptCompletion.text.slice(
buffer.text.length
);
if (!ghostSuffix) {
return { inlineGhost: "", additionalLines: [] };
}
const currentLogicalLine = buffer.lines[buffer.cursor[0]] || "";
const cursorCol = buffer.cursor[1];
const textBeforeCursor = cpSlice(currentLogicalLine, 0, cursorCol);
const usedWidth = stringWidth(textBeforeCursor);
const remainingWidth = Math.max(0, inputWidth - usedWidth);
const ghostTextLinesRaw = ghostSuffix.split("\n");
const firstLineRaw = ghostTextLinesRaw.shift() || "";
let inlineGhost2 = "";
let remainingFirstLine = "";
if (stringWidth(firstLineRaw) <= remainingWidth) {
inlineGhost2 = firstLineRaw;
} else {
const words = firstLineRaw.split(" ");
let currentLine = "";
let wordIdx = 0;
for (const word of words) {
const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
if (stringWidth(prospectiveLine) > remainingWidth) {
break;
}
currentLine = prospectiveLine;
wordIdx++;
}
inlineGhost2 = currentLine;
if (words.length > wordIdx) {
remainingFirstLine = words.slice(wordIdx).join(" ");
}
}
const linesToWrap = [];
if (remainingFirstLine) {
linesToWrap.push(remainingFirstLine);
}
linesToWrap.push(...ghostTextLinesRaw);
const remainingGhostText = linesToWrap.join("\n");
const additionalLines2 = [];
if (remainingGhostText) {
const textLines = remainingGhostText.split("\n");
for (const textLine of textLines) {
const words = textLine.split(" ");
let currentLine = "";
for (const word of words) {
const prospectiveLine = currentLine ? `${currentLine} ${word}` : word;
const prospectiveWidth = stringWidth(prospectiveLine);
if (prospectiveWidth > inputWidth) {
if (currentLine) {
additionalLines2.push(currentLine);
}
let wordToProcess = word;
while (stringWidth(wordToProcess) > inputWidth) {
let part = "";
const wordCP = toCodePoints(wordToProcess);
let partWidth = 0;
let splitIndex = 0;
for (let i = 0; i < wordCP.length; i++) {
const char = wordCP[i];
const charWidth = stringWidth(char);
if (partWidth + charWidth > inputWidth) {
break;
}
part += char;
partWidth += charWidth;
splitIndex = i + 1;
}
additionalLines2.push(part);
wordToProcess = cpSlice(wordToProcess, splitIndex);
}
currentLine = wordToProcess;
} else {
currentLine = prospectiveLine;
}
}
if (currentLine) {
additionalLines2.push(currentLine);
}
}
}
return { inlineGhost: inlineGhost2, additionalLines: additionalLines2 };
}, [
completion.promptCompletion.text,
buffer.text,
buffer.lines,
buffer.cursor,
inputWidth
]);
const { inlineGhost, additionalLines } = getGhostTextLines();
const scrollableData = (0, import_react75.useMemo)(() => {
const items = buffer.allVisualLines.map(
(lineText, index) => ({
type: "visualLine",
lineText,
absoluteVisualIdx: index
})
);
additionalLines.forEach((ghostLine, index) => {
items.push({
type: "ghostLine",
ghostLine,
index
});
});
return items;
}, [buffer.allVisualLines, additionalLines]);
const renderItem = (0, import_react75.useCallback)(
({ item }) => {
if (item.type === "ghostLine") {
const padding = Math.max(0, inputWidth - stringWidth(item.ghostLine));
return /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Box_default, { height: 1, children: /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(Text, { color: theme.text.secondary, children: [
item.ghostLine,
" ".repeat(padding)
] }) });
}
const { lineText, absoluteVisualIdx } = item;
const mapEntry = buffer.visualToLogicalMap[absoluteVisualIdx];
if (!mapEntry) return /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { children: " " });
const isOnCursorLine = focus && absoluteVisualIdx === cursorVisualRowAbsolute;
const renderedLine = [];
const [logicalLineIdx] = mapEntry;
const logicalLine = buffer.lines[logicalLineIdx] || "";
const transformations = buffer.transformationsByLine[logicalLineIdx] ?? [];
const tokens = parseInputForHighlighting(
logicalLine,
logicalLineIdx,
transformations,
...focus && buffer.cursor[0] === logicalLineIdx ? [buffer.cursor[1]] : []
);
const visualStartCol = buffer.visualToTransformedMap[absoluteVisualIdx] ?? 0;
const visualEndCol = visualStartCol + cpLen(lineText);
const segments = parseSegmentsFromTokens(
tokens,
visualStartCol,
visualEndCol
);
let charCount = 0;
segments.forEach((seg, segIdx) => {
const segLen = cpLen(seg.text);
let display = seg.text;
if (isOnCursorLine) {
const relCol = cursorVisualColAbsolute;
const segStart = charCount;
const segEnd = segStart + segLen;
if (relCol >= segStart && relCol < segEnd) {
const charToHighlight = cpSlice(
display,
relCol - segStart,
relCol - segStart + 1
);
const highlighted = showCursor ? import_chalk3.default.inverse(charToHighlight) : charToHighlight;
display = cpSlice(display, 0, relCol - segStart) + highlighted + cpSlice(display, relCol - segStart + 1);
}
charCount = segEnd;
} else {
charCount += segLen;
}
const color = seg.type === "command" || seg.type === "file" || seg.type === "paste" ? theme.text.accent : theme.text.primary;
renderedLine.push(
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color, children: display }, `token-${segIdx}`)
);
});
const currentLineGhost = isOnCursorLine ? inlineGhost : "";
if (isOnCursorLine && cursorVisualColAbsolute === cpLen(lineText) && !currentLineGhost) {
renderedLine.push(
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { children: showCursor ? import_chalk3.default.inverse(" ") : " " }, `cursor-end-${cursorVisualColAbsolute}`)
);
}
const showCursorBeforeGhost = focus && isOnCursorLine && cursorVisualColAbsolute === cpLen(lineText) && currentLineGhost;
return /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Box_default, { height: 1, children: /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
Text,
{
terminalCursorFocus: showCursor && isOnCursorLine,
terminalCursorPosition: cpIndexToOffset(
lineText,
cursorVisualColAbsolute
),
children: [
renderedLine,
showCursorBeforeGhost && (showCursor ? import_chalk3.default.inverse(" ") : " "),
currentLineGhost && /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color: theme.text.secondary, children: currentLineGhost })
]
}
) });
},
[
buffer.visualToLogicalMap,
buffer.lines,
buffer.transformationsByLine,
buffer.cursor,
buffer.visualToTransformedMap,
focus,
cursorVisualRowAbsolute,
cursorVisualColAbsolute,
showCursor,
inlineGhost,
inputWidth
]
);
const useBackgroundColor = config.getUseBackgroundColor();
const prevCursorRef = (0, import_react75.useRef)(buffer.visualCursor);
const prevTextRef = (0, import_react75.useRef)(buffer.text);
(0, import_react75.useEffect)(() => {
const cursorChanged = prevCursorRef.current !== buffer.visualCursor;
const textChanged = prevTextRef.current !== buffer.text;
prevCursorRef.current = buffer.visualCursor;
prevTextRef.current = buffer.text;
if (!cursorChanged && !textChanged) return;
if (!listRef.current || !focus) return;
const { scrollTop, innerHeight } = listRef.current.getScrollState();
if (innerHeight === 0) return;
const cursorVisualRow = buffer.visualCursor[0];
const actualScrollTop = Math.round(scrollTop);
if (cursorVisualRow < actualScrollTop || cursorVisualRow >= actualScrollTop + innerHeight) {
let newScrollTop = actualScrollTop;
if (cursorVisualRow < actualScrollTop) {
newScrollTop = cursorVisualRow;
} else if (cursorVisualRow >= actualScrollTop + innerHeight) {
newScrollTop = cursorVisualRow - innerHeight + 1;
}
listRef.current.scrollToIndex({ index: newScrollTop });
}
}, [buffer.visualCursor, buffer.text, focus]);
const listBackgroundColor = !useBackgroundColor ? void 0 : theme.background.input;
const useLineFallback = !!process.env["NO_COLOR"];
(0, import_react75.useEffect)(() => {
if (onSuggestionsVisibilityChange) {
onSuggestionsVisibilityChange(shouldShowSuggestions);
}
}, [shouldShowSuggestions, onSuggestionsVisibilityChange]);
const showAutoAcceptStyling = !shellModeActive && approvalMode === ApprovalMode.AUTO_EDIT;
const showYoloStyling = !shellModeActive && approvalMode === ApprovalMode.YOLO;
const showPlanStyling = !shellModeActive && approvalMode === ApprovalMode.PLAN;
let statusColor;
let statusText = "";
if (shellModeActive) {
statusColor = theme.ui.symbol;
statusText = "Shell mode";
} else if (showYoloStyling) {
statusColor = theme.status.error;
statusText = "YOLO mode";
} else if (showPlanStyling) {
statusColor = theme.status.success;
statusText = "Plan mode";
} else if (showAutoAcceptStyling) {
statusColor = theme.status.warning;
statusText = "Accepting edits";
}
const suggestionsNode = shouldShowSuggestions ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Box_default, { paddingRight: 2, children: /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
SuggestionsDisplay,
{
suggestions: activeCompletion.suggestions,
activeIndex: activeCompletion.activeSuggestionIndex,
isLoading: activeCompletion.isLoadingSuggestions,
width: suggestionsWidth,
scrollOffset: activeCompletion.visibleStartIndex,
userInput: buffer.text,
mode: completion.completionMode === "AT" /* AT */ || completion.completionMode === "SHELL" /* SHELL */ ? "reverse" : buffer.text.startsWith("/") && !reverseSearchActive && !commandSearchActive ? "slash" : "reverse",
expandedIndex: expandedSuggestionIndex
}
) }) : null;
const borderColor = isShellFocused && !isEmbeddedShellFocused ? statusColor ?? theme.ui.focus : theme.border.default;
return /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(import_jsx_runtime111.Fragment, { children: [
suggestionsPosition === "above" && suggestionsNode,
useLineFallback || !useBackgroundColor ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
Box_default,
{
borderStyle: "round",
borderTop: true,
borderBottom: false,
borderLeft: false,
borderRight: false,
borderColor,
width: terminalWidth,
flexDirection: "row",
alignItems: "flex-start",
height: 0
}
) : null,
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
HalfLinePaddedBox,
{
backgroundBaseColor: theme.background.input,
backgroundOpacity: 1,
useBackgroundColor,
children: /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(Box_default, { flexGrow: 1, flexDirection: "row", paddingX: 1, children: [
isVoiceModeEnabled && (isRecording ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(ListeningIndicator, { color: theme.text.accent }) : /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color: theme.text.accent, children: "\u{1F3A4} " })),
/* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
Text,
{
color: statusColor ?? theme.text.accent,
"aria-label": statusText || void 0,
children: [
shellModeActive ? reverseSearchActive ? /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
Text,
{
color: theme.text.link,
"aria-label": SCREEN_READER_USER_PREFIX,
children: [
"(r:)",
" "
]
}
) : "!" : commandSearchActive ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color: theme.text.accent, children: "(r:) " }) : showYoloStyling ? "*" : ">",
" "
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Box_default, { flexGrow: 1, flexDirection: "column", ref: innerBoxRef, children: buffer.text.length === 0 ? effectivePlaceholder ? showCursor ? /* @__PURE__ */ (0, import_jsx_runtime111.jsxs)(
Text,
{
terminalCursorFocus: showCursor,
terminalCursorPosition: 0,
children: [
import_chalk3.default.inverse(effectivePlaceholder.slice(0, 1)),
/* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color: theme.text.secondary, children: effectivePlaceholder.slice(1) })
]
}
) : /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(Text, { color: theme.text.secondary, children: effectivePlaceholder }) : null : /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
Box_default,
{
flexDirection: "column",
height: Math.min(buffer.viewportHeight, scrollableData.length),
width: "100%",
children: config.getUseTerminalBuffer() ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
ScrollableListWithForwardRef,
{
ref: listRef,
hasFocus: focus,
data: scrollableData,
renderItem,
estimatedItemHeight: () => 1,
fixedItemHeight: true,
keyExtractor: (item) => item.type === "visualLine" ? `line-${item.absoluteVisualIdx}` : `ghost-${item.index}`,
width: inputWidth + SCROLLBAR_GUTTER_WIDTH,
backgroundColor: listBackgroundColor,
containerHeight: Math.min(
buffer.viewportHeight,
scrollableData.length
)
}
) : scrollableData.slice(
buffer.visualScrollRow,
buffer.visualScrollRow + buffer.viewportHeight
).map((item, index) => {
const actualIndex = buffer.visualScrollRow + index;
const key = item.type === "visualLine" ? `line-${item.absoluteVisualIdx}` : `ghost-${item.index}`;
return /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(import_react75.Fragment, { children: renderItem({ item, index: actualIndex }) }, key);
})
}
) })
] })
}
),
useLineFallback || !useBackgroundColor ? /* @__PURE__ */ (0, import_jsx_runtime111.jsx)(
Box_default,
{
borderStyle: "round",
borderTop: false,
borderBottom: true,
borderLeft: false,
borderRight: false,
borderColor,
width: terminalWidth,
flexDirection: "row",
alignItems: "flex-start",
height: 0
}
) : null,
suggestionsPosition === "below" && suggestionsNode
] });
};
// packages/cli/src/ui/components/StatusRow.tsx
var import_react79 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/usePhraseCycler.ts
var import_react76 = __toESM(require_react(), 1);
// packages/cli/src/ui/constants/tips.ts
var INFORMATIVE_TIPS = [
//Settings tips start here
"Set your preferred editor for opening files (/settings)\u2026",
"Toggle Vim mode for a modal editing experience (/settings)\u2026",
"Disable automatic updates if you prefer manual control (/settings)\u2026",
"Turn off nagging update notifications (settings.json)\u2026",
"Enable checkpointing to recover your session after a crash (settings.json)\u2026",
"Change CLI output format to JSON for scripting (/settings)\u2026",
"Personalize your CLI with a new color theme (/settings)\u2026",
"Create and use your own custom themes (settings.json)\u2026",
"Hide window title for a more minimal UI (/settings)\u2026",
"Don't like these tips? You can hide them (/settings)\u2026",
"Hide the startup banner for a cleaner launch (/settings)\u2026",
"Hide the context summary above the input (/settings)\u2026",
"Reclaim vertical space by hiding the footer (/settings)\u2026",
"Hide individual footer elements like CWD or sandbox status (/settings)\u2026",
"Hide the context window percentage in the footer (/settings)\u2026",
"Show memory usage for performance monitoring (/settings)\u2026",
"Show line numbers in the chat for easier reference (/settings)\u2026",
"Show citations to see where the model gets information (/settings)\u2026",
"Customize loading phrases: tips, witty, all, or off (/settings)\u2026",
"Add custom witty phrases to the loading screen (settings.json)\u2026",
"Use alternate screen buffer to preserve shell history (/settings)\u2026",
"Choose a specific Gemini model for conversations (/settings)\u2026",
"Limit the number of turns in your session history (/settings)\u2026",
"Automatically summarize large tool outputs to save tokens (settings.json)\u2026",
"Control when chat history gets compressed based on context compression threshold (settings.json)\u2026",
"Define custom context file names, like CONTEXT.md (settings.json)\u2026",
"Set max directories to scan for context files (/settings)\u2026",
"Expand your workspace with additional directories (/directory)\u2026",
"Control how /memory reload loads context files (/settings)\u2026",
"Toggle respect for .gitignore files in context (/settings)\u2026",
"Toggle respect for .geminiignore files in context (/settings)\u2026",
"Enable recursive file search for @-file completions (/settings)\u2026",
"Disable fuzzy search when searching for files (/settings)\u2026",
"Run tools in a secure sandbox environment (settings.json)\u2026",
"Use an interactive terminal for shell commands (/settings)\u2026",
"Show color in shell command output (/settings)\u2026",
"Automatically accept safe read-only tool calls (/settings)\u2026",
"Restrict available built-in tools (settings.json)\u2026",
"Exclude specific tools from being used (settings.json)\u2026",
"Bypass confirmation for trusted tools (settings.json)\u2026",
"Use a custom command for tool discovery (settings.json)\u2026",
"Define a custom command for calling discovered tools (settings.json)\u2026",
"Define and manage connections to MCP servers (settings.json)\u2026",
"Enable folder trust to enhance security (/settings)\u2026",
"Disable YOLO mode to enforce confirmations (settings.json)\u2026",
"Block Git extensions for enhanced security (settings.json)\u2026",
"Change your authentication method (/settings)\u2026",
"Enforce auth type for enterprise use (settings.json)\u2026",
"Let Node.js auto-configure memory (settings.json)\u2026",
"Retry on fetch failed errors automatically (settings.json)\u2026",
"Customize the DNS resolution order (settings.json)\u2026",
"Exclude env vars from the context (settings.json)\u2026",
"Configure a custom command for filing bug reports (settings.json)\u2026",
"Enable or disable telemetry collection (/settings)\u2026",
"Send telemetry data to a local file or GCP (settings.json)\u2026",
"Configure the OTLP endpoint for telemetry (settings.json)\u2026",
"Choose whether to log prompt content (settings.json)\u2026",
"Enable AI-powered prompt completion while typing (/settings)\u2026",
"Enable debug logging of keystrokes to the console (/settings)\u2026",
"Enable automatic session cleanup of old conversations (/settings)\u2026",
"Show Gemini CLI status in the terminal window title (/settings)\u2026",
"Use the entire width of the terminal for output (/settings)\u2026",
"Enable screen reader mode for better accessibility (/settings)\u2026",
"Skip the next speaker check for faster responses (/settings)\u2026",
"Use ripgrep for faster file content search (/settings)\u2026",
"Enable truncation of large tool outputs to save tokens (/settings)\u2026",
"Set the character threshold for truncating tool outputs (/settings)\u2026",
"Set the number of lines to keep when truncating outputs (/settings)\u2026",
"Enable policy-based tool confirmation via message bus (/settings)\u2026",
"Enable write_todos_list tool to generate task lists (/settings)\u2026",
"Enable experimental subagents for task delegation (/settings)\u2026",
"Enable extension management features (settings.json)\u2026",
"Enable extension reloading within the CLI session (settings.json)\u2026",
//Settings tips end here
// Keyboard shortcut tips start here
"Close dialogs and suggestions with Esc",
"Cancel a request with Ctrl+C, or press twice to exit",
"Exit the app with Ctrl+D on an empty line",
"Clear your screen at any time with Ctrl+L",
"Toggle the debug console display with F12",
"Toggle the todo list display with Ctrl+T",
"See full, untruncated responses with Ctrl+O",
"Toggle auto-approval (YOLO mode) for all tools with Ctrl+Y",
"Cycle through approval modes (Default, Auto-Edit, Plan) with Shift+Tab",
"Toggle Markdown rendering (raw markdown mode) with Alt+M",
"Toggle shell mode by typing ! in an empty prompt",
"Insert a newline with a backslash (\\) followed by Enter",
"Navigate your prompt history with the Up and Down arrows",
"You can also use Ctrl+P (up) and Ctrl+N (down) for history",
"Search through command history with Ctrl+R",
"Accept an autocomplete suggestion with Tab or Enter",
"Move to the start of the line with Ctrl+A or Home",
"Move to the end of the line with Ctrl+E or End",
"Move one character left or right with Ctrl+B/F or the arrow keys",
"Move one word left or right with Ctrl+Left/Right Arrow",
"Delete the character to the left with Ctrl+H or Backspace",
"Delete the character to the right with Ctrl+D or Delete",
"Delete the word to the left of the cursor with Ctrl+W",
"Delete the word to the right of the cursor with Ctrl+Delete",
"Delete from the cursor to the start of the line with Ctrl+U",
"Delete from the cursor to the end of the line with Ctrl+K",
"Clear the entire input prompt with a double-press of Esc",
"Paste from your clipboard with Ctrl+V",
"Undo text edits in the input with Alt+Z or Cmd+Z",
"Redo undone text edits with Shift+Alt+Z or Shift+Cmd+Z",
"Open the current prompt in an external editor with Ctrl+G",
"In menus, move up/down with k/j or the arrow keys",
"In menus, select an item by typing its number",
"If you're using an IDE, see the context with F4",
"Toggle background shells with Ctrl+B or /shells",
"Toggle the background shell process list with Ctrl+L",
// Keyboard shortcut tips end here
// Command tips start here
"Show version info with /about",
"Change your authentication method with /auth",
"File a bug report directly with /bug",
"List your saved chat checkpoints with /resume list",
"Save your current conversation with /resume save <tag>",
"Resume a saved conversation with /resume resume <tag>",
"Delete a conversation checkpoint with /resume delete <tag>",
"Share your conversation to a file with /resume share <file>",
"Clear the screen and history with /clear",
"Save tokens by summarizing the context with /compress",
"Copy the last response to your clipboard with /copy",
"Open the full documentation in your browser with /docs",
"Add directories to your workspace with /directory add <path>",
"Show all directories in your workspace with /directory show",
"Use /dir as a shortcut for /directory",
"Set your preferred external editor with /editor",
"List all active extensions with /extensions list",
"Update all or specific extensions with /extensions update",
"Get help on commands with /help",
"Manage IDE integration with /ide",
"Create a project-specific GEMINI.md file with /init",
"List configured MCP servers and tools with /mcp list",
"Authenticate with an OAuth-enabled MCP server with /mcp auth",
"Reload MCP servers with /mcp reload",
"See the current instructional context with /memory show",
"Add content to the instructional memory with /memory add",
"Reload instructional context from GEMINI.md files with /memory reload",
"List the paths of the GEMINI.md files in use with /memory list",
"Choose your Gemini model with /model",
"Display the privacy notice with /privacy",
"Restore project files to a previous state with /restore",
"Exit the CLI with /quit or /exit",
"Check model-specific usage stats with /stats model",
"Check tool-specific usage stats with /stats tools",
"Change the CLI's color theme with /theme",
"List all available tools with /tools",
"View and edit settings with the /settings editor",
"Toggle Vim keybindings on and off with /vim",
"Set up GitHub Actions with /setup-github",
"Configure terminal keybindings for multiline input with /terminal-setup",
"Find relevant documentation with /find-docs",
"Execute any shell command with !<command>"
// Command tips end here
];
// packages/cli/src/ui/constants/wittyPhrases.ts
var WITTY_LOADING_PHRASES = [
"I'm Feeling Lucky",
"Shipping awesomeness",
"Painting the serifs back on",
"Navigating the slime mold",
"Consulting the digital spirits",
"Reticulating splines",
"Warming up the AI hamsters",
"Asking the magic conch shell",
"Generating witty retort",
"Polishing the algorithms",
"Don't rush perfection (or my code)",
"Brewing fresh bytes",
"Counting electrons",
"Engaging cognitive processors",
"Checking for syntax errors in the universe",
"One moment, optimizing humor",
"Shuffling punchlines",
"Untangling neural nets",
"Compiling brilliance",
"Loading wit.exe",
"Summoning the cloud of wisdom",
"Preparing a witty response",
"Just a sec, I'm debugging reality",
"Confuzzling the options",
"Tuning the cosmic frequencies",
"Crafting a response worthy of your patience",
"Compiling the 1s and 0s",
"Resolving dependencies\u2026 and existential crises",
"Defragmenting memories\u2026 both RAM and personal",
"Rebooting the humor module",
"Caching the essentials (mostly cat memes)",
"Optimizing for ludicrous speed",
"Swapping bits\u2026 don't tell the bytes",
"Garbage collecting\u2026 be right back",
"Assembling the interwebs",
"Converting coffee into code",
"Updating the syntax for reality",
"Rewiring the synapses",
"Looking for a misplaced semicolon",
"Greasin' the cogs of the machine",
"Pre-heating the servers",
"Calibrating the flux capacitor",
"Engaging the improbability drive",
"Channeling the Force",
"Aligning the stars for optimal response",
"So say we all",
"Loading the next great idea",
"Just a moment, I'm in the zone",
"Preparing to dazzle you with brilliance",
"Just a tick, I'm polishing my wit",
"Hold tight, I'm crafting a masterpiece",
"Just a jiffy, I'm debugging the universe",
"Just a moment, I'm aligning the pixels",
"Just a sec, I'm optimizing the humor",
"Just a moment, I'm tuning the algorithms",
"Warp speed engaged",
"Mining for more Dilithium crystals",
"Don't panic",
"Following the white rabbit",
"The truth is in here\u2026 somewhere",
"Blowing on the cartridge",
"Loading\u2026 Do a barrel roll!",
"Waiting for the respawn",
"Finishing the Kessel Run in less than 12 parsecs",
"The cake is not a lie, it's just still loading",
"Fiddling with the character creation screen",
"Just a moment, I'm finding the right meme",
"Pressing 'A' to continue",
"Herding digital cats",
"Polishing the pixels",
"Finding a suitable loading screen pun",
"Distracting you with this witty phrase",
"Almost there\u2026 probably",
"Our hamsters are working as fast as they can",
"Giving Cloudy a pat on the head",
"Petting the cat",
"Rickrolling my boss",
"Slapping the bass",
"Tasting the snozberries",
"I'm going the distance, I'm going for speed",
"Is this the real life? Is this just fantasy?",
"I've got a good feeling about this",
"Poking the bear",
"Doing research on the latest memes",
"Figuring out how to make this more witty",
"Hmmm\u2026 let me think",
"What do you call a fish with no eyes? A fsh",
"Why did the computer go to therapy? It had too many bytes",
"Why don't programmers like nature? It has too many bugs",
"Why do programmers prefer dark mode? Because light attracts bugs",
"Why did the developer go broke? Because they used up all their cache",
"What can you do with a broken pencil? Nothing, it's pointless",
"Applying percussive maintenance",
"Searching for the correct USB orientation",
"Ensuring the magic smoke stays inside the wires",
"Rewriting in Rust for no particular reason",
"Trying to exit Vim",
"Spinning up the hamster wheel",
"That's not a bug, it's an undocumented feature",
"Engage.",
"I'll be back\u2026 with an answer.",
"My other process is a TARDIS",
"Communing with the machine spirit",
"Letting the thoughts marinate",
"Just remembered where I put my keys",
"Pondering the orb",
"I've seen things you people wouldn't believe\u2026 like a user who reads loading messages.",
"Initiating thoughtful gaze",
"What's a computer's favorite snack? Microchips.",
"Why do Java developers wear glasses? Because they don't C#.",
"Charging the laser\u2026 pew pew!",
"Dividing by zero\u2026 just kidding!",
"Looking for an adult superviso\u2026 I mean, processing.",
"Making it go beep boop.",
"Buffering\u2026 because even AIs need a moment.",
"Entangling quantum particles for a faster response",
"Polishing the chrome\u2026 on the algorithms.",
"Are you not entertained? (Working on it!)",
"Summoning the code gremlins\u2026 to help, of course.",
"Just waiting for the dial-up tone to finish",
"Recalibrating the humor-o-meter.",
"My other loading screen is even funnier.",
"Pretty sure there's a cat walking on the keyboard somewhere",
"Enhancing\u2026 Enhancing\u2026 Still loading.",
"It's not a bug, it's a feature\u2026 of this loading screen.",
"Have you tried turning it off and on again? (The loading screen, not me.)",
"Constructing additional pylons",
"New line? That\u2019s Ctrl+J.",
"Releasing the HypnoDrones"
];
// packages/cli/src/ui/hooks/usePhraseCycler.ts
var PHRASE_CHANGE_INTERVAL_MS = 1e4;
var WITTY_PHRASE_CHANGE_INTERVAL_MS = 5e3;
var INTERACTIVE_SHELL_WAITING_PHRASE = "! Shell awaiting input (Tab to focus)";
var usePhraseCycler = (isActive, isWaiting, shouldShowFocusHint, showTips = true, showWit = true, customPhrases, maxLength) => {
const [currentTipState, setCurrentTipState] = (0, import_react76.useState)(
void 0
);
const [currentWittyPhraseState, setCurrentWittyPhraseState] = (0, import_react76.useState)(void 0);
const tipIntervalRef = (0, import_react76.useRef)(null);
const wittyIntervalRef = (0, import_react76.useRef)(null);
const lastTipChangeTimeRef = (0, import_react76.useRef)(0);
const lastWittyChangeTimeRef = (0, import_react76.useRef)(0);
const lastSelectedTipRef = (0, import_react76.useRef)(void 0);
const lastSelectedWittyPhraseRef = (0, import_react76.useRef)(void 0);
const MIN_TIP_DISPLAY_TIME_MS = 1e4;
const MIN_WIT_DISPLAY_TIME_MS = 5e3;
(0, import_react76.useEffect)(() => {
const clearTimers = () => {
if (tipIntervalRef.current) {
clearInterval(tipIntervalRef.current);
tipIntervalRef.current = null;
}
if (wittyIntervalRef.current) {
clearInterval(wittyIntervalRef.current);
wittyIntervalRef.current = null;
}
};
clearTimers();
if (shouldShowFocusHint || isWaiting) {
return clearTimers;
}
if (!isActive || !showTips && !showWit) {
return clearTimers;
}
const wittyPhrasesList = customPhrases && customPhrases.length > 0 ? customPhrases : WITTY_LOADING_PHRASES;
const setRandomTip = (force = false) => {
if (!showTips) {
setCurrentTipState(void 0);
lastSelectedTipRef.current = void 0;
return;
}
const now = Date.now();
if (!force && now - lastTipChangeTimeRef.current < MIN_TIP_DISPLAY_TIME_MS && lastSelectedTipRef.current) {
setCurrentTipState(lastSelectedTipRef.current);
return;
}
const filteredTips = maxLength !== void 0 ? INFORMATIVE_TIPS.filter((p) => p.length <= maxLength) : INFORMATIVE_TIPS;
if (filteredTips.length > 0) {
const selected = filteredTips[Math.floor(Math.random() * filteredTips.length)];
setCurrentTipState(selected);
lastSelectedTipRef.current = selected;
lastTipChangeTimeRef.current = now;
}
};
const setRandomWitty = (force = false) => {
if (!showWit) {
setCurrentWittyPhraseState(void 0);
lastSelectedWittyPhraseRef.current = void 0;
return;
}
const now = Date.now();
if (!force && now - lastWittyChangeTimeRef.current < MIN_WIT_DISPLAY_TIME_MS && lastSelectedWittyPhraseRef.current) {
setCurrentWittyPhraseState(lastSelectedWittyPhraseRef.current);
return;
}
const filteredWitty = maxLength !== void 0 ? wittyPhrasesList.filter((p) => p.length <= maxLength) : wittyPhrasesList;
if (filteredWitty.length > 0) {
const selected = filteredWitty[Math.floor(Math.random() * filteredWitty.length)];
setCurrentWittyPhraseState(selected);
lastSelectedWittyPhraseRef.current = selected;
lastWittyChangeTimeRef.current = now;
}
};
setRandomTip(false);
setRandomWitty(false);
if (showTips) {
tipIntervalRef.current = setInterval(() => {
setRandomTip(true);
}, PHRASE_CHANGE_INTERVAL_MS);
}
if (showWit) {
wittyIntervalRef.current = setInterval(() => {
setRandomWitty(true);
}, WITTY_PHRASE_CHANGE_INTERVAL_MS);
}
return clearTimers;
}, [
isActive,
isWaiting,
shouldShowFocusHint,
showTips,
showWit,
customPhrases,
maxLength
]);
let currentTip = void 0;
let currentWittyPhrase = void 0;
if (shouldShowFocusHint) {
currentTip = INTERACTIVE_SHELL_WAITING_PHRASE;
} else if (isWaiting) {
currentTip = "Waiting for user confirmation...";
} else if (isActive) {
currentTip = currentTipState;
currentWittyPhrase = currentWittyPhraseState;
}
return { currentTip, currentWittyPhrase };
};
// packages/cli/src/ui/components/GeminiSpinner.tsx
var import_react77 = __toESM(require_react(), 1);
var import_tinygradient = __toESM(require_tinygradient(), 1);
var import_jsx_runtime112 = __toESM(require_jsx_runtime(), 1);
var COLOR_CYCLE_DURATION_MS = 4e3;
var GeminiSpinner = ({
spinnerType = "dots",
altText
}) => {
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
const [time, setTime] = (0, import_react77.useState)(0);
const googleGradient = (0, import_react77.useMemo)(() => {
const brandColors = [
Colors.AccentPurple,
Colors.AccentBlue,
Colors.AccentCyan,
Colors.AccentGreen,
Colors.AccentYellow,
Colors.AccentRed
];
return (0, import_tinygradient.default)([...brandColors, brandColors[0]]);
}, []);
(0, import_react77.useEffect)(() => {
if (isScreenReaderEnabled) {
return;
}
const interval = setInterval(() => {
setTime((prevTime) => prevTime + 30);
}, 30);
return () => clearInterval(interval);
}, [isScreenReaderEnabled]);
const progress = time % COLOR_CYCLE_DURATION_MS / COLOR_CYCLE_DURATION_MS;
const currentColor = googleGradient.rgbAt(progress).toHexString();
return isScreenReaderEnabled ? /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(Text, { children: altText }) : /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(Text, { color: currentColor, children: /* @__PURE__ */ (0, import_jsx_runtime112.jsx)(CliSpinner, { type: spinnerType }) });
};
// packages/cli/src/ui/components/GeminiRespondingSpinner.tsx
var import_jsx_runtime113 = __toESM(require_jsx_runtime(), 1);
var GeminiRespondingSpinner = ({
nonRespondingDisplay,
spinnerType = "dots",
isHookActive = false,
color
}) => {
const streamingState = useStreamingContext();
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
if (streamingState === "responding" /* Responding */ && !isHookActive) {
return /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(
GeminiSpinner,
{
spinnerType,
altText: SCREEN_READER_RESPONDING
}
);
}
if (nonRespondingDisplay) {
return isScreenReaderEnabled ? /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(Text, { children: SCREEN_READER_LOADING }) : /* @__PURE__ */ (0, import_jsx_runtime113.jsx)(Text, { color: color ?? theme.text.primary, children: nonRespondingDisplay });
}
return null;
};
// packages/cli/src/ui/components/LoadingIndicator.tsx
var import_jsx_runtime114 = __toESM(require_jsx_runtime(), 1);
var LoadingIndicator = ({
currentLoadingPhrase,
wittyPhrase,
showWit = false,
elapsedTime,
inline = false,
rightContent,
thought,
thoughtLabel,
showCancelAndTimer = true,
forceRealStatusOnly = false,
spinnerIcon,
isHookActive = false
}) => {
const streamingState = useStreamingContext();
const { columns: terminalWidth } = useTerminalSize();
const isNarrow = isNarrowWidth(terminalWidth);
if (streamingState === "idle" /* Idle */ && !currentLoadingPhrase && !thought) {
return null;
}
const primaryText = currentLoadingPhrase === INTERACTIVE_SHELL_WAITING_PHRASE ? currentLoadingPhrase : thought?.subject ? thoughtLabel ?? thought.subject : currentLoadingPhrase || (streamingState === "responding" /* Responding */ ? "Thinking..." : void 0);
const cancelAndTimerContent = showCancelAndTimer && streamingState === "responding" /* Responding */ ? `(esc to cancel, ${elapsedTime < 60 ? `${elapsedTime}s` : formatDuration(elapsedTime * 1e3)})` : null;
const wittyPhraseNode = !forceRealStatusOnly && showWit && wittyPhrase && primaryText === "Thinking..." ? /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { marginLeft: 1, children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.secondary, dimColor: true, italic: true, children: wittyPhrase }) }) : null;
if (inline) {
return /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
GeminiRespondingSpinner,
{
nonRespondingDisplay: spinnerIcon ?? (streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */ ? "\u280F" : ""),
isHookActive
}
) }),
primaryText && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Box_default, { flexShrink: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.primary, italic: true, wrap: "truncate-end", children: primaryText }),
primaryText === INTERACTIVE_SHELL_WAITING_PHRASE && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Text, { color: theme.ui.active, italic: true, children: [
" ",
"(press tab to focus)"
] })
] }),
cancelAndTimerContent && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(import_jsx_runtime114.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { flexShrink: 0, width: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.secondary, children: cancelAndTimerContent })
] }),
wittyPhraseNode
] });
}
return /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Box_default, { paddingLeft: 0, flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(
Box_default,
{
width: "100%",
flexDirection: isNarrow ? "column" : "row",
alignItems: isNarrow ? "flex-start" : "center",
children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Box_default, { children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { marginRight: 1, children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(
GeminiRespondingSpinner,
{
nonRespondingDisplay: spinnerIcon ?? (streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */ ? "\u280F" : ""),
isHookActive
}
) }),
primaryText && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Box_default, { flexShrink: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.primary, italic: true, wrap: "truncate-end", children: primaryText }),
primaryText === INTERACTIVE_SHELL_WAITING_PHRASE && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(Text, { color: theme.ui.active, italic: true, children: [
" ",
"(press tab to focus)"
] })
] }),
!isNarrow && cancelAndTimerContent && /* @__PURE__ */ (0, import_jsx_runtime114.jsxs)(import_jsx_runtime114.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { flexShrink: 0, width: 1 }),
/* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.secondary, children: cancelAndTimerContent })
] }),
!isNarrow && wittyPhraseNode
] }),
!isNarrow && /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { flexGrow: 1 }),
!isNarrow && rightContent && /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { children: rightContent })
]
}
),
isNarrow && cancelAndTimerContent && /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Text, { color: theme.text.secondary, children: cancelAndTimerContent }) }),
isNarrow && wittyPhraseNode,
isNarrow && rightContent && /* @__PURE__ */ (0, import_jsx_runtime114.jsx)(Box_default, { children: rightContent })
] });
};
// packages/cli/src/ui/components/ContextSummaryDisplay.tsx
var import_jsx_runtime115 = __toESM(require_jsx_runtime(), 1);
var ContextSummaryDisplay = ({
geminiMdFileCount,
contextFileNames,
mcpServers,
blockedMcpServers,
ideContext,
skillCount,
backgroundProcessCount = 0
}) => {
const mcpServerCount = Object.keys(mcpServers || {}).length;
const blockedMcpServerCount = blockedMcpServers?.length || 0;
const openFileCount = ideContext?.workspaceState?.openFiles?.length ?? 0;
if (geminiMdFileCount === 0 && mcpServerCount === 0 && blockedMcpServerCount === 0 && openFileCount === 0 && skillCount === 0 && backgroundProcessCount === 0) {
return null;
}
const openFilesText = (() => {
if (openFileCount === 0) {
return "";
}
return `${openFileCount} open file${openFileCount > 1 ? "s" : ""} (${formatCommand("app.showIdeContextDetail" /* SHOW_IDE_CONTEXT_DETAIL */)} to view)`;
})();
const geminiMdText = (() => {
if (geminiMdFileCount === 0) {
return "";
}
const allNamesTheSame = new Set(contextFileNames).size < 2;
const name = allNamesTheSame ? contextFileNames[0] : "context";
return `${geminiMdFileCount} ${name} file${geminiMdFileCount > 1 ? "s" : ""}`;
})();
const mcpText = (() => {
if (mcpServerCount === 0 && blockedMcpServerCount === 0) {
return "";
}
const parts = [];
if (mcpServerCount > 0) {
parts.push(
`${mcpServerCount} MCP server${mcpServerCount > 1 ? "s" : ""}`
);
}
if (blockedMcpServerCount > 0) {
let blockedText = `${blockedMcpServerCount} Blocked`;
if (mcpServerCount === 0) {
blockedText += ` MCP server${blockedMcpServerCount > 1 ? "s" : ""}`;
}
parts.push(blockedText);
}
return parts.join(", ");
})();
const skillText = (() => {
if (skillCount === 0) {
return "";
}
return `${skillCount} skill${skillCount > 1 ? "s" : ""}`;
})();
const backgroundText = (() => {
if (backgroundProcessCount === 0) {
return "";
}
return `${backgroundProcessCount} Background process${backgroundProcessCount > 1 ? "es" : ""}`;
})();
const summaryParts = [
openFilesText,
geminiMdText,
mcpText,
skillText,
backgroundText
].filter(Boolean);
return /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(Box_default, { paddingX: 1, flexDirection: "row", flexWrap: "wrap", children: summaryParts.map((part, index) => /* @__PURE__ */ (0, import_jsx_runtime115.jsxs)(Box_default, { flexDirection: "row", children: [
index > 0 && /* @__PURE__ */ (0, import_jsx_runtime115.jsx)(Text, { color: theme.text.secondary, children: " \xB7 " }),
/* @__PURE__ */ (0, import_jsx_runtime115.jsx)(Text, { color: theme.text.secondary, children: part })
] }, index)) });
};
// packages/cli/src/ui/components/StatusDisplay.tsx
var import_jsx_runtime116 = __toESM(require_jsx_runtime(), 1);
var StatusDisplay = ({
hideContextSummary
}) => {
const uiState = useUIState();
const settings = useSettings();
const config = useConfig();
if (process.env["GEMINI_SYSTEM_MD"]) {
return /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(Text, { color: theme.status.error, children: "|\u2310\u25A0_\u25A0|" });
}
if (!settings.merged.ui.hideContextSummary && !hideContextSummary) {
return /* @__PURE__ */ (0, import_jsx_runtime116.jsx)(
ContextSummaryDisplay,
{
ideContext: uiState.ideContextState,
geminiMdFileCount: uiState.geminiMdFileCount,
contextFileNames: uiState.contextFileNames,
mcpServers: config.getMcpClientManager()?.getMcpServers() ?? {},
blockedMcpServers: config.getMcpClientManager()?.getBlockedMcpServers() ?? [],
skillCount: config.getSkillManager().getDisplayableSkills().length,
backgroundProcessCount: uiState.backgroundTaskCount
}
);
}
return null;
};
// packages/cli/src/ui/components/shared/HorizontalLine.tsx
var import_jsx_runtime117 = __toESM(require_jsx_runtime(), 1);
var HorizontalLine = ({
color = theme.border.default,
dim = false
}) => /* @__PURE__ */ (0, import_jsx_runtime117.jsx)(
Box_default,
{
width: "100%",
borderStyle: "single",
borderTop: true,
borderBottom: false,
borderLeft: false,
borderRight: false,
borderColor: color,
borderDimColor: dim
}
);
// packages/cli/src/ui/components/ApprovalModeIndicator.tsx
var import_jsx_runtime118 = __toESM(require_jsx_runtime(), 1);
var ApprovalModeIndicator = ({
approvalMode,
allowPlanMode
}) => {
let textColor = "";
let textContent = "";
let subText = "";
const cycleHint = formatCommand("app.cycleApprovalMode" /* CYCLE_APPROVAL_MODE */);
const yoloHint = formatCommand("app.toggleYolo" /* TOGGLE_YOLO */);
switch (approvalMode) {
case ApprovalMode.AUTO_EDIT:
textColor = theme.status.warning;
textContent = "auto-accept edits";
subText = allowPlanMode ? `${cycleHint} to plan` : `${cycleHint} to manual`;
break;
case ApprovalMode.PLAN:
textColor = theme.status.success;
textContent = "plan";
subText = `${cycleHint} to manual`;
break;
case ApprovalMode.YOLO:
textColor = theme.status.error;
textContent = "YOLO";
subText = yoloHint;
break;
case ApprovalMode.DEFAULT:
default:
textColor = theme.text.accent;
textContent = "";
subText = `${cycleHint} to accept edits`;
break;
}
return /* @__PURE__ */ (0, import_jsx_runtime118.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime118.jsxs)(Text, { color: textColor, children: [
textContent ? textContent : null,
subText ? /* @__PURE__ */ (0, import_jsx_runtime118.jsxs)(Text, { color: theme.text.secondary, children: [
textContent ? " " : "",
subText
] }) : null
] }) });
};
// packages/cli/src/ui/components/ShellModeIndicator.tsx
var import_jsx_runtime119 = __toESM(require_jsx_runtime(), 1);
var ShellModeIndicator = () => /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime119.jsxs)(Text, { color: theme.ui.symbol, children: [
"shell mode enabled",
/* @__PURE__ */ (0, import_jsx_runtime119.jsx)(Text, { color: theme.text.secondary, children: " (esc to disable)" })
] }) });
// packages/cli/src/ui/components/RawMarkdownIndicator.tsx
var import_jsx_runtime120 = __toESM(require_jsx_runtime(), 1);
var RawMarkdownIndicator = () => {
const modKey = formatCommand("app.toggleMarkdown" /* TOGGLE_MARKDOWN */);
return /* @__PURE__ */ (0, import_jsx_runtime120.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(Text, { children: [
"raw markdown mode",
/* @__PURE__ */ (0, import_jsx_runtime120.jsxs)(Text, { color: theme.text.secondary, children: [
" (",
modKey,
" to toggle) "
] })
] }) });
};
// packages/cli/src/ui/hooks/useComposerStatus.ts
var import_react78 = __toESM(require_react(), 1);
var useComposerStatus = () => {
const uiState = useUIState();
const quotaState = useQuotaState();
const settings = useSettings();
const hasPendingToolConfirmation = (0, import_react78.useMemo)(
() => (uiState.pendingHistoryItems ?? []).filter(
(item) => item.type === "tool_group"
).some(
(item) => item.tools.some(
(tool) => tool.status === CoreToolCallStatus.AwaitingApproval
)
),
[uiState.pendingHistoryItems]
);
const hasPendingActionRequired = hasPendingToolConfirmation || Boolean(uiState.commandConfirmationRequest) || Boolean(uiState.authConsentRequest) || (uiState.confirmUpdateExtensionRequests?.length ?? 0) > 0 || Boolean(uiState.loopDetectionConfirmationRequest) || Boolean(quotaState.proQuotaRequest) || Boolean(quotaState.validationRequest) || Boolean(uiState.customDialog);
const isInteractiveShellWaiting = Boolean(
uiState.currentLoadingPhrase?.includes(INTERACTIVE_SHELL_WAITING_PHRASE)
);
const showLoadingIndicator = (!uiState.embeddedShellFocused || uiState.isBackgroundTaskVisible) && uiState.streamingState === "responding" /* Responding */ && !hasPendingActionRequired;
const showApprovalModeIndicator = uiState.showApprovalModeIndicator;
const modeContentObj = (0, import_react78.useMemo)(() => {
const hideMinimalModeHintWhileBusy = !uiState.cleanUiDetailsVisible && (showLoadingIndicator || uiState.activeHooks.length > 0);
if (hideMinimalModeHintWhileBusy) return null;
switch (showApprovalModeIndicator) {
case ApprovalMode.YOLO:
return { text: "YOLO", color: theme.status.error };
case ApprovalMode.PLAN:
return { text: "plan", color: theme.status.success };
case ApprovalMode.AUTO_EDIT:
return { text: "auto edit", color: theme.status.warning };
case ApprovalMode.DEFAULT:
default:
return null;
}
}, [
uiState.cleanUiDetailsVisible,
showLoadingIndicator,
uiState.activeHooks.length,
showApprovalModeIndicator
]);
const showMinimalContext = isContextUsageHigh(
uiState.sessionStats.lastPromptTokenCount,
uiState.currentModel,
settings.merged.model?.compressionThreshold
);
const loadingPhrases = settings.merged.ui.loadingPhrases;
const showTips = loadingPhrases === "tips" || loadingPhrases === "all";
const showWit = loadingPhrases === "witty" || loadingPhrases === "all";
const shouldCollapseDuringApproval = settings.merged.ui.collapseDrawerDuringApproval !== false;
return {
hasPendingActionRequired,
shouldCollapseDuringApproval,
isInteractiveShellWaiting,
showLoadingIndicator,
showTips,
showWit,
modeContentObj,
showMinimalContext
};
};
// packages/cli/src/ui/components/StatusRow.tsx
var import_jsx_runtime121 = __toESM(require_jsx_runtime(), 1);
var LAYOUT = {
STATUS_MIN_HEIGHT: 1,
TIP_LEFT_MARGIN: 2,
TIP_RIGHT_MARGIN_NARROW: 0,
TIP_RIGHT_MARGIN_WIDE: 1,
INDICATOR_LEFT_MARGIN: 1,
CONTEXT_DISPLAY_TOP_MARGIN_NARROW: 1,
CONTEXT_DISPLAY_LEFT_MARGIN_NARROW: 1,
CONTEXT_DISPLAY_LEFT_MARGIN_WIDE: 0,
COLLISION_GAP: 10
};
var StatusNode = ({
showTips,
showWit,
thought,
elapsedTime,
currentWittyPhrase,
activeHooks,
showLoadingIndicator,
errorVerbosity,
onResize
}) => {
const observerRef = (0, import_react79.useRef)(null);
(0, import_react79.useEffect)(
() => () => {
observerRef.current?.disconnect();
},
[]
);
const onRefChange = (0, import_react79.useCallback)(
(node) => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
}
if (node && onResize) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
onResize(Math.round(entry.contentRect.width));
}
});
observer.observe(node);
observerRef.current = observer;
}
},
[onResize]
);
if (activeHooks.length === 0 && !showLoadingIndicator) return null;
let currentLoadingPhrase = void 0;
let currentThought = null;
if (activeHooks.length > 0) {
const userVisibleHooks = activeHooks.filter(
(h) => isUserVisibleHook(h.source)
);
if (userVisibleHooks.length > 0) {
const label = userVisibleHooks.length > 1 ? "Executing Hooks" : "Executing Hook";
const displayNames = userVisibleHooks.map((h) => {
let name = stripAnsi(h.name);
if (h.index && h.total && h.total > 1) {
name += ` (${h.index}/${h.total})`;
}
return name;
});
currentLoadingPhrase = `${label}: ${displayNames.join(", ")}`;
} else {
currentLoadingPhrase = GENERIC_WORKING_LABEL;
}
} else {
currentThought = thought ? { ...thought, subject: stripAnsi(thought.subject) } : null;
}
return /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { ref: onRefChange, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
LoadingIndicator,
{
inline: true,
showTips,
showWit,
errorVerbosity,
thought: currentThought,
currentLoadingPhrase,
elapsedTime,
forceRealStatusOnly: false,
wittyPhrase: currentWittyPhrase
}
) });
};
var StatusRow = ({
showUiDetails,
isNarrow,
terminalWidth,
hideContextSummary,
hideUiDetailsForSuggestions,
hasPendingActionRequired
}) => {
const uiState = useUIState();
const inputState = useInputState();
const settings = useSettings();
const {
isInteractiveShellWaiting,
showLoadingIndicator,
showTips,
showWit,
modeContentObj,
showMinimalContext
} = useComposerStatus();
const [statusWidth, setStatusWidth] = (0, import_react79.useState)(0);
const [tipWidth, setTipWidth] = (0, import_react79.useState)(0);
const tipObserverRef = (0, import_react79.useRef)(null);
(0, import_react79.useEffect)(
() => () => {
tipObserverRef.current?.disconnect();
},
[]
);
const onTipRefChange = (0, import_react79.useCallback)((node) => {
if (tipObserverRef.current) {
tipObserverRef.current.disconnect();
tipObserverRef.current = null;
}
if (node) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const width = Math.round(entry.contentRect.width);
if (width > 0) {
setTipWidth(width);
}
}
});
observer.observe(node);
tipObserverRef.current = observer;
}
}, []);
const tipContentStr = (() => {
if (showTips && uiState.currentTip && !(isInteractiveShellWaiting && uiState.currentTip === INTERACTIVE_SHELL_WAITING_PHRASE)) {
return uiState.currentTip;
}
if (settings.merged.ui.showShortcutsHint && !hideUiDetailsForSuggestions && !hasPendingActionRequired && inputState.buffer.text.length === 0) {
return showUiDetails ? "? for shortcuts" : "press tab twice for more";
}
return void 0;
})();
const willCollideTip = statusWidth + tipWidth + LAYOUT.COLLISION_GAP > terminalWidth;
const showTipLine = Boolean(
!hasPendingActionRequired && tipContentStr && !willCollideTip && !isNarrow
);
const showRow1Minimal = showLoadingIndicator || uiState.activeHooks.length > 0 || showTipLine;
const showRow2Minimal = Boolean(modeContentObj) && !hideUiDetailsForSuggestions || showMinimalContext;
const showRow1 = showUiDetails || showRow1Minimal;
const showRow2 = showUiDetails || showRow2Minimal;
const onStatusResize = (0, import_react79.useCallback)((width) => {
if (width > 0) setStatusWidth(width);
}, []);
const statusNode = /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
StatusNode,
{
showTips,
showWit,
thought: uiState.thought,
elapsedTime: uiState.elapsedTime,
currentWittyPhrase: uiState.currentWittyPhrase,
activeHooks: uiState.activeHooks,
showLoadingIndicator,
errorVerbosity: settings.merged.ui.errorVerbosity,
onResize: onStatusResize
}
);
const renderTipNode = () => {
if (!tipContentStr) return null;
const isShortcutHint = tipContentStr === "? for shortcuts" || tipContentStr === "press tab twice for more";
const color = isShortcutHint && uiState.shortcutsHelpVisible ? theme.text.accent : theme.text.secondary;
return /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { flexDirection: "row", justifyContent: "flex-end", ref: onTipRefChange, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
Text,
{
color,
wrap: "truncate-end",
italic: !isShortcutHint && tipContentStr === uiState.currentWittyPhrase,
children: tipContentStr === uiState.currentTip ? `Tip: ${tipContentStr}` : tipContentStr
}
) });
};
if (!showUiDetails && !showRow1Minimal && !showRow2Minimal) {
return /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { height: LAYOUT.STATUS_MIN_HEIGHT });
}
return /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(Box_default, { flexDirection: "column", width: "100%", children: [
showRow1 && /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(
Box_default,
{
width: "100%",
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
minHeight: LAYOUT.STATUS_MIN_HEIGHT,
children: [
/* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { flexDirection: "row", flexGrow: 1, flexShrink: 1, children: !showUiDetails && showRow1Minimal ? /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(Box_default, { flexDirection: "row", columnGap: 1, children: [
statusNode,
!showUiDetails && showRow2Minimal && modeContentObj && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(Text, { color: modeContentObj.color, children: [
"\u25CF ",
modeContentObj.text
] }) })
] }) : isInteractiveShellWaiting ? /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { width: "100%", marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Text, { color: theme.status.warning, children: INTERACTIVE_SHELL_WAITING_PHRASE }) }) : /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
Box_default,
{
flexDirection: "row",
alignItems: isNarrow ? "flex-start" : "center",
flexGrow: 1,
flexShrink: 0,
marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN,
children: statusNode
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
Box_default,
{
flexShrink: 0,
marginLeft: showTipLine ? LAYOUT.TIP_LEFT_MARGIN : 0,
marginRight: showTipLine ? isNarrow ? LAYOUT.TIP_RIGHT_MARGIN_NARROW : LAYOUT.TIP_RIGHT_MARGIN_WIDE : 0,
position: showTipLine ? "relative" : "absolute",
...showTipLine ? {} : { top: -100, left: -100 },
children: !isNarrow && tipContentStr && renderTipNode()
}
)
]
}
),
showRow1 && showRow2 && (showUiDetails || showRow1Minimal && showRow2Minimal) && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(HorizontalLine, { dim: true }) }),
showRow2 && /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(
Box_default,
{
width: "100%",
flexDirection: isNarrow ? "column" : "row",
alignItems: isNarrow ? "flex-start" : "center",
justifyContent: "space-between",
children: [
/* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
Box_default,
{
flexDirection: "row",
alignItems: "center",
marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN,
children: showUiDetails ? /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(import_jsx_runtime121.Fragment, { children: [
!hideUiDetailsForSuggestions && !inputState.shellModeActive && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
ApprovalModeIndicator,
{
approvalMode: uiState.showApprovalModeIndicator,
allowPlanMode: uiState.allowPlanMode
}
),
inputState.shellModeActive && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(ShellModeIndicator, {}) }),
!uiState.renderMarkdown && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(RawMarkdownIndicator, {}) })
] }) : showRow2Minimal && modeContentObj && /* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(Text, { color: modeContentObj.color, children: [
"\u25CF ",
modeContentObj.text
] })
}
),
/* @__PURE__ */ (0, import_jsx_runtime121.jsxs)(
Box_default,
{
marginTop: isNarrow ? LAYOUT.CONTEXT_DISPLAY_TOP_MARGIN_NARROW : 0,
flexDirection: "row",
alignItems: "center",
marginLeft: isNarrow ? LAYOUT.CONTEXT_DISPLAY_LEFT_MARGIN_NARROW : LAYOUT.CONTEXT_DISPLAY_LEFT_MARGIN_WIDE,
children: [
(showUiDetails || showMinimalContext) && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(StatusDisplay, { hideContextSummary }),
showMinimalContext && !showUiDetails && /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(Box_default, { marginLeft: LAYOUT.INDICATOR_LEFT_MARGIN, children: /* @__PURE__ */ (0, import_jsx_runtime121.jsx)(
ContextUsageDisplay,
{
promptTokenCount: uiState.sessionStats.lastPromptTokenCount,
model: typeof uiState.currentModel === "string" ? uiState.currentModel : void 0,
terminalWidth
}
) })
]
}
)
]
}
)
] });
};
// packages/cli/src/ui/components/QueuedMessageDisplay.tsx
var import_jsx_runtime122 = __toESM(require_jsx_runtime(), 1);
var MAX_DISPLAYED_QUEUED_MESSAGES = 3;
var QueuedMessageDisplay = ({
messageQueue: messageQueue2
}) => {
if (messageQueue2.length === 0) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime122.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime122.jsx)(Box_default, { paddingLeft: 2, children: /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(Text, { dimColor: true, children: "Queued (press \u2191 to edit):" }) }),
messageQueue2.slice(0, MAX_DISPLAYED_QUEUED_MESSAGES).map((message, index) => {
const preview = message.replace(/\s+/g, " ");
return /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(Box_default, { paddingLeft: 4, width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(Text, { dimColor: true, wrap: "truncate", children: preview }) }, index);
}),
messageQueue2.length > MAX_DISPLAYED_QUEUED_MESSAGES && /* @__PURE__ */ (0, import_jsx_runtime122.jsx)(Box_default, { paddingLeft: 4, children: /* @__PURE__ */ (0, import_jsx_runtime122.jsxs)(Text, { dimColor: true, children: [
"... (+",
messageQueue2.length - MAX_DISPLAYED_QUEUED_MESSAGES,
" more)"
] }) })
] });
};
// packages/cli/src/ui/components/ConfigInitDisplay.tsx
var import_react80 = __toESM(require_react(), 1);
var import_jsx_runtime123 = __toESM(require_jsx_runtime(), 1);
var ConfigInitDisplay = ({
message: initialMessage = "Working..."
}) => {
const [message, setMessage] = (0, import_react80.useState)(initialMessage);
(0, import_react80.useEffect)(() => {
const onChange = (clients) => {
if (!clients || clients.size === 0) {
setMessage(initialMessage);
return;
}
let connected = 0;
const connecting = [];
for (const [name, client] of clients.entries()) {
if (client.getStatus() === MCPServerStatus.CONNECTED) {
connected++;
} else {
connecting.push(name);
}
}
if (connecting.length > 0) {
const maxDisplay = 3;
const displayedServers = connecting.slice(0, maxDisplay).join(", ");
const remaining = connecting.length - maxDisplay;
const suffix = remaining > 0 ? `, +${remaining} more` : "";
const mcpMessage = `Connecting to MCP servers... (${connected}/${clients.size}) - Waiting for: ${displayedServers}${suffix}`;
setMessage(
initialMessage && initialMessage !== "Working..." ? `${initialMessage} (${mcpMessage})` : mcpMessage
);
} else {
const mcpMessage = `Connecting to MCP servers... (${connected}/${clients.size})`;
setMessage(
initialMessage && initialMessage !== "Working..." ? `${initialMessage} (${mcpMessage})` : mcpMessage
);
}
};
coreEvents.on(CoreEvent.McpClientUpdate, onChange);
return () => {
coreEvents.off(CoreEvent.McpClientUpdate, onChange);
};
}, [initialMessage]);
return /* @__PURE__ */ (0, import_jsx_runtime123.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime123.jsxs)(Text, { children: [
/* @__PURE__ */ (0, import_jsx_runtime123.jsx)(GeminiSpinner, {}),
" ",
/* @__PURE__ */ (0, import_jsx_runtime123.jsx)(Text, { color: theme.text.primary, children: message })
] }) });
};
// packages/cli/src/ui/components/messages/Todo.tsx
var import_react82 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/Checklist.tsx
var import_react81 = __toESM(require_react(), 1);
// packages/cli/src/ui/components/ChecklistItem.tsx
var import_jsx_runtime124 = __toESM(require_jsx_runtime(), 1);
var ChecklistStatusDisplay = ({
status
}) => {
switch (status) {
case "completed":
return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: theme.status.success, "aria-label": "Completed", children: "\u2713" });
case "in_progress":
return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: theme.text.accent, "aria-label": "In Progress", children: "\xBB" });
case "pending":
return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: theme.text.secondary, "aria-label": "Pending", children: "\u2610" });
case "cancelled":
return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: theme.status.error, "aria-label": "Cancelled", children: "\u2717" });
case "blocked":
return /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: theme.status.warning, "aria-label": "Blocked", children: "\u26D4" });
default:
checkExhaustive(status);
}
};
var ChecklistItem = ({
item,
wrap,
role: ariaRole
}) => {
const textColor = (() => {
switch (item.status) {
case "in_progress":
return theme.text.accent;
case "completed":
case "cancelled":
case "blocked":
return theme.text.secondary;
case "pending":
return theme.text.primary;
default:
checkExhaustive(item.status);
}
})();
const strikethrough = item.status === "cancelled";
return /* @__PURE__ */ (0, import_jsx_runtime124.jsxs)(Box_default, { flexDirection: "row", columnGap: 1, "aria-role": ariaRole, children: [
/* @__PURE__ */ (0, import_jsx_runtime124.jsx)(ChecklistStatusDisplay, { status: item.status }),
/* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Box_default, { flexShrink: 1, children: /* @__PURE__ */ (0, import_jsx_runtime124.jsx)(Text, { color: textColor, wrap, strikethrough, children: item.label }) })
] });
};
// packages/cli/src/ui/components/Checklist.tsx
var import_jsx_runtime125 = __toESM(require_jsx_runtime(), 1);
var ChecklistTitleDisplay = ({ title, items, toggleHint }) => {
const score = (0, import_react81.useMemo)(() => {
let total = 0;
let completed = 0;
for (const item of items) {
if (item.status !== "cancelled") {
total += 1;
if (item.status === "completed") {
completed += 1;
}
}
}
return `${completed}/${total} completed`;
}, [items]);
return /* @__PURE__ */ (0, import_jsx_runtime125.jsxs)(Box_default, { flexDirection: "row", columnGap: 2, height: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime125.jsx)(Text, { color: theme.text.primary, bold: true, "aria-label": `${title} list`, children: title }),
/* @__PURE__ */ (0, import_jsx_runtime125.jsxs)(Text, { color: theme.text.secondary, children: [
score,
toggleHint ? ` (${toggleHint})` : ""
] })
] });
};
var ChecklistListDisplay = ({
items
}) => /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(Box_default, { flexDirection: "column", "aria-role": "list", children: items.map((item, index) => /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
ChecklistItem,
{
item,
role: "listitem"
},
`${index}-${item.label}`
)) });
var Checklist = ({
title,
items,
isExpanded,
toggleHint
}) => {
const inProgress = (0, import_react81.useMemo)(
() => items.find((item) => item.status === "in_progress") || null,
[items]
);
const hasActiveItems = (0, import_react81.useMemo)(
() => items.some(
(item) => item.status === "pending" || item.status === "in_progress"
),
[items]
);
if (items.length === 0 || !isExpanded && !hasActiveItems) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
Box_default,
{
borderStyle: "single",
borderBottom: false,
borderRight: false,
borderLeft: false,
borderColor: theme.border.default,
paddingLeft: 1,
paddingRight: 1,
children: isExpanded ? /* @__PURE__ */ (0, import_jsx_runtime125.jsxs)(Box_default, { flexDirection: "column", rowGap: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
ChecklistTitleDisplay,
{
title,
items,
toggleHint
}
),
/* @__PURE__ */ (0, import_jsx_runtime125.jsx)(ChecklistListDisplay, { items })
] }) : /* @__PURE__ */ (0, import_jsx_runtime125.jsxs)(Box_default, { flexDirection: "row", columnGap: 1, height: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime125.jsx)(Box_default, { flexShrink: 0, flexGrow: 0, children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(
ChecklistTitleDisplay,
{
title,
items,
toggleHint
}
) }),
inProgress && /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(Box_default, { flexShrink: 1, flexGrow: 1, children: /* @__PURE__ */ (0, import_jsx_runtime125.jsx)(ChecklistItem, { item: inProgress, wrap: "truncate" }) })
] })
}
);
};
// packages/cli/src/ui/components/messages/Todo.tsx
var import_jsx_runtime126 = __toESM(require_jsx_runtime(), 1);
var TodoTray = () => {
const uiState = useUIState();
const todos = (0, import_react82.useMemo)(() => {
for (let i = uiState.history.length - 1; i >= 0; i--) {
const entry = uiState.history[i];
if (entry.type !== "tool_group") {
continue;
}
const toolGroup = entry;
for (const tool of toolGroup.tools) {
if (typeof tool.resultDisplay !== "object" || !("todos" in tool.resultDisplay)) {
continue;
}
return tool.resultDisplay;
}
}
return null;
}, [uiState.history]);
const checklistItems = (0, import_react82.useMemo)(() => {
if (!todos || !todos.todos) {
return [];
}
return todos.todos.map((todo) => ({
status: todo.status,
label: todo.description
}));
}, [todos]);
if (!todos || !todos.todos) {
return null;
}
return /* @__PURE__ */ (0, import_jsx_runtime126.jsx)(
Checklist,
{
title: "Todo",
items: checklistItems,
isExpanded: uiState.showFullTodos,
toggleHint: `${formatCommand("app.showFullTodos" /* SHOW_FULL_TODOS */)} to toggle`
}
);
};
// packages/cli/src/ui/components/Composer.tsx
var import_jsx_runtime127 = __toESM(require_jsx_runtime(), 1);
var Composer = ({ isFocused = true }) => {
const uiState = useUIState();
const inputState = useInputState();
const uiActions = useUIActions();
const settings = useSettings();
const config = useConfig();
const { vimEnabled, vimMode } = useVimMode();
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
const { columns: terminalWidth } = useTerminalSize();
const isNarrow = isNarrowWidth(terminalWidth);
const debugConsoleMaxHeight = Math.floor(Math.max(terminalWidth * 0.2, 5));
const [suggestionsVisible, setSuggestionsVisible] = (0, import_react83.useState)(false);
const isAlternateBuffer = useAlternateBuffer();
const showUiDetails = uiState.cleanUiDetailsVisible;
const suggestionsPosition = isAlternateBuffer ? "above" : "below";
const hideContextSummary = suggestionsVisible && suggestionsPosition === "above";
const { hasPendingActionRequired, shouldCollapseDuringApproval } = useComposerStatus();
const isPassiveShortcutsHelpState = uiState.isInputActive && uiState.streamingState === "idle" && !hasPendingActionRequired;
const { setShortcutsHelpVisible } = uiActions;
(0, import_react83.useEffect)(() => {
if (hasPendingActionRequired) {
appEvents.emit("scroll-to-bottom" /* ScrollToBottom */);
}
}, [hasPendingActionRequired]);
(0, import_react83.useEffect)(() => {
if (uiState.shortcutsHelpVisible && !isPassiveShortcutsHelpState) {
setShortcutsHelpVisible(false);
}
}, [
uiState.shortcutsHelpVisible,
isPassiveShortcutsHelpState,
setShortcutsHelpVisible
]);
const showShortcutsHelp = uiState.shortcutsHelpVisible && uiState.streamingState === "idle" && !hasPendingActionRequired;
if (hasPendingActionRequired && shouldCollapseDuringApproval) {
return null;
}
const showToast = shouldShowToast(uiState, inputState);
const hideUiDetailsForSuggestions = suggestionsVisible && suggestionsPosition === "above";
const showMinimalToast = showToast;
return /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)(
Box_default,
{
flexDirection: "column",
width: uiState.terminalWidth,
flexGrow: 0,
flexShrink: 0,
children: [
uiState.isResuming && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(ConfigInitDisplay, { message: "Resuming session..." }),
showUiDetails && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(QueuedMessageDisplay, { messageQueue: uiState.messageQueue }),
showUiDetails && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(TodoTray, {}),
showShortcutsHelp && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(ShortcutsHelp, {}),
(showUiDetails || showMinimalToast) && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(Box_default, { minHeight: 1, marginLeft: isNarrow ? 0 : 1, children: /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(ToastDisplay, {}) }),
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(Box_default, { width: "100%", flexDirection: "column", children: /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
StatusRow,
{
showUiDetails,
isNarrow,
terminalWidth,
hideContextSummary,
hideUiDetailsForSuggestions,
hasPendingActionRequired
}
) }),
showUiDetails && uiState.showErrorDetails && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(OverflowProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime127.jsxs)(Box_default, { flexDirection: "column", children: [
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
DetailedMessagesDisplay,
{
maxHeight: uiState.constrainHeight ? debugConsoleMaxHeight : void 0,
width: uiState.terminalWidth,
hasFocus: uiState.showErrorDetails
}
),
/* @__PURE__ */ (0, import_jsx_runtime127.jsx)(ShowMoreLines, { constrainHeight: uiState.constrainHeight })
] }) }),
uiState.isInputActive && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
InputPrompt,
{
onSubmit: uiActions.handleFinalSubmit,
setBannerVisible: uiActions.setBannerVisible,
onClearScreen: uiActions.handleClearScreen,
config,
slashCommands: uiState.slashCommands || [],
commandContext: uiState.commandContext,
setShellModeActive: uiActions.setShellModeActive,
approvalMode: uiState.showApprovalModeIndicator,
onEscapePromptChange: uiActions.onEscapePromptChange,
focus: isFocused,
vimHandleInput: uiActions.vimHandleInput,
isEmbeddedShellFocused: uiState.embeddedShellFocused,
popAllMessages: uiActions.popAllMessages,
onQueueMessage: uiActions.addMessage,
placeholder: vimEnabled ? vimMode === "INSERT" ? " Press 'Esc' for NORMAL mode." : " Press 'i' for INSERT mode." : inputState.shellModeActive ? " Type your shell command" : " Type your message or @path/to/file",
setQueueErrorMessage: uiActions.setQueueErrorMessage,
streamingState: uiState.streamingState,
suggestionsPosition,
onSuggestionsVisibilityChange: setSuggestionsVisible
}
),
showUiDetails && !settings.merged.ui.hideFooter && !isScreenReaderEnabled && /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(Footer, {})
]
}
);
};
// packages/cli/src/ui/components/ExitWarning.tsx
var import_jsx_runtime128 = __toESM(require_jsx_runtime(), 1);
var ExitWarning = () => {
const uiState = useUIState();
return /* @__PURE__ */ (0, import_jsx_runtime128.jsxs)(import_jsx_runtime128.Fragment, { children: [
uiState.dialogsVisible && uiState.ctrlCPressedOnce && /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(Text, { color: theme.status.warning, children: "Press Ctrl+C again to exit." }) }),
uiState.dialogsVisible && uiState.ctrlDPressedOnce && /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime128.jsx)(Text, { color: theme.status.warning, children: "Press Ctrl+D again to exit." }) })
] });
};
// packages/cli/src/ui/hooks/useFlickerDetector.ts
var import_react84 = __toESM(require_react(), 1);
function useFlickerDetector(rootUiRef, terminalHeight) {
const config = useConfig();
const { constrainHeight } = useUIState();
(0, import_react84.useEffect)(() => {
if (rootUiRef.current) {
const measurement = measure_element_default(rootUiRef.current);
if (measurement.height > terminalHeight) {
if (!constrainHeight) {
return;
}
recordFlickerFrame(config);
appEvents.emit("flicker" /* Flicker */);
}
}
});
}
// packages/cli/src/ui/layouts/ScreenReaderAppLayout.tsx
var import_jsx_runtime129 = __toESM(require_jsx_runtime(), 1);
var ScreenReaderAppLayout = () => {
const uiState = useUIState();
const { rootUiRef, terminalHeight } = uiState;
useFlickerDetector(rootUiRef, terminalHeight);
return /* @__PURE__ */ (0, import_jsx_runtime129.jsxs)(
Box_default,
{
flexDirection: "column",
width: "90%",
height: "100%",
ref: uiState.rootUiRef,
children: [
/* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Notifications, {}),
/* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Footer, {}),
/* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Box_default, { flexGrow: 1, overflow: "hidden", children: /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(MainContent, {}) }),
uiState.dialogsVisible ? /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(
DialogManager,
{
terminalWidth: uiState.terminalWidth,
addItem: uiState.historyManager.addItem
}
) : /* @__PURE__ */ (0, import_jsx_runtime129.jsx)(Composer, {}),
/* @__PURE__ */ (0, import_jsx_runtime129.jsx)(ExitWarning, {})
]
}
);
};
// packages/cli/src/ui/components/CopyModeWarning.tsx
var import_jsx_runtime130 = __toESM(require_jsx_runtime(), 1);
var CopyModeWarning = () => {
const { copyModeEnabled } = useInputState();
return /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(Box_default, { height: 1, children: copyModeEnabled && /* @__PURE__ */ (0, import_jsx_runtime130.jsx)(Text, { color: theme.status.warning, children: "In Copy Mode. Use Page Up/Down to scroll. Press Ctrl+S or any other key to exit." }) });
};
// packages/cli/src/ui/components/BackgroundTaskDisplay.tsx
var import_react86 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useExecutionLifecycle.ts
var import_react85 = __toESM(require_react(), 1);
import path11 from "node:path";
import os2 from "node:os";
import fs5 from "node:fs";
// packages/cli/src/ui/hooks/shellReducer.ts
var initialState4 = {
activeShellPtyId: null,
lastShellOutputTime: 0,
backgroundTasks: /* @__PURE__ */ new Map(),
isBackgroundTaskVisible: false
};
function shellReducer(state, action) {
switch (action.type) {
case "SET_ACTIVE_PTY":
return { ...state, activeShellPtyId: action.pid };
case "SET_OUTPUT_TIME":
return { ...state, lastShellOutputTime: action.time };
case "SET_VISIBILITY":
return { ...state, isBackgroundTaskVisible: action.visible };
case "TOGGLE_VISIBILITY":
return {
...state,
isBackgroundTaskVisible: !state.isBackgroundTaskVisible
};
case "REGISTER_TASK": {
if (state.backgroundTasks.has(action.pid)) return state;
const nextTasks = new Map(state.backgroundTasks);
nextTasks.set(action.pid, {
pid: action.pid,
command: action.command,
output: action.initialOutput,
isBinary: false,
binaryBytesReceived: 0,
status: "running",
completionBehavior: action.completionBehavior
});
return { ...state, backgroundTasks: nextTasks };
}
case "UPDATE_TASK": {
const task = state.backgroundTasks.get(action.pid);
if (!task) return state;
const nextTasks = new Map(state.backgroundTasks);
const updatedTask = { ...task, ...action.update };
if (action.update.status === "exited") {
nextTasks.delete(action.pid);
}
nextTasks.set(action.pid, updatedTask);
return { ...state, backgroundTasks: nextTasks };
}
case "APPEND_TASK_OUTPUT": {
const task = state.backgroundTasks.get(action.pid);
if (!task) return state;
let newOutput = task.output;
if (typeof action.chunk === "string") {
const currentOutput = typeof task.output === "string" ? task.output : "";
const combinedLength = currentOutput.length + action.chunk.length;
if (combinedLength > MAX_SHELL_OUTPUT_SIZE + SHELL_OUTPUT_TRUNCATION_BUFFER) {
if (action.chunk.length >= MAX_SHELL_OUTPUT_SIZE) {
newOutput = action.chunk.slice(-MAX_SHELL_OUTPUT_SIZE);
} else {
const keepFromCurrent = MAX_SHELL_OUTPUT_SIZE - action.chunk.length;
newOutput = currentOutput.slice(-keepFromCurrent) + action.chunk;
}
if (newOutput.length > 0) {
const firstCharCode = newOutput.charCodeAt(0);
if (firstCharCode >= 56320 && firstCharCode <= 57343) {
newOutput = newOutput.slice(1);
}
}
} else {
newOutput = currentOutput + action.chunk;
}
} else if (action.chunk) {
newOutput = action.chunk;
}
task.output = newOutput;
const nextState = { ...state, lastShellOutputTime: Date.now() };
if (state.isBackgroundTaskVisible) {
return {
...nextState,
backgroundTasks: new Map(state.backgroundTasks)
};
}
return nextState;
}
case "SYNC_BACKGROUND_TASKS": {
return { ...state, backgroundTasks: new Map(state.backgroundTasks) };
}
case "DISMISS_TASK": {
const nextTasks = new Map(state.backgroundTasks);
nextTasks.delete(action.pid);
return {
...state,
backgroundTasks: nextTasks,
isBackgroundTaskVisible: nextTasks.size === 0 ? false : state.isBackgroundTaskVisible
};
}
default:
return state;
}
}
// packages/cli/src/ui/hooks/useExecutionLifecycle.ts
var RESTORE_VISIBILITY_DELAY_MS = 300;
var MAX_OUTPUT_LENGTH = 1e4;
function addShellCommandToGeminiHistory(geminiClient, rawQuery, resultText) {
const modelContent = resultText.length > MAX_OUTPUT_LENGTH ? resultText.substring(0, MAX_OUTPUT_LENGTH) + "\n... (truncated)" : resultText;
const safeQuery = rawQuery.replace(/\\/g, "\\\\").replace(/\x60/g, "\\`");
const safeModelContent = modelContent.replace(/\\/g, "\\\\").replace(/\x60/g, "\\`");
geminiClient.addHistory({
role: "user",
parts: [
{
text: `I ran the following shell command:
\`\`\`sh
${safeQuery}
\`\`\`
This produced the following result:
\`\`\`
${safeModelContent}
\`\`\``
}
]
});
}
var useExecutionLifecycle = (addItemToHistory, setPendingHistoryItem, onExec, onDebugMessage, config, geminiClient, setShellInputFocused, terminalWidth, terminalHeight, activeBackgroundExecutionId, isWaitingForConfirmation) => {
const [state, dispatch] = (0, import_react85.useReducer)(shellReducer, initialState4);
const manager = (0, import_react85.useRef)(null);
if (!manager.current) {
manager.current = {
wasVisibleBeforeForeground: false,
restoreTimeout: null,
backgroundedPids: /* @__PURE__ */ new Set(),
subscriptions: /* @__PURE__ */ new Map()
};
}
const m = manager.current;
const activePtyId = state.activeShellPtyId ?? activeBackgroundExecutionId ?? void 0;
(0, import_react85.useEffect)(() => {
const isForegroundActive = !!activePtyId || !!isWaitingForConfirmation;
if (isForegroundActive) {
if (m.restoreTimeout) {
clearTimeout(m.restoreTimeout);
m.restoreTimeout = null;
}
if (state.isBackgroundTaskVisible && !m.wasVisibleBeforeForeground) {
m.wasVisibleBeforeForeground = true;
dispatch({ type: "SET_VISIBILITY", visible: false });
}
} else if (m.wasVisibleBeforeForeground && !m.restoreTimeout) {
m.restoreTimeout = setTimeout(() => {
dispatch({ type: "SET_VISIBILITY", visible: true });
m.wasVisibleBeforeForeground = false;
m.restoreTimeout = null;
}, RESTORE_VISIBILITY_DELAY_MS);
}
return () => {
if (m.restoreTimeout) {
clearTimeout(m.restoreTimeout);
}
};
}, [
activePtyId,
isWaitingForConfirmation,
state.isBackgroundTaskVisible,
m,
dispatch
]);
(0, import_react85.useEffect)(
() => () => {
for (const unsubscribe of m.subscriptions.values()) {
unsubscribe();
}
m.subscriptions.clear();
},
[m]
);
const toggleBackgroundTasks = (0, import_react85.useCallback)(() => {
if (state.backgroundTasks.size > 0) {
const willBeVisible = !state.isBackgroundTaskVisible;
dispatch({ type: "TOGGLE_VISIBILITY" });
const isForegroundActive = !!activePtyId || !!isWaitingForConfirmation;
if (willBeVisible && isForegroundActive) {
m.wasVisibleBeforeForeground = true;
} else {
m.wasVisibleBeforeForeground = false;
}
if (willBeVisible) {
dispatch({ type: "SYNC_BACKGROUND_TASKS" });
}
} else {
dispatch({ type: "SET_VISIBILITY", visible: false });
addItemToHistory(
{
type: "info",
text: "No background tasks are currently active."
},
Date.now()
);
}
}, [
addItemToHistory,
state.backgroundTasks.size,
state.isBackgroundTaskVisible,
activePtyId,
isWaitingForConfirmation,
m,
dispatch
]);
const backgroundCurrentExecution = (0, import_react85.useCallback)(() => {
const pidToBackground = state.activeShellPtyId ?? activeBackgroundExecutionId;
if (pidToBackground) {
m.backgroundedPids.add(pidToBackground);
if (state.activeShellPtyId) {
ShellExecutionService.background(pidToBackground);
} else {
ExecutionLifecycleService.background(pidToBackground);
}
m.wasVisibleBeforeForeground = false;
if (m.restoreTimeout) {
clearTimeout(m.restoreTimeout);
m.restoreTimeout = null;
}
}
}, [state.activeShellPtyId, activeBackgroundExecutionId, m]);
const dismissBackgroundTask = (0, import_react85.useCallback)(
async (pid) => {
const shell = state.backgroundTasks.get(pid);
if (shell) {
if (shell.status === "running") {
ExecutionLifecycleService.kill(pid);
}
dispatch({ type: "DISMISS_TASK", pid });
m.backgroundedPids.delete(pid);
const unsubscribe = m.subscriptions.get(pid);
if (unsubscribe) {
unsubscribe();
m.subscriptions.delete(pid);
}
}
},
[state.backgroundTasks, dispatch, m]
);
const registerBackgroundTask = (0, import_react85.useCallback)(
(pid, command, initialOutput, completionBehavior) => {
m.backgroundedPids.add(pid);
dispatch({
type: "REGISTER_TASK",
pid,
command,
initialOutput,
completionBehavior
});
const exitUnsubscribe = ExecutionLifecycleService.onExit(pid, (code) => {
dispatch({
type: "UPDATE_TASK",
pid,
update: { status: "exited", exitCode: code }
});
if (completionBehavior !== "silent") {
dispatch({ type: "DISMISS_TASK", pid });
}
const unsub = m.subscriptions.get(pid);
if (unsub) {
unsub();
m.subscriptions.delete(pid);
}
m.backgroundedPids.delete(pid);
});
const dataUnsubscribe = ExecutionLifecycleService.subscribe(
pid,
(event) => {
if (event.type === "data") {
dispatch({
type: "APPEND_TASK_OUTPUT",
pid,
chunk: event.chunk
});
} else if (event.type === "binary_detected") {
dispatch({
type: "UPDATE_TASK",
pid,
update: { isBinary: true }
});
} else if (event.type === "binary_progress") {
dispatch({
type: "UPDATE_TASK",
pid,
update: {
isBinary: true,
binaryBytesReceived: event.bytesReceived
}
});
}
}
);
m.subscriptions.set(pid, () => {
exitUnsubscribe();
dataUnsubscribe();
});
},
[dispatch, m]
);
(0, import_react85.useEffect)(() => {
const listener = (info) => {
if (m.backgroundedPids.has(info.executionId)) {
return;
}
registerBackgroundTask(
info.executionId,
info.label,
info.output,
info.completionBehavior
);
};
ExecutionLifecycleService.onBackground(listener);
return () => {
ExecutionLifecycleService.offBackground(listener);
};
}, [registerBackgroundTask, m]);
const handleShellCommand = (0, import_react85.useCallback)(
(rawQuery, abortSignal) => {
if (typeof rawQuery !== "string" || rawQuery.trim() === "") {
return false;
}
const userMessageTimestamp = Date.now();
const callId = `shell-${userMessageTimestamp}`;
addItemToHistory(
{ type: "user_shell", text: rawQuery },
userMessageTimestamp
);
const isWindows3 = os2.platform() === "win32";
const targetDir = config.getTargetDir();
let commandToExecute = rawQuery;
let pwdFilePath;
const executeCommand = async () => {
let cumulativeStdout = "";
let isBinaryStream = false;
let binaryBytesReceived = 0;
const initialToolDisplay = {
callId,
name: SHELL_COMMAND_NAME,
description: rawQuery,
status: CoreToolCallStatus.Executing,
isClientInitiated: true,
resultDisplay: "",
confirmationDetails: void 0
};
setPendingHistoryItem({
type: "tool_group",
tools: [initialToolDisplay]
});
let executionPid;
const abortHandler = () => {
onDebugMessage(
`Aborting shell command (PID: ${executionPid ?? "unknown"})`
);
};
abortSignal.addEventListener("abort", abortHandler, { once: true });
try {
if (!isWindows3) {
let command = rawQuery.trim();
if (command.endsWith("\\")) {
command += " ";
}
const tmpDir = fs5.mkdtempSync(
path11.join(os2.tmpdir(), "gemini-shell-")
);
pwdFilePath = path11.join(tmpDir, "pwd.tmp");
const escapedPwdFilePath = escapeShellArg(pwdFilePath, "bash");
commandToExecute = `{
${command}
}
__code=$?; pwd > ${escapedPwdFilePath}; exit $__code`;
}
onDebugMessage(`Executing in ${targetDir}: ${commandToExecute}`);
const activeTheme = themeManager.getActiveTheme();
const shellExecutionConfig = {
...config.getShellExecutionConfig(),
sessionId: config.getSessionId(),
terminalWidth,
terminalHeight,
defaultFg: activeTheme.colors.Foreground,
defaultBg: activeTheme.colors.Background
};
const { pid, result: resultPromise } = await ShellExecutionService.execute(
commandToExecute,
targetDir,
(event) => {
let shouldUpdate = false;
switch (event.type) {
case "data":
if (isBinaryStream) break;
if (typeof event.chunk === "string") {
if (typeof cumulativeStdout === "string") {
cumulativeStdout += event.chunk;
} else {
cumulativeStdout = event.chunk;
}
} else {
cumulativeStdout = event.chunk;
}
shouldUpdate = true;
break;
case "binary_detected":
isBinaryStream = true;
shouldUpdate = true;
break;
case "binary_progress":
isBinaryStream = true;
binaryBytesReceived = event.bytesReceived;
shouldUpdate = true;
break;
case "exit":
break;
default:
throw new Error("An unhandled ShellOutputEvent was found.");
}
if (executionPid && m.backgroundedPids.has(executionPid)) {
dispatch({
type: "APPEND_TASK_OUTPUT",
pid: executionPid,
chunk: event.type === "data" ? event.chunk : cumulativeStdout
});
return;
}
let currentDisplayOutput;
if (isBinaryStream) {
currentDisplayOutput = binaryBytesReceived > 0 ? `[Receiving binary output... ${formatBytes(binaryBytesReceived)} received]` : "[Binary output detected. Halting stream...]";
} else {
currentDisplayOutput = cumulativeStdout;
}
if (shouldUpdate) {
dispatch({ type: "SET_OUTPUT_TIME", time: Date.now() });
setPendingHistoryItem((prevItem) => {
if (prevItem?.type === "tool_group") {
return {
...prevItem,
tools: prevItem.tools.map(
(tool) => tool.callId === callId ? { ...tool, resultDisplay: currentDisplayOutput } : tool
)
};
}
return prevItem;
});
}
},
abortSignal,
config.getEnableInteractiveShell(),
shellExecutionConfig
);
executionPid = pid;
if (pid) {
dispatch({ type: "SET_ACTIVE_PTY", pid });
setPendingHistoryItem((prevItem) => {
if (prevItem?.type === "tool_group") {
return {
...prevItem,
tools: prevItem.tools.map(
(tool) => tool.callId === callId ? { ...tool, ptyId: pid } : tool
)
};
}
return prevItem;
});
}
const result = await resultPromise;
setPendingHistoryItem(null);
if (result.backgrounded && result.pid) {
registerBackgroundTask(
result.pid,
rawQuery,
cumulativeStdout,
"notify"
);
dispatch({ type: "SET_ACTIVE_PTY", pid: null });
}
let mainContent;
if (isBinaryStream || isBinary(result.rawOutput)) {
mainContent = "[Command produced binary output, which is not shown.]";
} else {
mainContent = result.output.trim() || "(Command produced no output)";
}
let finalOutput = result.ansiOutput && result.ansiOutput.length > 0 ? result.ansiOutput : mainContent;
let finalStatus = CoreToolCallStatus.Success;
const prependToAnsiOutput = (output, text) => {
const newLines = text.split("\n").map((line) => [
{
text: line,
fg: "",
bg: "",
dim: false,
bold: false,
italic: false,
underline: false,
inverse: false,
isUninitialized: false
}
]);
return [...newLines, [], ...output];
};
let prefix = "";
if (result.error) {
finalStatus = CoreToolCallStatus.Error;
prefix = result.error.message;
} else if (result.aborted) {
finalStatus = CoreToolCallStatus.Cancelled;
prefix = "Command was cancelled.";
} else if (result.backgrounded) {
finalStatus = CoreToolCallStatus.Success;
finalOutput = `Command moved to background (PID: ${result.pid}). Output hidden. Press Ctrl+B to view.`;
mainContent = finalOutput;
} else if (result.signal) {
finalStatus = CoreToolCallStatus.Error;
prefix = `Command terminated by signal: ${result.signal}.`;
} else if (result.exitCode !== 0) {
finalStatus = CoreToolCallStatus.Error;
prefix = `Command exited with code ${result.exitCode}.`;
}
if (prefix) {
finalOutput = typeof finalOutput === "string" ? `${prefix}
${finalOutput}` : prependToAnsiOutput(finalOutput, prefix);
mainContent = `${prefix}
${mainContent}`;
}
if (pwdFilePath && fs5.existsSync(pwdFilePath)) {
const finalPwd = fs5.readFileSync(pwdFilePath, "utf8").trim();
if (finalPwd && finalPwd !== targetDir) {
const warning = `WARNING: shell mode is stateless; the directory change to '${finalPwd}' will not persist.`;
finalOutput = typeof finalOutput === "string" ? `${warning}
${finalOutput}` : prependToAnsiOutput(finalOutput, warning);
mainContent = `${warning}
${mainContent}`;
}
}
const finalToolDisplay = {
...initialToolDisplay,
status: finalStatus,
resultDisplay: finalOutput
};
if (finalStatus !== CoreToolCallStatus.Cancelled) {
addItemToHistory(
{
type: "tool_group",
tools: [finalToolDisplay]
},
userMessageTimestamp
);
}
addShellCommandToGeminiHistory(geminiClient, rawQuery, mainContent);
} catch (err) {
setPendingHistoryItem(null);
const errorMessage = err instanceof Error ? err.message : String(err);
addItemToHistory(
{
type: "error",
text: `An unexpected error occurred: ${errorMessage}`
},
userMessageTimestamp
);
} finally {
abortSignal.removeEventListener("abort", abortHandler);
if (pwdFilePath) {
const tmpDir = path11.dirname(pwdFilePath);
try {
if (fs5.existsSync(pwdFilePath)) {
fs5.unlinkSync(pwdFilePath);
}
if (fs5.existsSync(tmpDir)) {
fs5.rmSync(tmpDir, { recursive: true, force: true });
}
} catch {
}
}
dispatch({ type: "SET_ACTIVE_PTY", pid: null });
setShellInputFocused(false);
}
};
onExec(executeCommand());
return true;
},
[
config,
onDebugMessage,
addItemToHistory,
setPendingHistoryItem,
onExec,
geminiClient,
setShellInputFocused,
terminalHeight,
terminalWidth,
registerBackgroundTask,
m,
dispatch
]
);
const backgroundTaskCount = Array.from(state.backgroundTasks.values()).filter(
(s) => s.status === "running"
).length;
return {
handleShellCommand,
activeShellPtyId: state.activeShellPtyId,
lastShellOutputTime: state.lastShellOutputTime,
backgroundTaskCount,
isBackgroundTaskVisible: state.isBackgroundTaskVisible,
toggleBackgroundTasks,
backgroundCurrentExecution,
registerBackgroundTask,
dismissBackgroundTask,
backgroundTasks: state.backgroundTasks
};
};
// packages/cli/src/ui/components/BackgroundTaskDisplay.tsx
var import_jsx_runtime131 = __toESM(require_jsx_runtime(), 1);
var CONTENT_PADDING_X = 1;
var BORDER_WIDTH = 2;
var MAIN_BORDER_HEIGHT = 2;
var HEADER_HEIGHT = 1;
var FOOTER_HEIGHT = 1;
var TOTAL_OVERHEAD_HEIGHT = MAIN_BORDER_HEIGHT + HEADER_HEIGHT + FOOTER_HEIGHT;
var PROCESS_LIST_HEADER_HEIGHT = 3;
var TAB_DISPLAY_HORIZONTAL_PADDING = 4;
var LOG_PATH_OVERHEAD = 7;
var formatShellCommandForDisplay = (command, maxWidth) => {
const commandFirstLine = command.split("\n")[0];
return cpLen(commandFirstLine) > maxWidth ? `${cpSlice(commandFirstLine, 0, maxWidth - 3)}...` : commandFirstLine;
};
var BackgroundTaskDisplay = ({
shells,
activePid,
width,
height,
isFocused,
isListOpenProp
}) => {
const keyMatchers = useKeyMatchers();
const {
dismissBackgroundTask,
setActiveBackgroundTaskPid,
setIsBackgroundTaskListOpen
} = useUIActions();
const activeShell = shells.get(activePid);
const [output, setOutput] = (0, import_react86.useState)(
activeShell?.output || ""
);
const [highlightedPid, setHighlightedPid] = (0, import_react86.useState)(
activePid
);
const outputRef = (0, import_react86.useRef)(null);
const subscribedRef = (0, import_react86.useRef)(false);
(0, import_react86.useEffect)(() => {
if (!activePid) return;
const ptyWidth = Math.max(1, width - BORDER_WIDTH - CONTENT_PADDING_X * 2);
const ptyHeight = Math.max(1, height - TOTAL_OVERHEAD_HEIGHT);
ShellExecutionService.resizePty(activePid, ptyWidth, ptyHeight);
}, [activePid, width, height]);
(0, import_react86.useEffect)(() => {
if (!activePid) {
setOutput("");
return;
}
const shell = shells.get(activePid);
if (shell) {
setOutput(shell.output);
}
subscribedRef.current = false;
const unsubscribe = ShellExecutionService.subscribe(activePid, (event) => {
if (event.type === "data") {
if (typeof event.chunk === "string") {
if (!subscribedRef.current) {
setOutput(event.chunk);
} else {
setOutput(
(prev) => typeof prev === "string" ? prev + event.chunk : event.chunk
);
}
} else {
setOutput(event.chunk);
}
}
});
subscribedRef.current = true;
return () => {
unsubscribe();
subscribedRef.current = false;
};
}, [activePid, shells]);
(0, import_react86.useEffect)(() => {
if (isListOpenProp) {
setHighlightedPid(activePid);
}
}, [isListOpenProp, activePid]);
useKeypress(
(key) => {
if (!activeShell) return;
if (isListOpenProp) {
if (keyMatchers["background.escape" /* BACKGROUND_SHELL_ESCAPE */](key)) {
setIsBackgroundTaskListOpen(false);
return true;
}
if (keyMatchers["background.kill" /* KILL_BACKGROUND_SHELL */](key)) {
if (highlightedPid) {
void dismissBackgroundTask(highlightedPid);
}
return true;
}
if (keyMatchers["background.toggleList" /* TOGGLE_BACKGROUND_SHELL_LIST */](key)) {
if (highlightedPid) {
setActiveBackgroundTaskPid(highlightedPid);
}
setIsBackgroundTaskListOpen(false);
return true;
}
return false;
}
if (keyMatchers["background.toggle" /* TOGGLE_BACKGROUND_SHELL */](key)) {
return false;
}
if (keyMatchers["background.kill" /* KILL_BACKGROUND_SHELL */](key)) {
void dismissBackgroundTask(activeShell.pid);
return true;
}
if (keyMatchers["background.toggleList" /* TOGGLE_BACKGROUND_SHELL_LIST */](key)) {
setIsBackgroundTaskListOpen(true);
return true;
}
if (keyMatchers["background.select" /* BACKGROUND_SHELL_SELECT */](key)) {
ShellExecutionService.writeToPty(activeShell.pid, "\r");
return true;
} else if (keyMatchers["edit.deleteLeft" /* DELETE_CHAR_LEFT */](key)) {
ShellExecutionService.writeToPty(activeShell.pid, "\b");
return true;
} else if (key.sequence) {
ShellExecutionService.writeToPty(activeShell.pid, key.sequence);
return true;
}
return false;
},
{ isActive: isFocused && !!activeShell }
);
const helpTextParts = [
{ label: "Close", command: "background.toggle" /* TOGGLE_BACKGROUND_SHELL */ },
{ label: "Kill", command: "background.kill" /* KILL_BACKGROUND_SHELL */ },
{ label: "List", command: "background.toggleList" /* TOGGLE_BACKGROUND_SHELL_LIST */ }
];
const helpTextStr = helpTextParts.map((p) => `${p.label} (${formatCommand(p.command)})`).join(" | ");
const renderHelpText = () => /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { children: helpTextParts.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Text, { children: [
i > 0 ? " | " : "",
p.label,
" (",
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { color: theme.text.accent, children: formatCommand(p.command) }),
")"
] }, p.label)) });
const renderTabs = () => {
const shellList = Array.from(shells.values()).filter(
(s) => s.status === "running"
);
const pidInfoWidth = getCachedStringWidth(
` (PID: ${activePid}) ${isFocused ? "(Focused)" : ""}`
);
const availableWidth = width - TAB_DISPLAY_HORIZONTAL_PADDING - getCachedStringWidth(helpTextStr) - pidInfoWidth;
let currentWidth = 0;
const tabs = [];
for (let i = 0; i < shellList.length; i++) {
const shell = shellList[i];
const labelOverhead = 4 + (i + 1).toString().length;
const maxTabLabelLength = Math.max(
1,
Math.floor(availableWidth / shellList.length) - labelOverhead
);
const truncatedCommand = formatShellCommandForDisplay(
shell.command,
maxTabLabelLength
);
const label = ` ${i + 1}: ${truncatedCommand} `;
const labelWidth = getCachedStringWidth(label);
if (i > 0 && currentWidth + labelWidth > availableWidth) {
break;
}
const isActive = shell.pid === activePid;
tabs.push(
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
Text,
{
color: isActive ? theme.text.primary : theme.text.secondary,
bold: isActive,
children: label
},
shell.pid
)
);
currentWidth += labelWidth;
}
if (shellList.length > tabs.length && !isListOpenProp) {
const overflowLabel = ` ... (${formatCommand("background.toggleList" /* TOGGLE_BACKGROUND_SHELL_LIST */)}) `;
const overflowWidth = getCachedStringWidth(overflowLabel);
const shouldShowOverflow = tabs.length > 1 || availableWidth - currentWidth >= overflowWidth;
if (shouldShowOverflow) {
tabs.push(
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { color: theme.status.warning, bold: true, children: overflowLabel }, "overflow")
);
}
}
return tabs;
};
const renderProcessList = () => {
const maxCommandLength = Math.max(
0,
width - BORDER_WIDTH - CONTENT_PADDING_X * 2 - 10
);
const items = Array.from(
shells.values()
).map((shell, index) => {
const truncatedCommand = formatShellCommandForDisplay(
shell.command,
maxCommandLength
);
let label = `${index + 1}: ${truncatedCommand} (PID: ${shell.pid})`;
if (shell.status === "exited") {
label += ` (Exit Code: ${shell.exitCode})`;
}
return {
key: shell.pid.toString(),
value: shell.pid,
label
};
});
const initialIndex = items.findIndex((item) => item.value === activePid);
return /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Box_default, { flexDirection: "column", height: "100%", width: "100%", children: [
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Box_default, { flexShrink: 0, marginBottom: 1, paddingTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { bold: true, children: `Select Process (${formatCommand("background.select" /* BACKGROUND_SHELL_SELECT */)} to select, ${formatCommand("background.kill" /* KILL_BACKGROUND_SHELL */)} to kill, ${formatCommand("background.escape" /* BACKGROUND_SHELL_ESCAPE */)} to cancel):` }) }),
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Box_default, { flexGrow: 1, width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
RadioButtonSelect,
{
items,
initialIndex: initialIndex >= 0 ? initialIndex : 0,
onSelect: (pid) => {
setActiveBackgroundTaskPid(pid);
setIsBackgroundTaskListOpen(false);
},
onHighlight: (pid) => setHighlightedPid(pid),
isFocused,
maxItemsToShow: Math.max(
1,
height - TOTAL_OVERHEAD_HEIGHT - PROCESS_LIST_HEADER_HEIGHT
),
renderItem: (item, { isSelected: _isSelected, titleColor: _titleColor }) => {
const shell = shells.get(item.value);
if (!shell) return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { children: item.label });
const truncatedCommand = formatShellCommandForDisplay(
shell.command,
maxCommandLength
);
return /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Text, { children: [
truncatedCommand,
" (PID: ",
shell.pid,
")",
shell.status === "exited" ? /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
Text,
{
color: shell.exitCode === 0 ? theme.status.success : theme.status.error,
children: [
" ",
"(Exit Code: ",
shell.exitCode,
")"
]
}
) : null
] });
}
}
) })
] });
};
const renderFooter = () => {
const pidToDisplay = isListOpenProp ? highlightedPid ?? activePid : activePid;
if (!pidToDisplay) return null;
const logPath = ShellExecutionService.getLogFilePath(pidToDisplay);
const displayPath = shortenPath(
tildeifyPath(logPath),
width - LOG_PATH_OVERHEAD
);
return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Box_default, { paddingX: 1, children: /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Text, { color: theme.text.secondary, children: [
"Log: ",
displayPath
] }) });
};
const renderOutput = () => {
const lines = typeof output === "string" ? output.split("\n") : output;
return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
ScrollableListWithForwardRef,
{
ref: outputRef,
data: lines,
renderItem: ({ item: line, index }) => {
if (typeof line === "string") {
return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { children: line }, index);
}
return /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Text, { wrap: "truncate", children: line.length > 0 ? line.map((token, tokenIndex) => /* @__PURE__ */ (0, import_jsx_runtime131.jsx)(
Text,
{
color: token.fg,
backgroundColor: token.bg,
inverse: token.inverse,
dimColor: token.dim,
bold: token.bold,
italic: token.italic,
underline: token.underline,
children: token.text
},
tokenIndex
)) : null }, index);
},
estimatedItemHeight: () => 1,
keyExtractor: (_, index) => index.toString(),
hasFocus: isFocused,
initialScrollIndex: SCROLL_TO_ITEM_END
}
);
};
return /* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
Box_default,
{
flexDirection: "column",
height: "100%",
width: "100%",
borderStyle: "single",
borderColor: isFocused ? theme.ui.focus : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(
Box_default,
{
flexDirection: "row",
justifyContent: "space-between",
borderStyle: "single",
borderBottom: false,
borderLeft: false,
borderRight: false,
borderTop: false,
paddingX: 1,
borderColor: isFocused ? theme.ui.focus : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Box_default, { flexDirection: "row", children: [
renderTabs(),
/* @__PURE__ */ (0, import_jsx_runtime131.jsxs)(Text, { bold: true, children: [
" ",
"(PID: ",
activeShell?.pid,
") ",
isFocused ? "(Focused)" : ""
] })
] }),
renderHelpText()
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime131.jsx)(Box_default, { flexGrow: 1, overflow: "hidden", paddingX: CONTENT_PADDING_X, children: isListOpenProp ? renderProcessList() : renderOutput() }),
renderFooter()
]
}
);
};
// packages/cli/src/ui/layouts/DefaultAppLayout.tsx
var import_jsx_runtime132 = __toESM(require_jsx_runtime(), 1);
var DefaultAppLayout = () => {
const uiState = useUIState();
const { copyModeEnabled } = useInputState();
const isAlternateBuffer = useAlternateBuffer();
const { rootUiRef, terminalHeight } = uiState;
useFlickerDetector(rootUiRef, terminalHeight);
return /* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
Box_default,
{
flexDirection: "column",
width: uiState.terminalWidth,
height: isAlternateBuffer ? terminalHeight : void 0,
paddingBottom: isAlternateBuffer ? 1 : void 0,
flexShrink: 0,
flexGrow: 0,
ref: uiState.rootUiRef,
children: [
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(MainContent, {}),
uiState.isBackgroundTaskVisible && uiState.backgroundTasks.size > 0 && uiState.activeBackgroundTaskPid && uiState.backgroundTaskHeight > 0 && uiState.streamingState !== "waiting_for_confirmation" /* WaitingForConfirmation */ && /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(Box_default, { height: uiState.backgroundTaskHeight, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
BackgroundTaskDisplay,
{
shells: uiState.backgroundTasks,
activePid: uiState.activeBackgroundTaskPid,
width: uiState.terminalWidth,
height: uiState.backgroundTaskHeight,
isFocused: uiState.embeddedShellFocused && !uiState.dialogsVisible,
isListOpenProp: uiState.isBackgroundTaskListOpen
}
) }),
/* @__PURE__ */ (0, import_jsx_runtime132.jsxs)(
Box_default,
{
flexDirection: "column",
ref: uiState.mainControlsRef,
flexShrink: 0,
flexGrow: 0,
width: uiState.terminalWidth,
height: copyModeEnabled ? uiState.stableControlsHeight : void 0,
children: [
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(Notifications, {}),
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(CopyModeWarning, {}),
uiState.customDialog ? uiState.customDialog : uiState.dialogsVisible ? /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(
DialogManager,
{
terminalWidth: uiState.terminalWidth,
addItem: uiState.historyManager.addItem
}
) : /* @__PURE__ */ (0, import_jsx_runtime132.jsx)(Composer, { isFocused: true }),
/* @__PURE__ */ (0, import_jsx_runtime132.jsx)(ExitWarning, {})
]
}
)
]
}
);
};
// packages/cli/src/ui/components/AlternateBufferQuittingDisplay.tsx
var import_jsx_runtime133 = __toESM(require_jsx_runtime(), 1);
var AlternateBufferQuittingDisplay = () => {
const { version } = useAppContext();
const uiState = useUIState();
const confirmingTool = useConfirmingTool();
const showPromptedTool = confirmingTool !== null;
return /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(
Box_default,
{
flexDirection: "column",
flexShrink: 0,
flexGrow: 0,
width: uiState.terminalWidth,
children: [
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(AppHeader, { version }, "app-header"),
uiState.history.map((h) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
HistoryItemDisplay,
{
terminalWidth: uiState.mainAreaWidth,
availableTerminalHeight: void 0,
availableTerminalHeightGemini: MAX_GEMINI_MESSAGE_LINES,
item: h,
isPending: false,
commands: uiState.slashCommands
},
h.id
)),
uiState.pendingHistoryItems.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
HistoryItemDisplay,
{
availableTerminalHeight: void 0,
terminalWidth: uiState.mainAreaWidth,
item: { ...item, id: 0 },
isPending: true
},
i
)),
showPromptedTool && /* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(Box_default, { flexDirection: "column", marginTop: 1, marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(Text, { color: theme.status.warning, bold: true, children: "Action Required (was prompted):" }),
/* @__PURE__ */ (0, import_jsx_runtime133.jsxs)(Box_default, { marginTop: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
ToolStatusIndicator,
{
status: confirmingTool.tool.status,
name: confirmingTool.tool.name
}
),
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(
ToolInfo,
{
name: confirmingTool.tool.name,
status: confirmingTool.tool.status,
description: confirmingTool.tool.description,
emphasis: "high"
}
)
] })
] }),
/* @__PURE__ */ (0, import_jsx_runtime133.jsx)(QuittingDisplay, {})
]
}
);
};
// packages/cli/src/ui/App.tsx
var import_jsx_runtime134 = __toESM(require_jsx_runtime(), 1);
var App = () => {
const uiState = useUIState();
const isAlternateBuffer = useAlternateBuffer();
const isScreenReaderEnabled = use_is_screen_reader_enabled_default();
if (uiState.quittingMessages) {
if (isAlternateBuffer) {
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(StreamingContext.Provider, { value: uiState.streamingState, children: /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(AlternateBufferQuittingDisplay, {}) });
} else {
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(QuittingDisplay, {});
}
}
return /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(StreamingContext.Provider, { value: uiState.streamingState, children: isScreenReaderEnabled ? /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(ScreenReaderAppLayout, {}) : /* @__PURE__ */ (0, import_jsx_runtime134.jsx)(DefaultAppLayout, {}) });
};
// packages/cli/src/ui/AppContainer.tsx
import process12 from "node:process";
// packages/cli/src/ui/hooks/useMemoryMonitor.ts
var import_react87 = __toESM(require_react(), 1);
import process6 from "node:process";
var MEMORY_WARNING_THRESHOLD = 7 * 1024 * 1024 * 1024;
var MEMORY_CHECK_INTERVAL = 60 * 1e3;
var useMemoryMonitor = ({ addItem }) => {
(0, import_react87.useEffect)(() => {
const intervalId = setInterval(() => {
const usage = process6.memoryUsage().rss;
if (usage > MEMORY_WARNING_THRESHOLD) {
addItem(
{
type: "warning" /* WARNING */,
text: `High memory usage detected: ${(usage / (1024 * 1024 * 1024)).toFixed(2)} GB. If you experience a crash, please file a bug report by running \`/bug\``
},
Date.now()
);
clearInterval(intervalId);
}
}, MEMORY_CHECK_INTERVAL);
return () => clearInterval(intervalId);
}, [addItem]);
};
// packages/cli/src/ui/hooks/useThemeCommand.ts
var import_react89 = __toESM(require_react(), 1);
import process7 from "node:process";
// packages/cli/src/ui/contexts/TerminalContext.tsx
var import_react88 = __toESM(require_react(), 1);
var import_jsx_runtime135 = __toESM(require_jsx_runtime(), 1);
var TerminalContext = (0, import_react88.createContext)(
void 0
);
function useTerminalContext() {
const context = (0, import_react88.useContext)(TerminalContext);
if (!context) {
throw new Error(
"useTerminalContext must be used within a TerminalProvider"
);
}
return context;
}
function TerminalProvider({ children }) {
const { stdin } = use_stdin_default();
const { stdout } = use_stdout_default();
const subscribers = (0, import_react88.useRef)(/* @__PURE__ */ new Set()).current;
const bufferRef = (0, import_react88.useRef)("");
const subscribe2 = (0, import_react88.useCallback)(
(handler) => {
subscribers.add(handler);
},
[subscribers]
);
const unsubscribe = (0, import_react88.useCallback)(
(handler) => {
subscribers.delete(handler);
},
[subscribers]
);
const queryTerminalBackground = (0, import_react88.useCallback)(
async () => new Promise((resolve3) => {
const handler = () => {
unsubscribe(handler);
resolve3();
};
subscribe2(handler);
TerminalCapabilityManager.queryBackgroundColor(stdout);
setTimeout(() => {
unsubscribe(handler);
resolve3();
}, 100);
}),
[stdout, subscribe2, unsubscribe]
);
(0, import_react88.useEffect)(() => {
const handleData = (data) => {
bufferRef.current += typeof data === "string" ? data : data.toString("utf-8");
const match = bufferRef.current.match(
TerminalCapabilityManager.OSC_11_REGEX
);
if (match) {
const colorStr = `rgb:${match[1]}/${match[2]}/${match[3]}`;
for (const handler of subscribers) {
handler(colorStr);
}
if (match.index !== void 0) {
bufferRef.current = bufferRef.current.slice(
match.index + match[0].length
);
}
} else if (bufferRef.current.length > 4096) {
bufferRef.current = bufferRef.current.slice(-1024);
}
};
stdin.on("data", handleData);
return () => {
stdin.removeListener("data", handleData);
};
}, [stdin, subscribers]);
return /* @__PURE__ */ (0, import_jsx_runtime135.jsx)(
TerminalContext.Provider,
{
value: { subscribe: subscribe2, unsubscribe, queryTerminalBackground },
children
}
);
}
// packages/cli/src/ui/hooks/useThemeCommand.ts
var useThemeCommand = (loadedSettings, setThemeError, addItem, initialThemeError, refreshStatic) => {
const [isThemeDialogOpen, setIsThemeDialogOpen] = (0, import_react89.useState)(!!initialThemeError);
const { queryTerminalBackground } = useTerminalContext();
const openThemeDialog = (0, import_react89.useCallback)(async () => {
if (process7.env["NO_COLOR"]) {
addItem(
{
type: "info" /* INFO */,
text: "Theme configuration unavailable due to NO_COLOR env variable."
},
Date.now()
);
return;
}
await queryTerminalBackground();
setIsThemeDialogOpen(true);
}, [addItem, queryTerminalBackground]);
const applyTheme = (0, import_react89.useCallback)(
(themeName) => {
if (!themeManager.setActiveTheme(themeName)) {
setIsThemeDialogOpen(true);
setThemeError(`Theme "${themeName}" not found.`);
} else {
setThemeError(null);
}
},
[setThemeError]
);
const handleThemeHighlight = (0, import_react89.useCallback)(
(themeName) => {
applyTheme(themeName);
},
[applyTheme]
);
const closeThemeDialog = (0, import_react89.useCallback)(() => {
applyTheme(loadedSettings.merged.ui.theme);
setIsThemeDialogOpen(false);
}, [applyTheme, loadedSettings]);
const handleThemeSelect = (0, import_react89.useCallback)(
async (themeName, scope) => {
try {
const mergedCustomThemes = {
...loadedSettings.user.settings.ui?.customThemes || {},
...loadedSettings.workspace.settings.ui?.customThemes || {}
};
const isBuiltIn = themeManager.findThemeByName(themeName);
const isCustom = themeName && mergedCustomThemes[themeName];
if (!isBuiltIn && !isCustom) {
setThemeError(`Theme "${themeName}" not found in selected scope.`);
setIsThemeDialogOpen(true);
return;
}
loadedSettings.setValue(scope, "ui.theme", themeName);
if (loadedSettings.merged.ui.customThemes) {
themeManager.loadCustomThemes(loadedSettings.merged.ui.customThemes);
}
applyTheme(loadedSettings.merged.ui.theme);
refreshStatic();
setThemeError(null);
} finally {
setIsThemeDialogOpen(false);
}
},
[applyTheme, loadedSettings, refreshStatic, setThemeError]
);
return {
isThemeDialogOpen,
openThemeDialog,
closeThemeDialog,
handleThemeSelect,
handleThemeHighlight
};
};
// packages/cli/src/ui/hooks/useQuotaAndFallback.ts
var import_react90 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/creditsFlowHandler.ts
async function handleCreditsFlow(args) {
const creditBalance = getG1CreditBalance(args.paidTier);
if (creditBalance == null) {
return null;
}
const { overageStrategy } = args;
if (shouldAutoUseCredits(overageStrategy, creditBalance)) {
return null;
}
if (shouldShowOverageMenu(overageStrategy, creditBalance)) {
return handleOverageMenu(args, creditBalance);
}
if (shouldShowEmptyWalletMenu(overageStrategy, creditBalance)) {
return handleEmptyWalletMenu(args);
}
return null;
}
async function handleOverageMenu(args, creditBalance) {
const {
config,
fallbackModel,
usageLimitReachedModel,
overageStrategy,
resetTime,
isDialogPending,
setOverageMenuRequest,
setModelSwitchedFromQuotaError
} = args;
logBillingEvent(
config,
new OverageMenuShownEvent(
usageLimitReachedModel,
creditBalance,
overageStrategy
)
);
if (isDialogPending.current) {
return "stop";
}
isDialogPending.current = true;
setModelSwitchedFromQuotaError(true);
config.setQuotaErrorOccurred(true);
const overageIntent = await new Promise((resolve3) => {
setOverageMenuRequest({
failedModel: usageLimitReachedModel,
fallbackModel,
resetTime,
creditBalance,
resolve: resolve3
});
});
setOverageMenuRequest(null);
isDialogPending.current = false;
logOverageOptionSelected(
config,
usageLimitReachedModel,
overageIntent,
creditBalance
);
switch (overageIntent) {
case "use_credits":
setModelSwitchedFromQuotaError(false);
config.setQuotaErrorOccurred(false);
config.setOverageStrategy("always");
return "retry_with_credits";
case "use_fallback":
return "retry_always";
case "manage": {
logCreditPurchaseClick(config, "manage", usageLimitReachedModel);
const manageUrl = await openG1Url(
"activity",
G1_UTM_CAMPAIGNS.MANAGE_ACTIVITY
);
if (manageUrl) {
args.historyManager.addItem(
{
type: "info" /* INFO */,
text: `Please open this URL in a browser: ${manageUrl}`
},
Date.now()
);
}
return "stop";
}
case "stop":
default:
return "stop";
}
}
async function handleEmptyWalletMenu(args) {
const {
config,
fallbackModel,
usageLimitReachedModel,
resetTime,
isDialogPending,
setEmptyWalletRequest,
setModelSwitchedFromQuotaError
} = args;
logBillingEvent(
config,
new EmptyWalletMenuShownEvent(usageLimitReachedModel)
);
if (isDialogPending.current) {
return "stop";
}
isDialogPending.current = true;
setModelSwitchedFromQuotaError(true);
config.setQuotaErrorOccurred(true);
const emptyWalletIntent = await new Promise((resolve3) => {
setEmptyWalletRequest({
failedModel: usageLimitReachedModel,
fallbackModel,
resetTime,
onGetCredits: async () => {
logCreditPurchaseClick(
config,
"empty_wallet_menu",
usageLimitReachedModel
);
const creditsUrl = await openG1Url(
"credits",
G1_UTM_CAMPAIGNS.EMPTY_WALLET_ADD_CREDITS
);
if (creditsUrl) {
args.historyManager.addItem(
{
type: "info" /* INFO */,
text: `Please open this URL in a browser: ${creditsUrl}`
},
Date.now()
);
}
},
resolve: resolve3
});
});
setEmptyWalletRequest(null);
isDialogPending.current = false;
switch (emptyWalletIntent) {
case "get_credits":
args.historyManager.addItem(
{
type: "info" /* INFO */,
text: "Newly purchased AI credits may take a few minutes to update. Run /stats to check your balance."
},
Date.now()
);
return "stop";
case "use_fallback":
return "retry_always";
case "stop":
default:
return "stop";
}
}
function logOverageOptionSelected(config, model, option, creditBalance) {
logBillingEvent(
config,
new OverageOptionSelectedEvent(model, option, creditBalance)
);
recordOverageOptionSelected(config, {
selected_option: option,
model
});
}
function logCreditPurchaseClick(config, source, model) {
logBillingEvent(config, new CreditPurchaseClickEvent(source, model));
recordCreditPurchaseClick(config, { source, model });
}
async function openG1Url(path14, campaign) {
try {
const userEmail = new UserAccountManager().getCachedGoogleAccount() ?? "";
const url2 = buildG1Url(path14, userEmail, campaign);
if (!shouldLaunchBrowser()) {
return url2;
}
await openBrowserSecurely(url2);
} catch {
}
return void 0;
}
// packages/cli/src/ui/hooks/useQuotaAndFallback.ts
function useQuotaAndFallback({
config,
historyManager,
userTier,
paidTier,
settings,
setModelSwitchedFromQuotaError,
onShowAuthSelection,
errorVerbosity = "full"
}) {
const [proQuotaRequest, setProQuotaRequest] = (0, import_react90.useState)(null);
const [validationRequest, setValidationRequest] = (0, import_react90.useState)(null);
const [overageMenuRequest, setOverageMenuRequest] = (0, import_react90.useState)(null);
const [emptyWalletRequest, setEmptyWalletRequest] = (0, import_react90.useState)(null);
const isDialogPending = (0, import_react90.useRef)(false);
const isValidationPending = (0, import_react90.useRef)(false);
(0, import_react90.useEffect)(() => {
const fallbackHandler = async (failedModel, fallbackModel, error) => {
const contentGeneratorConfig = config.getContentGeneratorConfig();
let message;
let isTerminalQuotaError = false;
let isModelNotFoundError = false;
const usageLimitReachedModel = isProModel(failedModel) ? "all Pro models" : failedModel;
if (error instanceof TerminalQuotaError) {
isTerminalQuotaError = true;
const isInsufficientCredits = error.isInsufficientCredits;
if (!isInsufficientCredits && paidTier?.availableCredits && isOverageEligibleModel(failedModel)) {
const resetTime = error.retryDelayMs ? getResetTimeMessage(error.retryDelayMs) : void 0;
const overageStrategy = config.getBillingSettings().overageStrategy;
const creditsResult = await handleCreditsFlow({
config,
paidTier,
overageStrategy,
failedModel,
fallbackModel,
usageLimitReachedModel,
resetTime,
historyManager,
setModelSwitchedFromQuotaError,
isDialogPending,
setOverageMenuRequest,
setEmptyWalletRequest
});
if (creditsResult) return creditsResult;
}
const messageLines = [
`Usage limit reached for ${usageLimitReachedModel}.`,
error.retryDelayMs ? `Access resets at ${getResetTimeMessage(error.retryDelayMs)}.` : null,
`/stats model for usage details`,
`/model to switch models.`,
contentGeneratorConfig?.authType === AuthType.LOGIN_WITH_GOOGLE ? `/auth to switch to API key.` : null
].filter(Boolean);
message = messageLines.join("\n");
} else if (error instanceof ModelNotFoundError) {
isModelNotFoundError = true;
if (VALID_GEMINI_MODELS.has(failedModel)) {
const messageLines = [
`It seems like you don't have access to ${getDisplayString(failedModel)}.`,
`Your admin might have disabled the access. Contact them to enable the Preview Release Channel.`
];
message = messageLines.join("\n");
} else {
const messageLines = [
`Model "${failedModel}" was not found or is invalid.`,
`/model to switch models.`
];
message = messageLines.join("\n");
}
} else {
const messageLines = [
`We are currently experiencing high demand.`,
"We apologize and appreciate your patience.",
"/model to switch models."
];
message = messageLines.join("\n");
}
if (errorVerbosity === "low" && !isTerminalQuotaError && !isModelNotFoundError) {
return "retry_once";
}
setModelSwitchedFromQuotaError(true);
config.setQuotaErrorOccurred(true);
if (isDialogPending.current) {
return "stop";
}
isDialogPending.current = true;
const intent = await new Promise(
(resolve3) => {
setProQuotaRequest({
failedModel,
fallbackModel,
resolve: resolve3,
message,
isTerminalQuotaError,
isModelNotFoundError,
authType: contentGeneratorConfig?.authType
});
}
);
return intent;
};
config.setFallbackModelHandler(fallbackHandler);
}, [
config,
historyManager,
userTier,
paidTier,
settings,
setModelSwitchedFromQuotaError,
onShowAuthSelection,
errorVerbosity
]);
(0, import_react90.useEffect)(() => {
const validationHandler = async (validationLink, validationDescription, learnMoreUrl) => {
if (isValidationPending.current) {
return "cancel";
}
isValidationPending.current = true;
const intent = await new Promise(
(resolve3) => {
setValidationRequest({
validationLink,
validationDescription,
learnMoreUrl,
resolve: resolve3
});
}
);
return intent;
};
config.setValidationHandler(validationHandler);
}, [config]);
const handleProQuotaChoice = (0, import_react90.useCallback)(
(choice) => {
if (!proQuotaRequest) return;
const intent = choice;
proQuotaRequest.resolve(intent);
setProQuotaRequest(null);
isDialogPending.current = false;
if (choice === "retry_always" || choice === "retry_once") {
setModelSwitchedFromQuotaError(false);
config.setQuotaErrorOccurred(false);
if (choice === "retry_always") {
historyManager.addItem(
{
type: "info" /* INFO */,
text: `Switched to fallback model ${proQuotaRequest.fallbackModel}`
},
Date.now()
);
}
}
},
[proQuotaRequest, historyManager, config, setModelSwitchedFromQuotaError]
);
const handleValidationChoice = (0, import_react90.useCallback)(
(choice) => {
if (!isValidationPending.current || !validationRequest) return;
isValidationPending.current = false;
validationRequest.resolve(choice);
setValidationRequest(null);
if (choice === "change_auth" || choice === "cancel") {
onShowAuthSelection();
}
},
[validationRequest, onShowAuthSelection]
);
const handleOverageMenuChoice = (0, import_react90.useCallback)(
(choice) => {
if (!overageMenuRequest) return;
overageMenuRequest.resolve(choice);
},
[overageMenuRequest]
);
const handleEmptyWalletChoice = (0, import_react90.useCallback)(
(choice) => {
if (!emptyWalletRequest) return;
emptyWalletRequest.resolve(choice);
},
[emptyWalletRequest]
);
return {
proQuotaRequest,
handleProQuotaChoice,
validationRequest,
handleValidationChoice,
// G1 AI Credits
overageMenuRequest,
handleOverageMenuChoice,
emptyWalletRequest,
handleEmptyWalletChoice
};
}
function getResetTimeMessage(delayMs) {
const resetDate = new Date(Date.now() + delayMs);
const timeFormatter = new Intl.DateTimeFormat("en-US", {
hour: "numeric",
minute: "2-digit",
timeZoneName: "short"
});
return timeFormatter.format(resetDate);
}
// packages/cli/src/ui/hooks/useEditorSettings.ts
var import_react91 = __toESM(require_react(), 1);
// packages/cli/src/config/settingPaths.ts
var SettingPaths = {
General: {
PreferredEditor: "general.preferredEditor"
}
};
// packages/cli/src/ui/hooks/useEditorSettings.ts
var useEditorSettings = (loadedSettings, setEditorError, addItem) => {
const [isEditorDialogOpen, setIsEditorDialogOpen] = (0, import_react91.useState)(false);
const openEditorDialog = (0, import_react91.useCallback)(() => {
setIsEditorDialogOpen(true);
}, []);
const handleEditorSelect = (0, import_react91.useCallback)(
(editorType, scope) => {
if (editorType && (!hasValidEditorCommand(editorType) || !allowEditorTypeInSandbox(editorType))) {
return;
}
try {
loadedSettings.setValue(
scope,
SettingPaths.General.PreferredEditor,
editorType
);
addItem(
{
type: "info" /* INFO */,
text: `Editor preference ${editorType ? `set to "${getEditorDisplayName(editorType)}"` : "cleared"} in ${scope} settings.`
},
Date.now()
);
setEditorError(null);
setIsEditorDialogOpen(false);
coreEvents.emit(CoreEvent.EditorSelected, { editor: editorType });
} catch (error) {
setEditorError(`Failed to set editor preference: ${error}`);
}
},
[loadedSettings, setEditorError, addItem]
);
const exitEditorDialog = (0, import_react91.useCallback)(() => {
setIsEditorDialogOpen(false);
coreEvents.emit(CoreEvent.EditorSelected, { editor: void 0 });
}, []);
return {
isEditorDialogOpen,
openEditorDialog,
handleEditorSelect,
exitEditorDialog
};
};
// packages/cli/src/ui/hooks/useSettingsCommand.ts
var import_react92 = __toESM(require_react(), 1);
function useSettingsCommand() {
const [isSettingsDialogOpen, setIsSettingsDialogOpen] = (0, import_react92.useState)(false);
const openSettingsDialog = (0, import_react92.useCallback)(() => {
setIsSettingsDialogOpen(true);
}, []);
const closeSettingsDialog = (0, import_react92.useCallback)(() => {
setIsSettingsDialogOpen(false);
}, []);
return {
isSettingsDialogOpen,
openSettingsDialog,
closeSettingsDialog
};
}
// packages/cli/src/ui/hooks/useModelCommand.ts
var import_react93 = __toESM(require_react(), 1);
var useModelCommand = () => {
const [isModelDialogOpen, setIsModelDialogOpen] = (0, import_react93.useState)(false);
const openModelDialog = (0, import_react93.useCallback)(() => {
setIsModelDialogOpen(true);
}, []);
const closeModelDialog = (0, import_react93.useCallback)(() => {
setIsModelDialogOpen(false);
}, []);
return {
isModelDialogOpen,
openModelDialog,
closeModelDialog
};
};
// packages/cli/src/ui/hooks/useVoiceModelCommand.ts
var import_react94 = __toESM(require_react(), 1);
var useVoiceModelCommand = () => {
const [isVoiceModelDialogOpen, setIsVoiceModelDialogOpen] = (0, import_react94.useState)(false);
const openVoiceModelDialog = (0, import_react94.useCallback)(() => {
setIsVoiceModelDialogOpen(true);
}, []);
const closeVoiceModelDialog = (0, import_react94.useCallback)(() => {
setIsVoiceModelDialogOpen(false);
}, []);
return {
isVoiceModelDialogOpen,
openVoiceModelDialog,
closeVoiceModelDialog
};
};
// packages/cli/src/ui/hooks/slashCommandProcessor.ts
var import_react95 = __toESM(require_react(), 1);
import process8 from "node:process";
// packages/cli/src/services/SkillCommandLoader.ts
var SkillCommandLoader = class {
constructor(config) {
this.config = config;
}
/**
* Discovers all available skills from the SkillManager and converts
* them into executable slash commands.
*
* @param _signal An AbortSignal (unused for this synchronous loader).
* @returns A promise that resolves to an array of `SlashCommand` objects.
*/
async loadCommands(_signal) {
if (!this.config || !this.config.isSkillsSupportEnabled()) {
return [];
}
const skillManager = this.config.getSkillManager();
if (!skillManager || !skillManager.isAdminEnabled()) {
return [];
}
const skills = skillManager.getDisplayableSkills();
return skills.map((skill) => {
const commandName = skill.name.trim().replace(/\s+/g, "-");
return {
name: commandName,
description: skill.description || `Activate the ${skill.name} skill`,
kind: "skill" /* SKILL */,
autoExecute: true,
extensionName: skill.extensionName,
action: async (_context, args) => ({
type: "tool",
toolName: ACTIVATE_SKILL_TOOL_NAME,
toolArgs: { name: skill.name },
postSubmitPrompt: args.trim().length > 0 ? args.trim() : `Use the skill ${skill.name}`
})
};
});
}
};
// packages/cli/src/ui/components/LogoutConfirmationDialog.tsx
var import_jsx_runtime136 = __toESM(require_jsx_runtime(), 1);
var LogoutConfirmationDialog = ({ onSelect }) => {
useKeypress(
(key) => {
if (key.name === "escape") {
onSelect("exit" /* EXIT */);
return true;
}
return false;
},
{ isActive: true }
);
const options = [
{
label: "Sign in",
value: "login" /* LOGIN */,
key: "login"
},
{
label: "Exit",
value: "exit" /* EXIT */,
key: "exit"
}
];
return /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(Box_default, { flexDirection: "row", width: "100%", children: /* @__PURE__ */ (0, import_jsx_runtime136.jsxs)(
Box_default,
{
flexDirection: "column",
borderStyle: "round",
borderColor: theme.ui.focus,
padding: 1,
flexGrow: 1,
marginLeft: 1,
marginRight: 1,
children: [
/* @__PURE__ */ (0, import_jsx_runtime136.jsxs)(Box_default, { flexDirection: "column", marginBottom: 1, children: [
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(Text, { bold: true, color: theme.text.primary, children: "You are now signed out" }),
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(Text, { color: theme.text.secondary, children: "Sign in again to continue using Gemini CLI, or exit the application." })
] }),
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(RadioButtonSelect, { items: options, onSelect, isFocused: true }),
/* @__PURE__ */ (0, import_jsx_runtime136.jsx)(Box_default, { marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime136.jsx)(Text, { color: theme.text.secondary, children: "(Use Enter to select, Esc to close)" }) })
]
}
) });
};
// packages/cli/src/ui/hooks/slashCommandProcessor.ts
var useSlashCommandProcessor = (config, settings, addItem, clearItems, loadHistory, refreshStatic, toggleVimEnabled, setIsProcessing, actions, extensionsUpdateState, isConfigInitialized, setBannerVisible, setCustomDialog) => {
const session = useSessionStats();
const [commands, setCommands] = (0, import_react95.useState)(
void 0
);
const [reloadTrigger, setReloadTrigger] = (0, import_react95.useState)(0);
const reloadCommands = (0, import_react95.useCallback)(() => {
setReloadTrigger((v) => v + 1);
}, []);
const [confirmationRequest, setConfirmationRequest] = (0, import_react95.useState)(null);
const [sessionShellAllowlist, setSessionShellAllowlist] = (0, import_react95.useState)(
/* @__PURE__ */ new Set()
);
const gitService = (0, import_react95.useMemo)(() => {
if (!config?.getProjectRoot()) {
return;
}
return new GitService(config.getProjectRoot(), config.storage);
}, [config]);
const logger = (0, import_react95.useMemo)(() => {
const l = new Logger(
config?.getSessionId() || "",
config?.storage ?? new Storage(process8.cwd())
);
return l;
}, [config]);
const [pendingItem, setPendingItem] = (0, import_react95.useState)(
null
);
const pendingHistoryItems = (0, import_react95.useMemo)(() => {
const items = [];
if (pendingItem != null) {
items.push(pendingItem);
}
return items;
}, [pendingItem]);
const addMessage = (0, import_react95.useCallback)(
(message) => {
let historyItemContent;
if (message.type === "about" /* ABOUT */) {
historyItemContent = {
type: "about",
cliVersion: message.cliVersion,
osVersion: message.osVersion,
sandboxEnv: message.sandboxEnv,
modelVersion: message.modelVersion,
selectedAuthType: message.selectedAuthType,
gcpProject: message.gcpProject,
ideClient: message.ideClient
};
} else if (message.type === "help" /* HELP */) {
historyItemContent = {
type: "help",
timestamp: message.timestamp
};
} else if (message.type === "stats" /* STATS */) {
historyItemContent = {
type: "stats",
duration: message.duration
};
} else if (message.type === "model_stats" /* MODEL_STATS */) {
historyItemContent = {
type: "model_stats"
};
} else if (message.type === "tool_stats" /* TOOL_STATS */) {
historyItemContent = {
type: "tool_stats"
};
} else if (message.type === "quit" /* QUIT */) {
historyItemContent = {
type: "quit",
duration: message.duration
};
} else if (message.type === "compression" /* COMPRESSION */) {
historyItemContent = {
type: "compression",
compression: message.compression
};
} else {
historyItemContent = {
type: message.type,
text: message.content
};
}
addItem(historyItemContent, message.timestamp.getTime());
},
[addItem]
);
const commandContext = (0, import_react95.useMemo)(
() => ({
services: {
agentContext: config,
settings,
git: gitService,
logger
},
ui: {
addItem,
clear: () => {
clearItems();
refreshStatic();
setBannerVisible(false);
},
loadHistory: (history, postLoadInput) => {
loadHistory(history);
refreshStatic();
if (postLoadInput !== void 0) {
actions.setText(postLoadInput);
}
},
setDebugMessage: actions.setDebugMessage,
pendingItem,
setPendingItem,
toggleCorgiMode: actions.toggleCorgiMode,
toggleVoiceMode: actions.toggleVoiceMode,
toggleDebugProfiler: actions.toggleDebugProfiler,
toggleVimEnabled,
reloadCommands,
openAgentConfigDialog: actions.openAgentConfigDialog,
extensionsUpdateState,
dispatchExtensionStateUpdate: actions.dispatchExtensionStateUpdate,
addConfirmUpdateExtensionRequest: actions.addConfirmUpdateExtensionRequest,
setConfirmationRequest,
removeComponent: () => setCustomDialog(null),
toggleBackgroundTasks: actions.toggleBackgroundTasks,
toggleShortcutsHelp: actions.toggleShortcutsHelp
},
session: {
stats: session.stats,
sessionShellAllowlist
}
}),
[
config,
settings,
gitService,
logger,
loadHistory,
addItem,
clearItems,
refreshStatic,
session.stats,
actions,
pendingItem,
setPendingItem,
setConfirmationRequest,
toggleVimEnabled,
sessionShellAllowlist,
reloadCommands,
extensionsUpdateState,
setBannerVisible,
setCustomDialog
]
);
(0, import_react95.useEffect)(() => {
if (!config) {
return;
}
const listener = () => {
reloadCommands();
};
let isActive = true;
let activeIdeClient;
(async () => {
const ideClient = await IdeClient.getInstance();
if (!isActive) {
return;
}
activeIdeClient = ideClient;
ideClient.addStatusChangeListener(listener);
})();
addMCPStatusChangeListener(listener);
const extensionEventListener = (_event) => {
reloadCommands();
};
coreEvents.on("extensionsStarting", extensionEventListener);
coreEvents.on("extensionsStopping", extensionEventListener);
return () => {
isActive = false;
activeIdeClient?.removeStatusChangeListener(listener);
removeMCPStatusChangeListener(listener);
coreEvents.off("extensionsStarting", extensionEventListener);
coreEvents.off("extensionsStopping", extensionEventListener);
};
}, [config, reloadCommands]);
(0, import_react95.useEffect)(() => {
const controller = new AbortController();
(async () => {
const commandService = await CommandService.create(
[
new BuiltinCommandLoader(config),
new SkillCommandLoader(config),
new McpPromptLoader(config),
new FileCommandLoader(config)
],
controller.signal
);
if (controller.signal.aborted) {
return;
}
setCommands(commandService.getCommands());
})();
return () => {
controller.abort();
};
}, [config, reloadTrigger, isConfigInitialized]);
const handleSlashCommand = (0, import_react95.useCallback)(
async (rawQuery, oneTimeShellAllowlist, overwriteConfirmed, addToHistory = true) => {
if (!commands) {
return false;
}
if (typeof rawQuery !== "string") {
return false;
}
const trimmed = rawQuery.trim();
if (!trimmed.startsWith("/") && !trimmed.startsWith("?")) {
return false;
}
const {
commandToExecute,
args,
canonicalPath: resolvedCommandPath
} = parseSlashCommand(trimmed, commands);
if (!commandToExecute) {
const isMcpLoading = config?.getMcpClientManager()?.getDiscoveryState() === MCPDiscoveryState.IN_PROGRESS;
if (isMcpLoading) {
setIsProcessing(true);
if (addToHistory) {
addItem({ type: "user" /* USER */, text: trimmed }, Date.now());
}
addMessage({
type: "error" /* ERROR */,
content: `Unknown command: ${trimmed}. Command might have been from an MCP server but MCP servers are not done loading.`,
timestamp: /* @__PURE__ */ new Date()
});
setIsProcessing(false);
return { type: "handled" };
}
return false;
}
setIsProcessing(true);
if (addToHistory) {
const userMessageTimestamp = Date.now();
addItem(
{ type: "user" /* USER */, text: trimmed },
userMessageTimestamp
);
}
let hasError = false;
const subcommand = resolvedCommandPath.length > 1 ? resolvedCommandPath.slice(1).join(" ") : void 0;
try {
if (commandToExecute) {
if (commandToExecute.action) {
const fullCommandContext = {
...commandContext,
invocation: {
raw: trimmed,
name: commandToExecute.name,
args
},
overwriteConfirmed
};
if (oneTimeShellAllowlist && oneTimeShellAllowlist.size > 0) {
fullCommandContext.session = {
...fullCommandContext.session,
sessionShellAllowlist: /* @__PURE__ */ new Set([
...fullCommandContext.session.sessionShellAllowlist,
...oneTimeShellAllowlist
])
};
}
const result = await commandToExecute.action(
fullCommandContext,
args
);
if (result) {
switch (result.type) {
case "tool":
return {
type: "schedule_tool",
toolName: result.toolName,
toolArgs: result.toolArgs,
postSubmitPrompt: result.postSubmitPrompt
};
case "message":
addItem(
{
type: result.messageType === "error" ? "error" /* ERROR */ : "info" /* INFO */,
text: result.content
},
Date.now()
);
return { type: "handled" };
case "logout":
setCustomDialog(
(0, import_react95.createElement)(LogoutConfirmationDialog, {
onSelect: async (choice) => {
setCustomDialog(null);
if (choice === "login" /* LOGIN */) {
actions.openAuthDialog();
} else {
await runExitCleanup();
process8.exit(0);
}
}
})
);
return { type: "handled" };
case "dialog":
switch (result.dialog) {
case "auth":
actions.openAuthDialog();
return { type: "handled" };
case "theme":
actions.openThemeDialog();
return { type: "handled" };
case "editor":
actions.openEditorDialog();
return { type: "handled" };
case "privacy":
actions.openPrivacyNotice();
return { type: "handled" };
case "sessionBrowser":
actions.openSessionBrowser();
return { type: "handled" };
case "settings":
actions.openSettingsDialog();
return { type: "handled" };
case "model":
actions.openModelDialog();
return { type: "handled" };
case "voice-model":
actions.openVoiceModelDialog();
return { type: "handled" };
case "agentConfig": {
const props = result.props;
if (!props || // eslint-disable-next-line no-restricted-syntax
typeof props["name"] !== "string" || // eslint-disable-next-line no-restricted-syntax
typeof props["displayName"] !== "string" || !props["definition"]) {
throw new Error(
"Received invalid properties for agentConfig dialog action."
);
}
actions.openAgentConfigDialog(
props["name"],
props["displayName"],
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
props["definition"]
);
return { type: "handled" };
}
case "permissions":
actions.openPermissionsDialog(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
result.props
);
return { type: "handled" };
case "help":
return { type: "handled" };
default: {
const unhandled = result.dialog;
throw new Error(
`Unhandled slash command result: ${unhandled}`
);
}
}
case "load_history": {
config?.getGeminiClient()?.setHistory(result.clientHistory);
fullCommandContext.ui.clear();
result.history.forEach((item, index) => {
fullCommandContext.ui.addItem(item, index);
});
return { type: "handled" };
}
case "quit":
if (result.deleteSession) {
try {
const chatRecordingService = config?.getGeminiClient()?.getChatRecordingService();
if (chatRecordingService) {
await chatRecordingService.deleteCurrentSessionAsync();
}
} catch {
}
}
actions.quit(result.messages);
return { type: "handled" };
case "submit_prompt":
return {
type: "submit_prompt",
content: result.content
};
case "confirm_shell_commands": {
const callId = `expansion-${Date.now()}`;
const { outcome, approvedCommands } = await new Promise((resolve3) => {
const confirmationDetails = {
type: "exec",
title: `Confirm Shell Expansion`,
command: result.commandsToConfirm[0] || "",
rootCommand: result.commandsToConfirm[0] || "",
rootCommands: result.commandsToConfirm,
commands: result.commandsToConfirm,
onConfirm: async (resolvedOutcome) => {
resolve3({
outcome: resolvedOutcome,
approvedCommands: resolvedOutcome === ToolConfirmationOutcome.Cancel ? [] : result.commandsToConfirm
});
}
};
const toolDisplay = {
callId,
name: "Expansion",
description: "Command expansion needs shell access",
status: CoreToolCallStatus.AwaitingApproval,
isClientInitiated: true,
resultDisplay: void 0,
confirmationDetails
};
setPendingItem({
type: "tool_group",
tools: [toolDisplay]
});
});
setPendingItem(null);
if (outcome === ToolConfirmationOutcome.Cancel || !approvedCommands || approvedCommands.length === 0) {
addItem(
{
type: "info" /* INFO */,
text: "Slash command shell execution declined."
},
Date.now()
);
return { type: "handled" };
}
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
setSessionShellAllowlist(
(prev) => /* @__PURE__ */ new Set([...prev, ...approvedCommands])
);
}
return await handleSlashCommand(
result.originalInvocation.raw,
// Pass the approved commands as a one-time grant for this execution.
new Set(approvedCommands),
void 0,
false
// Do not add to history again
);
}
case "confirm_action": {
const { confirmed } = await new Promise((resolve3) => {
setConfirmationRequest({
prompt: result.prompt,
onConfirm: (resolvedConfirmed) => {
setConfirmationRequest(null);
resolve3({ confirmed: resolvedConfirmed });
}
});
});
if (!confirmed) {
addItem(
{
type: "info" /* INFO */,
text: "Operation cancelled."
},
Date.now()
);
return { type: "handled" };
}
return await handleSlashCommand(
result.originalInvocation.raw,
void 0,
true
);
}
case "custom_dialog": {
setCustomDialog(result.component);
return { type: "handled" };
}
default: {
const unhandled = result;
throw new Error(
`Unhandled slash command result: ${unhandled}`
);
}
}
}
return { type: "handled" };
} else if (commandToExecute.subCommands) {
const helpText = `Command '/${commandToExecute.name}' requires a subcommand. Available:
${commandToExecute.subCommands.map((sc) => ` - ${sc.name}: ${sc.description || ""}`).join("\n")}`;
addMessage({
type: "info" /* INFO */,
content: helpText,
timestamp: /* @__PURE__ */ new Date()
});
return { type: "handled" };
}
}
return { type: "handled" };
} catch (e) {
hasError = true;
if (config) {
const event = makeSlashCommandEvent({
command: resolvedCommandPath[0],
subcommand,
status: SlashCommandStatus.ERROR,
extension_id: commandToExecute?.extensionId
});
logSlashCommand(config, event);
}
addItem(
{
type: "error" /* ERROR */,
text: e instanceof Error ? e.message : String(e)
},
Date.now()
);
return { type: "handled" };
} finally {
if (config && resolvedCommandPath[0] && !hasError) {
const event = makeSlashCommandEvent({
command: resolvedCommandPath[0],
subcommand,
status: SlashCommandStatus.SUCCESS,
extension_id: commandToExecute?.extensionId
});
logSlashCommand(config, event);
}
setIsProcessing(false);
}
},
[
config,
addItem,
actions,
commands,
commandContext,
addMessage,
setSessionShellAllowlist,
setIsProcessing,
setConfirmationRequest,
setCustomDialog
]
);
return {
handleSlashCommand,
slashCommands: commands,
pendingHistoryItems,
commandContext,
confirmationRequest
};
};
// packages/cli/src/ui/utils/ui-sizing.ts
var calculateMainAreaWidth = (terminalWidth, config) => {
if (isAlternateBufferEnabled(config)) {
return terminalWidth - 1;
}
return terminalWidth;
};
// packages/cli/node_modules/ansi-escapes/base.js
var base_exports = {};
__export(base_exports, {
ConEmu: () => ConEmu,
beep: () => beep,
beginSynchronizedOutput: () => beginSynchronizedOutput,
clearScreen: () => clearScreen,
clearTerminal: () => clearTerminal,
clearViewport: () => clearViewport,
cursorBackward: () => cursorBackward,
cursorDown: () => cursorDown,
cursorForward: () => cursorForward,
cursorGetPosition: () => cursorGetPosition,
cursorHide: () => cursorHide,
cursorLeft: () => cursorLeft,
cursorMove: () => cursorMove,
cursorNextLine: () => cursorNextLine,
cursorPrevLine: () => cursorPrevLine,
cursorRestorePosition: () => cursorRestorePosition,
cursorSavePosition: () => cursorSavePosition,
cursorShow: () => cursorShow,
cursorTo: () => cursorTo,
cursorUp: () => cursorUp,
endSynchronizedOutput: () => endSynchronizedOutput,
enterAlternativeScreen: () => enterAlternativeScreen,
eraseDown: () => eraseDown,
eraseEndLine: () => eraseEndLine,
eraseLine: () => eraseLine,
eraseLines: () => eraseLines,
eraseScreen: () => eraseScreen,
eraseStartLine: () => eraseStartLine,
eraseUp: () => eraseUp,
exitAlternativeScreen: () => exitAlternativeScreen,
iTerm: () => iTerm,
image: () => image,
link: () => link,
scrollDown: () => scrollDown,
scrollUp: () => scrollUp,
setCwd: () => setCwd,
synchronizedOutput: () => synchronizedOutput
});
import process9 from "node:process";
import os3 from "node:os";
var ESC = "\x1B[";
var OSC = "\x1B]";
var BEL = "\x07";
var SEP = ";";
var isTerminalApp = !isBrowser && process9.env.TERM_PROGRAM === "Apple_Terminal";
var isWindows2 = !isBrowser && process9.platform === "win32";
var isTmux = !isBrowser && (process9.env.TERM?.startsWith("screen") || process9.env.TERM?.startsWith("tmux") || process9.env.TMUX !== void 0);
var cwdFunction = isBrowser ? () => {
throw new Error("`process.cwd()` only works in Node.js, not the browser.");
} : process9.cwd;
var wrapOsc = (sequence) => {
if (isTmux) {
return "\x1BPtmux;" + sequence.replaceAll("\x1B", "\x1B\x1B") + "\x1B\\";
}
return sequence;
};
var cursorTo = (x, y) => {
if (typeof x !== "number") {
throw new TypeError("The `x` argument is required");
}
if (typeof y !== "number") {
return ESC + (x + 1) + "G";
}
return ESC + (y + 1) + SEP + (x + 1) + "H";
};
var cursorMove = (x, y) => {
if (typeof x !== "number") {
throw new TypeError("The `x` argument is required");
}
let returnValue = "";
if (x < 0) {
returnValue += ESC + -x + "D";
} else if (x > 0) {
returnValue += ESC + x + "C";
}
if (y < 0) {
returnValue += ESC + -y + "A";
} else if (y > 0) {
returnValue += ESC + y + "B";
}
return returnValue;
};
var cursorUp = (count = 1) => ESC + count + "A";
var cursorDown = (count = 1) => ESC + count + "B";
var cursorForward = (count = 1) => ESC + count + "C";
var cursorBackward = (count = 1) => ESC + count + "D";
var cursorLeft = ESC + "G";
var cursorSavePosition = isTerminalApp ? "\x1B7" : ESC + "s";
var cursorRestorePosition = isTerminalApp ? "\x1B8" : ESC + "u";
var cursorGetPosition = ESC + "6n";
var cursorNextLine = ESC + "E";
var cursorPrevLine = ESC + "F";
var cursorHide = ESC + "?25l";
var cursorShow = ESC + "?25h";
var eraseLines = (count) => {
let clear = "";
for (let i = 0; i < count; i++) {
clear += eraseLine + (i < count - 1 ? cursorUp() : "");
}
if (count) {
clear += cursorLeft;
}
return clear;
};
var eraseEndLine = ESC + "K";
var eraseStartLine = ESC + "1K";
var eraseLine = ESC + "2K";
var eraseDown = ESC + "J";
var eraseUp = ESC + "1J";
var eraseScreen = ESC + "2J";
var scrollUp = ESC + "S";
var scrollDown = ESC + "T";
var clearScreen = "\x1Bc";
var clearViewport = `${eraseScreen}${ESC}H`;
var isOldWindows = () => {
if (isBrowser || !isWindows2) {
return false;
}
const parts = os3.release().split(".");
const major = Number(parts[0]);
const build = Number(parts[2] ?? 0);
if (major < 10) {
return true;
}
if (major === 10 && build < 10586) {
return true;
}
return false;
};
var clearTerminal = isOldWindows() ? `${eraseScreen}${ESC}0f` : `${eraseScreen}${ESC}3J${ESC}H`;
var enterAlternativeScreen = ESC + "?1049h";
var exitAlternativeScreen = ESC + "?1049l";
var beginSynchronizedOutput = ESC + "?2026h";
var endSynchronizedOutput = ESC + "?2026l";
var synchronizedOutput = (text) => beginSynchronizedOutput + text + endSynchronizedOutput;
var beep = BEL;
var link = (text, url2) => {
const openLink = wrapOsc(`${OSC}8${SEP}${SEP}${url2}${BEL}`);
const closeLink = wrapOsc(`${OSC}8${SEP}${SEP}${BEL}`);
return openLink + text + closeLink;
};
var image = (data, options = {}) => {
let returnValue = `${OSC}1337;File=inline=1`;
if (options.width) {
returnValue += `;width=${options.width}`;
}
if (options.height) {
returnValue += `;height=${options.height}`;
}
if (options.preserveAspectRatio === false) {
returnValue += ";preserveAspectRatio=0";
}
const imageBuffer = Buffer.from(data);
return wrapOsc(returnValue + `;size=${imageBuffer.byteLength}:` + imageBuffer.toString("base64") + BEL);
};
var iTerm = {
setCwd: (cwd5 = cwdFunction()) => wrapOsc(`${OSC}50;CurrentDir=${cwd5}${BEL}`),
annotation(message, options = {}) {
let returnValue = `${OSC}1337;`;
const hasX = options.x !== void 0;
const hasY = options.y !== void 0;
if ((hasX || hasY) && !(hasX && hasY && options.length !== void 0)) {
throw new Error("`x`, `y` and `length` must be defined when `x` or `y` is defined");
}
message = message.replaceAll("|", "");
returnValue += options.isHidden ? "AddHiddenAnnotation=" : "AddAnnotation=";
if (options.length > 0) {
returnValue += (hasX ? [message, options.length, options.x, options.y] : [options.length, message]).join("|");
} else {
returnValue += message;
}
return wrapOsc(returnValue + BEL);
}
};
var ConEmu = {
setCwd: (cwd5 = cwdFunction()) => wrapOsc(`${OSC}9;9;${cwd5}${BEL}`)
};
var setCwd = (cwd5 = cwdFunction()) => iTerm.setCwd(cwd5) + ConEmu.setCwd(cwd5);
// packages/cli/src/ui/AppContainer.tsx
import { basename as basename4 } from "node:path";
// packages/cli/src/utils/windowTitle.ts
function truncate(text, maxLen) {
if (text.length <= maxLen) {
return text;
}
return text.substring(0, maxLen - 1) + "\u2026";
}
function computeTerminalTitle({
streamingState,
thoughtSubject,
isConfirming,
isSilentWorking,
folderName,
showThoughts,
useDynamicTitle
}) {
const MAX_LEN = 80;
let displayContext = process.env["CLI_TITLE"] || folderName;
if (!useDynamicTitle) {
const base = "Gemini CLI ";
const maxContextLen = MAX_LEN - base.length - 2;
displayContext = truncate(displayContext, maxContextLen);
return `${base}(${displayContext})`.padEnd(MAX_LEN, " ");
}
const getSuffix = (context) => ` (${context})`;
let title;
if (isConfirming || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */) {
const base = "\u270B Action Required";
const maxContextLen = MAX_LEN - base.length - 3;
const context = truncate(displayContext, maxContextLen);
title = `${base}${getSuffix(context)}`;
} else if (isSilentWorking) {
const base = "\u23F2 Working\u2026";
const maxContextLen = MAX_LEN - base.length - 3;
const context = truncate(displayContext, maxContextLen);
title = `${base}${getSuffix(context)}`;
} else if (streamingState === "idle" /* Idle */) {
const base = "\u25C7 Ready";
const maxContextLen = MAX_LEN - base.length - 3;
const context = truncate(displayContext, maxContextLen);
title = `${base}${getSuffix(context)}`;
} else {
const cleanSubject = showThoughts && thoughtSubject?.replace(/[\r\n]+/g, " ").trim();
const suffix = getSuffix(displayContext);
const suffixLen = suffix.length;
const canFitThoughtWithSuffix = cleanSubject ? cleanSubject.length + suffixLen + 3 <= MAX_LEN : true;
let activeSuffix = "";
let maxStatusLen = MAX_LEN - 3;
if (!cleanSubject || canFitThoughtWithSuffix) {
activeSuffix = suffix;
maxStatusLen -= activeSuffix.length;
}
const displayStatus = cleanSubject ? truncate(cleanSubject, maxStatusLen) : "Working\u2026";
title = `\u2726 ${displayStatus}${activeSuffix}`;
}
const safeTitle = title.replace(/[\x00-\x1F\x7F]/g, "");
return safeTitle.padEnd(MAX_LEN, " ").substring(0, MAX_LEN);
}
// packages/cli/src/ui/hooks/useLogger.ts
var import_react96 = __toESM(require_react(), 1);
var useLogger = (config) => {
const [logger, setLogger] = (0, import_react96.useState)(null);
(0, import_react96.useEffect)(() => {
const newLogger = new Logger(config.getSessionId(), config.storage);
newLogger.initialize().then(() => setLogger(newLogger)).catch(() => {
});
}, [config]);
return logger;
};
// packages/cli/src/ui/hooks/useGeminiStream.ts
var import_react99 = __toESM(require_react(), 1);
// packages/cli/src/ui/utils/markdownUtilities.ts
var isIndexInsideCodeBlock = (content, indexToTest) => {
let fenceCount = 0;
let searchPos = 0;
while (searchPos < content.length) {
const nextFence = content.indexOf("```", searchPos);
if (nextFence === -1 || nextFence >= indexToTest) {
break;
}
fenceCount++;
searchPos = nextFence + 3;
}
return fenceCount % 2 === 1;
};
var findEnclosingCodeBlockStart = (content, index) => {
if (!isIndexInsideCodeBlock(content, index)) {
return -1;
}
let currentSearchPos = 0;
while (currentSearchPos < index) {
const blockStartIndex = content.indexOf("```", currentSearchPos);
if (blockStartIndex === -1 || blockStartIndex >= index) {
break;
}
const blockEndIndex = content.indexOf("```", blockStartIndex + 3);
if (blockStartIndex < index) {
if (blockEndIndex === -1 || index < blockEndIndex + 3) {
return blockStartIndex;
}
}
if (blockEndIndex === -1) break;
currentSearchPos = blockEndIndex + 3;
}
return -1;
};
var findLastSafeSplitPoint = (content) => {
const enclosingBlockStart = findEnclosingCodeBlockStart(
content,
content.length
);
if (enclosingBlockStart !== -1) {
return enclosingBlockStart;
}
let searchStartIndex = content.length;
while (searchStartIndex >= 0) {
const dnlIndex = content.lastIndexOf("\n\n", searchStartIndex);
if (dnlIndex === -1) {
break;
}
const potentialSplitPoint = dnlIndex + 2;
if (!isIndexInsideCodeBlock(content, potentialSplitPoint)) {
return potentialSplitPoint;
}
searchStartIndex = dnlIndex - 1;
}
return content.length;
};
// packages/cli/src/ui/hooks/useStateAndRef.ts
var import_react97 = __toESM(require_react(), 1);
var useStateAndRef = (initialValue) => {
const [state, setState] = import_react97.default.useState(initialValue);
const ref = import_react97.default.useRef(initialValue);
const setStateInternal = import_react97.default.useCallback(
(newStateOrCallback) => {
let newValue;
if (typeof newStateOrCallback === "function") {
newValue = newStateOrCallback(ref.current);
} else {
newValue = newStateOrCallback;
}
setState(newValue);
ref.current = newValue;
},
[]
);
return [state, ref, setStateInternal];
};
// packages/cli/src/ui/hooks/toolMapping.ts
function hasSubagentHistory(call) {
return "subagentHistory" in call && call.subagentHistory !== void 0;
}
function mapToDisplay(toolOrTools, options = {}) {
const toolCalls = Array.isArray(toolOrTools) ? toolOrTools : [toolOrTools];
const { borderTop, borderBottom, borderColor, borderDimColor } = options;
const toolDisplays = toolCalls.map((call) => {
let description;
let renderOutputAsMarkdown = false;
const displayName = call.tool?.displayName ?? call.request.name;
if (call.status === CoreToolCallStatus.Error) {
description = JSON.stringify(call.request.args);
} else {
description = call.invocation.getDescription();
renderOutputAsMarkdown = call.tool.isOutputMarkdown;
}
const baseDisplayProperties = {
callId: call.request.callId,
parentCallId: call.request.parentCallId,
name: displayName,
args: call.request.args,
description,
renderOutputAsMarkdown
};
let resultDisplay = void 0;
let confirmationDetails = void 0;
let outputFile = void 0;
let ptyId = void 0;
let correlationId = void 0;
let progressMessage = void 0;
let progress = void 0;
let progressTotal = void 0;
switch (call.status) {
case CoreToolCallStatus.Success:
resultDisplay = call.response.resultDisplay;
outputFile = call.response.outputFile;
break;
case CoreToolCallStatus.Error:
case CoreToolCallStatus.Cancelled:
resultDisplay = call.response.resultDisplay;
break;
case CoreToolCallStatus.AwaitingApproval:
correlationId = call.correlationId;
confirmationDetails = call.confirmationDetails;
break;
case CoreToolCallStatus.Executing:
resultDisplay = call.liveOutput;
ptyId = call.pid;
progressMessage = call.progressMessage;
progress = call.progress;
progressTotal = call.progressTotal;
break;
case CoreToolCallStatus.Scheduled:
case CoreToolCallStatus.Validating:
break;
default: {
const exhaustiveCheck = call;
debugLogger.warn(
`Unhandled tool call status in mapper: ${exhaustiveCheck.status}`
);
break;
}
}
return {
...baseDisplayProperties,
status: call.status,
isClientInitiated: !!call.request.isClientInitiated,
kind: call.tool?.kind,
resultDisplay,
confirmationDetails,
outputFile,
ptyId,
correlationId,
progressMessage,
progress,
progressTotal,
approvalMode: call.approvalMode,
originalRequestName: call.request.originalRequestName,
subagentHistory: hasSubagentHistory(call) ? call.subagentHistory : void 0
};
});
return {
type: "tool_group",
tools: toolDisplays,
borderTop,
borderBottom,
borderColor,
borderDimColor
};
}
// packages/cli/src/ui/hooks/useToolScheduler.ts
var import_react98 = __toESM(require_react(), 1);
function useToolScheduler(onComplete, config, getPreferredEditor) {
const [toolCallsMap, setToolCallsMap] = (0, import_react98.useState)({});
const [lastToolOutputTime, setLastToolOutputTime] = (0, import_react98.useState)(0);
const [subagentHistoryMap, setSubagentHistoryMap] = (0, import_react98.useState)({});
const messageBus = (0, import_react98.useMemo)(() => config.getMessageBus(), [config]);
const onCompleteRef = (0, import_react98.useRef)(onComplete);
(0, import_react98.useEffect)(() => {
onCompleteRef.current = onComplete;
}, [onComplete]);
const getPreferredEditorRef = (0, import_react98.useRef)(getPreferredEditor);
(0, import_react98.useEffect)(() => {
getPreferredEditorRef.current = getPreferredEditor;
}, [getPreferredEditor]);
const scheduler = (0, import_react98.useMemo)(
() => new Scheduler({
context: config,
messageBus,
getPreferredEditor: () => getPreferredEditorRef.current(),
schedulerId: ROOT_SCHEDULER_ID
}),
[config, messageBus]
);
(0, import_react98.useEffect)(() => () => scheduler.dispose(), [scheduler]);
const internalAdaptToolCalls = (0, import_react98.useCallback)(
(coreCalls, prevTracked) => adaptToolCalls(coreCalls, prevTracked),
[]
);
(0, import_react98.useEffect)(() => {
const handler = (event) => {
const isRoot = event.schedulerId === ROOT_SCHEDULER_ID;
const hasExecuting = event.toolCalls.some(
(tc) => tc.status === CoreToolCallStatus.Executing || (tc.status === CoreToolCallStatus.Success || tc.status === CoreToolCallStatus.Error) && "tailToolCallRequest" in tc && tc.tailToolCallRequest != null
);
if (hasExecuting) {
setLastToolOutputTime(Date.now());
}
setToolCallsMap((prev) => {
const prevCalls = prev[event.schedulerId] ?? [];
const prevCallIds = new Set(prevCalls.map((tc) => tc.request.callId));
const filteredToolCalls = isRoot ? event.toolCalls : event.toolCalls.filter(
(tc) => tc.status === CoreToolCallStatus.AwaitingApproval || prevCallIds.has(tc.request.callId)
);
if (!isRoot && filteredToolCalls.length === 0 && prevCalls.length === 0) {
return prev;
}
const adapted = internalAdaptToolCalls(filteredToolCalls, prevCalls);
return {
...prev,
[event.schedulerId]: adapted
};
});
};
messageBus.subscribe(MessageBusType.TOOL_CALLS_UPDATE, handler);
return () => {
messageBus.unsubscribe(MessageBusType.TOOL_CALLS_UPDATE, handler);
};
}, [messageBus, internalAdaptToolCalls]);
(0, import_react98.useEffect)(() => {
const handler = (event) => {
setSubagentHistoryMap((prev) => {
const history = prev[event.subagentName] ?? [];
const index = history.findIndex(
(item) => item.id === event.activity.id
);
const nextHistory = [...history];
if (index >= 0) {
nextHistory[index] = event.activity;
} else {
nextHistory.push(event.activity);
}
return {
...prev,
[event.subagentName]: nextHistory
};
});
};
messageBus.subscribe(MessageBusType.SUBAGENT_ACTIVITY, handler);
return () => {
messageBus.unsubscribe(MessageBusType.SUBAGENT_ACTIVITY, handler);
};
}, [messageBus]);
const schedule = (0, import_react98.useCallback)(
async (request, signal) => {
setToolCallsMap({});
setSubagentHistoryMap({});
const results = await scheduler.schedule(request, signal);
await onCompleteRef.current(results);
return results;
},
[scheduler]
);
const cancelAll = (0, import_react98.useCallback)(
(_signal) => {
scheduler.cancelAll();
},
[scheduler]
);
const markToolsAsSubmitted = (0, import_react98.useCallback)(
(callIdsToMark) => {
setToolCallsMap((prevMap) => {
const nextMap = { ...prevMap };
for (const [sid, calls] of Object.entries(nextMap)) {
nextMap[sid] = calls.map(
(tc) => callIdsToMark.includes(tc.request.callId) ? { ...tc, responseSubmittedToGemini: true } : tc
);
}
return nextMap;
});
},
[]
);
const toolCalls = (0, import_react98.useMemo)(() => {
const flattened = Object.values(toolCallsMap).flat();
return flattened.map((tc) => {
let subagentName = tc.request.name;
if (tc.request.name === AGENT_TOOL_NAME) {
const argsObj = tc.request.args;
let parsedArgs = argsObj;
if (typeof argsObj === "string") {
try {
parsedArgs = JSON.parse(argsObj);
} catch {
parsedArgs = null;
}
}
if (typeof parsedArgs === "object" && parsedArgs !== null) {
for (const [key, value] of Object.entries(parsedArgs)) {
if (key === "agent_name" && typeof value === "string") {
subagentName = value;
break;
}
}
}
}
return {
...tc,
subagentHistory: subagentHistoryMap[subagentName] ?? tc.subagentHistory
};
});
}, [toolCallsMap, subagentHistoryMap]);
const setToolCallsForDisplay = (0, import_react98.useCallback)(
(action) => {
setToolCallsMap((prev) => {
const currentFlattened = Object.values(prev).flat();
const nextFlattened = typeof action === "function" ? action(currentFlattened) : action;
if (nextFlattened.length === 0) {
return {};
}
const nextMap = {};
for (const call of nextFlattened) {
const sid = call.schedulerId ?? ROOT_SCHEDULER_ID;
if (!nextMap[sid]) {
nextMap[sid] = [];
}
nextMap[sid].push(call);
}
return nextMap;
});
},
[]
);
return [
toolCalls,
schedule,
markToolsAsSubmitted,
setToolCallsForDisplay,
cancelAll,
lastToolOutputTime,
scheduler
];
}
function adaptToolCalls(coreCalls, prevTracked) {
const prevMap = new Map(prevTracked.map((t) => [t.request.callId, t]));
return coreCalls.map((coreCall) => {
const prev = prevMap.get(coreCall.request.callId);
const responseSubmittedToGemini = prev?.responseSubmittedToGemini ?? false;
let status = coreCall.status;
if ((status === CoreToolCallStatus.Success || status === CoreToolCallStatus.Error) && "tailToolCallRequest" in coreCall && coreCall.tailToolCallRequest != null) {
status = CoreToolCallStatus.Executing;
}
return {
...coreCall,
status,
responseSubmittedToGemini
};
});
}
// packages/cli/src/ui/hooks/useGeminiStream.ts
import { promises as fs6 } from "node:fs";
import path12 from "node:path";
var isTopicTool2 = (name) => name === UPDATE_TOPIC_TOOL_NAME || name === UPDATE_TOPIC_DISPLAY_NAME;
var SUPPRESSED_TOOL_ERRORS_NOTE = 'Some internal tool attempts failed before this final error. Press F12 for diagnostics, or run /settings and change "Error Verbosity" to full for details.';
var LOW_VERBOSITY_FAILURE_NOTE = 'This request failed. Press F12 for diagnostics, or run /settings and change "Error Verbosity" to full for full details.';
function getBackgroundedToolInfo(toolCall) {
const response = toolCall.response;
const rawData = response?.data;
if (!isBackgroundExecutionData(rawData)) {
return void 0;
}
if (rawData.pid === void 0) {
return void 0;
}
return {
pid: rawData.pid,
command: rawData.command ?? toolCall.request.name,
initialOutput: rawData.initialOutput ?? ""
};
}
function isBackgroundableExecutingToolCall(toolCall) {
return toolCall.status === CoreToolCallStatus.Executing && typeof toolCall.pid === "number";
}
function showCitations(settings) {
const enabled = settings.merged.ui.showCitations;
if (enabled !== void 0) {
return enabled;
}
return true;
}
function calculateStreamingState(isResponding, toolCalls) {
if (toolCalls.some((tc) => tc.status === CoreToolCallStatus.AwaitingApproval)) {
return "waiting_for_confirmation" /* WaitingForConfirmation */;
}
const isAnyToolActive = toolCalls.some((tc) => {
if (tc.status === CoreToolCallStatus.Executing || tc.status === CoreToolCallStatus.Scheduled || tc.status === CoreToolCallStatus.Validating) {
return true;
}
if (tc.status === CoreToolCallStatus.Success || tc.status === CoreToolCallStatus.Error || tc.status === CoreToolCallStatus.Cancelled) {
return !tc.responseSubmittedToGemini;
}
return false;
});
if (isResponding || isAnyToolActive) {
return "responding" /* Responding */;
}
return "idle" /* Idle */;
}
var useGeminiStream = (geminiClient, history, addItem, config, settings, onDebugMessage, handleSlashCommand, shellModeActive, getPreferredEditor, onAuthError, performMemoryRefresh, modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError, onCancelSubmit, setShellInputFocused, terminalWidth, terminalHeight, isShellFocused, consumeUserHint) => {
const [initError, setInitError] = (0, import_react99.useState)(null);
const [retryStatus, setRetryStatus] = (0, import_react99.useState)(
null
);
const isLowErrorVerbosity = settings.merged.ui?.errorVerbosity !== "full";
const suppressedToolErrorCountRef = (0, import_react99.useRef)(0);
const suppressedToolErrorNoteShownRef = (0, import_react99.useRef)(false);
const lowVerbosityFailureNoteShownRef = (0, import_react99.useRef)(false);
const abortControllerRef = (0, import_react99.useRef)(null);
const turnCancelledRef = (0, import_react99.useRef)(false);
const activeQueryIdRef = (0, import_react99.useRef)(null);
const previousApprovalModeRef = (0, import_react99.useRef)(
config.getApprovalMode()
);
const [isResponding, isRespondingRef, setIsResponding] = useStateAndRef(false);
const [thought, thoughtRef, setThought] = useStateAndRef(null);
const [pendingHistoryItem, pendingHistoryItemRef, setPendingHistoryItem] = useStateAndRef(null);
const [lastGeminiActivityTime, setLastGeminiActivityTime] = (0, import_react99.useState)(0);
const [pushedToolCallIds, pushedToolCallIdsRef, setPushedToolCallIds] = useStateAndRef(/* @__PURE__ */ new Set());
const [_isFirstToolInGroup, isFirstToolInGroupRef, setIsFirstToolInGroup] = useStateAndRef(true);
const processedMemoryToolsRef = (0, import_react99.useRef)(/* @__PURE__ */ new Set());
const { startNewPrompt, getPromptCount } = useSessionStats();
const logger = useLogger(config);
const gitService = (0, import_react99.useMemo)(() => {
if (!config.getProjectRoot()) {
return;
}
return new GitService(config.getProjectRoot(), config.storage);
}, [config]);
(0, import_react99.useEffect)(() => {
const handleRetryAttempt = (payload) => {
if (turnCancelledRef.current || !isRespondingRef.current) {
return;
}
setRetryStatus(payload);
};
coreEvents.on(CoreEvent.RetryAttempt, handleRetryAttempt);
return () => {
coreEvents.off(CoreEvent.RetryAttempt, handleRetryAttempt);
};
}, [isRespondingRef]);
const [
toolCalls,
scheduleToolCalls,
markToolsAsSubmitted,
setToolCallsForDisplay,
cancelAllToolCalls,
lastToolOutputTime
] = useToolScheduler(
async (completedToolCallsFromScheduler) => {
if (completedToolCallsFromScheduler.length > 0) {
const toolsToPush = completedToolCallsFromScheduler.filter(
(tc) => !pushedToolCallIdsRef.current.has(tc.request.callId)
);
if (toolsToPush.length > 0) {
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
const firstToolToPush = toolsToPush[0];
const tcIndex = toolCalls.indexOf(firstToolToPush);
const prevTool = tcIndex > 0 ? toolCalls[tcIndex - 1] : null;
let borderTop = isFirstToolInGroupRef.current;
if (!borderTop && prevTool) {
const currentIsCompact = isCompactTool(
mapToDisplay(firstToolToPush).tools[0],
isCompactModeEnabled
);
const prevWasCompact = isCompactTool(
mapToDisplay(prevTool).tools[0],
isCompactModeEnabled
);
if (!currentIsCompact && prevWasCompact) {
borderTop = true;
}
}
addItem(
mapToDisplay(toolsToPush, {
borderTop,
borderBottom: true,
borderColor: theme.border.default,
borderDimColor: false
})
);
}
setToolCallsForDisplay([]);
try {
const currentModel = config.getGeminiClient().getCurrentSequenceModel() ?? config.getModel();
config.getGeminiClient().getChat().recordCompletedToolCalls(
currentModel,
completedToolCallsFromScheduler
);
await recordToolCallInteractions(
config,
completedToolCallsFromScheduler
);
} catch (error) {
debugLogger.warn(
`Error recording completed tool call information: ${error}`
);
}
await handleCompletedTools(completedToolCallsFromScheduler);
}
},
config,
getPreferredEditor
);
const activeBackgroundExecutionId = (0, import_react99.useMemo)(() => {
const executingBackgroundableTool = toolCalls.find(
isBackgroundableExecutingToolCall
);
return executingBackgroundableTool?.pid;
}, [toolCalls]);
const onExec = (0, import_react99.useCallback)(
async (done) => {
setIsResponding(true);
await done;
setIsResponding(false);
},
[setIsResponding]
);
const {
handleShellCommand,
activeShellPtyId,
lastShellOutputTime,
backgroundTaskCount,
isBackgroundTaskVisible,
toggleBackgroundTasks,
backgroundCurrentExecution,
registerBackgroundTask,
dismissBackgroundTask,
backgroundTasks
} = useExecutionLifecycle(
addItem,
setPendingHistoryItem,
onExec,
onDebugMessage,
config,
geminiClient,
setShellInputFocused,
terminalWidth,
terminalHeight,
activeBackgroundExecutionId
);
const streamingState = (0, import_react99.useMemo)(
() => calculateStreamingState(isResponding, toolCalls),
[isResponding, toolCalls]
);
(0, import_react99.useEffect)(() => {
if (toolCalls.length > 0) {
const isNewBatch = !toolCalls.some(
(tc) => pushedToolCallIdsRef.current.has(tc.request.callId)
);
if (isNewBatch) {
setPushedToolCallIds(/* @__PURE__ */ new Set());
setIsFirstToolInGroup(true);
}
} else if (streamingState === "idle" /* Idle */) {
setPushedToolCallIds(/* @__PURE__ */ new Set());
setIsFirstToolInGroup(true);
}
}, [
toolCalls,
pushedToolCallIdsRef,
setPushedToolCallIds,
setIsFirstToolInGroup,
streamingState
]);
(0, import_react99.useEffect)(() => {
const toolsToPush = [];
for (let i = 0; i < toolCalls.length; i++) {
const tc = toolCalls[i];
if (pushedToolCallIdsRef.current.has(tc.request.callId)) continue;
if (tc.status === "success" || tc.status === "error" || tc.status === "cancelled") {
const isAgent = tc.tool?.kind === Kind.Agent;
if (isAgent) {
let contigAgentsComplete = true;
for (let j = i + 1; j < toolCalls.length; j++) {
const nextTc = toolCalls[j];
if (nextTc.tool?.kind === Kind.Agent) {
if (nextTc.status !== "success" && nextTc.status !== "error" && nextTc.status !== "cancelled") {
contigAgentsComplete = false;
break;
}
} else {
break;
}
}
if (!contigAgentsComplete) {
break;
}
}
toolsToPush.push(tc);
} else {
break;
}
}
if (toolsToPush.length > 0) {
const newPushed = new Set(pushedToolCallIdsRef.current);
const isFirstInThisPush = isFirstToolInGroupRef.current;
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
const groups = [];
let currentGroup = [];
for (const tc of toolsToPush) {
newPushed.add(tc.request.callId);
if (tc.tool?.kind === Kind.Agent) {
currentGroup.push(tc);
} else {
if (currentGroup.length > 0) {
groups.push(currentGroup);
currentGroup = [];
}
groups.push([tc]);
}
}
if (currentGroup.length > 0) {
groups.push(currentGroup);
}
for (let i = 0; i < groups.length; i++) {
const group = groups[i];
const isFirstInBatch = i === 0 && isFirstInThisPush;
const lastTcInGroup = group[group.length - 1];
const tcIndexInBatch = toolCalls.indexOf(lastTcInGroup);
const isLastInBatch = tcIndexInBatch === toolCalls.length - 1;
const nextTcInBatch = tcIndexInBatch < toolCalls.length - 1 ? toolCalls[tcIndexInBatch + 1] : null;
const prevTcInBatch = toolCalls.indexOf(group[0]) > 0 ? toolCalls[toolCalls.indexOf(group[0]) - 1] : null;
const historyItem = mapToDisplay(group, {
...getToolGroupBorderAppearance(
{ type: "tool_group", tools: toolCalls },
activeShellPtyId,
!!isShellFocused,
[],
backgroundTasks
)
});
const currentIsCompact = historyItem.tools.length === 1 && isCompactTool(historyItem.tools[0], isCompactModeEnabled);
let nextIsCompact = false;
if (nextTcInBatch) {
const nextHistoryItem = mapToDisplay(nextTcInBatch);
nextIsCompact = nextHistoryItem.tools.length === 1 && isCompactTool(nextHistoryItem.tools[0], isCompactModeEnabled);
}
let prevWasCompact = false;
if (prevTcInBatch) {
const prevHistoryItem = mapToDisplay(prevTcInBatch);
prevWasCompact = prevHistoryItem.tools.length === 1 && isCompactTool(prevHistoryItem.tools[0], isCompactModeEnabled);
}
historyItem.borderTop = isFirstInBatch || !currentIsCompact && prevWasCompact;
historyItem.borderBottom = currentIsCompact ? isLastInBatch && !nextIsCompact : isLastInBatch || nextIsCompact;
addItem(historyItem);
}
setPushedToolCallIds(newPushed);
if (isFirstToolInGroupRef.current && toolsToPush.every((tc) => isTopicTool2(tc.request.name))) {
} else {
setIsFirstToolInGroup(false);
}
}
}, [
toolCalls,
pushedToolCallIdsRef,
isFirstToolInGroupRef,
setPushedToolCallIds,
setIsFirstToolInGroup,
addItem,
activeShellPtyId,
isShellFocused,
backgroundTasks,
settings.merged.ui?.compactToolOutput
]);
const pendingToolGroupItems = (0, import_react99.useMemo)(() => {
const remainingTools = toolCalls.filter(
(tc) => !pushedToolCallIds.has(tc.request.callId)
);
const items = [];
const appearance = getToolGroupBorderAppearance(
{ type: "tool_group", tools: toolCalls },
activeShellPtyId,
!!isShellFocused,
[],
backgroundTasks
);
if (remainingTools.length > 0) {
let needsTopBorder = pushedToolCallIds.size === 0;
if (!needsTopBorder) {
const allPushedWereTopics = toolCalls.filter((tc) => pushedToolCallIds.has(tc.request.callId)).every((tc) => isTopicTool2(tc.request.name));
if (allPushedWereTopics) {
needsTopBorder = true;
}
}
items.push(
mapToDisplay(remainingTools, {
borderTop: needsTopBorder,
borderBottom: false,
// Stay open to connect with the slice below
...appearance
})
);
}
const allTerminal = toolCalls.length > 0 && toolCalls.every(
(tc) => tc.status === "success" || tc.status === "error" || tc.status === "cancelled"
);
const allPushed = toolCalls.length > 0 && toolCalls.every((tc) => pushedToolCallIds.has(tc.request.callId));
const isToolVisible = (tc) => {
if (!isRenderedInHistory(buildToolVisibilityContext(tc))) {
return false;
}
const displayStatus = mapCoreStatusToDisplayStatus(tc.status);
if (displayStatus === "Confirming" /* Confirming */) {
return false;
}
return true;
};
let lastVisibleIsCompact = false;
const isCompactModeEnabled = settings.merged.ui?.compactToolOutput === true;
for (let i = toolCalls.length - 1; i >= 0; i--) {
if (isToolVisible(toolCalls[i])) {
const mapped = mapToDisplay(toolCalls[i]);
lastVisibleIsCompact = mapped.tools[0] ? isCompactTool(mapped.tools[0], isCompactModeEnabled) : false;
break;
}
}
if (toolCalls.length > 0 && !(allTerminal && allPushed) && toolCalls.some(isToolVisible) && !lastVisibleIsCompact) {
items.push({
type: "tool_group",
tools: [],
borderTop: false,
borderBottom: true,
...appearance
});
}
return items;
}, [
toolCalls,
pushedToolCallIds,
activeShellPtyId,
isShellFocused,
backgroundTasks,
settings.merged.ui?.compactToolOutput
]);
const lastQueryRef = (0, import_react99.useRef)(null);
const lastPromptIdRef = (0, import_react99.useRef)(null);
const loopDetectedRef = (0, import_react99.useRef)(false);
const [
loopDetectionConfirmationRequest,
setLoopDetectionConfirmationRequest
] = (0, import_react99.useState)(null);
const activePtyId = activeShellPtyId ?? activeBackgroundExecutionId ?? void 0;
const prevActiveShellPtyIdRef = (0, import_react99.useRef)(null);
(0, import_react99.useEffect)(() => {
if (turnCancelledRef.current && prevActiveShellPtyIdRef.current !== null && activeShellPtyId === null) {
addItem({ type: "info" /* INFO */, text: "Request cancelled." });
setIsResponding(false);
}
prevActiveShellPtyIdRef.current = activeShellPtyId;
}, [activeShellPtyId, addItem, setIsResponding]);
(0, import_react99.useEffect)(() => {
if (config.getApprovalMode() === ApprovalMode.YOLO && streamingState === "idle" /* Idle */) {
const lastUserMessageIndex = history.findLastIndex(
(item) => item.type === "user" /* USER */
);
const turnCount = lastUserMessageIndex === -1 ? 0 : history.length - lastUserMessageIndex;
if (turnCount > 0) {
logConversationFinishedEvent(
config,
new ConversationFinishedEvent(config.getApprovalMode(), turnCount)
);
}
}
}, [streamingState, config, history]);
(0, import_react99.useEffect)(() => {
if (!isResponding) {
setRetryStatus(null);
}
}, [isResponding]);
const maybeAddSuppressedToolErrorNote = (0, import_react99.useCallback)(
(userMessageTimestamp) => {
if (!isLowErrorVerbosity) {
return;
}
if (suppressedToolErrorCountRef.current === 0) {
return;
}
if (suppressedToolErrorNoteShownRef.current) {
return;
}
addItem(
{
type: "info" /* INFO */,
text: SUPPRESSED_TOOL_ERRORS_NOTE
},
userMessageTimestamp
);
suppressedToolErrorNoteShownRef.current = true;
},
[addItem, isLowErrorVerbosity]
);
const maybeAddLowVerbosityFailureNote = (0, import_react99.useCallback)(
(userMessageTimestamp) => {
if (!isLowErrorVerbosity || config.getDebugMode()) {
return;
}
if (lowVerbosityFailureNoteShownRef.current || suppressedToolErrorNoteShownRef.current) {
return;
}
addItem(
{
type: "info" /* INFO */,
text: LOW_VERBOSITY_FAILURE_NOTE
},
userMessageTimestamp
);
lowVerbosityFailureNoteShownRef.current = true;
},
[addItem, config, isLowErrorVerbosity]
);
const cancelOngoingRequest = (0, import_react99.useCallback)(
(clearBuffer = true) => {
if (turnCancelledRef.current) {
if (clearBuffer) {
onCancelSubmit(false, true);
}
return;
}
const hasActiveTools = toolCalls.some(
(tc) => tc.status === CoreToolCallStatus.Executing || tc.status === CoreToolCallStatus.Scheduled || tc.status === CoreToolCallStatus.Validating
);
if (streamingState === "idle" /* Idle */ && !isRespondingRef.current && !hasActiveTools) {
if (clearBuffer) {
onCancelSubmit(false, true);
}
return;
}
turnCancelledRef.current = true;
setRetryStatus(null);
const isFullCancellation = !toolCalls.some(
(tc) => tc.status === "success" || tc.status === "error"
);
if (!abortControllerRef.current) {
abortControllerRef.current = new AbortController();
}
abortControllerRef.current.abort();
cancelAllToolCalls(abortControllerRef.current.signal);
if (pendingHistoryItemRef.current) {
if (pendingHistoryItemRef.current.type === "tool_group" && pendingHistoryItemRef.current.tools.some(
(t) => t.name === SHELL_COMMAND_NAME
)) {
const toolGroup = pendingHistoryItemRef.current;
const updatedTools = toolGroup.tools.map((tool) => {
if (tool.name === SHELL_COMMAND_NAME) {
return {
...tool,
status: CoreToolCallStatus.Cancelled,
resultDisplay: tool.resultDisplay
};
}
return tool;
});
const newToolGroup = {
...toolGroup,
tools: updatedTools
};
addItem(newToolGroup);
} else {
addItem(pendingHistoryItemRef.current);
}
}
setPendingHistoryItem(null);
if (isFullCancellation) {
if (!activeShellPtyId) {
addItem({
type: "info" /* INFO */,
text: "Request cancelled."
});
setIsResponding(false);
}
}
onCancelSubmit(false, clearBuffer);
setShellInputFocused(false);
},
[
streamingState,
addItem,
setPendingHistoryItem,
onCancelSubmit,
pendingHistoryItemRef,
isRespondingRef,
setShellInputFocused,
cancelAllToolCalls,
toolCalls,
activeShellPtyId,
setIsResponding
]
);
useKeypress(
(key) => {
if (key.name === "escape" && !isShellFocused) {
cancelOngoingRequest(false);
return true;
}
return false;
},
{
isActive: streamingState === "responding" /* Responding */ || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */
}
);
const prepareQueryForGemini = (0, import_react99.useCallback)(
async (query, userMessageTimestamp, abortSignal, prompt_id) => {
if (turnCancelledRef.current) {
return { queryToSend: null, shouldProceed: false };
}
if (typeof query === "string" && query.trim().length === 0) {
return { queryToSend: null, shouldProceed: false };
}
let localQueryToSendToGemini = null;
if (typeof query === "string") {
const trimmedQuery = query.trim();
await logger?.logMessage(MessageSenderType.USER, trimmedQuery);
if (!shellModeActive) {
const slashCommandResult = isSlashCommand(trimmedQuery) ? await handleSlashCommand(trimmedQuery) : false;
if (slashCommandResult) {
switch (slashCommandResult.type) {
case "schedule_tool": {
const { toolName, toolArgs, postSubmitPrompt } = slashCommandResult;
const toolCallRequest = {
callId: `${toolName}-${Date.now()}-${Math.random().toString(16).slice(2)}`,
name: toolName,
args: toolArgs,
isClientInitiated: true,
prompt_id
};
await scheduleToolCalls([toolCallRequest], abortSignal);
if (postSubmitPrompt) {
localQueryToSendToGemini = postSubmitPrompt;
return {
queryToSend: localQueryToSendToGemini,
shouldProceed: true
};
}
return { queryToSend: null, shouldProceed: false };
}
case "submit_prompt": {
localQueryToSendToGemini = slashCommandResult.content;
return {
queryToSend: localQueryToSendToGemini,
shouldProceed: true
};
}
case "handled": {
return { queryToSend: null, shouldProceed: false };
}
default: {
const unreachable = slashCommandResult;
throw new Error(
`Unhandled slash command result type: ${unreachable}`
);
}
}
}
}
if (shellModeActive && handleShellCommand(trimmedQuery, abortSignal)) {
return { queryToSend: null, shouldProceed: false };
}
if (isAtCommand(trimmedQuery)) {
addItem(
{ type: "user" /* USER */, text: trimmedQuery },
userMessageTimestamp
);
const atCommandResult = await handleAtCommand({
query: trimmedQuery,
config,
addItem,
onDebugMessage,
messageId: userMessageTimestamp,
signal: abortSignal,
escapePastedAtSymbols: settings.merged.ui?.escapePastedAtSymbols
});
if (atCommandResult.error) {
onDebugMessage(atCommandResult.error);
return { queryToSend: null, shouldProceed: false };
}
localQueryToSendToGemini = atCommandResult.processedQuery;
} else {
addItem(
{ type: "user" /* USER */, text: trimmedQuery },
userMessageTimestamp
);
localQueryToSendToGemini = trimmedQuery;
}
} else {
localQueryToSendToGemini = query;
}
if (localQueryToSendToGemini === null) {
onDebugMessage(
"Query processing resulted in null, not sending to Gemini."
);
return { queryToSend: null, shouldProceed: false };
}
return { queryToSend: localQueryToSendToGemini, shouldProceed: true };
},
[
config,
addItem,
onDebugMessage,
handleShellCommand,
handleSlashCommand,
logger,
shellModeActive,
scheduleToolCalls,
settings
]
);
const handleContentEvent = (0, import_react99.useCallback)(
(eventValue, currentGeminiMessageBuffer, userMessageTimestamp) => {
setRetryStatus(null);
if (turnCancelledRef.current) {
return "";
}
let newGeminiMessageBuffer = currentGeminiMessageBuffer + eventValue;
if (pendingHistoryItemRef.current?.type !== "gemini" && pendingHistoryItemRef.current?.type !== "gemini_content") {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
}
setPendingHistoryItem({ type: "gemini", text: "" });
newGeminiMessageBuffer = eventValue;
}
const splitPoint = findLastSafeSplitPoint(newGeminiMessageBuffer);
if (splitPoint === newGeminiMessageBuffer.length) {
setPendingHistoryItem((item) => ({
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
type: item?.type,
text: newGeminiMessageBuffer
}));
} else {
const beforeText = newGeminiMessageBuffer.substring(0, splitPoint);
const afterText = newGeminiMessageBuffer.substring(splitPoint);
if (beforeText.length > 0) {
addItem(
{
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
type: pendingHistoryItemRef.current?.type,
text: beforeText
},
userMessageTimestamp
);
}
setPendingHistoryItem({ type: "gemini_content", text: afterText });
newGeminiMessageBuffer = afterText;
}
return newGeminiMessageBuffer;
},
[addItem, pendingHistoryItemRef, setPendingHistoryItem]
);
const handleThoughtEvent = (0, import_react99.useCallback)(
(eventValue, _userMessageTimestamp) => {
setThought(eventValue);
if (getInlineThinkingMode(settings) === "full") {
addItem({
type: "thinking",
thought: eventValue
});
}
},
[addItem, settings, setThought]
);
const handleUserCancelledEvent = (0, import_react99.useCallback)(
(userMessageTimestamp) => {
if (turnCancelledRef.current) {
return;
}
if (pendingHistoryItemRef.current) {
if (pendingHistoryItemRef.current.type === "tool_group") {
const updatedTools = pendingHistoryItemRef.current.tools.map(
(tool) => tool.status === CoreToolCallStatus.Validating || tool.status === CoreToolCallStatus.Scheduled || tool.status === CoreToolCallStatus.AwaitingApproval || tool.status === CoreToolCallStatus.Executing ? { ...tool, status: CoreToolCallStatus.Cancelled } : tool
);
const pendingItem = {
...pendingHistoryItemRef.current,
tools: updatedTools
};
addItem(pendingItem, userMessageTimestamp);
} else {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
}
setPendingHistoryItem(null);
}
addItem(
{ type: "info" /* INFO */, text: "User cancelled the request." },
userMessageTimestamp
);
setIsResponding(false);
setThought(null);
},
[
addItem,
pendingHistoryItemRef,
setPendingHistoryItem,
setThought,
setIsResponding
]
);
const handleErrorEvent = (0, import_react99.useCallback)(
(eventValue, userMessageTimestamp) => {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
maybeAddSuppressedToolErrorNote(userMessageTimestamp);
addItem(
{
type: "error" /* ERROR */,
text: parseAndFormatApiError(
eventValue.error,
config.getContentGeneratorConfig()?.authType,
void 0,
config.getModel(),
DEFAULT_GEMINI_FLASH_MODEL
)
},
userMessageTimestamp
);
maybeAddLowVerbosityFailureNote(userMessageTimestamp);
setThought(null);
},
[
addItem,
pendingHistoryItemRef,
setPendingHistoryItem,
config,
setThought,
maybeAddSuppressedToolErrorNote,
maybeAddLowVerbosityFailureNote
]
);
const handleCitationEvent = (0, import_react99.useCallback)(
(text, userMessageTimestamp) => {
if (!showCitations(settings)) {
return;
}
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
addItem({ type: "info" /* INFO */, text }, userMessageTimestamp);
},
[addItem, pendingHistoryItemRef, setPendingHistoryItem, settings]
);
const handleFinishedEvent = (0, import_react99.useCallback)(
(event, userMessageTimestamp) => {
const finishReason = event.value.reason;
if (!finishReason) {
return;
}
const finishReasonMessages = {
[FinishReason.FINISH_REASON_UNSPECIFIED]: void 0,
[FinishReason.STOP]: void 0,
[FinishReason.MAX_TOKENS]: "Response truncated due to token limits.",
[FinishReason.SAFETY]: "Response stopped due to safety reasons.",
[FinishReason.RECITATION]: "Response stopped due to recitation policy.",
[FinishReason.LANGUAGE]: "Response stopped due to unsupported language.",
[FinishReason.BLOCKLIST]: "Response stopped due to forbidden terms.",
[FinishReason.PROHIBITED_CONTENT]: "Response stopped due to prohibited content.",
[FinishReason.SPII]: "Response stopped due to sensitive personally identifiable information.",
[FinishReason.OTHER]: "Response stopped for other reasons.",
[FinishReason.MALFORMED_FUNCTION_CALL]: "Response stopped due to malformed function call.",
[FinishReason.IMAGE_SAFETY]: "Response stopped due to image safety violations.",
[FinishReason.UNEXPECTED_TOOL_CALL]: "Response stopped due to unexpected tool call.",
[FinishReason.IMAGE_PROHIBITED_CONTENT]: "Response stopped due to prohibited image content.",
[FinishReason.NO_IMAGE]: "Response stopped because no image was generated."
};
const message = finishReasonMessages[finishReason];
if (message) {
addItem(
{
type: "info",
text: `\u26A0\uFE0F ${message}`
},
userMessageTimestamp
);
}
},
[addItem]
);
const handleChatCompressionEvent = (0, import_react99.useCallback)(
(eventValue, userMessageTimestamp) => {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
const limit = tokenLimit(config.getModel());
const originalPercentage = Math.round(
(eventValue?.originalTokenCount ?? 0) / limit * 100
);
const newPercentage = Math.round(
(eventValue?.newTokenCount ?? 0) / limit * 100
);
addItem(
{
type: "info" /* INFO */,
text: `Context compressed from ${originalPercentage}% to ${newPercentage}%.`,
secondaryText: `Change threshold in /settings.`,
color: theme.status.warning,
marginBottom: 1
},
userMessageTimestamp
);
},
[addItem, pendingHistoryItemRef, setPendingHistoryItem, config]
);
const handleMaxSessionTurnsEvent = (0, import_react99.useCallback)(
() => addItem({
type: "info",
text: `The session has reached the maximum number of turns: ${config.getMaxSessionTurns()}. Please update this limit in your setting.json file.`
}),
[addItem, config]
);
const handleContextWindowWillOverflowEvent = (0, import_react99.useCallback)(
(estimatedRequestTokenCount, remainingTokenCount) => {
onCancelSubmit(true);
const limit = tokenLimit(config.getModel());
const isMoreThan25PercentUsed = limit > 0 && remainingTokenCount < limit * 0.75;
let text = `Sending this message (${estimatedRequestTokenCount} tokens) might exceed the context window limit (${remainingTokenCount.toLocaleString()} tokens left).`;
if (isMoreThan25PercentUsed) {
text += " Please try reducing the size of your message or use the `/compress` command to compress the chat history.";
}
addItem({
type: "info",
text
});
},
[addItem, onCancelSubmit, config]
);
const handleChatModelEvent = (0, import_react99.useCallback)(
(eventValue, userMessageTimestamp) => {
if (!settings.merged.ui.showModelInfoInChat) {
return;
}
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
addItem(
{
type: "model",
model: eventValue
},
userMessageTimestamp
);
},
[addItem, pendingHistoryItemRef, setPendingHistoryItem, settings]
);
const handleAgentExecutionStoppedEvent = (0, import_react99.useCallback)(
(reason, userMessageTimestamp, systemMessage, contextCleared) => {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
addItem(
{
type: "info" /* INFO */,
text: `Agent execution stopped: ${systemMessage?.trim() || reason}`
},
userMessageTimestamp
);
maybeAddLowVerbosityFailureNote(userMessageTimestamp);
if (contextCleared) {
addItem(
{
type: "info" /* INFO */,
text: "Conversation context has been cleared."
},
userMessageTimestamp
);
}
setIsResponding(false);
},
[
addItem,
pendingHistoryItemRef,
setPendingHistoryItem,
setIsResponding,
maybeAddLowVerbosityFailureNote
]
);
const handleAgentExecutionBlockedEvent = (0, import_react99.useCallback)(
(reason, userMessageTimestamp, systemMessage, contextCleared) => {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
addItem(
{
type: "warning" /* WARNING */,
text: `Agent execution blocked: ${systemMessage?.trim() || reason}`
},
userMessageTimestamp
);
maybeAddLowVerbosityFailureNote(userMessageTimestamp);
if (contextCleared) {
addItem(
{
type: "info" /* INFO */,
text: "Conversation context has been cleared."
},
userMessageTimestamp
);
}
},
[
addItem,
pendingHistoryItemRef,
setPendingHistoryItem,
maybeAddLowVerbosityFailureNote
]
);
const processGeminiStreamEvents = (0, import_react99.useCallback)(
async (stream, userMessageTimestamp, signal) => {
let geminiMessageBuffer = "";
const toolCallRequests = [];
for await (const event of stream) {
if (event.type !== GeminiEventType.Thought && thoughtRef.current !== null) {
setThought(null);
}
switch (event.type) {
case GeminiEventType.Thought:
setLastGeminiActivityTime(Date.now());
handleThoughtEvent(event.value, userMessageTimestamp);
break;
case GeminiEventType.Content:
setLastGeminiActivityTime(Date.now());
geminiMessageBuffer = handleContentEvent(
event.value,
geminiMessageBuffer,
userMessageTimestamp
);
break;
case GeminiEventType.ToolCallRequest:
toolCallRequests.push(event.value);
break;
case GeminiEventType.UserCancelled:
handleUserCancelledEvent(userMessageTimestamp);
break;
case GeminiEventType.Error:
handleErrorEvent(event.value, userMessageTimestamp);
break;
case GeminiEventType.AgentExecutionStopped:
handleAgentExecutionStoppedEvent(
event.value.reason,
userMessageTimestamp,
event.value.systemMessage,
event.value.contextCleared
);
break;
case GeminiEventType.AgentExecutionBlocked:
handleAgentExecutionBlockedEvent(
event.value.reason,
userMessageTimestamp,
event.value.systemMessage,
event.value.contextCleared
);
break;
case GeminiEventType.ChatCompressed:
handleChatCompressionEvent(event.value, userMessageTimestamp);
break;
case GeminiEventType.ToolCallConfirmation:
case GeminiEventType.ToolCallResponse:
break;
case GeminiEventType.MaxSessionTurns:
handleMaxSessionTurnsEvent();
break;
case GeminiEventType.ContextWindowWillOverflow:
handleContextWindowWillOverflowEvent(
event.value.estimatedRequestTokenCount,
event.value.remainingTokenCount
);
break;
case GeminiEventType.Finished:
handleFinishedEvent(event, userMessageTimestamp);
break;
case GeminiEventType.Citation:
handleCitationEvent(event.value, userMessageTimestamp);
break;
case GeminiEventType.ModelInfo:
handleChatModelEvent(event.value, userMessageTimestamp);
break;
case GeminiEventType.LoopDetected:
loopDetectedRef.current = true;
break;
case GeminiEventType.Retry:
case GeminiEventType.InvalidStream:
break;
default: {
const unreachable = event;
return unreachable;
}
}
}
if (toolCallRequests.length > 0) {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
await scheduleToolCalls(toolCallRequests, signal);
}
return 0 /* Completed */;
},
[
handleContentEvent,
handleThoughtEvent,
thoughtRef,
handleUserCancelledEvent,
handleErrorEvent,
scheduleToolCalls,
handleChatCompressionEvent,
handleFinishedEvent,
handleMaxSessionTurnsEvent,
handleContextWindowWillOverflowEvent,
handleCitationEvent,
handleChatModelEvent,
handleAgentExecutionStoppedEvent,
handleAgentExecutionBlockedEvent,
addItem,
pendingHistoryItemRef,
setPendingHistoryItem,
setThought
]
);
const submitQuery = (0, import_react99.useCallback)(
async (query, options, prompt_id) => runInDevTraceSpan(
{
operation: options?.isContinuation ? GeminiCliOperation.SystemPrompt : GeminiCliOperation.UserPrompt,
sessionId: config.getSessionId()
},
async ({ metadata: spanMetadata }) => {
spanMetadata.input = query;
if ((isRespondingRef.current || streamingState === "responding" /* Responding */ || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */) && !options?.isContinuation)
return;
const queryId = `${Date.now()}-${Math.random()}`;
activeQueryIdRef.current = queryId;
const userMessageTimestamp = Date.now();
if (!options?.isContinuation) {
setModelSwitchedFromQuotaError(false);
config.setQuotaErrorOccurred(false);
config.resetBillingTurnState(
settings.merged.billing?.overageStrategy
);
suppressedToolErrorCountRef.current = 0;
suppressedToolErrorNoteShownRef.current = false;
lowVerbosityFailureNoteShownRef.current = false;
}
abortControllerRef.current = new AbortController();
const abortSignal = abortControllerRef.current.signal;
turnCancelledRef.current = false;
if (!prompt_id) {
prompt_id = config.getSessionId() + "########" + getPromptCount();
}
return promptIdContext.run(prompt_id, async () => {
const { queryToSend, shouldProceed } = await prepareQueryForGemini(
query,
userMessageTimestamp,
abortSignal,
prompt_id
);
if (!shouldProceed || queryToSend === null) {
return;
}
if (!options?.isContinuation) {
if (typeof queryToSend === "string") {
const promptText = queryToSend;
logUserPrompt(
config,
new UserPromptEvent(
promptText.length,
prompt_id,
config.getContentGeneratorConfig()?.authType,
promptText
)
);
}
startNewPrompt();
setThought(null);
}
setIsResponding(true);
setInitError(null);
lastQueryRef.current = queryToSend;
lastPromptIdRef.current = prompt_id;
try {
const stream = geminiClient.sendMessageStream(
queryToSend,
abortSignal,
prompt_id,
void 0,
query
);
const processingStatus = await processGeminiStreamEvents(
stream,
userMessageTimestamp,
abortSignal
);
if (processingStatus === 1 /* UserCancelled */) {
return;
}
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestamp);
setPendingHistoryItem(null);
}
if (loopDetectedRef.current) {
loopDetectedRef.current = false;
setLoopDetectionConfirmationRequest({
onComplete: async (result) => {
setLoopDetectionConfirmationRequest(null);
if (result.userSelection === "disable") {
config.getGeminiClient().getLoopDetectionService().disableForSession();
addItem({
type: "info",
text: `Loop detection has been disabled for this session. Retrying request...`
});
if (lastQueryRef.current && lastPromptIdRef.current) {
await submitQuery(
lastQueryRef.current,
{ isContinuation: true },
lastPromptIdRef.current
);
}
} else {
addItem({
type: "info",
text: `A potential loop was detected. This can happen due to repetitive tool calls or other model behavior. The request has been halted.`
});
}
}
});
}
} catch (error) {
spanMetadata.error = error;
if (error instanceof UnauthorizedError) {
onAuthError("Session expired or is unauthorized.");
} else if (
// Suppress ValidationRequiredError if it was marked as handled (e.g. user clicked change_auth or cancelled)
error instanceof ValidationRequiredError && error.userHandled
) {
} else if (!isNodeError(error) || error.name !== "AbortError") {
maybeAddSuppressedToolErrorNote(userMessageTimestamp);
addItem(
{
type: "error" /* ERROR */,
text: parseAndFormatApiError(
getErrorMessage(error) || "Unknown error",
config.getContentGeneratorConfig()?.authType,
void 0,
config.getModel(),
DEFAULT_GEMINI_FLASH_MODEL
)
},
userMessageTimestamp
);
maybeAddLowVerbosityFailureNote(userMessageTimestamp);
}
} finally {
if (activeQueryIdRef.current === queryId) {
setIsResponding(false);
}
}
});
}
),
[
streamingState,
setModelSwitchedFromQuotaError,
prepareQueryForGemini,
processGeminiStreamEvents,
pendingHistoryItemRef,
addItem,
setPendingHistoryItem,
setInitError,
geminiClient,
onAuthError,
config,
startNewPrompt,
getPromptCount,
setThought,
maybeAddSuppressedToolErrorNote,
maybeAddLowVerbosityFailureNote,
isRespondingRef,
settings.merged.billing?.overageStrategy,
setIsResponding
]
);
const handleApprovalModeChange = (0, import_react99.useCallback)(
async (newApprovalMode) => {
if (previousApprovalModeRef.current === ApprovalMode.PLAN && newApprovalMode !== ApprovalMode.PLAN && streamingState === "idle" /* Idle */) {
if (geminiClient) {
try {
await geminiClient.addHistory({
role: "user",
parts: [
{
text: getPlanModeExitMessage(newApprovalMode, true)
}
]
});
} catch (error) {
onDebugMessage(
`Failed to notify model of Plan Mode exit: ${getErrorMessage(error)}`
);
addItem({
type: "error" /* ERROR */,
text: "Failed to update the model about exiting Plan Mode. The model might be out of sync. Please consider restarting the session if you see unexpected behavior."
});
}
}
}
previousApprovalModeRef.current = newApprovalMode;
if (newApprovalMode === ApprovalMode.YOLO || newApprovalMode === ApprovalMode.AUTO_EDIT) {
let awaitingApprovalCalls = toolCalls.filter(
(call) => call.status === "awaiting_approval" && !call.request.forcedAsk
);
if (newApprovalMode === ApprovalMode.AUTO_EDIT) {
awaitingApprovalCalls = awaitingApprovalCalls.filter(
(call) => EDIT_TOOL_NAMES.has(call.request.name)
);
}
for (const call of awaitingApprovalCalls) {
if (call.correlationId) {
try {
await config.getMessageBus().publish({
type: MessageBusType.TOOL_CONFIRMATION_RESPONSE,
correlationId: call.correlationId,
confirmed: true,
requiresUserConfirmation: false,
outcome: ToolConfirmationOutcome.ProceedOnce
});
} catch (error) {
debugLogger.warn(
`Failed to auto-approve tool call ${call.request.callId}:`,
error
);
}
}
}
}
},
[config, toolCalls, geminiClient, streamingState, addItem, onDebugMessage]
);
const handleCompletedTools = (0, import_react99.useCallback)(
async (completedToolCallsFromScheduler) => {
const completedAndReadyToSubmitTools = completedToolCallsFromScheduler.filter(
(tc) => {
const isTerminalState = tc.status === "success" || tc.status === "error" || tc.status === "cancelled";
if (isTerminalState) {
const completedOrCancelledCall = tc;
return completedOrCancelledCall.response?.responseParts !== void 0;
}
return false;
}
);
const clientTools = completedAndReadyToSubmitTools.filter(
(t) => t.request.isClientInitiated
);
if (clientTools.length > 0) {
markToolsAsSubmitted(clientTools.map((t) => t.request.callId));
if (geminiClient) {
for (const tool of clientTools) {
if (tool.request.name !== ACTIVATE_SKILL_TOOL_NAME) {
continue;
}
await geminiClient.addHistory({
role: "model",
parts: [
{
functionCall: {
name: tool.request.name,
args: tool.request.args
}
}
]
});
await geminiClient.addHistory({
role: "user",
parts: tool.response.responseParts
});
}
}
}
const newSuccessfulMemorySaves = completedAndReadyToSubmitTools.filter(
(t) => t.request.name === "save_memory" && t.status === "success" && !processedMemoryToolsRef.current.has(t.request.callId)
);
for (const toolCall of completedAndReadyToSubmitTools) {
const backgroundedTool = getBackgroundedToolInfo(toolCall);
if (backgroundedTool) {
registerBackgroundTask(
backgroundedTool.pid,
backgroundedTool.command,
backgroundedTool.initialOutput
);
}
}
if (newSuccessfulMemorySaves.length > 0) {
void performMemoryRefresh();
newSuccessfulMemorySaves.forEach(
(t) => processedMemoryToolsRef.current.add(t.request.callId)
);
}
const geminiTools = completedAndReadyToSubmitTools.filter(
(t) => !t.request.isClientInitiated
);
if (isLowErrorVerbosity) {
suppressedToolErrorCountRef.current += geminiTools.filter(
(tc) => tc.status === CoreToolCallStatus.Error
).length;
}
if (geminiTools.length === 0) {
return;
}
const stopExecutionTool = geminiTools.find(
(tc) => tc.response.errorType === ToolErrorType.STOP_EXECUTION
);
if (stopExecutionTool && stopExecutionTool.response.error) {
maybeAddSuppressedToolErrorNote();
addItem({
type: "info" /* INFO */,
text: `Agent execution stopped: ${stopExecutionTool.response.error.message}`
});
maybeAddLowVerbosityFailureNote();
setIsResponding(false);
const callIdsToMarkAsSubmitted2 = geminiTools.map(
(toolCall) => toolCall.request.callId
);
markToolsAsSubmitted(callIdsToMarkAsSubmitted2);
return;
}
const declinableTools = geminiTools.filter(
(tc) => !isTopicTool2(tc.request.name)
);
const allDeclinableToolsCancelled = declinableTools.length > 0 && declinableTools.every(
(tc) => tc.status === CoreToolCallStatus.Cancelled
);
const allToolsCancelled = geminiTools.length > 0 && geminiTools.every((tc) => tc.status === CoreToolCallStatus.Cancelled);
if (allDeclinableToolsCancelled || allToolsCancelled) {
if (!turnCancelledRef.current) {
addItem({
type: "info" /* INFO */,
text: "Request cancelled."
});
}
setIsResponding(false);
if (geminiClient) {
const combinedParts = geminiTools.flatMap(
(toolCall) => toolCall.response.responseParts
);
geminiClient.addHistory({
role: "user",
parts: combinedParts
});
}
const callIdsToMarkAsSubmitted2 = geminiTools.map(
(toolCall) => toolCall.request.callId
);
markToolsAsSubmitted(callIdsToMarkAsSubmitted2);
return;
}
const responsesToSend = geminiTools.flatMap(
(toolCall) => toolCall.response.responseParts
);
if (consumeUserHint) {
const userHint = consumeUserHint();
if (userHint && userHint.trim().length > 0) {
const hintText = userHint.trim();
responsesToSend.unshift({
text: buildUserSteeringHintPrompt(hintText)
});
}
}
const callIdsToMarkAsSubmitted = geminiTools.map(
(toolCall) => toolCall.request.callId
);
const prompt_ids = geminiTools.map(
(toolCall) => toolCall.request.prompt_id
);
markToolsAsSubmitted(callIdsToMarkAsSubmitted);
if (modelSwitchedFromQuotaError) {
return;
}
submitQuery(
responsesToSend,
{
isContinuation: true
},
prompt_ids[0]
);
},
[
submitQuery,
markToolsAsSubmitted,
geminiClient,
performMemoryRefresh,
modelSwitchedFromQuotaError,
addItem,
registerBackgroundTask,
consumeUserHint,
isLowErrorVerbosity,
maybeAddSuppressedToolErrorNote,
maybeAddLowVerbosityFailureNote,
setIsResponding
]
);
const pendingHistoryItems = (0, import_react99.useMemo)(
() => [pendingHistoryItem, ...pendingToolGroupItems].filter(
(i) => i !== void 0 && i !== null
),
[pendingHistoryItem, pendingToolGroupItems]
);
(0, import_react99.useEffect)(() => {
const saveRestorableToolCalls = async () => {
if (!config.getCheckpointingEnabled()) {
return;
}
const restorableToolCalls = toolCalls.filter(
(toolCall) => EDIT_TOOL_NAMES.has(toolCall.request.name) && toolCall.status === CoreToolCallStatus.AwaitingApproval
);
if (restorableToolCalls.length > 0) {
if (!gitService) {
onDebugMessage(
"Checkpointing is enabled but Git service is not available. Failed to create snapshot. Ensure Git is installed and working properly."
);
return;
}
const { checkpointsToWrite, errors } = await processRestorableToolCalls(
restorableToolCalls.map((call) => call.request),
gitService,
geminiClient,
history
);
if (errors.length > 0) {
errors.forEach(onDebugMessage);
}
if (checkpointsToWrite.size > 0) {
const checkpointDir = config.storage.getProjectTempCheckpointsDir();
try {
await fs6.mkdir(checkpointDir, { recursive: true });
for (const [fileName, content] of checkpointsToWrite) {
const filePath = path12.join(checkpointDir, fileName);
await fs6.writeFile(filePath, content);
}
} catch (error) {
onDebugMessage(
`Failed to write checkpoint file: ${getErrorMessage(error)}`
);
}
}
}
};
saveRestorableToolCalls();
}, [toolCalls, config, onDebugMessage, gitService, history, geminiClient]);
const lastOutputTime = Math.max(
lastToolOutputTime,
lastShellOutputTime,
lastGeminiActivityTime
);
return {
streamingState,
submitQuery,
initError,
pendingHistoryItems,
thought,
cancelOngoingRequest,
pendingToolCalls: toolCalls,
handleApprovalModeChange,
activePtyId,
loopDetectionConfirmationRequest,
lastOutputTime,
backgroundTaskCount,
isBackgroundTaskVisible,
toggleBackgroundTasks,
backgroundCurrentExecution,
backgroundTasks,
dismissBackgroundTask,
retryStatus
};
};
// packages/cli/src/ui/hooks/useAgentStream.ts
var import_react101 = __toESM(require_react(), 1);
// packages/cli/src/ui/hooks/useTurnActivityMonitor.ts
var import_react100 = __toESM(require_react(), 1);
var useTurnActivityMonitor = (streamingState, activePtyId, pendingToolCalls = []) => {
const [operationStartTime, setOperationStartTime] = (0, import_react100.useState)(0);
const prevPtyIdRef = (0, import_react100.useRef)(void 0);
const prevStreamingStateRef = (0, import_react100.useRef)(void 0);
(0, import_react100.useEffect)(() => {
const isNowResponding = streamingState === "responding" /* Responding */;
const wasResponding = prevStreamingStateRef.current === "responding" /* Responding */;
const ptyChanged = activePtyId !== prevPtyIdRef.current;
if (isNowResponding && (!wasResponding || ptyChanged)) {
setOperationStartTime(Date.now());
} else if (!isNowResponding && wasResponding) {
setOperationStartTime(0);
}
prevPtyIdRef.current = activePtyId;
prevStreamingStateRef.current = streamingState;
}, [streamingState, activePtyId]);
const isRedirectionActive = (0, import_react100.useMemo)(
() => (
// Check active tool calls for run_shell_command
pendingToolCalls.some((tc) => {
if (tc.request.name !== "run_shell_command") return false;
const command = tc.request.args?.command || "";
return hasRedirection(command);
})
),
[pendingToolCalls]
);
return {
operationStartTime,
isRedirectionActive
};
};
// packages/cli/src/ui/hooks/useAgentStream.ts
var useAgentStream = ({
agent,
addItem,
onCancelSubmit,
isShellFocused,
logger
}) => {
const [initError] = (0, import_react101.useState)(null);
const [retryStatus] = (0, import_react101.useState)(null);
const [streamingState, setStreamingState] = (0, import_react101.useState)(
"idle" /* Idle */
);
const [thought, setThought] = (0, import_react101.useState)(null);
const [lastOutputTime, setLastOutputTime] = (0, import_react101.useState)(Date.now());
const currentStreamIdRef = (0, import_react101.useRef)(null);
const userMessageTimestampRef = (0, import_react101.useRef)(0);
const geminiMessageBufferRef = (0, import_react101.useRef)("");
const [pendingHistoryItem, pendingHistoryItemRef, setPendingHistoryItem] = useStateAndRef(null);
const [trackedTools, , setTrackedTools] = useStateAndRef([]);
const [pushedToolCallIds, pushedToolCallIdsRef, setPushedToolCallIds] = useStateAndRef(/* @__PURE__ */ new Set());
const [_isFirstToolInGroup, isFirstToolInGroupRef, setIsFirstToolInGroup] = useStateAndRef(true);
const [_hasEmittedBoxInTurn, hasEmittedBoxInTurnRef, setHasEmittedBoxInTurn] = useStateAndRef(false);
const { startNewPrompt } = useSessionStats();
const activePtyId = void 0;
const backgroundTaskCount = 0;
const isBackgroundTaskVisible = false;
const toggleBackgroundTasks = (0, import_react101.useCallback)(() => {
}, []);
const backgroundCurrentExecution = void 0;
const backgroundTasks = (0, import_react101.useMemo)(() => /* @__PURE__ */ new Map(), []);
const dismissBackgroundTask = (0, import_react101.useCallback)(async (_pid) => {
}, []);
const pendingToolCalls = (0, import_react101.useMemo)(
() => trackedTools.map((t) => ({
request: {
name: t.originalRequestName || t.name,
args: { command: t.description },
callId: t.callId,
isClientInitiated: t.isClientInitiated ?? false,
prompt_id: ""
},
status: t.status
})),
[trackedTools]
);
const [loopDetectionConfirmationRequest] = (0, import_react101.useState)(null);
const flushPendingText = (0, import_react101.useCallback)(() => {
if (pendingHistoryItemRef.current) {
addItem(pendingHistoryItemRef.current, userMessageTimestampRef.current);
setPendingHistoryItem(null);
geminiMessageBufferRef.current = "";
}
}, [addItem, pendingHistoryItemRef, setPendingHistoryItem]);
const cancelOngoingRequest = (0, import_react101.useCallback)(
async (clearBuffer = true) => {
if (agent) {
await agent.abort();
setStreamingState("idle" /* Idle */);
onCancelSubmit(false, clearBuffer);
}
},
[agent, onCancelSubmit]
);
const handleApprovalModeChange = (0, import_react101.useCallback)(
async (newApprovalMode) => {
debugLogger.debug(`Approval mode changed to ${newApprovalMode} (stub)`);
},
[]
);
const handleEvent = (0, import_react101.useCallback)(
(event) => {
setLastOutputTime(Date.now());
switch (event.type) {
case "agent_start":
setStreamingState("responding" /* Responding */);
break;
case "agent_end":
setStreamingState("idle" /* Idle */);
flushPendingText();
break;
case "message":
if (event.role === "agent") {
for (const part of event.content) {
if (part.type === "text") {
geminiMessageBufferRef.current += part.text;
const splitPoint = findLastSafeSplitPoint(
geminiMessageBufferRef.current
);
if (splitPoint === geminiMessageBufferRef.current.length) {
setPendingHistoryItem({
type: "gemini",
text: geminiMessageBufferRef.current
});
} else {
const before = geminiMessageBufferRef.current.substring(
0,
splitPoint
);
const after = geminiMessageBufferRef.current.substring(splitPoint);
addItem(
{ type: "gemini", text: before },
userMessageTimestampRef.current
);
geminiMessageBufferRef.current = after;
setPendingHistoryItem({
type: "gemini_content",
text: after
});
}
} else if (part.type === "thought") {
setThought(parseThought(part.thought));
}
}
}
break;
case "tool_request": {
flushPendingText();
const legacyState = event._meta?.legacyState;
const displayName = legacyState?.displayName ?? event.name;
const isOutputMarkdown = legacyState?.isOutputMarkdown ?? false;
const desc = legacyState?.description ?? "";
const fallbackKind = Kind.Other;
const newCall = {
callId: event.requestId,
name: displayName,
originalRequestName: event.name,
description: desc,
display: event.display,
status: CoreToolCallStatus.Scheduled,
isClientInitiated: false,
renderOutputAsMarkdown: isOutputMarkdown,
kind: legacyState?.kind ?? fallbackKind,
confirmationDetails: void 0,
resultDisplay: void 0
};
setTrackedTools((prev) => [...prev, newCall]);
break;
}
case "tool_update": {
setTrackedTools(
(prev) => prev.map((tc) => {
if (tc.callId !== event.requestId) return tc;
const legacyState = event._meta?.legacyState;
const evtStatus = legacyState?.status;
let status = tc.status;
if (evtStatus === "executing")
status = CoreToolCallStatus.Executing;
else if (evtStatus === "error") status = CoreToolCallStatus.Error;
else if (evtStatus === "success")
status = CoreToolCallStatus.Success;
const display = event.display?.result;
const liveOutput = displayContentToString(display) ?? tc.resultDisplay;
const progressMessage = legacyState?.progressMessage ?? tc.progressMessage;
const progress = legacyState?.progress ?? tc.progress;
const progressTotal = legacyState?.progressTotal ?? tc.progressTotal;
const ptyId = legacyState?.pid ?? tc.ptyId;
const description = legacyState?.description ?? tc.description;
return {
...tc,
status,
display: event.display ? { ...tc.display, ...event.display } : tc.display,
resultDisplay: liveOutput,
progressMessage,
progress,
progressTotal,
ptyId,
description
};
})
);
break;
}
case "tool_response": {
setTrackedTools(
(prev) => prev.map((tc) => {
if (tc.callId !== event.requestId) return tc;
const legacyState = event._meta?.legacyState;
const outputFile = legacyState?.outputFile;
const display = event.display?.result;
const resultDisplay = displayContentToString(display) ?? tc.resultDisplay;
return {
...tc,
status: event.isError ? CoreToolCallStatus.Error : CoreToolCallStatus.Success,
display: event.display ? { ...tc.display, ...event.display } : tc.display,
resultDisplay,
outputFile
};
})
);
break;
}
case "error": {
const message = event._meta?.["code"] === "AGENT_EXECUTION_BLOCKED" ? `Agent execution blocked: ${event.message}` : event.message;
addItem(
{ type: "error" /* ERROR */, text: message },
userMessageTimestampRef.current
);
break;
}
case "initialize":
case "session_update":
case "elicitation_request":
case "elicitation_response":
case "usage":
case "custom":
break;
default:
debugLogger.error("Unknown agent event type:", event);
event;
break;
}
},
[
addItem,
flushPendingText,
setPendingHistoryItem,
setTrackedTools,
setStreamingState,
setThought,
setLastOutputTime
]
);
(0, import_react101.useEffect)(() => {
const unsubscribe = agent?.subscribe(handleEvent);
return () => unsubscribe?.();
}, [agent, handleEvent]);
useKeypress(
(key) => {
if (key.name === "escape" && !isShellFocused) {
void cancelOngoingRequest(false);
return true;
}
return false;
},
{
isActive: streamingState === "responding" /* Responding */ || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */
}
);
const submitQuery = (0, import_react101.useCallback)(
async (query, options, _prompt_id) => {
if (!agent) return;
const timestamp = Date.now();
setLastOutputTime(timestamp);
userMessageTimestampRef.current = timestamp;
geminiMessageBufferRef.current = "";
if (!options?.isContinuation) {
if (typeof query === "string") {
addItem({ type: "user" /* USER */, text: query }, timestamp);
void logger?.logMessage(MessageSenderType.USER, query);
}
startNewPrompt();
}
const parts = geminiPartsToContentParts(
typeof query === "string" ? [{ text: query }] : query
);
try {
const { streamId } = await agent.send({
message: { content: parts }
});
currentStreamIdRef.current = streamId;
} catch (err) {
addItem(
{ type: "error" /* ERROR */, text: getErrorMessage(err) },
timestamp
);
}
},
[agent, addItem, logger, startNewPrompt]
);
(0, import_react101.useEffect)(() => {
if (trackedTools.length > 0) {
const isNewBatch = !trackedTools.some(
(tc) => pushedToolCallIdsRef.current.has(tc.callId)
);
if (isNewBatch) {
setPushedToolCallIds(/* @__PURE__ */ new Set());
setIsFirstToolInGroup(true);
}
} else if (streamingState === "idle" /* Idle */) {
setPushedToolCallIds(/* @__PURE__ */ new Set());
setIsFirstToolInGroup(true);
}
}, [
trackedTools,
pushedToolCallIdsRef,
setPushedToolCallIds,
setIsFirstToolInGroup,
streamingState
]);
(0, import_react101.useEffect)(() => {
if (trackedTools.length === 0) return;
const allTerminal = trackedTools.every(
(tc) => tc.status === "success" || tc.status === "error" || tc.status === "cancelled"
);
const toolsToPush = trackedTools.filter(
(tc) => !pushedToolCallIdsRef.current.has(tc.callId)
);
if (allTerminal && toolsToPush.length > 0) {
const newPushed = new Set(pushedToolCallIdsRef.current);
for (const tc of toolsToPush) {
newPushed.add(tc.callId);
}
const appearance = getToolGroupBorderAppearance(
{ type: "tool_group", tools: trackedTools },
activePtyId,
!!isShellFocused,
[],
backgroundTasks
);
const hasBoxInBatch = toolsToPush.some(
(tc) => tc.display?.format !== "notice"
);
const shouldStartNewBlock = isFirstToolInGroupRef.current || !hasEmittedBoxInTurnRef.current && hasBoxInBatch;
const historyItem = {
type: "tool_display_group",
tools: toolsToPush.map((tc) => ({
name: tc.name,
description: tc.description,
...tc.display,
status: tc.status,
originalRequestName: tc.originalRequestName
})),
borderTop: shouldStartNewBlock,
borderBottom: true,
...appearance
};
addItem(historyItem);
setPushedToolCallIds(newPushed);
if (hasBoxInBatch) {
setHasEmittedBoxInTurn(true);
}
setIsFirstToolInGroup(false);
}
}, [
trackedTools,
pushedToolCallIdsRef,
isFirstToolInGroupRef,
hasEmittedBoxInTurnRef,
setPushedToolCallIds,
setIsFirstToolInGroup,
setHasEmittedBoxInTurn,
addItem,
activePtyId,
isShellFocused,
backgroundTasks
]);
const pendingToolGroupItems = (0, import_react101.useMemo)(() => {
const remainingTools = trackedTools.filter(
(tc) => !pushedToolCallIdsRef.current.has(tc.callId)
);
const items = [];
const appearance = getToolGroupBorderAppearance(
{ type: "tool_group", tools: trackedTools },
activePtyId,
!!isShellFocused,
[],
backgroundTasks
);
if (remainingTools.length > 0) {
const hasBoxInPending = remainingTools.some(
(tc) => tc.display?.format !== "notice"
);
const shouldStartNewBlock = pushedToolCallIds.size === 0 || !hasEmittedBoxInTurnRef.current && hasBoxInPending;
items.push({
type: "tool_display_group",
tools: remainingTools.map((tc) => ({
name: tc.name,
description: tc.description,
...tc.display,
status: tc.status,
originalRequestName: tc.originalRequestName
})),
borderTop: shouldStartNewBlock,
borderBottom: false,
...appearance
});
}
const allTerminal = trackedTools.length > 0 && trackedTools.every(
(tc) => tc.status === "success" || tc.status === "error" || tc.status === "cancelled"
);
const allPushed = trackedTools.length > 0 && trackedTools.every((tc) => pushedToolCallIds.has(tc.callId));
const anyVisibleInHistory = pushedToolCallIds.size > 0;
const anyVisibleInPending = remainingTools.length > 0;
if (trackedTools.length > 0 && !(allTerminal && allPushed) && (anyVisibleInHistory || anyVisibleInPending)) {
items.push({
type: "tool_display_group",
tools: [],
borderTop: false,
borderBottom: true,
...appearance
});
}
return items;
}, [
trackedTools,
pushedToolCallIds,
pushedToolCallIdsRef,
hasEmittedBoxInTurnRef,
activePtyId,
isShellFocused,
backgroundTasks
]);
const pendingHistoryItems = (0, import_react101.useMemo)(
() => [pendingHistoryItem, ...pendingToolGroupItems].filter(
(i) => i !== void 0 && i !== null
),
[pendingHistoryItem, pendingToolGroupItems]
);
return {
streamingState,
submitQuery,
initError,
pendingHistoryItems,
thought,
cancelOngoingRequest,
pendingToolCalls,
handleApprovalModeChange,
activePtyId,
loopDetectionConfirmationRequest,
lastOutputTime,
backgroundTaskCount,
isBackgroundTaskVisible,
toggleBackgroundTasks,
backgroundCurrentExecution,
backgroundTasks,
retryStatus,
dismissBackgroundTask
};
};
// packages/cli/src/ui/hooks/vim.ts
var import_react102 = __toESM(require_react(), 1);
var DIGIT_MULTIPLIER = 10;
var DEFAULT_COUNT = 1;
var DIGIT_1_TO_9 = /^[1-9]$/;
var DOUBLE_ESCAPE_TIMEOUT_MS = 500;
var CMD_TYPES = {
DELETE_WORD_FORWARD: "dw",
DELETE_WORD_BACKWARD: "db",
DELETE_WORD_END: "de",
DELETE_BIG_WORD_FORWARD: "dW",
DELETE_BIG_WORD_BACKWARD: "dB",
DELETE_BIG_WORD_END: "dE",
CHANGE_WORD_FORWARD: "cw",
CHANGE_WORD_BACKWARD: "cb",
CHANGE_WORD_END: "ce",
CHANGE_BIG_WORD_FORWARD: "cW",
CHANGE_BIG_WORD_BACKWARD: "cB",
CHANGE_BIG_WORD_END: "cE",
DELETE_CHAR: "x",
DELETE_CHAR_BEFORE: "X",
TOGGLE_CASE: "~",
REPLACE_CHAR: "r",
DELETE_LINE: "dd",
CHANGE_LINE: "cc",
DELETE_TO_EOL: "D",
CHANGE_TO_EOL: "C",
CHANGE_MOVEMENT: {
LEFT: "ch",
DOWN: "cj",
UP: "ck",
RIGHT: "cl"
},
DELETE_MOVEMENT: {
LEFT: "dh",
DOWN: "dj",
UP: "dk",
RIGHT: "dl"
},
DELETE_TO_SOL: "d0",
DELETE_TO_FIRST_NONWS: "d^",
CHANGE_TO_SOL: "c0",
CHANGE_TO_FIRST_NONWS: "c^",
DELETE_TO_FIRST_LINE: "dgg",
DELETE_TO_LAST_LINE: "dG",
CHANGE_TO_FIRST_LINE: "cgg",
CHANGE_TO_LAST_LINE: "cG",
YANK_LINE: "yy",
YANK_WORD_FORWARD: "yw",
YANK_BIG_WORD_FORWARD: "yW",
YANK_WORD_END: "ye",
YANK_BIG_WORD_END: "yE",
YANK_TO_EOL: "y$",
PASTE_AFTER: "p",
PASTE_BEFORE: "P"
};
var createClearPendingState = () => ({
count: 0,
pendingOperator: null,
pendingFindOp: void 0
});
var initialVimState = {
mode: "INSERT",
count: 0,
pendingOperator: null,
pendingFindOp: void 0,
lastCommand: null,
lastFind: void 0
};
var vimReducer = (state, action) => {
switch (action.type) {
case "SET_MODE":
return { ...state, mode: action.mode };
case "SET_COUNT":
return { ...state, count: action.count };
case "INCREMENT_COUNT":
return { ...state, count: state.count * DIGIT_MULTIPLIER + action.digit };
case "CLEAR_COUNT":
return { ...state, count: 0 };
case "SET_PENDING_OPERATOR":
return { ...state, pendingOperator: action.operator };
case "SET_PENDING_FIND_OP":
return { ...state, pendingFindOp: action.pendingFindOp };
case "SET_LAST_FIND":
return { ...state, lastFind: action.find };
case "SET_LAST_COMMAND":
return { ...state, lastCommand: action.command };
case "CLEAR_PENDING_STATES":
return {
...state,
...createClearPendingState()
};
case "ESCAPE_TO_NORMAL":
return {
...state,
...createClearPendingState()
};
default:
return state;
}
};
function useVim(buffer, onSubmit) {
const keyMatchers = useKeyMatchers();
const { vimEnabled, vimMode, setVimMode } = useVimMode();
const [state, dispatch] = (0, import_react102.useReducer)(vimReducer, initialVimState);
const lastEscapeTimestampRef = (0, import_react102.useRef)(0);
(0, import_react102.useEffect)(() => {
dispatch({ type: "SET_MODE", mode: vimMode });
}, [vimMode]);
const updateMode = (0, import_react102.useCallback)(
(mode) => {
setVimMode(mode);
dispatch({ type: "SET_MODE", mode });
},
[setVimMode]
);
const getCurrentCount = (0, import_react102.useCallback)(
() => state.count || DEFAULT_COUNT,
[state.count]
);
const checkDoubleEscape = (0, import_react102.useCallback)(() => {
const now = Date.now();
const lastEscape = lastEscapeTimestampRef.current;
lastEscapeTimestampRef.current = now;
if (now - lastEscape <= DOUBLE_ESCAPE_TIMEOUT_MS) {
lastEscapeTimestampRef.current = 0;
return true;
}
return false;
}, []);
const executeCommand = (0, import_react102.useCallback)(
(cmdType, count, char) => {
switch (cmdType) {
case CMD_TYPES.DELETE_WORD_FORWARD: {
buffer.vimDeleteWordForward(count);
break;
}
case CMD_TYPES.DELETE_WORD_BACKWARD: {
buffer.vimDeleteWordBackward(count);
break;
}
case CMD_TYPES.DELETE_WORD_END: {
buffer.vimDeleteWordEnd(count);
break;
}
case CMD_TYPES.DELETE_BIG_WORD_FORWARD: {
buffer.vimDeleteBigWordForward(count);
break;
}
case CMD_TYPES.DELETE_BIG_WORD_BACKWARD: {
buffer.vimDeleteBigWordBackward(count);
break;
}
case CMD_TYPES.DELETE_BIG_WORD_END: {
buffer.vimDeleteBigWordEnd(count);
break;
}
case CMD_TYPES.CHANGE_WORD_FORWARD: {
buffer.vimChangeWordForward(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_WORD_BACKWARD: {
buffer.vimChangeWordBackward(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_WORD_END: {
buffer.vimChangeWordEnd(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_BIG_WORD_FORWARD: {
buffer.vimChangeBigWordForward(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_BIG_WORD_BACKWARD: {
buffer.vimChangeBigWordBackward(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_BIG_WORD_END: {
buffer.vimChangeBigWordEnd(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.DELETE_CHAR: {
buffer.vimDeleteChar(count);
break;
}
case CMD_TYPES.DELETE_CHAR_BEFORE: {
buffer.vimDeleteCharBefore(count);
break;
}
case CMD_TYPES.TOGGLE_CASE: {
buffer.vimToggleCase(count);
break;
}
case CMD_TYPES.REPLACE_CHAR: {
if (char) buffer.vimReplaceChar(char, count);
break;
}
case CMD_TYPES.DELETE_LINE: {
buffer.vimDeleteLine(count);
break;
}
case CMD_TYPES.CHANGE_LINE: {
buffer.vimChangeLine(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_MOVEMENT.LEFT:
case CMD_TYPES.CHANGE_MOVEMENT.DOWN:
case CMD_TYPES.CHANGE_MOVEMENT.UP:
case CMD_TYPES.CHANGE_MOVEMENT.RIGHT: {
const movementMap = {
[CMD_TYPES.CHANGE_MOVEMENT.LEFT]: "h",
[CMD_TYPES.CHANGE_MOVEMENT.DOWN]: "j",
[CMD_TYPES.CHANGE_MOVEMENT.UP]: "k",
[CMD_TYPES.CHANGE_MOVEMENT.RIGHT]: "l"
};
const movementType = movementMap[cmdType];
if (movementType) {
buffer.vimChangeMovement(movementType, count);
updateMode("INSERT");
}
break;
}
case CMD_TYPES.DELETE_TO_EOL: {
buffer.vimDeleteToEndOfLine(count);
break;
}
case CMD_TYPES.DELETE_TO_SOL: {
buffer.vimDeleteToStartOfLine();
break;
}
case CMD_TYPES.DELETE_MOVEMENT.LEFT:
case CMD_TYPES.DELETE_MOVEMENT.DOWN:
case CMD_TYPES.DELETE_MOVEMENT.UP:
case CMD_TYPES.DELETE_MOVEMENT.RIGHT: {
const movementMap = {
[CMD_TYPES.DELETE_MOVEMENT.LEFT]: "h",
[CMD_TYPES.DELETE_MOVEMENT.DOWN]: "j",
[CMD_TYPES.DELETE_MOVEMENT.UP]: "k",
[CMD_TYPES.DELETE_MOVEMENT.RIGHT]: "l"
};
const movementType = movementMap[cmdType];
if (movementType) {
buffer.vimChangeMovement(movementType, count);
}
break;
}
case CMD_TYPES.CHANGE_TO_EOL: {
buffer.vimChangeToEndOfLine(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.DELETE_TO_FIRST_NONWS: {
buffer.vimDeleteToFirstNonWhitespace();
break;
}
case CMD_TYPES.CHANGE_TO_SOL: {
buffer.vimChangeToStartOfLine();
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_TO_FIRST_NONWS: {
buffer.vimChangeToFirstNonWhitespace();
updateMode("INSERT");
break;
}
case CMD_TYPES.DELETE_TO_FIRST_LINE: {
buffer.vimDeleteToFirstLine(count);
break;
}
case CMD_TYPES.DELETE_TO_LAST_LINE: {
buffer.vimDeleteToLastLine(count);
break;
}
case CMD_TYPES.CHANGE_TO_FIRST_LINE: {
buffer.vimDeleteToFirstLine(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.CHANGE_TO_LAST_LINE: {
buffer.vimDeleteToLastLine(count);
updateMode("INSERT");
break;
}
case CMD_TYPES.YANK_LINE: {
buffer.vimYankLine(count);
break;
}
case CMD_TYPES.YANK_WORD_FORWARD: {
buffer.vimYankWordForward(count);
break;
}
case CMD_TYPES.YANK_BIG_WORD_FORWARD: {
buffer.vimYankBigWordForward(count);
break;
}
case CMD_TYPES.YANK_WORD_END: {
buffer.vimYankWordEnd(count);
break;
}
case CMD_TYPES.YANK_BIG_WORD_END: {
buffer.vimYankBigWordEnd(count);
break;
}
case CMD_TYPES.YANK_TO_EOL: {
buffer.vimYankToEndOfLine(count);
break;
}
case CMD_TYPES.PASTE_AFTER: {
buffer.vimPasteAfter(count);
break;
}
case CMD_TYPES.PASTE_BEFORE: {
buffer.vimPasteBefore(count);
break;
}
default:
return false;
}
return true;
},
[buffer, updateMode]
);
const handleInsertModeInput = (0, import_react102.useCallback)(
(normalizedKey) => {
if (keyMatchers["basic.cancel" /* ESCAPE */](normalizedKey)) {
checkDoubleEscape();
buffer.vimEscapeInsertMode();
dispatch({ type: "ESCAPE_TO_NORMAL" });
updateMode("NORMAL");
return true;
}
if (normalizedKey.name === "tab" || normalizedKey.name === "enter" && !normalizedKey.ctrl || normalizedKey.name === "up" || normalizedKey.name === "down" || normalizedKey.ctrl && normalizedKey.name === "r") {
return false;
}
if (normalizedKey.ctrl && (normalizedKey.name === "u" || normalizedKey.name === "k")) {
return false;
}
if (normalizedKey.ctrl && normalizedKey.name === "v") {
return false;
}
if (normalizedKey.sequence === "!" && buffer.text.length === 0) {
return false;
}
if (normalizedKey.name === "enter" && !normalizedKey.alt && !normalizedKey.ctrl && !normalizedKey.cmd) {
if (buffer.text.trim() && onSubmit) {
const submittedValue = buffer.text;
buffer.setText("");
onSubmit(submittedValue);
return true;
}
return true;
}
return buffer.handleInput(normalizedKey);
},
[buffer, dispatch, updateMode, onSubmit, checkDoubleEscape, keyMatchers]
);
const normalizeKey = (0, import_react102.useCallback)(
(key) => ({
name: key.name || "",
sequence: key.sequence || "",
shift: key.shift || false,
alt: key.alt || false,
ctrl: key.ctrl || false,
cmd: key.cmd || false,
insertable: key.insertable || false
}),
[]
);
const handleChangeMovement = (0, import_react102.useCallback)(
(movement) => {
const count = getCurrentCount();
dispatch({ type: "CLEAR_COUNT" });
buffer.vimChangeMovement(movement, count);
updateMode("INSERT");
const cmdTypeMap = {
h: CMD_TYPES.CHANGE_MOVEMENT.LEFT,
j: CMD_TYPES.CHANGE_MOVEMENT.DOWN,
k: CMD_TYPES.CHANGE_MOVEMENT.UP,
l: CMD_TYPES.CHANGE_MOVEMENT.RIGHT
};
dispatch({
type: "SET_LAST_COMMAND",
command: { type: cmdTypeMap[movement], count }
});
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
},
[getCurrentCount, dispatch, buffer, updateMode]
);
const handleDeleteMovement = (0, import_react102.useCallback)(
(movement) => {
const count = getCurrentCount();
dispatch({ type: "CLEAR_COUNT" });
buffer.vimChangeMovement(movement, count);
const cmdTypeMap = {
h: CMD_TYPES.DELETE_MOVEMENT.LEFT,
j: CMD_TYPES.DELETE_MOVEMENT.DOWN,
k: CMD_TYPES.DELETE_MOVEMENT.UP,
l: CMD_TYPES.DELETE_MOVEMENT.RIGHT
};
dispatch({
type: "SET_LAST_COMMAND",
command: { type: cmdTypeMap[movement], count }
});
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
},
[getCurrentCount, dispatch, buffer]
);
const handleOperatorMotion = (0, import_react102.useCallback)(
(operator, motion) => {
const count = getCurrentCount();
const commandMap = {
d: {
w: CMD_TYPES.DELETE_WORD_FORWARD,
b: CMD_TYPES.DELETE_WORD_BACKWARD,
e: CMD_TYPES.DELETE_WORD_END,
W: CMD_TYPES.DELETE_BIG_WORD_FORWARD,
B: CMD_TYPES.DELETE_BIG_WORD_BACKWARD,
E: CMD_TYPES.DELETE_BIG_WORD_END
},
c: {
w: CMD_TYPES.CHANGE_WORD_FORWARD,
b: CMD_TYPES.CHANGE_WORD_BACKWARD,
e: CMD_TYPES.CHANGE_WORD_END,
W: CMD_TYPES.CHANGE_BIG_WORD_FORWARD,
B: CMD_TYPES.CHANGE_BIG_WORD_BACKWARD,
E: CMD_TYPES.CHANGE_BIG_WORD_END
}
};
const cmdType = commandMap[operator][motion];
executeCommand(cmdType, count);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: cmdType, count }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
},
[getCurrentCount, executeCommand, dispatch]
);
const handleInput = (0, import_react102.useCallback)(
(key) => {
if (!vimEnabled) {
return false;
}
let normalizedKey;
try {
normalizedKey = normalizeKey(key);
} catch (error) {
debugLogger.warn("Malformed key input in vim mode:", key, error);
return false;
}
if (keyMatchers["edit.clear" /* CLEAR_INPUT */](normalizedKey)) {
return false;
}
if (state.mode === "INSERT") {
return handleInsertModeInput(normalizedKey);
}
if (state.mode === "NORMAL") {
if (keyMatchers["basic.cancel" /* ESCAPE */](normalizedKey)) {
if (state.pendingOperator || state.pendingFindOp) {
dispatch({ type: "CLEAR_PENDING_STATES" });
lastEscapeTimestampRef.current = 0;
return true;
}
if (checkDoubleEscape()) {
buffer.setText("");
return true;
}
return false;
}
if (DIGIT_1_TO_9.test(normalizedKey.sequence) || normalizedKey.sequence === "0" && state.count > 0) {
dispatch({
type: "INCREMENT_COUNT",
digit: parseInt(normalizedKey.sequence, 10)
});
return true;
}
const repeatCount = getCurrentCount();
if (state.pendingFindOp !== void 0) {
const targetChar = normalizedKey.sequence;
const { op, operator, count: findCount } = state.pendingFindOp;
dispatch({ type: "SET_PENDING_FIND_OP", pendingFindOp: void 0 });
dispatch({ type: "CLEAR_COUNT" });
if (targetChar && toCodePoints(targetChar).length === 1) {
if (op === "r") {
buffer.vimReplaceChar(targetChar, findCount);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.REPLACE_CHAR,
count: findCount,
char: targetChar
}
});
} else {
const isBackward = op === "F" || op === "T";
const isTill = op === "t" || op === "T";
if (operator === "d" || operator === "c") {
const del = isBackward ? buffer.vimDeleteToCharBackward : buffer.vimDeleteToCharForward;
del(targetChar, findCount, isTill);
if (operator === "c") updateMode("INSERT");
} else {
const find = isBackward ? buffer.vimFindCharBackward : buffer.vimFindCharForward;
find(targetChar, findCount, isTill);
dispatch({
type: "SET_LAST_FIND",
find: { op, char: targetChar }
});
}
}
}
return true;
}
switch (normalizedKey.sequence) {
case "h": {
if (state.pendingOperator === "d") {
return handleDeleteMovement("h");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("h");
}
buffer.vimMoveLeft(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "j": {
if (state.pendingOperator === "d") {
return handleDeleteMovement("j");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("j");
}
buffer.vimMoveDown(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "k": {
if (state.pendingOperator === "d") {
return handleDeleteMovement("k");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("k");
}
buffer.vimMoveUp(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "l": {
if (state.pendingOperator === "d") {
return handleDeleteMovement("l");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("l");
}
buffer.vimMoveRight(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "w": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "w");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "w");
}
if (state.pendingOperator === "y") {
const count = getCurrentCount();
executeCommand(CMD_TYPES.YANK_WORD_FORWARD, count);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_WORD_FORWARD, count }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
buffer.vimMoveWordForward(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "W": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "W");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "W");
}
if (state.pendingOperator === "y") {
const count = getCurrentCount();
executeCommand(CMD_TYPES.YANK_BIG_WORD_FORWARD, count);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_BIG_WORD_FORWARD, count }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
buffer.vimMoveBigWordForward(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "b": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "b");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "b");
}
buffer.vimMoveWordBackward(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "B": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "B");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "B");
}
buffer.vimMoveBigWordBackward(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "e": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "e");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "e");
}
if (state.pendingOperator === "y") {
const count = getCurrentCount();
executeCommand(CMD_TYPES.YANK_WORD_END, count);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_WORD_END, count }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
buffer.vimMoveWordEnd(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "E": {
if (state.pendingOperator === "d") {
return handleOperatorMotion("d", "E");
}
if (state.pendingOperator === "c") {
return handleOperatorMotion("c", "E");
}
if (state.pendingOperator === "y") {
const count = getCurrentCount();
executeCommand(CMD_TYPES.YANK_BIG_WORD_END, count);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_BIG_WORD_END, count }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
buffer.vimMoveBigWordEnd(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "x": {
buffer.vimDeleteChar(repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_CHAR, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "X": {
buffer.vimDeleteCharBefore(repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.DELETE_CHAR_BEFORE,
count: repeatCount
}
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "~": {
buffer.vimToggleCase(repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.TOGGLE_CASE, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "r": {
dispatch({ type: "CLEAR_PENDING_STATES" });
dispatch({
type: "SET_PENDING_FIND_OP",
pendingFindOp: {
op: "r",
operator: void 0,
count: repeatCount
}
});
return true;
}
case "f":
case "F":
case "t":
case "T": {
const op = normalizedKey.sequence;
const operator = state.pendingOperator === "d" || state.pendingOperator === "c" ? state.pendingOperator : void 0;
dispatch({ type: "CLEAR_PENDING_STATES" });
dispatch({
type: "SET_PENDING_FIND_OP",
pendingFindOp: { op, operator, count: repeatCount }
});
return true;
}
case ";":
case ",": {
if (state.lastFind) {
const { op, char } = state.lastFind;
const isForward = op === "f" || op === "t";
const isTill = op === "t" || op === "T";
const reverse = normalizedKey.sequence === ",";
const shouldMoveForward = reverse ? !isForward : isForward;
if (shouldMoveForward) {
buffer.vimFindCharForward(char, repeatCount, isTill);
} else {
buffer.vimFindCharBackward(char, repeatCount, isTill);
}
}
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "i": {
buffer.vimInsertAtCursor();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "a": {
buffer.vimAppendAtCursor();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "o": {
buffer.vimOpenLineBelow();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "O": {
buffer.vimOpenLineAbove();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "0": {
if (state.pendingOperator === "d") {
buffer.vimDeleteToStartOfLine();
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_TO_SOL, count: 1 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
if (state.pendingOperator === "c") {
buffer.vimChangeToStartOfLine();
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.CHANGE_TO_SOL, count: 1 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
updateMode("INSERT");
return true;
}
buffer.vimMoveToLineStart();
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "$": {
if (state.pendingOperator === "d") {
buffer.vimDeleteToEndOfLine(repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_TO_EOL, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
if (state.pendingOperator === "c") {
buffer.vimChangeToEndOfLine(repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.CHANGE_TO_EOL, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
updateMode("INSERT");
return true;
}
if (state.pendingOperator === "y") {
executeCommand(CMD_TYPES.YANK_TO_EOL, repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_TO_EOL, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
if (repeatCount > 1) {
buffer.vimMoveDown(repeatCount - 1);
}
buffer.vimMoveToLineEnd();
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "^": {
if (state.pendingOperator === "d") {
buffer.vimDeleteToFirstNonWhitespace();
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_TO_FIRST_NONWS, count: 1 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
if (state.pendingOperator === "c") {
buffer.vimChangeToFirstNonWhitespace();
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.CHANGE_TO_FIRST_NONWS, count: 1 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
updateMode("INSERT");
return true;
}
buffer.vimMoveToFirstNonWhitespace();
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "g": {
if (state.pendingOperator === "d") {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "dg" });
return true;
}
if (state.pendingOperator === "c") {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "cg" });
return true;
}
if (state.pendingOperator === "dg") {
buffer.vimDeleteToFirstLine(state.count);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.DELETE_TO_FIRST_LINE,
count: state.count
}
});
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
dispatch({ type: "CLEAR_COUNT" });
return true;
}
if (state.pendingOperator === "cg") {
buffer.vimDeleteToFirstLine(state.count);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.CHANGE_TO_FIRST_LINE,
count: state.count
}
});
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
dispatch({ type: "CLEAR_COUNT" });
updateMode("INSERT");
return true;
}
if (state.pendingOperator === "g") {
if (state.count > 0) {
buffer.vimMoveToLine(state.count);
} else {
buffer.vimMoveToFirstLine();
}
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
dispatch({ type: "CLEAR_COUNT" });
} else {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "g" });
}
return true;
}
case "G": {
if (state.pendingOperator === "d") {
buffer.vimDeleteToLastLine(state.count);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.DELETE_TO_LAST_LINE,
count: state.count
}
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
return true;
}
if (state.pendingOperator === "c") {
buffer.vimDeleteToLastLine(state.count);
dispatch({
type: "SET_LAST_COMMAND",
command: {
type: CMD_TYPES.CHANGE_TO_LAST_LINE,
count: state.count
}
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
updateMode("INSERT");
return true;
}
if (state.count > 0) {
buffer.vimMoveToLine(state.count);
} else {
buffer.vimMoveToLastLine();
}
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "I": {
buffer.vimInsertAtLineStart();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "A": {
buffer.vimAppendAtLineEnd();
updateMode("INSERT");
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "d": {
if (state.pendingOperator === "d") {
const repeatCount2 = getCurrentCount();
executeCommand(CMD_TYPES.DELETE_LINE, repeatCount2);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_LINE, count: repeatCount2 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
} else {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "d" });
}
return true;
}
case "c": {
if (state.pendingOperator === "c") {
const repeatCount2 = getCurrentCount();
executeCommand(CMD_TYPES.CHANGE_LINE, repeatCount2);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.CHANGE_LINE, count: repeatCount2 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
} else {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "c" });
}
return true;
}
case "y": {
if (state.pendingOperator === "y") {
const repeatCount2 = getCurrentCount();
executeCommand(CMD_TYPES.YANK_LINE, repeatCount2);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_LINE, count: repeatCount2 }
});
dispatch({ type: "CLEAR_COUNT" });
dispatch({ type: "SET_PENDING_OPERATOR", operator: null });
} else if (state.pendingOperator === null) {
dispatch({ type: "SET_PENDING_OPERATOR", operator: "y" });
} else {
dispatch({ type: "CLEAR_PENDING_STATES" });
}
return true;
}
case "Y": {
const repeatCount2 = getCurrentCount();
executeCommand(CMD_TYPES.YANK_TO_EOL, repeatCount2);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.YANK_TO_EOL, count: repeatCount2 }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "p": {
executeCommand(CMD_TYPES.PASTE_AFTER, repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.PASTE_AFTER, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "P": {
executeCommand(CMD_TYPES.PASTE_BEFORE, repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.PASTE_BEFORE, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "D": {
executeCommand(CMD_TYPES.DELETE_TO_EOL, repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.DELETE_TO_EOL, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "C": {
executeCommand(CMD_TYPES.CHANGE_TO_EOL, repeatCount);
dispatch({
type: "SET_LAST_COMMAND",
command: { type: CMD_TYPES.CHANGE_TO_EOL, count: repeatCount }
});
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case "u": {
for (let i = 0; i < repeatCount; i++) {
buffer.undo();
}
dispatch({ type: "CLEAR_COUNT" });
return true;
}
case ".": {
if (state.lastCommand) {
const cmdData = state.lastCommand;
const count = state.count > 0 ? state.count : cmdData.count;
executeCommand(cmdData.type, count, cmdData.char);
}
dispatch({ type: "CLEAR_COUNT" });
return true;
}
default: {
if (normalizedKey.name === "left") {
if (state.pendingOperator === "d") {
return handleDeleteMovement("h");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("h");
}
buffer.vimMoveLeft(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
if (normalizedKey.name === "down") {
if (state.pendingOperator === "d") {
return handleDeleteMovement("j");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("j");
}
buffer.vimMoveDown(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
if (normalizedKey.name === "up") {
if (state.pendingOperator === "d") {
return handleDeleteMovement("k");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("k");
}
buffer.vimMoveUp(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
if (normalizedKey.name === "right") {
if (state.pendingOperator === "d") {
return handleDeleteMovement("l");
}
if (state.pendingOperator === "c") {
return handleChangeMovement("l");
}
buffer.vimMoveRight(repeatCount);
dispatch({ type: "CLEAR_COUNT" });
return true;
}
dispatch({ type: "CLEAR_PENDING_STATES" });
return false;
}
}
}
return false;
},
[
vimEnabled,
normalizeKey,
handleInsertModeInput,
state.mode,
state.count,
state.pendingOperator,
state.pendingFindOp,
state.lastCommand,
state.lastFind,
dispatch,
getCurrentCount,
handleChangeMovement,
handleDeleteMovement,
handleOperatorMotion,
buffer,
executeCommand,
updateMode,
checkDoubleEscape,
keyMatchers
]
);
return {
mode: state.mode,
vimModeEnabled: vimEnabled,
handleInput
// Expose the input handler for InputPrompt to use
};
}
// packages/cli/src/ui/hooks/useTimer.ts
var import_react103 = __toESM(require_react(), 1);
var useTimer = (isActive, resetKey) => {
const [elapsedTime, setElapsedTime] = (0, import_react103.useState)(0);
const timerRef = (0, import_react103.useRef)(null);
const prevResetKeyRef = (0, import_react103.useRef)(resetKey);
const prevIsActiveRef = (0, import_react103.useRef)(isActive);
(0, import_react103.useEffect)(() => {
let shouldResetTime = false;
if (prevResetKeyRef.current !== resetKey) {
shouldResetTime = true;
prevResetKeyRef.current = resetKey;
}
if (prevIsActiveRef.current === false && isActive) {
shouldResetTime = true;
}
if (shouldResetTime) {
setElapsedTime(0);
}
prevIsActiveRef.current = isActive;
if (isActive) {
if (timerRef.current) {
clearInterval(timerRef.current);
}
timerRef.current = setInterval(() => {
setElapsedTime((prev) => prev + 1);
}, 1e3);
} else {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
}
return () => {
if (timerRef.current) {
clearInterval(timerRef.current);
timerRef.current = null;
}
};
}, [isActive, resetKey]);
return elapsedTime;
};
// packages/cli/src/ui/hooks/useLoadingIndicator.ts
var import_react104 = __toESM(require_react(), 1);
var LOW_VERBOSITY_RETRY_HINT_ATTEMPT_THRESHOLD = 2;
var useLoadingIndicator = ({
streamingState,
shouldShowFocusHint,
retryStatus,
showTips = true,
showWit = false,
customWittyPhrases,
errorVerbosity = "full",
maxLength
}) => {
const [timerResetKey, setTimerResetKey] = (0, import_react104.useState)(0);
const isTimerActive = streamingState === "responding" /* Responding */;
const elapsedTimeFromTimer = useTimer(isTimerActive, timerResetKey);
const isPhraseCyclingActive = streamingState === "responding" /* Responding */;
const isWaiting = streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */;
const { currentTip, currentWittyPhrase } = usePhraseCycler(
isPhraseCyclingActive,
isWaiting,
shouldShowFocusHint,
showTips,
showWit,
customWittyPhrases,
maxLength
);
const [retainedElapsedTime, setRetainedElapsedTime] = (0, import_react104.useState)(0);
const prevStreamingStateRef = (0, import_react104.useRef)(null);
(0, import_react104.useEffect)(() => {
if (prevStreamingStateRef.current === "waiting_for_confirmation" /* WaitingForConfirmation */ && streamingState === "responding" /* Responding */) {
setTimerResetKey((prevKey) => prevKey + 1);
setRetainedElapsedTime(0);
} else if (streamingState === "idle" /* Idle */ && prevStreamingStateRef.current === "responding" /* Responding */) {
setTimerResetKey((prevKey) => prevKey + 1);
setRetainedElapsedTime(0);
} else if (streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */) {
setRetainedElapsedTime(elapsedTimeFromTimer);
}
prevStreamingStateRef.current = streamingState;
}, [streamingState, elapsedTimeFromTimer]);
const retryPhrase = streamingState === "responding" /* Responding */ && retryStatus ? errorVerbosity === "low" ? retryStatus.attempt >= LOW_VERBOSITY_RETRY_HINT_ATTEMPT_THRESHOLD ? "This is taking a bit longer, we're still on it." : null : `Trying to reach ${getDisplayString(retryStatus.model)} (Attempt ${retryStatus.attempt + 1}/${retryStatus.maxAttempts})` : null;
return {
elapsedTime: streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */ ? retainedElapsedTime : elapsedTimeFromTimer,
currentLoadingPhrase: retryPhrase || currentTip || currentWittyPhrase,
currentTip,
currentWittyPhrase
};
};
// packages/cli/src/ui/hooks/useShellInactivityStatus.ts
var useShellInactivityStatus = ({
activePtyId,
lastOutputTime,
streamingState,
pendingToolCalls,
embeddedShellFocused,
isInteractiveShellEnabled
}) => {
const { operationStartTime, isRedirectionActive } = useTurnActivityMonitor(
streamingState,
activePtyId,
pendingToolCalls
);
const isAwaitingFocus = !!activePtyId && !embeddedShellFocused && isInteractiveShellEnabled;
const hasProducedOutput = lastOutputTime > operationStartTime;
const shouldShowFocusHint = useInactivityTimer(
isAwaitingFocus && !isRedirectionActive,
lastOutputTime,
hasProducedOutput ? SHELL_FOCUS_HINT_DELAY_MS : SHELL_FOCUS_HINT_DELAY_MS * 4
);
const shouldShowActionRequiredTitle = useInactivityTimer(
isAwaitingFocus && !isRedirectionActive && hasProducedOutput,
lastOutputTime,
SHELL_ACTION_REQUIRED_TITLE_DELAY_MS
);
const shouldShowSilentWorkingTitle = useInactivityTimer(
isAwaitingFocus && (isRedirectionActive || !hasProducedOutput),
lastOutputTime,
isRedirectionActive ? SHELL_SILENT_WORKING_TITLE_DELAY_MS : SHELL_ACTION_REQUIRED_TITLE_DELAY_MS * 2
);
let inactivityStatus = "none";
if (shouldShowActionRequiredTitle) {
inactivityStatus = "action_required";
} else if (shouldShowSilentWorkingTitle) {
inactivityStatus = "silent_working";
}
return {
shouldShowFocusHint,
inactivityStatus
};
};
// packages/cli/src/ui/hooks/useFolderTrust.ts
var import_react105 = __toESM(require_react(), 1);
import * as process10 from "node:process";
var useFolderTrust = (settings, onTrustChange, addItem) => {
const [isTrusted, setIsTrusted] = (0, import_react105.useState)(void 0);
const [isFolderTrustDialogOpen, setIsFolderTrustDialogOpen] = (0, import_react105.useState)(false);
const [discoveryResults, setDiscoveryResults] = (0, import_react105.useState)(null);
const [isRestarting, setIsRestarting] = (0, import_react105.useState)(false);
const startupMessageSent = (0, import_react105.useRef)(false);
const folderTrust = settings.merged.security.folderTrust.enabled ?? true;
(0, import_react105.useEffect)(() => {
let isMounted = true;
const { isTrusted: trusted } = isWorkspaceTrusted(settings.merged);
if (trusted === void 0 || trusted === false) {
void FolderTrustDiscoveryService.discover(process10.cwd()).then((results) => {
if (isMounted) {
setDiscoveryResults(results);
}
}).catch(() => {
});
}
const showUntrustedMessage = () => {
if (trusted === false && !startupMessageSent.current) {
addItem(
{
type: "info" /* INFO */,
text: "This folder is untrusted, project settings, hooks, MCPs, and GEMINI.md files will not be applied for this folder.\nUse the `/permissions` command to change the trust level."
},
Date.now()
);
startupMessageSent.current = true;
}
};
if (isHeadlessMode()) {
if (isMounted) {
setIsTrusted(trusted);
setIsFolderTrustDialogOpen(false);
onTrustChange(true);
showUntrustedMessage();
}
} else if (isMounted) {
setIsTrusted(trusted);
setIsFolderTrustDialogOpen(trusted === void 0);
onTrustChange(trusted);
showUntrustedMessage();
}
return () => {
isMounted = false;
};
}, [folderTrust, onTrustChange, settings.merged, addItem]);
const handleFolderTrustSelect = (0, import_react105.useCallback)(
async (choice) => {
const trustLevelMap = {
["trust_folder" /* TRUST_FOLDER */]: TrustLevel.TRUST_FOLDER,
["trust_parent" /* TRUST_PARENT */]: TrustLevel.TRUST_PARENT,
["do_not_trust" /* DO_NOT_TRUST */]: TrustLevel.DO_NOT_TRUST
};
const trustLevel = trustLevelMap[choice];
if (!trustLevel) return;
const cwd5 = process10.cwd();
const trustedFolders = loadTrustedFolders();
try {
await trustedFolders.setValue(cwd5, trustLevel);
} catch {
coreEvents.emitFeedback(
"error",
"Failed to save trust settings. Exiting Gemini CLI."
);
setTimeout(async () => {
await runExitCleanup();
process10.exit(ExitCodes.FATAL_CONFIG_ERROR);
}, 100);
return;
}
const currentIsTrusted = trustLevel === TrustLevel.TRUST_FOLDER || trustLevel === TrustLevel.TRUST_PARENT;
onTrustChange(currentIsTrusted);
setIsTrusted(currentIsTrusted);
const wasTrusted = isTrusted ?? false;
if (wasTrusted !== currentIsTrusted) {
setIsRestarting(true);
setIsFolderTrustDialogOpen(true);
} else {
setIsFolderTrustDialogOpen(false);
}
},
[onTrustChange, isTrusted]
);
return {
isTrusted,
isFolderTrustDialogOpen,
discoveryResults,
handleFolderTrustSelect,
isRestarting
};
};
// node_modules/ky/distribution/errors/HTTPError.js
var HTTPError = class extends Error {
response;
request;
options;
constructor(response, request, options) {
const code = response.status || response.status === 0 ? response.status : "";
const title = response.statusText ?? "";
const status = `${code} ${title}`.trim();
const reason = status ? `status code ${status}` : "an unknown error";
super(`Request failed with ${reason}: ${request.method} ${request.url}`);
this.name = "HTTPError";
this.response = response;
this.request = request;
this.options = options;
}
};
// node_modules/ky/distribution/errors/NonError.js
var NonError = class extends Error {
name = "NonError";
value;
constructor(value) {
let message = "Non-error value was thrown";
try {
if (typeof value === "string") {
message = value;
} else if (value && typeof value === "object" && "message" in value && typeof value.message === "string") {
message = value.message;
}
} catch {
}
super(message);
this.value = value;
}
};
// node_modules/ky/distribution/core/constants.js
var supportsRequestStreams = (() => {
let duplexAccessed = false;
let hasContentType = false;
const supportsReadableStream = typeof globalThis.ReadableStream === "function";
const supportsRequest = typeof globalThis.Request === "function";
if (supportsReadableStream && supportsRequest) {
try {
hasContentType = new globalThis.Request("https://empty.invalid", {
body: new globalThis.ReadableStream(),
method: "POST",
// @ts-expect-error - Types are outdated.
get duplex() {
duplexAccessed = true;
return "half";
}
}).headers.has("Content-Type");
} catch (error) {
if (error instanceof Error && error.message === "unsupported BodyInit type") {
return false;
}
throw error;
}
}
return duplexAccessed && !hasContentType;
})();
var supportsAbortController = typeof globalThis.AbortController === "function";
var supportsAbortSignal = typeof globalThis.AbortSignal === "function" && typeof globalThis.AbortSignal.any === "function";
var supportsResponseStreams = typeof globalThis.ReadableStream === "function";
var supportsFormData = typeof globalThis.FormData === "function";
var requestMethods = ["get", "post", "put", "patch", "head", "delete"];
var validate = () => void 0;
validate();
var responseTypes = {
json: "application/json",
text: "text/*",
formData: "multipart/form-data",
arrayBuffer: "*/*",
blob: "*/*",
// Supported in modern Fetch implementations (for example, browsers and recent Node.js/undici).
// We still feature-check at runtime before exposing the shortcut.
bytes: "*/*"
};
var maxSafeTimeout = 2147483647;
var usualFormBoundarySize = new TextEncoder().encode("------WebKitFormBoundaryaxpyiPgbbPti10Rw").length;
var stop = Symbol("stop");
var kyOptionKeys = {
json: true,
parseJson: true,
stringifyJson: true,
searchParams: true,
prefixUrl: true,
retry: true,
timeout: true,
hooks: true,
throwHttpErrors: true,
onDownloadProgress: true,
onUploadProgress: true,
fetch: true,
context: true
};
var vendorSpecificOptions = {
next: true
// Next.js cache revalidation (revalidate, tags)
};
var requestOptionsRegistry = {
method: true,
headers: true,
body: true,
mode: true,
credentials: true,
cache: true,
redirect: true,
referrer: true,
referrerPolicy: true,
integrity: true,
keepalive: true,
signal: true,
window: true,
duplex: true
};
// node_modules/ky/distribution/utils/body.js
var getBodySize = (body) => {
if (!body) {
return 0;
}
if (body instanceof FormData) {
let size = 0;
for (const [key, value] of body) {
size += usualFormBoundarySize;
size += new TextEncoder().encode(`Content-Disposition: form-data; name="${key}"`).length;
size += typeof value === "string" ? new TextEncoder().encode(value).length : value.size;
}
return size;
}
if (body instanceof Blob) {
return body.size;
}
if (body instanceof ArrayBuffer) {
return body.byteLength;
}
if (typeof body === "string") {
return new TextEncoder().encode(body).length;
}
if (body instanceof URLSearchParams) {
return new TextEncoder().encode(body.toString()).length;
}
if ("byteLength" in body) {
return body.byteLength;
}
if (typeof body === "object" && body !== null) {
try {
const jsonString = JSON.stringify(body);
return new TextEncoder().encode(jsonString).length;
} catch {
return 0;
}
}
return 0;
};
var withProgress = (stream, totalBytes, onProgress) => {
let previousChunk;
let transferredBytes = 0;
return stream.pipeThrough(new TransformStream({
transform(currentChunk, controller) {
controller.enqueue(currentChunk);
if (previousChunk) {
transferredBytes += previousChunk.byteLength;
let percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
if (percent >= 1) {
percent = 1 - Number.EPSILON;
}
onProgress?.({ percent, totalBytes: Math.max(totalBytes, transferredBytes), transferredBytes }, previousChunk);
}
previousChunk = currentChunk;
},
flush() {
if (previousChunk) {
transferredBytes += previousChunk.byteLength;
onProgress?.({ percent: 1, totalBytes: Math.max(totalBytes, transferredBytes), transferredBytes }, previousChunk);
}
}
}));
};
var streamResponse = (response, onDownloadProgress) => {
if (!response.body) {
return response;
}
if (response.status === 204) {
return new Response(null, {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
}
const totalBytes = Math.max(0, Number(response.headers.get("content-length")) || 0);
return new Response(withProgress(response.body, totalBytes, onDownloadProgress), {
status: response.status,
statusText: response.statusText,
headers: response.headers
});
};
var streamRequest = (request, onUploadProgress, originalBody) => {
if (!request.body) {
return request;
}
const totalBytes = getBodySize(originalBody ?? request.body);
return new Request(request, {
// @ts-expect-error - Types are outdated.
duplex: "half",
body: withProgress(request.body, totalBytes, onUploadProgress)
});
};
// node_modules/ky/distribution/utils/is.js
var isObject = (value) => value !== null && typeof value === "object";
// node_modules/ky/distribution/utils/merge.js
var validateAndMerge = (...sources) => {
for (const source of sources) {
if ((!isObject(source) || Array.isArray(source)) && source !== void 0) {
throw new TypeError("The `options` argument must be an object");
}
}
return deepMerge({}, ...sources);
};
var mergeHeaders = (source1 = {}, source2 = {}) => {
const result = new globalThis.Headers(source1);
const isHeadersInstance = source2 instanceof globalThis.Headers;
const source = new globalThis.Headers(source2);
for (const [key, value] of source.entries()) {
if (isHeadersInstance && value === "undefined" || value === void 0) {
result.delete(key);
} else {
result.set(key, value);
}
}
return result;
};
function newHookValue(original, incoming, property) {
return Object.hasOwn(incoming, property) && incoming[property] === void 0 ? [] : deepMerge(original[property] ?? [], incoming[property] ?? []);
}
var mergeHooks = (original = {}, incoming = {}) => ({
beforeRequest: newHookValue(original, incoming, "beforeRequest"),
beforeRetry: newHookValue(original, incoming, "beforeRetry"),
afterResponse: newHookValue(original, incoming, "afterResponse"),
beforeError: newHookValue(original, incoming, "beforeError")
});
var appendSearchParameters = (target, source) => {
const result = new URLSearchParams();
for (const input of [target, source]) {
if (input === void 0) {
continue;
}
if (input instanceof URLSearchParams) {
for (const [key, value] of input.entries()) {
result.append(key, value);
}
} else if (Array.isArray(input)) {
for (const pair of input) {
if (!Array.isArray(pair) || pair.length !== 2) {
throw new TypeError("Array search parameters must be provided in [[key, value], ...] format");
}
result.append(String(pair[0]), String(pair[1]));
}
} else if (isObject(input)) {
for (const [key, value] of Object.entries(input)) {
if (value !== void 0) {
result.append(key, String(value));
}
}
} else {
const parameters = new URLSearchParams(input);
for (const [key, value] of parameters.entries()) {
result.append(key, value);
}
}
}
return result;
};
var deepMerge = (...sources) => {
let returnValue = {};
let headers = {};
let hooks = {};
let searchParameters;
const signals = [];
for (const source of sources) {
if (Array.isArray(source)) {
if (!Array.isArray(returnValue)) {
returnValue = [];
}
returnValue = [...returnValue, ...source];
} else if (isObject(source)) {
for (let [key, value] of Object.entries(source)) {
if (key === "signal" && value instanceof globalThis.AbortSignal) {
signals.push(value);
continue;
}
if (key === "context") {
if (value !== void 0 && value !== null && (!isObject(value) || Array.isArray(value))) {
throw new TypeError("The `context` option must be an object");
}
returnValue = {
...returnValue,
context: value === void 0 || value === null ? {} : { ...returnValue.context, ...value }
};
continue;
}
if (key === "searchParams") {
if (value === void 0 || value === null) {
searchParameters = void 0;
} else {
searchParameters = searchParameters === void 0 ? value : appendSearchParameters(searchParameters, value);
}
continue;
}
if (isObject(value) && key in returnValue) {
value = deepMerge(returnValue[key], value);
}
returnValue = { ...returnValue, [key]: value };
}
if (isObject(source.hooks)) {
hooks = mergeHooks(hooks, source.hooks);
returnValue.hooks = hooks;
}
if (isObject(source.headers)) {
headers = mergeHeaders(headers, source.headers);
returnValue.headers = headers;
}
}
}
if (searchParameters !== void 0) {
returnValue.searchParams = searchParameters;
}
if (signals.length > 0) {
if (signals.length === 1) {
returnValue.signal = signals[0];
} else if (supportsAbortSignal) {
returnValue.signal = AbortSignal.any(signals);
} else {
returnValue.signal = signals.at(-1);
}
}
if (returnValue.context === void 0) {
returnValue.context = {};
}
return returnValue;
};
// node_modules/ky/distribution/utils/normalize.js
var normalizeRequestMethod = (input) => requestMethods.includes(input) ? input.toUpperCase() : input;
var retryMethods = ["get", "put", "head", "delete", "options", "trace"];
var retryStatusCodes = [408, 413, 429, 500, 502, 503, 504];
var retryAfterStatusCodes = [413, 429, 503];
var defaultRetryOptions = {
limit: 2,
methods: retryMethods,
statusCodes: retryStatusCodes,
afterStatusCodes: retryAfterStatusCodes,
maxRetryAfter: Number.POSITIVE_INFINITY,
backoffLimit: Number.POSITIVE_INFINITY,
delay: (attemptCount) => 0.3 * 2 ** (attemptCount - 1) * 1e3,
jitter: void 0,
retryOnTimeout: false
};
var normalizeRetryOptions = (retry = {}) => {
if (typeof retry === "number") {
return {
...defaultRetryOptions,
limit: retry
};
}
if (retry.methods && !Array.isArray(retry.methods)) {
throw new Error("retry.methods must be an array");
}
if (retry.statusCodes && !Array.isArray(retry.statusCodes)) {
throw new Error("retry.statusCodes must be an array");
}
return {
...defaultRetryOptions,
...retry
};
};
// node_modules/ky/distribution/errors/TimeoutError.js
var TimeoutError = class extends Error {
request;
constructor(request) {
super(`Request timed out: ${request.method} ${request.url}`);
this.name = "TimeoutError";
this.request = request;
}
};
// node_modules/ky/distribution/utils/timeout.js
async function timeout(request, init, abortController, options) {
return new Promise((resolve3, reject) => {
const timeoutId2 = setTimeout(() => {
if (abortController) {
abortController.abort();
}
reject(new TimeoutError(request));
}, options.timeout);
void options.fetch(request, init).then(resolve3).catch(reject).then(() => {
clearTimeout(timeoutId2);
});
});
}
// node_modules/ky/distribution/utils/delay.js
async function delay(ms, { signal }) {
return new Promise((resolve3, reject) => {
if (signal) {
signal.throwIfAborted();
signal.addEventListener("abort", abortHandler, { once: true });
}
function abortHandler() {
clearTimeout(timeoutId2);
reject(signal.reason);
}
const timeoutId2 = setTimeout(() => {
signal?.removeEventListener("abort", abortHandler);
resolve3();
}, ms);
});
}
// node_modules/ky/distribution/utils/options.js
var findUnknownOptions = (request, options) => {
const unknownOptions = {};
for (const key in options) {
if (!Object.hasOwn(options, key)) {
continue;
}
if (!(key in requestOptionsRegistry) && !(key in kyOptionKeys) && (!(key in request) || key in vendorSpecificOptions)) {
unknownOptions[key] = options[key];
}
}
return unknownOptions;
};
var hasSearchParameters = (search) => {
if (search === void 0) {
return false;
}
if (Array.isArray(search)) {
return search.length > 0;
}
if (search instanceof URLSearchParams) {
return search.size > 0;
}
if (typeof search === "object") {
return Object.keys(search).length > 0;
}
if (typeof search === "string") {
return search.trim().length > 0;
}
return Boolean(search);
};
// node_modules/ky/distribution/utils/type-guards.js
function isHTTPError(error) {
return error instanceof HTTPError || error?.name === HTTPError.name;
}
function isTimeoutError(error) {
return error instanceof TimeoutError || error?.name === TimeoutError.name;
}
// node_modules/ky/distribution/core/Ky.js
var Ky = class _Ky {
static create(input, options) {
const ky2 = new _Ky(input, options);
const function_ = async () => {
if (typeof ky2.#options.timeout === "number" && ky2.#options.timeout > maxSafeTimeout) {
throw new RangeError(`The \`timeout\` option cannot be greater than ${maxSafeTimeout}`);
}
await Promise.resolve();
let response = await ky2.#fetch();
for (const hook of ky2.#options.hooks.afterResponse) {
const modifiedResponse = await hook(ky2.request, ky2.#getNormalizedOptions(), ky2.#decorateResponse(response.clone()), { retryCount: ky2.#retryCount });
if (modifiedResponse instanceof globalThis.Response) {
response = modifiedResponse;
}
}
ky2.#decorateResponse(response);
if (!response.ok && ky2.#options.throwHttpErrors) {
let error = new HTTPError(response, ky2.request, ky2.#getNormalizedOptions());
for (const hook of ky2.#options.hooks.beforeError) {
error = await hook(error, { retryCount: ky2.#retryCount });
}
throw error;
}
if (ky2.#options.onDownloadProgress) {
if (typeof ky2.#options.onDownloadProgress !== "function") {
throw new TypeError("The `onDownloadProgress` option must be a function");
}
if (!supportsResponseStreams) {
throw new Error("Streams are not supported in your environment. `ReadableStream` is missing.");
}
return streamResponse(response.clone(), ky2.#options.onDownloadProgress);
}
return response;
};
const isRetriableMethod = ky2.#options.retry.methods.includes(ky2.request.method.toLowerCase());
const result = (isRetriableMethod ? ky2.#retry(function_) : function_()).finally(async () => {
const originalRequest = ky2.#originalRequest;
const cleanupPromises = [];
if (originalRequest && !originalRequest.bodyUsed) {
cleanupPromises.push(originalRequest.body?.cancel());
}
if (!ky2.request.bodyUsed) {
cleanupPromises.push(ky2.request.body?.cancel());
}
await Promise.all(cleanupPromises);
});
for (const [type, mimeType] of Object.entries(responseTypes)) {
if (type === "bytes" && typeof globalThis.Response?.prototype?.bytes !== "function") {
continue;
}
result[type] = async () => {
ky2.request.headers.set("accept", ky2.request.headers.get("accept") || mimeType);
const response = await result;
if (type === "json") {
if (response.status === 204) {
return "";
}
const text = await response.text();
if (text === "") {
return "";
}
if (options.parseJson) {
return options.parseJson(text);
}
return JSON.parse(text);
}
return response[type]();
};
}
return result;
}
// eslint-disable-next-line unicorn/prevent-abbreviations
static #normalizeSearchParams(searchParams) {
if (searchParams && typeof searchParams === "object" && !Array.isArray(searchParams) && !(searchParams instanceof URLSearchParams)) {
return Object.fromEntries(Object.entries(searchParams).filter(([, value]) => value !== void 0));
}
return searchParams;
}
request;
#abortController;
#retryCount = 0;
// eslint-disable-next-line @typescript-eslint/prefer-readonly -- False positive: #input is reassigned on line 202
#input;
#options;
#originalRequest;
#userProvidedAbortSignal;
#cachedNormalizedOptions;
// eslint-disable-next-line complexity
constructor(input, options = {}) {
this.#input = input;
this.#options = {
...options,
headers: mergeHeaders(this.#input.headers, options.headers),
hooks: mergeHooks({
beforeRequest: [],
beforeRetry: [],
beforeError: [],
afterResponse: []
}, options.hooks),
method: normalizeRequestMethod(options.method ?? this.#input.method ?? "GET"),
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
prefixUrl: String(options.prefixUrl || ""),
retry: normalizeRetryOptions(options.retry),
throwHttpErrors: options.throwHttpErrors !== false,
timeout: options.timeout ?? 1e4,
fetch: options.fetch ?? globalThis.fetch.bind(globalThis),
context: options.context ?? {}
};
if (typeof this.#input !== "string" && !(this.#input instanceof URL || this.#input instanceof globalThis.Request)) {
throw new TypeError("`input` must be a string, URL, or Request");
}
if (this.#options.prefixUrl && typeof this.#input === "string") {
if (this.#input.startsWith("/")) {
throw new Error("`input` must not begin with a slash when using `prefixUrl`");
}
if (!this.#options.prefixUrl.endsWith("/")) {
this.#options.prefixUrl += "/";
}
this.#input = this.#options.prefixUrl + this.#input;
}
if (supportsAbortController && supportsAbortSignal) {
this.#userProvidedAbortSignal = this.#options.signal ?? this.#input.signal;
this.#abortController = new globalThis.AbortController();
this.#options.signal = this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
}
if (supportsRequestStreams) {
this.#options.duplex = "half";
}
if (this.#options.json !== void 0) {
this.#options.body = this.#options.stringifyJson?.(this.#options.json) ?? JSON.stringify(this.#options.json);
this.#options.headers.set("content-type", this.#options.headers.get("content-type") ?? "application/json");
}
const userProvidedContentType = options.headers && new globalThis.Headers(options.headers).has("content-type");
if (this.#input instanceof globalThis.Request && (supportsFormData && this.#options.body instanceof globalThis.FormData || this.#options.body instanceof URLSearchParams) && !userProvidedContentType) {
this.#options.headers.delete("content-type");
}
this.request = new globalThis.Request(this.#input, this.#options);
if (hasSearchParameters(this.#options.searchParams)) {
const textSearchParams = typeof this.#options.searchParams === "string" ? this.#options.searchParams.replace(/^\?/, "") : new URLSearchParams(_Ky.#normalizeSearchParams(this.#options.searchParams)).toString();
const searchParams = "?" + textSearchParams;
const url2 = this.request.url.replace(/(?:\?.*?)?(?=#|$)/, searchParams);
this.request = new globalThis.Request(new globalThis.Request(url2, { ...this.request }), this.#options);
}
if (this.#options.onUploadProgress) {
if (typeof this.#options.onUploadProgress !== "function") {
throw new TypeError("The `onUploadProgress` option must be a function");
}
if (!supportsRequestStreams) {
throw new Error("Request streams are not supported in your environment. The `duplex` option for `Request` is not available.");
}
const originalBody = this.request.body;
if (originalBody) {
this.request = streamRequest(this.request, this.#options.onUploadProgress, this.#options.body);
}
}
}
#calculateDelay() {
const retryDelay = this.#options.retry.delay(this.#retryCount);
let jitteredDelay = retryDelay;
if (this.#options.retry.jitter === true) {
jitteredDelay = Math.random() * retryDelay;
} else if (typeof this.#options.retry.jitter === "function") {
jitteredDelay = this.#options.retry.jitter(retryDelay);
if (!Number.isFinite(jitteredDelay) || jitteredDelay < 0) {
jitteredDelay = retryDelay;
}
}
return Math.min(this.#options.retry.backoffLimit, jitteredDelay);
}
async #calculateRetryDelay(error) {
this.#retryCount++;
if (this.#retryCount > this.#options.retry.limit) {
throw error;
}
const errorObject = error instanceof Error ? error : new NonError(error);
if (this.#options.retry.shouldRetry !== void 0) {
const result = await this.#options.retry.shouldRetry({ error: errorObject, retryCount: this.#retryCount });
if (result === false) {
throw error;
}
if (result === true) {
return this.#calculateDelay();
}
}
if (isTimeoutError(error) && !this.#options.retry.retryOnTimeout) {
throw error;
}
if (isHTTPError(error)) {
if (!this.#options.retry.statusCodes.includes(error.response.status)) {
throw error;
}
const retryAfter = error.response.headers.get("Retry-After") ?? error.response.headers.get("RateLimit-Reset") ?? error.response.headers.get("X-RateLimit-Retry-After") ?? error.response.headers.get("X-RateLimit-Reset") ?? error.response.headers.get("X-Rate-Limit-Reset");
if (retryAfter && this.#options.retry.afterStatusCodes.includes(error.response.status)) {
let after = Number(retryAfter) * 1e3;
if (Number.isNaN(after)) {
after = Date.parse(retryAfter) - Date.now();
} else if (after >= Date.parse("2024-01-01")) {
after -= Date.now();
}
const max = this.#options.retry.maxRetryAfter ?? after;
return after < max ? after : max;
}
if (error.response.status === 413) {
throw error;
}
}
return this.#calculateDelay();
}
#decorateResponse(response) {
if (this.#options.parseJson) {
response.json = async () => this.#options.parseJson(await response.text());
}
return response;
}
async #retry(function_) {
try {
return await function_();
} catch (error) {
const ms = Math.min(await this.#calculateRetryDelay(error), maxSafeTimeout);
if (this.#retryCount < 1) {
throw error;
}
await delay(ms, this.#userProvidedAbortSignal ? { signal: this.#userProvidedAbortSignal } : {});
for (const hook of this.#options.hooks.beforeRetry) {
const hookResult = await hook({
request: this.request,
options: this.#getNormalizedOptions(),
error,
retryCount: this.#retryCount
});
if (hookResult instanceof globalThis.Request) {
this.request = hookResult;
break;
}
if (hookResult instanceof globalThis.Response) {
return hookResult;
}
if (hookResult === stop) {
return;
}
}
return this.#retry(function_);
}
}
async #fetch() {
if (this.#abortController?.signal.aborted) {
this.#abortController = new globalThis.AbortController();
this.#options.signal = this.#userProvidedAbortSignal ? AbortSignal.any([this.#userProvidedAbortSignal, this.#abortController.signal]) : this.#abortController.signal;
this.request = new globalThis.Request(this.request, { signal: this.#options.signal });
}
for (const hook of this.#options.hooks.beforeRequest) {
const result = await hook(this.request, this.#getNormalizedOptions(), { retryCount: this.#retryCount });
if (result instanceof Request) {
this.request = result;
break;
}
if (result instanceof Response) {
return result;
}
}
const nonRequestOptions = findUnknownOptions(this.request, this.#options);
this.#originalRequest = this.request;
this.request = this.#originalRequest.clone();
if (this.#options.timeout === false) {
return this.#options.fetch(this.#originalRequest, nonRequestOptions);
}
return timeout(this.#originalRequest, nonRequestOptions, this.#abortController, this.#options);
}
#getNormalizedOptions() {
if (!this.#cachedNormalizedOptions) {
const { hooks, ...normalizedOptions } = this.#options;
this.#cachedNormalizedOptions = Object.freeze(normalizedOptions);
}
return this.#cachedNormalizedOptions;
}
};
// node_modules/ky/distribution/index.js
var createInstance = (defaults) => {
const ky2 = (input, options) => Ky.create(input, validateAndMerge(defaults, options));
for (const method of requestMethods) {
ky2[method] = (input, options) => Ky.create(input, validateAndMerge(defaults, options, { method }));
}
ky2.create = (newDefaults) => createInstance(validateAndMerge(newDefaults));
ky2.extend = (newDefaults) => {
if (typeof newDefaults === "function") {
newDefaults = newDefaults(defaults ?? {});
}
return createInstance(validateAndMerge(defaults, newDefaults));
};
ky2.stop = stop;
return ky2;
};
var ky = createInstance();
var distribution_default = ky;
// node_modules/registry-url/index.js
var import_rc = __toESM(require_rc(), 1);
function registryUrl(scope) {
const result = (0, import_rc.default)("npm", { registry: "https://registry.npmjs.org/" });
const url2 = result[`${scope}:registry`] || result.config_registry || result.registry;
return url2.slice(-1) === "/" ? url2 : `${url2}/`;
}
// node_modules/package-json/index.js
var import_registry_auth_token = __toESM(require_registry_auth_token(), 1);
var import_semver = __toESM(require_semver(), 1);
var PackageNotFoundError = class extends Error {
constructor(packageName) {
super(`Package \`${packageName}\` could not be found`);
this.name = "PackageNotFoundError";
}
};
var VersionNotFoundError = class extends Error {
constructor(packageName, version) {
super(`Version \`${version}\` for package \`${packageName}\` could not be found`);
this.name = "VersionNotFoundError";
}
};
async function packageJson(packageName, options = {}) {
let { version = "latest" } = options;
const { omitDeprecated = true } = options;
const scope = packageName.split("/")[0];
const registryUrl_ = options.registryUrl ?? registryUrl(scope);
const packageUrl = new URL(encodeURIComponent(packageName).replace(/^%40/, "@"), registryUrl_);
const authInfo = (0, import_registry_auth_token.default)(registryUrl_.toString(), { recursive: true });
const headers = {
accept: "application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*"
};
if (options.fullMetadata) {
delete headers.accept;
}
if (authInfo) {
headers.authorization = `${authInfo.type} ${authInfo.token}`;
}
let data;
try {
data = await distribution_default(packageUrl, { headers, keepalive: true }).json();
} catch (error) {
if (error?.response?.status === 404) {
throw new PackageNotFoundError(packageName);
}
throw error;
}
if (options.allVersions) {
return data;
}
const versionError = new VersionNotFoundError(packageName, version);
if (data["dist-tags"][version]) {
const { time } = data;
data = data.versions[data["dist-tags"][version]];
data.time = time;
} else if (version) {
const versionExists = Boolean(data.versions[version]);
if (omitDeprecated && !versionExists) {
for (const [metadataVersion, metadata] of Object.entries(data.versions)) {
if (metadata.deprecated) {
delete data.versions[metadataVersion];
}
}
}
if (!versionExists) {
const versions = Object.keys(data.versions);
version = import_semver.default.maxSatisfying(versions, version);
if (!version) {
throw versionError;
}
}
const { time } = data;
data = data.versions[version];
data.time = time;
if (!data) {
throw versionError;
}
}
return data;
}
// node_modules/latest-version/index.js
async function latestVersion(packageName, options) {
const { version } = await packageJson(packageName.toLowerCase(), options);
return version;
}
// packages/cli/src/ui/utils/updateCheck.ts
var import_semver2 = __toESM(require_semver(), 1);
import { fileURLToPath } from "node:url";
import path13 from "node:path";
var __filename = fileURLToPath(import.meta.url);
var __dirname2 = path13.dirname(__filename);
function getBestAvailableUpdate(nightly, stable) {
if (!nightly) return stable || null;
if (!stable) return nightly || null;
if (import_semver2.default.coerce(stable)?.version === import_semver2.default.coerce(nightly)?.version) {
return nightly;
}
return import_semver2.default.gt(stable, nightly) ? stable : nightly;
}
async function checkForUpdates(settings) {
try {
if (!settings.merged.general.enableAutoUpdateNotification) {
return null;
}
if (false) {
return null;
}
const packageJson2 = await getPackageJson(__dirname2);
if (!packageJson2 || !packageJson2.name || !packageJson2.version) {
return null;
}
const { name, version: currentVersion } = packageJson2;
const currentChannel = getChannelFromVersion(currentVersion);
const isNightly = currentVersion.includes("nightly");
if (isNightly) {
const [nightlyUpdate, latestUpdate] = await Promise.all([
latestVersion(name, { version: "nightly" }),
latestVersion(name)
]);
const bestUpdate = getBestAvailableUpdate(nightlyUpdate, latestUpdate);
if (bestUpdate && import_semver2.default.gt(bestUpdate, currentVersion)) {
const message = `A new version of Gemini CLI is available! ${currentVersion} \u2192 ${bestUpdate}`;
const type = import_semver2.default.diff(bestUpdate, currentVersion) || void 0;
return {
message,
update: {
latest: bestUpdate,
current: currentVersion,
name,
type
}
};
}
} else {
const latestUpdate = await latestVersion(name);
if (!latestUpdate) {
return null;
}
const targetChannel = getChannelFromVersion(latestUpdate);
if (RELEASE_CHANNEL_STABILITY[targetChannel] < RELEASE_CHANNEL_STABILITY[currentChannel]) {
return null;
}
if (import_semver2.default.gt(latestUpdate, currentVersion)) {
const message = `Gemini CLI update available! ${currentVersion} \u2192 ${latestUpdate}`;
const type = import_semver2.default.diff(latestUpdate, currentVersion) || void 0;
return {
message,
update: {
latest: latestUpdate,
current: currentVersion,
name,
type
}
};
}
}
return null;
} catch (e) {
debugLogger.warn("Failed to check for updates: " + e);
return null;
}
}
// packages/cli/src/ui/hooks/useMessageQueue.ts
var import_react106 = __toESM(require_react(), 1);
function useMessageQueue({
isConfigInitialized,
streamingState,
submitQuery,
isMcpReady,
isCompressing = false
}) {
const [messageQueue2, setMessageQueue] = (0, import_react106.useState)([]);
const addMessage = (0, import_react106.useCallback)((message) => {
const trimmedMessage = message.trim();
if (trimmedMessage.length > 0) {
setMessageQueue((prev) => [...prev, trimmedMessage]);
}
}, []);
const clearQueue = (0, import_react106.useCallback)(() => {
setMessageQueue([]);
}, []);
const getQueuedMessagesText = (0, import_react106.useCallback)(() => {
if (messageQueue2.length === 0) return "";
return messageQueue2.join("\n\n");
}, [messageQueue2]);
const popAllMessages = (0, import_react106.useCallback)(() => {
if (messageQueue2.length === 0) {
return void 0;
}
const allMessages = messageQueue2.join("\n\n");
setMessageQueue([]);
return allMessages;
}, [messageQueue2]);
(0, import_react106.useEffect)(() => {
if (isConfigInitialized && streamingState === "idle" /* Idle */ && !isCompressing && isMcpReady && messageQueue2.length > 0) {
const combinedMessage = messageQueue2.join("\n\n");
setMessageQueue([]);
submitQuery(combinedMessage);
}
}, [
isConfigInitialized,
streamingState,
isMcpReady,
messageQueue2,
submitQuery,
isCompressing
]);
return {
messageQueue: messageQueue2,
addMessage,
clearQueue,
getQueuedMessagesText,
popAllMessages
};
}
// packages/cli/src/ui/hooks/useMcpStatus.ts
var import_react107 = __toESM(require_react(), 1);
function useMcpStatus(config) {
const [discoveryState, setDiscoveryState] = (0, import_react107.useState)(
() => config.getMcpClientManager()?.getDiscoveryState() ?? MCPDiscoveryState.NOT_STARTED
);
const [mcpServerCount, setMcpServerCount] = (0, import_react107.useState)(
() => config.getMcpClientManager()?.getMcpServerCount() ?? 0
);
(0, import_react107.useEffect)(() => {
const onChange = () => {
const manager = config.getMcpClientManager();
if (manager) {
setDiscoveryState(manager.getDiscoveryState());
setMcpServerCount(manager.getMcpServerCount());
}
};
coreEvents.on(CoreEvent.McpClientUpdate, onChange);
return () => {
coreEvents.off(CoreEvent.McpClientUpdate, onChange);
};
}, [config]);
const isMcpReady = discoveryState === MCPDiscoveryState.COMPLETED || discoveryState === MCPDiscoveryState.NOT_STARTED && mcpServerCount === 0;
return {
discoveryState,
mcpServerCount,
isMcpReady
};
}
// packages/cli/src/ui/hooks/useApprovalModeIndicator.ts
var import_react108 = __toESM(require_react(), 1);
function useApprovalModeIndicator({
config,
addItem,
onApprovalModeChange,
isActive = true,
allowPlanMode = false
}) {
const keyMatchers = useKeyMatchers();
const currentConfigValue = config.getApprovalMode();
const [showApprovalMode, setApprovalMode] = (0, import_react108.useState)(currentConfigValue);
(0, import_react108.useEffect)(() => {
setApprovalMode(currentConfigValue);
}, [currentConfigValue]);
useKeypress(
(key) => {
let nextApprovalMode;
if (keyMatchers["app.toggleYolo" /* TOGGLE_YOLO */](key)) {
if (config.isYoloModeDisabled() && config.getApprovalMode() !== ApprovalMode.YOLO) {
if (addItem) {
let text = "You cannot enter YOLO mode since it is disabled in your settings.";
const adminSettings = config.getRemoteAdminSettings();
const hasSettings = adminSettings && Object.keys(adminSettings).length > 0;
if (hasSettings && !adminSettings.strictModeDisabled) {
text = getAdminErrorMessage("YOLO mode", config);
}
addItem(
{
type: "warning" /* WARNING */,
text
},
Date.now()
);
}
return;
}
nextApprovalMode = config.getApprovalMode() === ApprovalMode.YOLO ? ApprovalMode.DEFAULT : ApprovalMode.YOLO;
} else if (keyMatchers["app.cycleApprovalMode" /* CYCLE_APPROVAL_MODE */](key)) {
const currentMode = config.getApprovalMode();
switch (currentMode) {
case ApprovalMode.DEFAULT:
nextApprovalMode = ApprovalMode.AUTO_EDIT;
break;
case ApprovalMode.AUTO_EDIT:
nextApprovalMode = allowPlanMode ? ApprovalMode.PLAN : ApprovalMode.DEFAULT;
break;
case ApprovalMode.PLAN:
nextApprovalMode = ApprovalMode.DEFAULT;
break;
case ApprovalMode.YOLO:
nextApprovalMode = ApprovalMode.AUTO_EDIT;
break;
default:
}
}
if (nextApprovalMode) {
try {
config.setApprovalMode(nextApprovalMode);
setApprovalMode(nextApprovalMode);
onApprovalModeChange?.(nextApprovalMode);
} catch (e) {
if (addItem) {
addItem(
{
type: "info" /* INFO */,
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
text: e.message
},
Date.now()
);
}
}
}
},
{ isActive }
);
return showApprovalMode;
}
// packages/cli/src/ui/hooks/useGitBranchName.ts
var import_react109 = __toESM(require_react(), 1);
import fs7 from "node:fs";
import fsPromises from "node:fs/promises";
function useGitBranchName(cwd5) {
const [branchName, setBranchName] = (0, import_react109.useState)(void 0);
const timeoutRef = (0, import_react109.useRef)(null);
const fetchBranchName = (0, import_react109.useCallback)(async () => {
try {
const { stdout } = await spawnAsync(
"git",
["rev-parse", "--abbrev-ref", "HEAD"],
{ cwd: cwd5 }
);
const branch = stdout.toString().trim();
if (branch && branch !== "HEAD") {
setBranchName(branch);
} else {
const { stdout: hashStdout } = await spawnAsync(
"git",
["rev-parse", "--short", "HEAD"],
{ cwd: cwd5 }
);
setBranchName(hashStdout.toString().trim());
}
} catch {
setBranchName(void 0);
}
}, [cwd5, setBranchName]);
(0, import_react109.useEffect)(() => {
void fetchBranchName();
let watcher;
let cancelled = false;
const setupWatcher = async () => {
try {
const gitDir = await getAbsoluteGitDir(cwd5);
if (!gitDir) return;
await fsPromises.access(gitDir, fs7.constants.F_OK);
if (cancelled) return;
const w = fs7.watch(
gitDir,
(eventType, filename) => {
if (!filename || filename === "HEAD") {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
timeoutRef.current = setTimeout(() => {
void fetchBranchName();
}, 100);
}
}
);
if (cancelled) {
w.close();
} else {
watcher = w;
}
} catch {
}
};
void setupWatcher();
return () => {
cancelled = true;
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
watcher?.close();
};
}, [cwd5, fetchBranchName]);
return branchName;
}
// packages/cli/src/ui/hooks/useSessionResume.ts
var import_react110 = __toESM(require_react(), 1);
function useSessionResume({
config,
historyManager,
refreshStatic,
isGeminiClientInitialized,
setQuittingMessages,
resumedSessionData,
isAuthenticating
}) {
const [isResuming, setIsResuming] = (0, import_react110.useState)(false);
const historyManagerRef = (0, import_react110.useRef)(historyManager);
const refreshStaticRef = (0, import_react110.useRef)(refreshStatic);
(0, import_react110.useEffect)(() => {
historyManagerRef.current = historyManager;
refreshStaticRef.current = refreshStatic;
});
const loadHistoryForResume = (0, import_react110.useCallback)(
async (uiHistory, clientHistory, resumedData) => {
if (!isGeminiClientInitialized) {
return;
}
setIsResuming(true);
try {
setQuittingMessages(null);
historyManagerRef.current.clearItems();
uiHistory.forEach((item, index) => {
historyManagerRef.current.addItem(item, index, true);
});
refreshStaticRef.current();
if (resumedData.conversation.directories && resumedData.conversation.directories.length > 0) {
const workspaceContext = config.getWorkspaceContext();
workspaceContext.addDirectories(resumedData.conversation.directories);
}
await config.getGeminiClient()?.resumeChat(clientHistory, resumedData);
} catch (error) {
coreEvents.emitFeedback(
"error",
"Failed to resume session. Please try again.",
error
);
} finally {
setIsResuming(false);
}
},
[config, isGeminiClientInitialized, setQuittingMessages]
);
const hasLoadedResumedSession = (0, import_react110.useRef)(false);
(0, import_react110.useEffect)(() => {
if (resumedSessionData && !isAuthenticating && isGeminiClientInitialized && !hasLoadedResumedSession.current) {
hasLoadedResumedSession.current = true;
const historyData = convertSessionToHistoryFormats(
resumedSessionData.conversation.messages
);
void loadHistoryForResume(
historyData.uiHistory,
convertSessionToClientHistory(resumedSessionData.conversation.messages),
resumedSessionData
);
}
}, [
resumedSessionData,
isAuthenticating,
isGeminiClientInitialized,
loadHistoryForResume
]);
return { loadHistoryForResume, isResuming };
}
// packages/cli/src/ui/hooks/useIncludeDirsTrust.tsx
var import_react111 = __toESM(require_react(), 1);
var import_jsx_runtime137 = __toESM(require_jsx_runtime(), 1);
async function finishAddingDirectories(config, addItem, added, errors) {
if (!config) {
addItem({
type: "error" /* ERROR */,
text: "Configuration is not available."
});
return;
}
try {
if (config.shouldLoadMemoryFromIncludeDirectories()) {
await refreshServerHierarchicalMemory(config);
}
} catch (error) {
errors.push(`Error refreshing memory: ${error.message}`);
}
if (added.length > 0) {
const gemini = config.getGeminiClient();
if (gemini) {
await gemini.addDirectoryContext();
}
}
if (errors.length > 0) {
addItem({ type: "error" /* ERROR */, text: errors.join("\n") });
}
}
function useIncludeDirsTrust(config, isTrustedFolder, historyManager, setCustomDialog) {
const { addItem } = historyManager;
(0, import_react111.useEffect)(() => {
if (isTrustedFolder === void 0 || !config) {
return;
}
const pendingDirs = config.getPendingIncludeDirectories();
if (pendingDirs.length === 0) {
return;
}
if (config.getFolderTrust() === false || isTrustedFolder === false) {
const added2 = [];
const errors2 = [];
const workspaceContext2 = config.getWorkspaceContext();
const result = batchAddDirectories(workspaceContext2, pendingDirs);
added2.push(...result.added);
errors2.push(...result.errors);
if (added2.length > 0 || errors2.length > 0) {
finishAddingDirectories(config, addItem, added2, errors2);
}
config.clearPendingIncludeDirectories();
return;
}
const trustedFolders = loadTrustedFolders();
const untrustedDirs = [];
const undefinedTrustDirs = [];
const trustedDirs = [];
const added = [];
const errors = [];
for (const pathToAdd of pendingDirs) {
const expandedPath = expandHomeDir(pathToAdd.trim());
const isTrusted = trustedFolders.isPathTrusted(expandedPath);
if (isTrusted === false) {
untrustedDirs.push(pathToAdd.trim());
} else if (isTrusted === void 0) {
undefinedTrustDirs.push(pathToAdd.trim());
} else {
trustedDirs.push(pathToAdd.trim());
}
}
if (untrustedDirs.length > 0) {
errors.push(
`The following directories are explicitly untrusted and cannot be added to a trusted workspace:
- ${untrustedDirs.join(
"\n- "
)}
Please use the permissions command to modify their trust level.`
);
}
const workspaceContext = config.getWorkspaceContext();
if (trustedDirs.length > 0) {
const result = batchAddDirectories(workspaceContext, trustedDirs);
added.push(...result.added);
errors.push(...result.errors);
}
if (undefinedTrustDirs.length > 0) {
debugLogger.log(
"Creating custom dialog with undecidedDirs:",
undefinedTrustDirs
);
setCustomDialog(
/* @__PURE__ */ (0, import_jsx_runtime137.jsx)(
MultiFolderTrustDialog,
{
folders: undefinedTrustDirs,
onComplete: () => {
setCustomDialog(null);
config.clearPendingIncludeDirectories();
},
trustedDirs: added,
errors,
finishAddingDirectories,
config,
addItem
}
)
);
} else if (added.length > 0 || errors.length > 0) {
finishAddingDirectories(config, addItem, added, errors);
config.clearPendingIncludeDirectories();
}
}, [isTrustedFolder, config, addItem, setCustomDialog]);
}
// packages/cli/src/ui/hooks/useInputHistoryStore.ts
var import_react112 = __toESM(require_react(), 1);
function useInputHistoryStore() {
const [inputHistory, setInputHistory] = (0, import_react112.useState)([]);
const [_pastSessionMessages, setPastSessionMessages] = (0, import_react112.useState)([]);
const [_currentSessionMessages, setCurrentSessionMessages] = (0, import_react112.useState)([]);
const [isInitialized, setIsInitialized] = (0, import_react112.useState)(false);
const recalculateHistory = (0, import_react112.useCallback)(
(currentSession, pastSession) => {
const combinedMessages = [...currentSession, ...pastSession];
const deduplicatedMessages = [];
if (combinedMessages.length > 0) {
deduplicatedMessages.push(combinedMessages[0]);
for (let i = 1; i < combinedMessages.length; i++) {
if (combinedMessages[i] !== combinedMessages[i - 1]) {
deduplicatedMessages.push(combinedMessages[i]);
}
}
}
setInputHistory(deduplicatedMessages.reverse());
},
[]
);
const initializeFromLogger = (0, import_react112.useCallback)(
async (logger) => {
if (isInitialized || !logger) return;
try {
const pastMessages = await logger.getPreviousUserMessages() || [];
setPastSessionMessages(pastMessages);
recalculateHistory([], pastMessages);
setIsInitialized(true);
} catch (error) {
debugLogger.warn(
"Failed to initialize input history from logger:",
error
);
setPastSessionMessages([]);
recalculateHistory([], []);
setIsInitialized(true);
}
},
[isInitialized, recalculateHistory]
);
const addInput = (0, import_react112.useCallback)(
(input) => {
const trimmedInput = input.trim();
if (!trimmedInput) return;
setCurrentSessionMessages((prevCurrent) => {
const newCurrentSession = [...prevCurrent, trimmedInput];
setPastSessionMessages((prevPast) => {
recalculateHistory(
newCurrentSession.slice().reverse(),
// Convert to newest first
prevPast
);
return prevPast;
});
return newCurrentSession;
});
},
[recalculateHistory]
);
return {
inputHistory,
addInput,
initializeFromLogger
};
}
// packages/cli/src/ui/hooks/useHookDisplayState.ts
var import_react113 = __toESM(require_react(), 1);
var useHookDisplayState = () => {
const [activeHooks, setActiveHooks] = (0, import_react113.useState)([]);
const hookStartTimes = (0, import_react113.useRef)(/* @__PURE__ */ new Map());
const timeouts = (0, import_react113.useRef)(/* @__PURE__ */ new Set());
(0, import_react113.useEffect)(() => {
const activeTimeouts = timeouts.current;
const startTimes = hookStartTimes.current;
const handleHookStart = (payload) => {
const key = `${payload.hookName}:${payload.eventName}`;
const now = Date.now();
if (!startTimes.has(key)) {
startTimes.set(key, []);
}
startTimes.get(key).push(now);
setActiveHooks((prev) => [
...prev,
{
name: payload.hookName,
eventName: payload.eventName,
source: payload.source,
index: payload.hookIndex,
total: payload.totalHooks
}
]);
};
const handleHookEnd = (payload) => {
const key = `${payload.hookName}:${payload.eventName}`;
const starts = startTimes.get(key);
const startTime = starts?.shift();
if (starts && starts.length === 0) {
startTimes.delete(key);
}
const now = Date.now();
const elapsed = startTime ? now - startTime : WARNING_PROMPT_DURATION_MS;
const remaining = WARNING_PROMPT_DURATION_MS - elapsed;
const removeHook = () => {
setActiveHooks((prev) => {
const index = prev.findIndex(
(h) => h.name === payload.hookName && h.eventName === payload.eventName
);
if (index === -1) return prev;
const newHooks = [...prev];
newHooks.splice(index, 1);
return newHooks;
});
};
if (remaining > 0) {
const timeoutId2 = setTimeout(() => {
removeHook();
activeTimeouts.delete(timeoutId2);
}, remaining);
activeTimeouts.add(timeoutId2);
} else {
removeHook();
}
};
coreEvents.on(CoreEvent.HookStart, handleHookStart);
coreEvents.on(CoreEvent.HookEnd, handleHookEnd);
return () => {
coreEvents.off(CoreEvent.HookStart, handleHookStart);
coreEvents.off(CoreEvent.HookEnd, handleHookEnd);
activeTimeouts.forEach(clearTimeout);
activeTimeouts.clear();
};
}, []);
return activeHooks;
};
// packages/cli/src/ui/hooks/useBackgroundTaskManager.ts
var import_react114 = __toESM(require_react(), 1);
function useBackgroundTaskManager({
backgroundTasks,
backgroundTaskCount,
isBackgroundTaskVisible,
activePtyId,
embeddedShellFocused,
setEmbeddedShellFocused,
terminalHeight
}) {
const [isBackgroundTaskListOpen, setIsBackgroundTaskListOpen] = (0, import_react114.useState)(false);
const [activeBackgroundTaskPid, setActiveBackgroundTaskPid] = (0, import_react114.useState)(null);
(0, import_react114.useEffect)(() => {
if (backgroundTasks.size === 0) {
if (activeBackgroundTaskPid !== null) {
setActiveBackgroundTaskPid(null);
}
if (isBackgroundTaskListOpen) {
setIsBackgroundTaskListOpen(false);
}
} else if (activeBackgroundTaskPid === null || !backgroundTasks.has(activeBackgroundTaskPid)) {
setActiveBackgroundTaskPid(backgroundTasks.keys().next().value ?? null);
}
}, [
backgroundTasks,
activeBackgroundTaskPid,
backgroundTaskCount,
isBackgroundTaskListOpen
]);
(0, import_react114.useEffect)(() => {
if (embeddedShellFocused) {
const hasActiveForegroundShell = !!activePtyId;
const hasVisibleBackgroundTask = isBackgroundTaskVisible && backgroundTasks.size > 0;
if (!hasActiveForegroundShell && !hasVisibleBackgroundTask) {
setEmbeddedShellFocused(false);
}
}
}, [
isBackgroundTaskVisible,
backgroundTasks,
embeddedShellFocused,
backgroundTaskCount,
activePtyId,
setEmbeddedShellFocused
]);
const backgroundTaskHeight = (0, import_react114.useMemo)(
() => isBackgroundTaskVisible && backgroundTasks.size > 0 ? Math.max(Math.floor(terminalHeight * 0.3), 5) : 0,
[isBackgroundTaskVisible, backgroundTasks.size, terminalHeight]
);
return {
isBackgroundTaskListOpen,
setIsBackgroundTaskListOpen,
activeBackgroundTaskPid,
setActiveBackgroundTaskPid,
backgroundTaskHeight
};
}
// packages/cli/src/ui/hooks/useTerminalTheme.ts
var import_react115 = __toESM(require_react(), 1);
function useTerminalTheme(handleThemeSelect, config, refreshStatic) {
const settings = useSettings();
const { subscribe: subscribe2, unsubscribe, queryTerminalBackground } = useTerminalContext();
(0, import_react115.useEffect)(() => {
if (settings.merged.ui.autoThemeSwitching === false) {
return;
}
if (config.getTerminalBackground() === void 0) {
return;
}
const pollIntervalId = setInterval(() => {
const currentThemeName = settings.merged.ui.theme;
if (!themeManager.isDefaultTheme(currentThemeName)) {
return;
}
void queryTerminalBackground();
}, settings.merged.ui.terminalBackgroundPollingInterval * 1e3);
const handleTerminalBackground = (colorStr) => {
const match = /^rgb:([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})\/([0-9a-fA-F]{1,4})$/.exec(
colorStr
);
if (!match) return;
const hexColor = parseColor(match[1], match[2], match[3]);
if (!hexColor) return;
const previousColor = config.getTerminalBackground();
const luminance = getLuminance(hexColor);
const currentThemeName = settings.merged.ui.theme;
const newTheme = shouldSwitchTheme(
currentThemeName,
luminance,
DEFAULT_THEME.name,
DefaultLight.name
);
if (previousColor === hexColor) {
if (newTheme) {
void handleThemeSelect(newTheme, "User" /* User */);
}
return;
}
config.setTerminalBackground(hexColor);
themeManager.setTerminalBackground(hexColor);
if (newTheme) {
void handleThemeSelect(newTheme, "User" /* User */);
} else {
refreshStatic();
}
};
subscribe2(handleTerminalBackground);
return () => {
clearInterval(pollIntervalId);
unsubscribe(handleTerminalBackground);
};
}, [
settings.merged.ui.theme,
settings.merged.ui.autoThemeSwitching,
settings.merged.ui.terminalBackgroundPollingInterval,
config,
handleThemeSelect,
subscribe2,
unsubscribe,
queryTerminalBackground,
refreshStatic
]);
}
// packages/cli/src/ui/hooks/useTimedMessage.ts
var import_react116 = __toESM(require_react(), 1);
function useTimedMessage(durationMs) {
const [message, setMessage] = (0, import_react116.useState)(null);
const timeoutRef = (0, import_react116.useRef)(null);
const showMessage = (0, import_react116.useCallback)(
(msg) => {
setMessage(msg);
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
if (msg !== null) {
timeoutRef.current = setTimeout(() => {
setMessage(null);
}, durationMs);
}
},
[durationMs]
);
(0, import_react116.useEffect)(
() => () => {
if (timeoutRef.current) {
clearTimeout(timeoutRef.current);
}
},
[]
);
return [message, showMessage];
}
// packages/cli/src/ui/hooks/useSuspend.ts
var import_react117 = __toESM(require_react(), 1);
import process11 from "node:process";
function useSuspend({
handleWarning,
setRawMode,
refreshStatic,
setForceRerenderKey,
shouldUseAlternateScreen
}) {
const [ctrlZPressCount, setCtrlZPressCount] = (0, import_react117.useState)(0);
const ctrlZTimerRef = (0, import_react117.useRef)(null);
const onResumeHandlerRef = (0, import_react117.useRef)(null);
(0, import_react117.useEffect)(
() => () => {
if (ctrlZTimerRef.current) {
clearTimeout(ctrlZTimerRef.current);
ctrlZTimerRef.current = null;
}
if (onResumeHandlerRef.current) {
process11.off("SIGCONT", onResumeHandlerRef.current);
onResumeHandlerRef.current = null;
}
},
[]
);
(0, import_react117.useEffect)(() => {
if (ctrlZTimerRef.current) {
clearTimeout(ctrlZTimerRef.current);
ctrlZTimerRef.current = null;
}
const suspendKey = formatCommand("app.suspend" /* SUSPEND_APP */);
if (ctrlZPressCount > 1) {
setCtrlZPressCount(0);
if (process11.platform === "win32") {
handleWarning(`${suspendKey} suspend is not supported on Windows.`);
return;
}
if (shouldUseAlternateScreen) {
exitAlternateScreen();
enableLineWrapping();
writeToStdout("\x1B[2J\x1B[H");
}
writeToStdout("\x1B[?25h");
disableMouseEvents();
cleanupTerminalOnExit();
if (process11.stdin.isTTY) {
process11.stdin.setRawMode(false);
}
setRawMode(false);
const onResume = () => {
try {
if (process11.stdin.isTTY) {
process11.stdin.setRawMode(true);
process11.stdin.resume();
process11.stdin.ref();
}
setRawMode(true);
if (shouldUseAlternateScreen) {
enterAlternateScreen();
disableLineWrapping();
writeToStdout("\x1B[2J\x1B[H");
}
terminalCapabilityManager.enableSupportedModes();
writeToStdout("\x1B[?25l");
if (shouldUseAlternateScreen) {
enableMouseEvents();
}
process11.stdout.emit("resize");
setImmediate(() => {
refreshStatic();
setForceRerenderKey((prev) => prev + 1);
});
} finally {
if (onResumeHandlerRef.current === onResume) {
onResumeHandlerRef.current = null;
}
}
};
if (onResumeHandlerRef.current) {
process11.off("SIGCONT", onResumeHandlerRef.current);
}
onResumeHandlerRef.current = onResume;
process11.once("SIGCONT", onResume);
process11.kill(0, "SIGTSTP");
} else if (ctrlZPressCount > 0) {
const undoKey = formatCommand("edit.undo" /* UNDO */);
handleWarning(
`Press ${suspendKey} again to suspend. Undo has moved to ${undoKey}.`
);
ctrlZTimerRef.current = setTimeout(() => {
setCtrlZPressCount(0);
ctrlZTimerRef.current = null;
}, WARNING_PROMPT_DURATION_MS);
}
}, [
ctrlZPressCount,
handleWarning,
setRawMode,
refreshStatic,
setForceRerenderKey,
shouldUseAlternateScreen
]);
const handleSuspend = (0, import_react117.useCallback)(() => {
setCtrlZPressCount((prev) => prev + 1);
}, []);
return { handleSuspend };
}
// packages/cli/src/ui/hooks/useRunEventNotifications.ts
var import_react119 = __toESM(require_react(), 1);
// packages/cli/src/ui/utils/pendingAttentionNotification.ts
var import_react118 = __toESM(require_react(), 1);
// packages/cli/src/utils/terminalNotifications.ts
var MAX_NOTIFICATION_TITLE_CHARS = 48;
var MAX_NOTIFICATION_SUBTITLE_CHARS = 64;
var MAX_NOTIFICATION_BODY_CHARS = 180;
var BEL2 = "\x07";
var OSC9_PREFIX = "\x1B]9;";
var OSC777_PREFIX = "\x1B]777;notify;";
var OSC_TEXT_SEPARATOR = " | ";
function sanitizeNotificationContent(content) {
const title = sanitizeForDisplay(content.title, MAX_NOTIFICATION_TITLE_CHARS);
const subtitle = content.subtitle ? sanitizeForDisplay(content.subtitle, MAX_NOTIFICATION_SUBTITLE_CHARS) : void 0;
const body = sanitizeForDisplay(content.body, MAX_NOTIFICATION_BODY_CHARS);
return {
title: title || "Gemini CLI",
subtitle: subtitle || void 0,
body: body || "Open Gemini CLI for details."
};
}
function buildRunEventNotificationContent(event) {
if (event.type === "attention") {
return sanitizeNotificationContent({
title: "Gemini CLI needs your attention",
subtitle: event.heading ?? "Action required",
body: event.detail ?? "Open Gemini CLI to continue."
});
}
return sanitizeNotificationContent({
title: "Gemini CLI session complete",
subtitle: "Run finished",
body: event.detail ?? "The session finished successfully."
});
}
function isNotificationsEnabled(settings) {
const general = settings.merged.general;
return general?.enableNotifications === true;
}
function getNotificationMethod(settings) {
switch (settings.merged.general?.notificationMethod) {
case "osc9" /* Osc9 */:
return "osc9" /* Osc9 */;
case "osc777" /* Osc777 */:
return "osc777" /* Osc777 */;
case "bell" /* Bell */:
return "bell" /* Bell */;
default:
return "auto" /* Auto */;
}
}
function wrapWithPassthrough(sequence) {
const capabilityManager = TerminalCapabilityManager.getInstance();
if (capabilityManager.isTmux()) {
return `\x1BPtmux;${sequence.replace(/\x1b/g, "\x1B\x1B")}\x1B\\`;
} else if (capabilityManager.isScreen()) {
return `\x1BP${sequence}\x1B\\`;
}
return sequence;
}
function emitOsc9Notification(content) {
const sanitized = sanitizeNotificationContent(content);
const pieces = [sanitized.title, sanitized.subtitle, sanitized.body].filter(
Boolean
);
const combined = pieces.join(OSC_TEXT_SEPARATOR);
writeToStdout(wrapWithPassthrough(`${OSC9_PREFIX}${combined}${BEL2}`));
}
function emitOsc777Notification(content) {
const sanitized = sanitizeNotificationContent(content);
const bodyParts = [sanitized.subtitle, sanitized.body].filter(Boolean);
const body = bodyParts.join(OSC_TEXT_SEPARATOR);
const safeTitle = sanitized.title.replace(/;/g, ":");
const safeBody = body.replace(/;/g, ":");
writeToStdout(
wrapWithPassthrough(`${OSC777_PREFIX}${safeTitle};${safeBody}${BEL2}`)
);
}
function emitBellNotification() {
writeToStdout(BEL2);
}
async function notifyViaTerminal(notificationsEnabled, content, method = "auto" /* Auto */) {
if (!notificationsEnabled) {
return false;
}
try {
if (method === "osc9" /* Osc9 */) {
emitOsc9Notification(content);
} else if (method === "osc777" /* Osc777 */) {
emitOsc777Notification(content);
} else if (method === "bell" /* Bell */) {
emitBellNotification();
} else {
const capabilityManager = TerminalCapabilityManager.getInstance();
if (capabilityManager.isITerm2()) {
emitOsc9Notification(content);
} else if (capabilityManager.isAlacritty() || capabilityManager.isAppleTerminal() || capabilityManager.isVSCodeTerminal() || capabilityManager.isWindowsTerminal()) {
emitBellNotification();
} else {
emitOsc777Notification(content);
}
}
return true;
} catch (error) {
debugLogger.debug("Failed to emit terminal notification:", error);
return false;
}
}
// packages/cli/src/ui/utils/pendingAttentionNotification.ts
function keyFromReactNode(node) {
if (typeof node === "string" || typeof node === "number") {
return String(node);
}
if (Array.isArray(node)) {
return node.map((item) => keyFromReactNode(item)).join("|");
}
return "react-node";
}
function getPendingAttentionNotification(pendingHistoryItems, commandConfirmationRequest, authConsentRequest, permissionConfirmationRequest, hasConfirmUpdateExtensionRequests, hasLoopDetectionConfirmationRequest) {
const confirmingToolState = getConfirmingToolState(pendingHistoryItems);
if (confirmingToolState) {
const details = confirmingToolState.tool.confirmationDetails;
if (details?.type === "ask_user") {
const firstQuestion = details.questions.at(0)?.header;
return {
key: `ask_user:${confirmingToolState.tool.callId}`,
event: {
type: "attention",
heading: "Answer requested by agent",
detail: firstQuestion || "The agent needs your response to continue."
}
};
}
const toolTitle = details?.title || confirmingToolState.tool.description;
return {
key: `tool_confirmation:${confirmingToolState.tool.callId}`,
event: {
type: "attention",
heading: "Approval required",
detail: toolTitle ? `Approve tool action: ${toolTitle}` : "Approve a pending tool action to continue."
}
};
}
if (commandConfirmationRequest) {
const promptKey = keyFromReactNode(commandConfirmationRequest.prompt);
return {
key: `command_confirmation:${promptKey}`,
event: {
type: "attention",
heading: "Confirmation required",
detail: "A command is waiting for your confirmation."
}
};
}
if (authConsentRequest) {
const promptKey = keyFromReactNode(authConsentRequest.prompt);
return {
key: `auth_consent:${promptKey}`,
event: {
type: "attention",
heading: "Authentication confirmation required",
detail: "Authentication is waiting for your confirmation."
}
};
}
if (permissionConfirmationRequest) {
const filesKey = permissionConfirmationRequest.files.join("|");
return {
key: `filesystem_permission_confirmation:${filesKey}`,
event: {
type: "attention",
heading: "Filesystem permission required",
detail: "Read-only path access is waiting for your confirmation."
}
};
}
if (hasConfirmUpdateExtensionRequests) {
return {
key: "extension_update_confirmation",
event: {
type: "attention",
heading: "Extension update confirmation required",
detail: "An extension update is waiting for your confirmation."
}
};
}
if (hasLoopDetectionConfirmationRequest) {
return {
key: "loop_detection_confirmation",
event: {
type: "attention",
heading: "Loop detection confirmation required",
detail: "A loop detection prompt is waiting for your response."
}
};
}
return null;
}
// packages/cli/src/ui/hooks/useRunEventNotifications.ts
var ATTENTION_NOTIFICATION_COOLDOWN_MS = 2e4;
function useRunEventNotifications({
notificationsEnabled,
notificationMethod,
isFocused,
hasReceivedFocusEvent,
streamingState,
hasPendingActionRequired,
pendingHistoryItems,
commandConfirmationRequest,
authConsentRequest,
permissionConfirmationRequest,
hasConfirmUpdateExtensionRequests,
hasLoopDetectionConfirmationRequest
}) {
const pendingAttentionNotification = (0, import_react119.useMemo)(
() => getPendingAttentionNotification(
pendingHistoryItems,
commandConfirmationRequest,
authConsentRequest,
permissionConfirmationRequest,
hasConfirmUpdateExtensionRequests,
hasLoopDetectionConfirmationRequest
),
[
pendingHistoryItems,
commandConfirmationRequest,
authConsentRequest,
permissionConfirmationRequest,
hasConfirmUpdateExtensionRequests,
hasLoopDetectionConfirmationRequest
]
);
const hadPendingAttentionRef = (0, import_react119.useRef)(false);
const previousFocusedRef = (0, import_react119.useRef)(isFocused);
const previousStreamingStateRef = (0, import_react119.useRef)(streamingState);
const lastSentAttentionNotificationRef = (0, import_react119.useRef)(null);
(0, import_react119.useEffect)(() => {
if (!notificationsEnabled) {
return;
}
const wasFocused = previousFocusedRef.current;
previousFocusedRef.current = isFocused;
const hasPendingAttention = pendingAttentionNotification !== null;
const hadPendingAttention = hadPendingAttentionRef.current;
hadPendingAttentionRef.current = hasPendingAttention;
if (!hasPendingAttention) {
lastSentAttentionNotificationRef.current = null;
return;
}
const shouldSuppressForFocus = hasReceivedFocusEvent && isFocused;
if (shouldSuppressForFocus) {
return;
}
const justEnteredAttentionState = !hadPendingAttention;
const justLostFocus = wasFocused && !isFocused;
const now = Date.now();
const currentKey = pendingAttentionNotification.key;
const lastSent = lastSentAttentionNotificationRef.current;
const keyChanged = !lastSent || lastSent.key !== currentKey;
const onCooldown = !!lastSent && lastSent.key === currentKey && now - lastSent.sentAt < ATTENTION_NOTIFICATION_COOLDOWN_MS;
const shouldNotifyByStateChange = hasReceivedFocusEvent ? justEnteredAttentionState || justLostFocus || keyChanged : justEnteredAttentionState || keyChanged;
if (!shouldNotifyByStateChange || onCooldown) {
return;
}
lastSentAttentionNotificationRef.current = {
key: currentKey,
sentAt: now
};
void notifyViaTerminal(
notificationsEnabled,
buildRunEventNotificationContent(pendingAttentionNotification.event),
notificationMethod
);
}, [
isFocused,
hasReceivedFocusEvent,
notificationsEnabled,
notificationMethod,
pendingAttentionNotification
]);
(0, import_react119.useEffect)(() => {
if (!notificationsEnabled) {
return;
}
const previousStreamingState = previousStreamingStateRef.current;
previousStreamingStateRef.current = streamingState;
const justCompletedTurn = previousStreamingState === "responding" /* Responding */ && streamingState === "idle" /* Idle */;
const shouldSuppressForFocus = hasReceivedFocusEvent && isFocused;
if (!justCompletedTurn || shouldSuppressForFocus || hasPendingActionRequired) {
return;
}
void notifyViaTerminal(
notificationsEnabled,
buildRunEventNotificationContent({
type: "session_complete",
detail: "Gemini CLI finished responding."
}),
notificationMethod
);
}, [
streamingState,
isFocused,
hasReceivedFocusEvent,
notificationsEnabled,
notificationMethod,
hasPendingActionRequired
]);
}
// packages/cli/src/ui/hooks/useVisibilityToggle.ts
var import_react120 = __toESM(require_react(), 1);
var APPROVAL_MODE_REVEAL_DURATION_MS = 1200;
var FOCUS_UI_ENABLED_STATE_KEY = "focusUiEnabled";
function useVisibilityToggle() {
const [focusUiEnabledByDefault] = (0, import_react120.useState)(
() => persistentState.get(FOCUS_UI_ENABLED_STATE_KEY) === true
);
const [cleanUiDetailsVisible, setCleanUiDetailsVisibleState] = (0, import_react120.useState)(
!focusUiEnabledByDefault
);
const modeRevealTimeoutRef = (0, import_react120.useRef)(null);
const cleanUiDetailsPinnedRef = (0, import_react120.useRef)(!focusUiEnabledByDefault);
const clearModeRevealTimeout = (0, import_react120.useCallback)(() => {
if (modeRevealTimeoutRef.current) {
clearTimeout(modeRevealTimeoutRef.current);
modeRevealTimeoutRef.current = null;
}
}, []);
const persistFocusUiPreference = (0, import_react120.useCallback)((isFullUiVisible) => {
persistentState.set(FOCUS_UI_ENABLED_STATE_KEY, !isFullUiVisible);
}, []);
const setCleanUiDetailsVisible = (0, import_react120.useCallback)(
(visible) => {
clearModeRevealTimeout();
cleanUiDetailsPinnedRef.current = visible;
setCleanUiDetailsVisibleState(visible);
persistFocusUiPreference(visible);
},
[clearModeRevealTimeout, persistFocusUiPreference]
);
const toggleCleanUiDetailsVisible = (0, import_react120.useCallback)(() => {
clearModeRevealTimeout();
setCleanUiDetailsVisibleState((visible) => {
const nextVisible = !visible;
cleanUiDetailsPinnedRef.current = nextVisible;
persistFocusUiPreference(nextVisible);
return nextVisible;
});
}, [clearModeRevealTimeout, persistFocusUiPreference]);
const revealCleanUiDetailsTemporarily = (0, import_react120.useCallback)(
(durationMs = APPROVAL_MODE_REVEAL_DURATION_MS) => {
if (cleanUiDetailsPinnedRef.current) {
return;
}
clearModeRevealTimeout();
setCleanUiDetailsVisibleState(true);
modeRevealTimeoutRef.current = setTimeout(() => {
if (!cleanUiDetailsPinnedRef.current) {
setCleanUiDetailsVisibleState(false);
}
modeRevealTimeoutRef.current = null;
}, durationMs);
},
[clearModeRevealTimeout]
);
(0, import_react120.useEffect)(() => () => clearModeRevealTimeout(), [clearModeRevealTimeout]);
return {
cleanUiDetailsVisible,
setCleanUiDetailsVisible,
toggleCleanUiDetailsVisible,
revealCleanUiDetailsTemporarily
};
}
// packages/cli/src/ui/AppContainer.tsx
var import_jsx_runtime138 = __toESM(require_jsx_runtime(), 1);
var SHELL_WIDTH_FRACTION = 0.89;
var SHELL_HEIGHT_PADDING = 10;
var AppContainer = (props) => {
const isHelpDismissKey = useIsHelpDismissKey();
const keyMatchers = useKeyMatchers();
const { config, initializationResult, resumedSessionData } = props;
const settings = useSettings();
const { reset } = useOverflowActions();
const notificationsEnabled = isNotificationsEnabled(settings);
const notificationMethod = getNotificationMethod(settings);
const { setOptions, dumpCurrentFrame, startRecording, stopRecording } = (0, import_react121.useContext)(AppContext_default);
const recordingFilenameRef = (0, import_react121.useRef)(null);
const historyManager = useHistory({
chatRecordingService: config.getGeminiClient()?.getChatRecordingService()
});
useMemoryMonitor(historyManager);
const isAlternateBuffer = config.getUseAlternateBuffer();
const [mouseMode, setMouseMode] = (0, import_react121.useState)(
() => config.getUseAlternateBuffer()
);
(0, import_react121.useEffect)(() => {
setOptions({
stickyHeadersInBackbuffer: mouseMode
});
if (mouseMode) {
enableMouseEvents();
} else {
disableMouseEvents();
}
}, [mouseMode, setOptions]);
const [corgiMode, setCorgiMode] = (0, import_react121.useState)(false);
const [forceRerenderKey, setForceRerenderKey] = (0, import_react121.useState)(0);
const [debugMessage, setDebugMessage] = (0, import_react121.useState)("");
const [quittingMessages, setQuittingMessages] = (0, import_react121.useState)(null);
const [showPrivacyNotice, setShowPrivacyNotice] = (0, import_react121.useState)(false);
const [themeError, setThemeError] = (0, import_react121.useState)(
initializationResult.themeError
);
const [isProcessing, setIsProcessing] = (0, import_react121.useState)(false);
const [embeddedShellFocused, setEmbeddedShellFocused] = (0, import_react121.useState)(false);
const [showDebugProfiler, setShowDebugProfiler] = (0, import_react121.useState)(false);
const [customDialog, setCustomDialog] = (0, import_react121.useState)(
null
);
const [copyModeEnabled, setCopyModeEnabled] = (0, import_react121.useState)(false);
const [pendingRestorePrompt, setPendingRestorePrompt] = (0, import_react121.useState)(false);
const toggleBackgroundTasksRef = (0, import_react121.useRef)(() => {
});
const isBackgroundTaskVisibleRef = (0, import_react121.useRef)(false);
const backgroundTasksRef = (0, import_react121.useRef)(/* @__PURE__ */ new Map());
const [adminSettingsChanged, setAdminSettingsChanged] = (0, import_react121.useState)(false);
const [expandedTools, setExpandedTools] = (0, import_react121.useState)(/* @__PURE__ */ new Set());
const toggleExpansion = (0, import_react121.useCallback)((callId) => {
setExpandedTools((prev) => {
const next = new Set(prev);
if (next.has(callId)) {
next.delete(callId);
} else {
next.add(callId);
}
return next;
});
}, []);
const toggleAllExpansion = (0, import_react121.useCallback)((callIds) => {
setExpandedTools((prev) => {
const next = new Set(prev);
const anyCollapsed = callIds.some((id) => !next.has(id));
if (anyCollapsed) {
callIds.forEach((id) => next.add(id));
} else {
callIds.forEach((id) => next.delete(id));
}
return next;
});
}, []);
const isExpanded = (0, import_react121.useCallback)(
(callId) => expandedTools.has(callId),
[expandedTools]
);
const [shellModeActive, setShellModeActive] = (0, import_react121.useState)(false);
const [isVoiceModeEnabled, setVoiceModeEnabled] = (0, import_react121.useState)(false);
const [modelSwitchedFromQuotaError, setModelSwitchedFromQuotaError] = (0, import_react121.useState)(false);
const [historyRemountKey, setHistoryRemountKey] = (0, import_react121.useState)(0);
const [settingsNonce, setSettingsNonce] = (0, import_react121.useState)(0);
const activeHooks = useHookDisplayState();
const [updateInfo, setUpdateInfo] = (0, import_react121.useState)(null);
const [isTrustedFolder, setIsTrustedFolder] = (0, import_react121.useState)(
() => isWorkspaceTrusted(settings.merged).isTrusted
);
const [queueErrorMessage, setQueueErrorMessage] = useTimedMessage(
QUEUE_ERROR_DISPLAY_DURATION_MS
);
const [newAgents, setNewAgents] = (0, import_react121.useState)(null);
const [constrainHeight, setConstrainHeight] = (0, import_react121.useState)(true);
const [expandHintTrigger, triggerExpandHint] = useTimedMessage(
EXPAND_HINT_DURATION_MS
);
const showIsExpandableHint = Boolean(expandHintTrigger);
const overflowState = useOverflowState();
const overflowingIdsSize = overflowState?.overflowingIds.size ?? 0;
const hasOverflowState = overflowingIdsSize > 0 || !constrainHeight;
(0, import_react121.useEffect)(() => {
if (hasOverflowState) {
triggerExpandHint(true);
}
}, [hasOverflowState, overflowingIdsSize, triggerExpandHint]);
const [defaultBannerText, setDefaultBannerText] = (0, import_react121.useState)("");
const [warningBannerText, setWarningBannerText] = (0, import_react121.useState)("");
const [bannerVisible, setBannerVisible] = (0, import_react121.useState)(true);
const bannerData = (0, import_react121.useMemo)(
() => ({
defaultText: defaultBannerText,
warningText: warningBannerText
}),
[defaultBannerText, warningBannerText]
);
const { bannerText } = useBanner(bannerData);
const extensionManager = config.getExtensionLoader();
extensionManager.setRequestConsent(
(description) => requestConsentInteractive(description, addConfirmUpdateExtensionRequest)
);
extensionManager.setRequestSetting();
const { addConfirmUpdateExtensionRequest, confirmUpdateExtensionRequests } = useConfirmUpdateRequests();
const {
extensionsUpdateState,
extensionsUpdateStateInternal,
dispatchExtensionStateUpdate
} = useExtensionUpdates(
extensionManager,
historyManager.addItem,
config.getEnableExtensionReloading()
);
const [isPermissionsDialogOpen, setPermissionsDialogOpen] = (0, import_react121.useState)(false);
const [permissionsDialogProps, setPermissionsDialogProps] = (0, import_react121.useState)(null);
const openPermissionsDialog = (0, import_react121.useCallback)(
(props2) => {
setPermissionsDialogOpen(true);
setPermissionsDialogProps(props2 ?? null);
},
[]
);
const closePermissionsDialog = (0, import_react121.useCallback)(() => {
setPermissionsDialogOpen(false);
setPermissionsDialogProps(null);
}, []);
const [isAgentConfigDialogOpen, setIsAgentConfigDialogOpen] = (0, import_react121.useState)(false);
const [selectedAgentName, setSelectedAgentName] = (0, import_react121.useState)();
const [selectedAgentDisplayName, setSelectedAgentDisplayName] = (0, import_react121.useState)();
const [selectedAgentDefinition, setSelectedAgentDefinition] = (0, import_react121.useState)();
const openAgentConfigDialog = (0, import_react121.useCallback)(
(name, displayName, definition) => {
setSelectedAgentName(name);
setSelectedAgentDisplayName(displayName);
setSelectedAgentDefinition(definition);
setIsAgentConfigDialogOpen(true);
},
[]
);
const closeAgentConfigDialog = (0, import_react121.useCallback)(() => {
setIsAgentConfigDialogOpen(false);
setSelectedAgentName(void 0);
setSelectedAgentDisplayName(void 0);
setSelectedAgentDefinition(void 0);
}, []);
const toggleDebugProfiler = (0, import_react121.useCallback)(
() => setShowDebugProfiler((prev) => !prev),
[]
);
const [currentModel, setCurrentModel] = (0, import_react121.useState)(config.getModel());
const [userTier, setUserTier] = (0, import_react121.useState)(void 0);
const [quotaStats, setQuotaStats] = (0, import_react121.useState)(() => {
const remaining = config.getQuotaRemaining();
const limit = config.getQuotaLimit();
const resetTime = config.getQuotaResetTime();
return remaining !== void 0 || limit !== void 0 || resetTime !== void 0 ? { remaining, limit, resetTime } : void 0;
});
const [paidTier, setPaidTier] = (0, import_react121.useState)(
void 0
);
const [isConfigInitialized, setConfigInitialized] = (0, import_react121.useState)(false);
const logger = useLogger(config);
const { inputHistory, addInput, initializeFromLogger } = useInputHistoryStore();
const { columns: terminalWidth, rows: terminalHeight } = useTerminalSize();
const { stdin, setRawMode } = use_stdin_default();
const { stdout } = use_stdout_default();
const app = use_app_default();
const { stats: sessionStats } = useSessionStats();
const branchName = useGitBranchName(config.getTargetDir());
const rootUiRef = (0, import_react121.useRef)(null);
const lastTitleRef = (0, import_react121.useRef)(null);
const staticExtraHeight = 3;
(0, import_react121.useEffect)(() => {
(async () => {
if (!config.isInitialized()) {
await config.initialize();
}
setConfigInitialized(true);
startupProfiler.flush(config);
startAutoMemoryIfEnabled(config);
const sessionStartSource = resumedSessionData ? SessionStartSource.Resume : SessionStartSource.Startup;
const result = await config.getHookSystem()?.fireSessionStartEvent(sessionStartSource);
if (result) {
const additionalContext = result.getAdditionalContext();
const geminiClient2 = config.getGeminiClient();
if (additionalContext && geminiClient2) {
await geminiClient2.addHistory({
role: "user",
parts: [
{ text: `<hook_context>${additionalContext}</hook_context>` }
]
});
}
}
generateSummary(config).catch((e) => {
debugLogger.warn("Background summary generation failed:", e);
});
})();
const cleanupFn = async () => {
disableMouseEvents();
await Promise.all(
Array.from(backgroundTasksRef.current.keys()).map(
(pid) => ShellExecutionService.kill(pid)
)
);
const ideClient = await IdeClient.getInstance();
await ideClient.disconnect();
await config?.getHookSystem()?.fireSessionEndEvent(SessionEndReason.Exit);
};
registerCleanup(cleanupFn);
return () => {
removeCleanup(cleanupFn);
cleanupFn().catch(
(e) => debugLogger.error("Error during cleanup:", e)
);
};
}, [config, resumedSessionData]);
(0, import_react121.useEffect)(
() => setUpdateHandler(historyManager.addItem, setUpdateInfo),
[historyManager.addItem]
);
(0, import_react121.useEffect)(() => {
const handleModelChanged = () => {
setCurrentModel(config.getModel());
};
const handleQuotaChanged = (payload) => {
setQuotaStats({
remaining: payload.remaining,
limit: payload.limit,
resetTime: payload.resetTime
});
};
coreEvents.on(CoreEvent.ModelChanged, handleModelChanged);
coreEvents.on(CoreEvent.QuotaChanged, handleQuotaChanged);
return () => {
coreEvents.off(CoreEvent.ModelChanged, handleModelChanged);
coreEvents.off(CoreEvent.QuotaChanged, handleQuotaChanged);
};
}, [config]);
(0, import_react121.useEffect)(() => {
const handleSettingsChanged = () => {
setSettingsNonce((prev) => prev + 1);
};
const handleAdminSettingsChanged = () => {
setAdminSettingsChanged(true);
};
const handleAgentsDiscovered = (payload) => {
setNewAgents(payload.agents);
};
coreEvents.on(CoreEvent.SettingsChanged, handleSettingsChanged);
coreEvents.on(CoreEvent.AdminSettingsChanged, handleAdminSettingsChanged);
coreEvents.on(CoreEvent.AgentsDiscovered, handleAgentsDiscovered);
return () => {
coreEvents.off(CoreEvent.SettingsChanged, handleSettingsChanged);
coreEvents.off(
CoreEvent.AdminSettingsChanged,
handleAdminSettingsChanged
);
coreEvents.off(CoreEvent.AgentsDiscovered, handleAgentsDiscovered);
};
}, [settings]);
const { errorCount, clearErrorCount } = useErrorCount();
const mainAreaWidth = calculateMainAreaWidth(terminalWidth, config);
const { inputWidth, suggestionsWidth } = (0, import_react121.useMemo)(() => {
const { inputWidth: inputWidth2, suggestionsWidth: suggestionsWidth2 } = calculatePromptWidths(mainAreaWidth);
return { inputWidth: inputWidth2, suggestionsWidth: suggestionsWidth2 };
}, [mainAreaWidth]);
const staticAreaMaxItemHeight = Math.max(terminalHeight * 4, 100);
const getPreferredEditor = (0, import_react121.useCallback)(
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
() => settings.merged.general.preferredEditor,
[settings.merged.general.preferredEditor]
);
const buffer = useTextBuffer({
initialText: "",
viewport: { height: 10, width: inputWidth },
stdin,
setRawMode,
escapePastedPaths: true,
shellModeActive,
getPreferredEditor
});
const bufferRef = (0, import_react121.useRef)(buffer);
(0, import_react121.useEffect)(() => {
bufferRef.current = buffer;
}, [buffer]);
const stableSetText = (0, import_react121.useCallback)((text) => {
bufferRef.current.setText(text);
}, []);
(0, import_react121.useEffect)(() => {
initializeFromLogger(logger);
}, [logger, initializeFromLogger]);
useTerminalSetupPrompt({
addConfirmUpdateExtensionRequest,
addItem: historyManager.addItem
});
const refreshStatic = (0, import_react121.useCallback)(() => {
if (!isAlternateBuffer && !config.getUseTerminalBuffer()) {
stdout.write(base_exports.clearTerminal);
setHistoryRemountKey((prev) => prev + 1);
}
}, [setHistoryRemountKey, isAlternateBuffer, stdout, config]);
const shouldUseAlternateScreen = shouldEnterAlternateScreen(
isAlternateBuffer,
config.getScreenReader()
);
const handleEditorClose = (0, import_react121.useCallback)(() => {
if (shouldUseAlternateScreen) {
enterAlternateScreen();
enableMouseEvents();
disableLineWrapping();
app.rerender();
}
terminalCapabilityManager.enableSupportedModes();
refreshStatic();
}, [refreshStatic, shouldUseAlternateScreen, app]);
const [editorError, setEditorError] = (0, import_react121.useState)(null);
const {
isEditorDialogOpen,
openEditorDialog,
handleEditorSelect,
exitEditorDialog
} = useEditorSettings(settings, setEditorError, historyManager.addItem);
(0, import_react121.useEffect)(() => {
coreEvents.on(CoreEvent.ExternalEditorClosed, handleEditorClose);
coreEvents.on(CoreEvent.RequestEditorSelection, openEditorDialog);
return () => {
coreEvents.off(CoreEvent.ExternalEditorClosed, handleEditorClose);
coreEvents.off(CoreEvent.RequestEditorSelection, openEditorDialog);
};
}, [handleEditorClose, openEditorDialog]);
(0, import_react121.useEffect)(() => {
if (!(settings.merged.ui.hideBanner || config.getScreenReader()) && bannerVisible && bannerText) {
refreshStatic();
}
}, [bannerVisible, bannerText, settings, config, refreshStatic]);
const { isSettingsDialogOpen, openSettingsDialog, closeSettingsDialog } = useSettingsCommand();
const {
isThemeDialogOpen,
openThemeDialog,
closeThemeDialog,
handleThemeSelect,
handleThemeHighlight
} = useThemeCommand(
settings,
setThemeError,
historyManager.addItem,
initializationResult.themeError,
refreshStatic
);
useTerminalTheme(handleThemeSelect, config, refreshStatic);
const {
authState,
setAuthState,
authError,
onAuthError,
apiKeyDefaultValue,
reloadApiKey,
accountSuspensionInfo,
setAccountSuspensionInfo
} = useAuthCommand(
settings,
config,
initializationResult.authError,
initializationResult.accountSuspensionInfo
);
const [authContext, setAuthContext] = (0, import_react121.useState)(
{}
);
(0, import_react121.useEffect)(() => {
if (authState === "authenticated" /* Authenticated */ && authContext.requiresRestart) {
setAuthState("awaiting_login_restart" /* AwaitingLoginRestart */);
setAuthContext({});
}
}, [authState, authContext, setAuthState]);
const {
proQuotaRequest,
handleProQuotaChoice,
validationRequest,
handleValidationChoice,
// G1 AI Credits
overageMenuRequest,
handleOverageMenuChoice,
emptyWalletRequest,
handleEmptyWalletChoice
} = useQuotaAndFallback({
config,
historyManager,
userTier,
paidTier,
settings,
setModelSwitchedFromQuotaError,
onShowAuthSelection: () => setAuthState("updating" /* Updating */),
errorVerbosity: settings.merged.ui.errorVerbosity
});
const isAuthDialogOpen = authState === "updating" /* Updating */;
const isAuthenticating = authState === "unauthenticated" /* Unauthenticated */ && settings.merged.security.auth.selectedType !== AuthType.USE_GEMINI;
const isGeminiClientInitialized = config.getGeminiClient()?.isInitialized();
const { loadHistoryForResume, isResuming } = useSessionResume({
config,
historyManager,
refreshStatic,
isGeminiClientInitialized,
setQuittingMessages,
resumedSessionData,
isAuthenticating
});
const {
isSessionBrowserOpen,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession: handleDeleteSessionSync
} = useSessionBrowser(config, loadHistoryForResume);
const handleDeleteSession = (0, import_react121.useCallback)(
async (session) => {
await handleDeleteSessionSync(session);
},
[handleDeleteSessionSync]
);
const handleAuthSelect = (0, import_react121.useCallback)(
async (authType, scope) => {
if (authType) {
const previousAuthType = config.getContentGeneratorConfig()?.authType ?? "unknown";
if (authType === AuthType.LOGIN_WITH_GOOGLE) {
setAuthContext({ requiresRestart: true });
} else {
setAuthContext({});
}
await clearCachedCredentialFile();
settings.setValue(scope, "security.auth.selectedType", authType);
try {
config.setRemoteAdminSettings(void 0);
await config.refreshAuth(authType);
setAuthState("authenticated" /* Authenticated */);
logBillingEvent(
config,
new ApiKeyUpdatedEvent(previousAuthType, authType)
);
} catch (e) {
if (e instanceof ChangeAuthRequestedError) {
return;
}
if (e instanceof ProjectIdRequiredError) {
onAuthError(getErrorMessage(e));
return;
}
onAuthError(
`Failed to authenticate: ${e instanceof Error ? e.message : String(e)}`
);
return;
}
if (authType === AuthType.LOGIN_WITH_GOOGLE && config.isBrowserLaunchSuppressed()) {
writeToStdout(`
----------------------------------------------------------------
Logging in with Google... Restarting Gemini CLI to continue.
----------------------------------------------------------------
`);
await relaunchApp();
}
}
setAuthState("authenticated" /* Authenticated */);
},
[settings, config, setAuthState, onAuthError, setAuthContext]
);
const handleApiKeySubmit = (0, import_react121.useCallback)(
async (apiKey) => {
try {
onAuthError(null);
if (!apiKey.trim()) {
onAuthError("API key cannot be empty or whitespace only.");
return;
}
await saveApiKey(apiKey);
await reloadApiKey();
await config.refreshAuth(AuthType.USE_GEMINI);
setAuthState("authenticated" /* Authenticated */);
} catch (e) {
onAuthError(
`Failed to save API key: ${e instanceof Error ? e.message : String(e)}`
);
}
},
[setAuthState, onAuthError, reloadApiKey, config]
);
const handleApiKeyCancel = (0, import_react121.useCallback)(() => {
setAuthState("updating" /* Updating */);
}, [setAuthState]);
(0, import_react121.useEffect)(() => {
if (authState === "authenticated" /* Authenticated */) {
setUserTier(config.getUserTier());
setPaidTier(config.getUserPaidTier());
}
}, [config, authState]);
(0, import_react121.useEffect)(() => {
if (settings.merged.security.auth.enforcedType && settings.merged.security.auth.selectedType && settings.merged.security.auth.enforcedType !== settings.merged.security.auth.selectedType) {
onAuthError(
`Authentication is enforced to be ${settings.merged.security.auth.enforcedType}, but you are currently using ${settings.merged.security.auth.selectedType}.`
);
} else if (settings.merged.security.auth.selectedType && !settings.merged.security.auth.useExternal) {
if (settings.merged.security.auth.selectedType === AuthType.USE_GEMINI) {
return;
}
const error = validateAuthMethod(
settings.merged.security.auth.selectedType
);
if (error) {
onAuthError(error);
}
}
}, [
settings.merged.security.auth.selectedType,
settings.merged.security.auth.enforcedType,
settings.merged.security.auth.useExternal,
onAuthError
]);
const { isModelDialogOpen, openModelDialog, closeModelDialog } = useModelCommand();
const {
isVoiceModelDialogOpen,
openVoiceModelDialog,
closeVoiceModelDialog
} = useVoiceModelCommand();
const { toggleVimEnabled } = useVimMode();
const setIsBackgroundTaskListOpenRef = (0, import_react121.useRef)(
() => {
}
);
const [shortcutsHelpVisible, setShortcutsHelpVisible] = (0, import_react121.useState)(false);
const {
cleanUiDetailsVisible,
setCleanUiDetailsVisible,
toggleCleanUiDetailsVisible,
revealCleanUiDetailsTemporarily
} = useVisibilityToggle();
const slashCommandActions = (0, import_react121.useMemo)(
() => ({
openAuthDialog: () => setAuthState("updating" /* Updating */),
openThemeDialog,
openEditorDialog,
openPrivacyNotice: () => setShowPrivacyNotice(true),
openSettingsDialog,
openSessionBrowser,
openModelDialog,
openVoiceModelDialog,
openAgentConfigDialog,
openPermissionsDialog,
quit: (messages) => {
closeThemeDialog();
setQuittingMessages(messages);
setTimeout(async () => {
await runExitCleanup();
process12.exit(0);
}, 100);
},
setDebugMessage,
toggleCorgiMode: () => setCorgiMode((prev) => !prev),
toggleVoiceMode: () => setVoiceModeEnabled((prev) => !prev),
toggleDebugProfiler,
dispatchExtensionStateUpdate,
addConfirmUpdateExtensionRequest,
toggleBackgroundTasks: () => {
toggleBackgroundTasksRef.current();
if (!isBackgroundTaskVisibleRef.current) {
setEmbeddedShellFocused(true);
if (backgroundTasksRef.current.size > 1) {
setIsBackgroundTaskListOpenRef.current(true);
} else {
setIsBackgroundTaskListOpenRef.current(false);
}
}
},
toggleShortcutsHelp: () => setShortcutsHelpVisible((visible) => !visible),
setText: stableSetText
}),
[
setAuthState,
openThemeDialog,
closeThemeDialog,
openEditorDialog,
openSettingsDialog,
openSessionBrowser,
openModelDialog,
openVoiceModelDialog,
openAgentConfigDialog,
setQuittingMessages,
setDebugMessage,
setShowPrivacyNotice,
setCorgiMode,
dispatchExtensionStateUpdate,
openPermissionsDialog,
addConfirmUpdateExtensionRequest,
toggleDebugProfiler,
setShortcutsHelpVisible,
stableSetText
]
);
const {
handleSlashCommand,
slashCommands,
pendingHistoryItems: pendingSlashCommandHistoryItems,
commandContext,
confirmationRequest: commandConfirmationRequest
} = useSlashCommandProcessor(
config,
settings,
historyManager.addItem,
historyManager.clearItems,
historyManager.loadHistory,
refreshStatic,
toggleVimEnabled,
setIsProcessing,
slashCommandActions,
extensionsUpdateStateInternal,
isConfigInitialized,
setBannerVisible,
setCustomDialog
);
const [authConsentRequest, setAuthConsentRequest] = (0, import_react121.useState)(null);
const [permissionConfirmationRequest, setPermissionConfirmationRequest] = (0, import_react121.useState)(null);
(0, import_react121.useEffect)(() => {
const handleConsentRequest = (payload) => {
setAuthConsentRequest({
prompt: payload.prompt,
onConfirm: (confirmed) => {
setAuthConsentRequest(null);
payload.onConfirm(confirmed);
}
});
};
coreEvents.on(CoreEvent.ConsentRequest, handleConsentRequest);
return () => {
coreEvents.off(CoreEvent.ConsentRequest, handleConsentRequest);
};
}, []);
const performMemoryRefresh = (0, import_react121.useCallback)(async () => {
historyManager.addItem(
{
type: "info" /* INFO */,
text: "Refreshing hierarchical memory (GEMINI.md or other context files)..."
},
Date.now()
);
try {
let flattenedMemory;
let fileCount;
if (config.isJitContextEnabled()) {
await config.getMemoryContextManager()?.refresh();
config.updateSystemInstructionIfInitialized();
flattenedMemory = flattenMemory(config.getUserMemory());
fileCount = config.getGeminiMdFileCount();
} else {
const result = await refreshServerHierarchicalMemory(config);
flattenedMemory = flattenMemory(result.memoryContent);
fileCount = result.fileCount;
}
historyManager.addItem(
{
type: "info" /* INFO */,
text: `Memory reloaded successfully. ${flattenedMemory.length > 0 ? `Loaded ${flattenedMemory.length} characters from ${fileCount} file(s)` : "No memory content found"}`
},
Date.now()
);
if (config.getDebugMode()) {
debugLogger.log(
`[DEBUG] Refreshed memory content in config: ${flattenedMemory.substring(
0,
200
)}...`
);
}
} catch (error) {
const errorMessage = getErrorMessage(error);
historyManager.addItem(
{
type: "error" /* ERROR */,
text: `Error refreshing memory: ${errorMessage}`
},
Date.now()
);
debugLogger.warn("Error refreshing memory:", error);
}
}, [config, historyManager]);
const cancelHandlerRef = (0, import_react121.useRef)(() => {
});
const onCancelSubmit = (0, import_react121.useCallback)(
(shouldRestorePrompt, clearBuffer = false) => {
if (shouldRestorePrompt) {
setPendingRestorePrompt(true);
} else {
setPendingRestorePrompt(false);
cancelHandlerRef.current(false, clearBuffer);
}
},
[]
);
(0, import_react121.useEffect)(() => {
if (pendingRestorePrompt) {
const lastHistoryUserMsg = historyManager.history.findLast(
(h) => h.type === "user"
);
const lastUserMsg = inputHistory.at(-1);
if (!lastHistoryUserMsg || typeof lastHistoryUserMsg.text === "string" && lastHistoryUserMsg.text === lastUserMsg) {
cancelHandlerRef.current(true);
setPendingRestorePrompt(false);
}
}
}, [pendingRestorePrompt, inputHistory, historyManager.history]);
const pendingHintsRef = (0, import_react121.useRef)([]);
const [pendingHintCount, setPendingHintCount] = (0, import_react121.useState)(0);
const consumePendingHints = (0, import_react121.useCallback)(() => {
if (pendingHintsRef.current.length === 0) {
return null;
}
const hint = pendingHintsRef.current.join("\n");
pendingHintsRef.current = [];
setPendingHintCount(0);
return hint;
}, []);
(0, import_react121.useEffect)(() => {
const hintListener = (text, source) => {
if (source !== "user_steering" && source !== "background_completion") {
return;
}
pendingHintsRef.current.push(text);
setPendingHintCount((prev) => prev + 1);
};
config.injectionService.onInjection(hintListener);
return () => {
config.injectionService.offInjection(hintListener);
};
}, [config]);
const streamAgent = (0, import_react121.useMemo)(
() => config?.getAgentSessionInteractiveEnabled() ? new LegacyAgentProtocol({ config, getPreferredEditor }) : void 0,
[config, getPreferredEditor]
);
const activeStream = streamAgent ? (
// eslint-disable-next-line react-hooks/rules-of-hooks
useAgentStream({
agent: streamAgent,
addItem: historyManager.addItem,
onCancelSubmit,
isShellFocused: embeddedShellFocused,
logger
})
) : (
// eslint-disable-next-line react-hooks/rules-of-hooks
useGeminiStream(
config.getGeminiClient(),
historyManager.history,
historyManager.addItem,
config,
settings,
setDebugMessage,
handleSlashCommand,
shellModeActive,
getPreferredEditor,
onAuthError,
performMemoryRefresh,
modelSwitchedFromQuotaError,
setModelSwitchedFromQuotaError,
onCancelSubmit,
setEmbeddedShellFocused,
terminalWidth,
terminalHeight,
embeddedShellFocused,
consumePendingHints
)
);
const {
streamingState,
submitQuery,
initError,
pendingHistoryItems: pendingGeminiHistoryItems,
thought,
cancelOngoingRequest,
pendingToolCalls,
handleApprovalModeChange,
activePtyId,
loopDetectionConfirmationRequest,
lastOutputTime,
backgroundTaskCount,
isBackgroundTaskVisible,
toggleBackgroundTasks,
backgroundCurrentExecution,
backgroundTasks,
dismissBackgroundTask,
retryStatus
} = activeStream;
const pendingHistoryItems = (0, import_react121.useMemo)(
() => [...pendingSlashCommandHistoryItems, ...pendingGeminiHistoryItems],
[pendingSlashCommandHistoryItems, pendingGeminiHistoryItems]
);
toggleBackgroundTasksRef.current = toggleBackgroundTasks;
isBackgroundTaskVisibleRef.current = isBackgroundTaskVisible;
backgroundTasksRef.current = backgroundTasks;
const {
activeBackgroundTaskPid,
setIsBackgroundTaskListOpen,
isBackgroundTaskListOpen,
setActiveBackgroundTaskPid,
backgroundTaskHeight
} = useBackgroundTaskManager({
backgroundTasks,
backgroundTaskCount,
isBackgroundTaskVisible,
activePtyId,
embeddedShellFocused,
setEmbeddedShellFocused,
terminalHeight
});
setIsBackgroundTaskListOpenRef.current = setIsBackgroundTaskListOpen;
const lastOutputTimeRef = (0, import_react121.useRef)(0);
(0, import_react121.useEffect)(() => {
lastOutputTimeRef.current = lastOutputTime;
}, [lastOutputTime]);
const { shouldShowFocusHint, inactivityStatus } = useShellInactivityStatus({
activePtyId,
lastOutputTime,
streamingState,
pendingToolCalls,
embeddedShellFocused,
isInteractiveShellEnabled: config.isInteractiveShellEnabled()
});
const shouldShowActionRequiredTitle = inactivityStatus === "action_required";
const shouldShowSilentWorkingTitle = inactivityStatus === "silent_working";
const handleApprovalModeChangeWithUiReveal = (0, import_react121.useCallback)(
(mode) => {
void handleApprovalModeChange(mode);
if (!cleanUiDetailsVisible) {
revealCleanUiDetailsTemporarily(APPROVAL_MODE_REVEAL_DURATION_MS);
}
},
[
handleApprovalModeChange,
cleanUiDetailsVisible,
revealCleanUiDetailsTemporarily
]
);
const { isMcpReady } = useMcpStatus(config);
const isCompressing = (0, import_react121.useMemo)(
() => pendingHistoryItems.some(
(item) => item.type === "compression" /* COMPRESSION */ && item.compression.isPending
),
[pendingHistoryItems]
);
const {
messageQueue: messageQueue2,
addMessage,
clearQueue,
getQueuedMessagesText,
popAllMessages
} = useMessageQueue({
isConfigInitialized,
streamingState,
submitQuery,
isMcpReady,
isCompressing
});
cancelHandlerRef.current = (0, import_react121.useCallback)(
(shouldRestorePrompt = true, clearBuffer = false) => {
if (!clearBuffer && isToolAwaitingConfirmation(pendingHistoryItems)) {
return;
}
if (!shouldRestorePrompt) {
if (clearBuffer) {
buffer.setText("");
}
return;
}
const lastUserMessage = inputHistory.at(-1);
let textToSet = lastUserMessage || "";
const queuedText = getQueuedMessagesText();
if (queuedText) {
textToSet = textToSet ? `${textToSet}
${queuedText}` : queuedText;
clearQueue();
}
if (textToSet) {
buffer.setText(textToSet);
}
},
[
buffer,
inputHistory,
getQueuedMessagesText,
clearQueue,
pendingHistoryItems
]
);
const handleHintSubmit = (0, import_react121.useCallback)(
(hint) => {
const trimmed = hint.trim();
if (!trimmed) {
return;
}
config.injectionService.addInjection(trimmed, "user_steering");
historyManager.addItem({
type: "hint",
text: trimmed
});
},
[config, historyManager]
);
const handleFinalSubmit = (0, import_react121.useCallback)(
async (submittedValue) => {
reset();
triggerExpandHint(null);
if (!constrainHeight) {
setConstrainHeight(true);
if (!isAlternateBuffer) {
refreshStatic();
}
}
const isSlash = isSlashCommand(submittedValue.trim());
const isIdle = streamingState === "idle" /* Idle */;
const isAgentRunning = streamingState === "responding" /* Responding */ || isToolExecuting(pendingHistoryItems);
if (isSlash && isAgentRunning) {
const { commandToExecute } = parseSlashCommand(
submittedValue,
slashCommands ?? []
);
if (commandToExecute?.isSafeConcurrent) {
void handleSlashCommand(submittedValue);
addInput(submittedValue);
return;
}
}
if (config.isModelSteeringEnabled() && isAgentRunning && !isSlash) {
handleHintSubmit(submittedValue);
addInput(submittedValue);
return;
}
const isMcpOrConfigReady = isConfigInitialized && isMcpReady;
if (isSlash && isConfigInitialized || !isCompressing && isIdle && isMcpOrConfigReady) {
if (!isSlash) {
const permissions = await checkPermissions(submittedValue, config);
if (permissions.length > 0) {
setPermissionConfirmationRequest({
files: permissions,
onComplete: (result) => {
setPermissionConfirmationRequest(null);
if (result.allowed) {
permissions.forEach(
(p) => config.getWorkspaceContext().addReadOnlyPath(p)
);
}
void submitQuery(submittedValue);
}
});
addInput(submittedValue);
return;
}
}
void submitQuery(submittedValue);
} else {
if (isIdle && !isCompressing && !isMcpOrConfigReady && messageQueue2.length === 0) {
coreEvents.emitFeedback(
"info",
!isConfigInitialized ? "Initializing... Prompts will be queued." : "Waiting for MCP servers to initialize... Slash commands are still available and prompts will be queued."
);
}
addMessage(submittedValue);
}
addInput(submittedValue);
},
[
addMessage,
addInput,
submitQuery,
handleSlashCommand,
slashCommands,
isMcpReady,
streamingState,
isCompressing,
messageQueue2.length,
pendingHistoryItems,
config,
constrainHeight,
setConstrainHeight,
isAlternateBuffer,
refreshStatic,
reset,
handleHintSubmit,
isConfigInitialized,
triggerExpandHint
]
);
const handleClearScreen = (0, import_react121.useCallback)(() => {
reset();
triggerExpandHint(null);
historyManager.clearItems();
clearErrorCount();
refreshStatic();
}, [
historyManager,
clearErrorCount,
refreshStatic,
reset,
triggerExpandHint
]);
const { handleInput: vimHandleInput } = useVim(buffer, handleFinalSubmit);
const isInputActive = !initError && !isProcessing && !isResuming && (streamingState === "idle" /* Idle */ || streamingState === "responding" /* Responding */ || streamingState === "waiting_for_confirmation" /* WaitingForConfirmation */) && !proQuotaRequest;
const observerRef = (0, import_react121.useRef)(null);
(0, import_react121.useEffect)(
() => () => {
observerRef.current?.disconnect();
},
[]
);
const [controlsHeight, setControlsHeight] = (0, import_react121.useState)(0);
const [lastNonCopyControlsHeight, setLastNonCopyControlsHeight] = (0, import_react121.useState)(0);
(0, import_react121.useLayoutEffect)(() => {
if (!copyModeEnabled && controlsHeight > 0) {
setLastNonCopyControlsHeight(controlsHeight);
}
}, [copyModeEnabled, controlsHeight]);
const stableControlsHeight = copyModeEnabled && lastNonCopyControlsHeight > 0 ? lastNonCopyControlsHeight : controlsHeight;
const mainControlsRef = (0, import_react121.useCallback)((node) => {
if (observerRef.current) {
observerRef.current.disconnect();
observerRef.current = null;
}
if (node) {
const observer = new ResizeObserver((entries) => {
const entry = entries[0];
if (entry) {
const roundedHeight = Math.round(entry.contentRect.height);
setControlsHeight(
(prev) => roundedHeight !== prev ? roundedHeight : prev
);
}
});
observer.observe(node);
observerRef.current = observer;
}
}, []);
const availableTerminalHeight = Math.max(
0,
terminalHeight - stableControlsHeight - backgroundTaskHeight - 1
);
config.setShellExecutionConfig({
terminalWidth: Math.floor(terminalWidth * SHELL_WIDTH_FRACTION),
terminalHeight: Math.max(
Math.floor(availableTerminalHeight - SHELL_HEIGHT_PADDING),
1
),
pager: settings.merged.tools.shell.pager,
showColor: settings.merged.tools.shell.showColor,
sanitizationConfig: config.sanitizationConfig,
sandboxManager: config.sandboxManager
});
const { isFocused, hasReceivedFocusEvent } = useFocus();
const contextFileNames = (0, import_react121.useMemo)(() => {
const fromSettings = settings.merged.context.fileName;
return fromSettings ? Array.isArray(fromSettings) ? fromSettings : [fromSettings] : getAllGeminiMdFilenames();
}, [settings.merged.context.fileName]);
const initialPrompt = (0, import_react121.useMemo)(() => config.getQuestion(), [config]);
const initialPromptSubmitted = (0, import_react121.useRef)(false);
const geminiClient = config.getGeminiClient();
(0, import_react121.useEffect)(() => {
if (initialPrompt && isConfigInitialized && !initialPromptSubmitted.current && !isAuthenticating && !isAuthDialogOpen && !isThemeDialogOpen && !isEditorDialogOpen && !showPrivacyNotice && geminiClient?.isInitialized?.()) {
void handleFinalSubmit(initialPrompt);
initialPromptSubmitted.current = true;
}
}, [
initialPrompt,
isConfigInitialized,
handleFinalSubmit,
isAuthenticating,
isAuthDialogOpen,
isThemeDialogOpen,
isEditorDialogOpen,
showPrivacyNotice,
geminiClient
]);
const [idePromptAnswered, setIdePromptAnswered] = (0, import_react121.useState)(false);
const [currentIDE, setCurrentIDE] = (0, import_react121.useState)(null);
(0, import_react121.useEffect)(() => {
const getIde = async () => {
const ideClient = await IdeClient.getInstance();
const currentIde = ideClient.getCurrentIde();
setCurrentIDE(currentIde || null);
};
getIde();
}, []);
const shouldShowIdePrompt = Boolean(
currentIDE && !config.getIdeMode() && !settings.merged.ide.hasSeenNudge && !idePromptAnswered
);
const [showErrorDetails, setShowErrorDetails] = (0, import_react121.useState)(false);
const [showFullTodos, setShowFullTodos] = (0, import_react121.useState)(false);
const [renderMarkdown, setRenderMarkdown] = (0, import_react121.useState)(true);
const handleExitRepeat = (0, import_react121.useCallback)(
(count) => {
if (count > 2) {
recordExitFail(config);
}
if (count > 1) {
void handleSlashCommand("/quit", void 0, void 0, false);
}
},
[config, handleSlashCommand]
);
const { pressCount: ctrlCPressCount, handlePress: handleCtrlCPress } = useRepeatedKeyPress({
windowMs: WARNING_PROMPT_DURATION_MS,
onRepeat: handleExitRepeat
});
const { pressCount: ctrlDPressCount, handlePress: handleCtrlDPress } = useRepeatedKeyPress({
windowMs: WARNING_PROMPT_DURATION_MS,
onRepeat: handleExitRepeat
});
const [ideContextState, setIdeContextState] = (0, import_react121.useState)();
const [showEscapePrompt, setShowEscapePrompt] = (0, import_react121.useState)(false);
const [showIdeRestartPrompt, setShowIdeRestartPrompt] = (0, import_react121.useState)(false);
const [transientMessage, showTransientMessage] = useTimedMessage(WARNING_PROMPT_DURATION_MS);
const {
isFolderTrustDialogOpen,
discoveryResults: folderDiscoveryResults,
handleFolderTrustSelect,
isRestarting
} = useFolderTrust(settings, setIsTrustedFolder, historyManager.addItem);
const policyUpdateConfirmationRequest = config.getPolicyUpdateConfirmationRequest();
const [isPolicyUpdateDialogOpen, setIsPolicyUpdateDialogOpen] = (0, import_react121.useState)(
!!policyUpdateConfirmationRequest
);
const {
needsRestart: ideNeedsRestart,
restartReason: ideTrustRestartReason
} = useIdeTrustListener();
const isInitialMount = (0, import_react121.useRef)(true);
useIncludeDirsTrust(config, isTrustedFolder, historyManager, setCustomDialog);
const tabFocusTimeoutRef = (0, import_react121.useRef)(null);
(0, import_react121.useEffect)(() => {
const handleTransientMessage = (payload) => {
showTransientMessage({ text: payload.message, type: payload.type });
};
const handleSelectionWarning = () => {
showTransientMessage({
text: "Press Ctrl-S to enter selection mode to copy text.",
type: "warning" /* Warning */
});
};
const handlePasteTimeout = () => {
showTransientMessage({
text: "Paste Timed out. Possibly due to slow connection.",
type: "warning" /* Warning */
});
};
appEvents.on("transient-message" /* TransientMessage */, handleTransientMessage);
appEvents.on("selection-warning" /* SelectionWarning */, handleSelectionWarning);
appEvents.on("paste-timeout" /* PasteTimeout */, handlePasteTimeout);
return () => {
appEvents.off("transient-message" /* TransientMessage */, handleTransientMessage);
appEvents.off("selection-warning" /* SelectionWarning */, handleSelectionWarning);
appEvents.off("paste-timeout" /* PasteTimeout */, handlePasteTimeout);
if (tabFocusTimeoutRef.current) {
clearTimeout(tabFocusTimeoutRef.current);
}
};
}, [showTransientMessage]);
const handleWarning = (0, import_react121.useCallback)(
(message) => {
showTransientMessage({
text: message,
type: "warning" /* Warning */
});
},
[showTransientMessage]
);
const { handleSuspend } = useSuspend({
handleWarning,
setRawMode,
refreshStatic,
setForceRerenderKey,
shouldUseAlternateScreen
});
(0, import_react121.useEffect)(() => {
if (ideNeedsRestart) {
setShowIdeRestartPrompt(true);
}
}, [ideNeedsRestart]);
(0, import_react121.useEffect)(() => {
if (isInitialMount.current) {
isInitialMount.current = false;
return;
}
const handler = setTimeout(() => {
refreshStatic();
}, 300);
return () => {
clearTimeout(handler);
};
}, [terminalWidth, refreshStatic]);
(0, import_react121.useEffect)(() => {
const unsubscribe = ideContextStore.subscribe(setIdeContextState);
setIdeContextState(ideContextStore.get());
return unsubscribe;
}, []);
(0, import_react121.useEffect)(() => {
const openDebugConsole = () => {
setShowErrorDetails(true);
setConstrainHeight(false);
};
appEvents.on("open-debug-console" /* OpenDebugConsole */, openDebugConsole);
return () => {
appEvents.off("open-debug-console" /* OpenDebugConsole */, openDebugConsole);
};
}, [config]);
const handleEscapePromptChange = (0, import_react121.useCallback)((showPrompt) => {
setShowEscapePrompt(showPrompt);
}, []);
const handleIdePromptComplete = (0, import_react121.useCallback)(
(result) => {
if (result.userSelection === "yes") {
handleSlashCommand("/ide install");
settings.setValue("User" /* User */, "ide.hasSeenNudge", true);
} else if (result.userSelection === "dismiss") {
settings.setValue("User" /* User */, "ide.hasSeenNudge", true);
}
setIdePromptAnswered(true);
},
[handleSlashCommand, settings]
);
const handleGlobalKeypress = (0, import_react121.useCallback)(
(key) => {
if (settings.merged.general.debugKeystrokeLogging) {
debugLogger.log("[DEBUG] Keystroke:", JSON.stringify(key));
}
if (shortcutsHelpVisible && isHelpDismissKey(key)) {
setShortcutsHelpVisible(false);
}
if (keyMatchers["app.toggleMouseMode" /* TOGGLE_MOUSE_MODE */](key)) {
setMouseMode((prev) => !prev);
if (mouseMode && !isAlternateBuffer) {
appEvents.emit("scroll-to-bottom" /* ScrollToBottom */);
}
return true;
}
if (isAlternateBuffer && keyMatchers["app.toggleCopyMode" /* TOGGLE_COPY_MODE */](key)) {
setCopyModeEnabled(true);
disableMouseEvents();
return true;
}
if (keyMatchers["basic.quit" /* QUIT */](key)) {
void cancelOngoingRequest?.();
handleCtrlCPress();
return true;
} else if (keyMatchers["basic.exit" /* EXIT */](key)) {
if (bufferRef.current.text.length > 0) {
return false;
}
handleCtrlDPress();
return true;
} else if (keyMatchers["app.suspend" /* SUSPEND_APP */](key)) {
handleSuspend();
} else if (keyMatchers["app.dumpFrame" /* DUMP_FRAME */](key)) {
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
const filename = `snapshot-${timestamp}.json`;
if (dumpCurrentFrame) {
dumpCurrentFrame(filename);
debugLogger.log(`Dumped frame to: ${filename}`);
}
return true;
} else if (keyMatchers["app.startRecording" /* START_RECORDING */](key)) {
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
const filename = `recording-${timestamp}.json`;
if (startRecording) {
startRecording(filename);
recordingFilenameRef.current = filename;
debugLogger.log(`Started recording to: ${filename}`);
}
return true;
} else if (keyMatchers["app.stopRecording" /* STOP_RECORDING */](key)) {
if (stopRecording) {
stopRecording();
debugLogger.log(
`Stopped recording, saved to: ${recordingFilenameRef.current ?? "unknown"}`
);
recordingFilenameRef.current = null;
}
return true;
} else if (keyMatchers["app.toggleCopyMode" /* TOGGLE_COPY_MODE */](key) && !isAlternateBuffer) {
showTransientMessage({
text: "Use Ctrl+O to expand and collapse blocks of content.",
type: "warning" /* Warning */
});
return true;
}
const toggleLastTurnTools = () => {
triggerExpandHint(true);
const targetToolCallIds = getLastTurnToolCallIds(
historyManager.history,
pendingHistoryItems
);
if (targetToolCallIds.length > 0) {
toggleAllExpansion(targetToolCallIds);
}
};
let enteringConstrainHeightMode = false;
if (!constrainHeight) {
enteringConstrainHeightMode = true;
setConstrainHeight(true);
if (keyMatchers["app.showMoreLines" /* SHOW_MORE_LINES */](key)) {
toggleLastTurnTools();
}
if (!isAlternateBuffer) {
refreshStatic();
}
}
if (keyMatchers["app.showErrorDetails" /* SHOW_ERROR_DETAILS */](key)) {
if (settings.merged.general.devtools) {
void (async () => {
const { toggleDevToolsPanel } = await import("./devtoolsService-I65SE4OX.js");
await toggleDevToolsPanel(
config,
showErrorDetails,
() => setShowErrorDetails((prev) => !prev),
() => setShowErrorDetails(true)
);
})();
} else {
setShowErrorDetails((prev) => !prev);
}
return true;
} else if (keyMatchers["app.showFullTodos" /* SHOW_FULL_TODOS */](key)) {
setShowFullTodos((prev) => !prev);
return true;
} else if (keyMatchers["app.toggleMarkdown" /* TOGGLE_MARKDOWN */](key)) {
setRenderMarkdown((prev) => {
const newValue = !prev;
refreshStatic();
return newValue;
});
return true;
} else if (keyMatchers["app.showIdeContextDetail" /* SHOW_IDE_CONTEXT_DETAIL */](key) && config.getIdeMode() && ideContextState) {
handleSlashCommand("/ide status");
return true;
} else if (keyMatchers["app.showMoreLines" /* SHOW_MORE_LINES */](key) && !enteringConstrainHeightMode) {
setConstrainHeight(false);
toggleLastTurnTools();
refreshStatic();
return true;
} else if ((keyMatchers["app.focusShellInput" /* FOCUS_SHELL_INPUT */](key) || keyMatchers["background.unfocusList" /* UNFOCUS_BACKGROUND_SHELL_LIST */](key)) && (activePtyId || isBackgroundTaskVisible && backgroundTasks.size > 0)) {
if (embeddedShellFocused) {
const capturedTime = lastOutputTimeRef.current;
if (tabFocusTimeoutRef.current)
clearTimeout(tabFocusTimeoutRef.current);
tabFocusTimeoutRef.current = setTimeout(() => {
if (lastOutputTimeRef.current === capturedTime) {
setEmbeddedShellFocused(false);
} else {
showTransientMessage({
text: "Use Shift+Tab to unfocus",
type: "warning" /* Warning */
});
}
}, 150);
return false;
}
const isIdle = Date.now() - lastOutputTimeRef.current >= 100;
if (isIdle && !activePtyId && !isBackgroundTaskVisible) {
if (tabFocusTimeoutRef.current)
clearTimeout(tabFocusTimeoutRef.current);
toggleBackgroundTasks();
setEmbeddedShellFocused(true);
if (backgroundTasks.size > 1) setIsBackgroundTaskListOpen(true);
return true;
}
setEmbeddedShellFocused(true);
return true;
} else if (keyMatchers["app.unfocusShellInput" /* UNFOCUS_SHELL_INPUT */](key) || keyMatchers["background.unfocus" /* UNFOCUS_BACKGROUND_SHELL */](key)) {
if (embeddedShellFocused) {
setEmbeddedShellFocused(false);
return true;
}
return false;
} else if (keyMatchers["background.toggle" /* TOGGLE_BACKGROUND_SHELL */](key)) {
if (activePtyId) {
backgroundCurrentExecution();
} else {
toggleBackgroundTasks();
if (!isBackgroundTaskVisible && backgroundTasks.size > 0) {
setEmbeddedShellFocused(true);
if (backgroundTasks.size > 1) {
setIsBackgroundTaskListOpen(true);
}
} else {
setEmbeddedShellFocused(false);
}
}
return true;
} else if (keyMatchers["background.toggleList" /* TOGGLE_BACKGROUND_SHELL_LIST */](key)) {
if (backgroundTasks.size > 0 && isBackgroundTaskVisible) {
if (!embeddedShellFocused) {
setEmbeddedShellFocused(true);
}
setIsBackgroundTaskListOpen(true);
}
return true;
}
return false;
},
[
constrainHeight,
setConstrainHeight,
setShowErrorDetails,
config,
ideContextState,
handleCtrlCPress,
handleCtrlDPress,
handleSlashCommand,
cancelOngoingRequest,
activePtyId,
handleSuspend,
embeddedShellFocused,
settings.merged.general.debugKeystrokeLogging,
refreshStatic,
setCopyModeEnabled,
tabFocusTimeoutRef,
isAlternateBuffer,
shortcutsHelpVisible,
backgroundCurrentExecution,
toggleBackgroundTasks,
backgroundTasks,
isBackgroundTaskVisible,
setIsBackgroundTaskListOpen,
lastOutputTimeRef,
showTransientMessage,
settings.merged.general.devtools,
showErrorDetails,
triggerExpandHint,
keyMatchers,
isHelpDismissKey,
historyManager.history,
pendingHistoryItems,
toggleAllExpansion,
dumpCurrentFrame,
startRecording,
stopRecording,
mouseMode
]
);
useKeypress(handleGlobalKeypress, { isActive: true, priority: true });
useKeypress(
(key) => {
if (keyMatchers["scroll.up" /* SCROLL_UP */](key) || keyMatchers["scroll.down" /* SCROLL_DOWN */](key) || keyMatchers["scroll.pageUp" /* PAGE_UP */](key) || keyMatchers["scroll.pageDown" /* PAGE_DOWN */](key) || keyMatchers["scroll.home" /* SCROLL_HOME */](key) || keyMatchers["scroll.end" /* SCROLL_END */](key)) {
return false;
}
setCopyModeEnabled(false);
if (mouseMode) {
enableMouseEvents();
}
return true;
},
{
isActive: copyModeEnabled,
// We need to receive keypresses first so they do not bubble to other
// handlers.
priority: 200 /* Critical */
}
);
(0, import_react121.useEffect)(() => {
if (settings.merged.ui.hideWindowTitle) return;
const paddedTitle = computeTerminalTitle({
streamingState,
thoughtSubject: thought?.subject,
isConfirming: !!commandConfirmationRequest || shouldShowActionRequiredTitle,
isSilentWorking: shouldShowSilentWorkingTitle,
folderName: basename4(config.getTargetDir()),
showThoughts: !!settings.merged.ui.showStatusInTitle,
useDynamicTitle: settings.merged.ui.dynamicWindowTitle
});
if (lastTitleRef.current !== paddedTitle) {
lastTitleRef.current = paddedTitle;
stdout.write(`\x1B]0;${paddedTitle}\x07`);
}
}, [
streamingState,
thought,
commandConfirmationRequest,
shouldShowActionRequiredTitle,
shouldShowSilentWorkingTitle,
settings.merged.ui.showStatusInTitle,
settings.merged.ui.dynamicWindowTitle,
settings.merged.ui.hideWindowTitle,
config,
stdout
]);
(0, import_react121.useEffect)(() => {
const handleUserFeedback = (payload) => {
let type;
switch (payload.severity) {
case "error":
type = "error" /* ERROR */;
break;
case "warning":
type = "warning" /* WARNING */;
break;
case "info":
type = "info" /* INFO */;
break;
default:
throw new Error(
`Unexpected severity for user feedback: ${payload.severity}`
);
}
historyManager.addItem(
{
type,
text: payload.message
},
Date.now()
);
if (payload.error) {
debugLogger.warn(
`[Feedback Details for "${payload.message}"]`,
payload.error
);
}
};
const handleHookSystemMessage = (payload) => {
historyManager.addItem(
{
type: "info" /* INFO */,
text: payload.message,
source: payload.hookName
},
Date.now()
);
};
coreEvents.on(CoreEvent.UserFeedback, handleUserFeedback);
coreEvents.on(CoreEvent.HookSystemMessage, handleHookSystemMessage);
coreEvents.drainBacklogs();
return () => {
coreEvents.off(CoreEvent.UserFeedback, handleUserFeedback);
coreEvents.off(CoreEvent.HookSystemMessage, handleHookSystemMessage);
};
}, [historyManager]);
const nightly = props.version.includes("nightly");
const isAwaitingLoginRestart = authState === "awaiting_login_restart" /* AwaitingLoginRestart */;
const loginRestartMessage = settings.merged.security.auth.selectedType === AuthType.USE_VERTEX_AI ? "Authenticating to Vertex AI in Cloud Shell requires a restart to apply project settings." : void 0;
const dialogsVisible = shouldShowIdePrompt || isFolderTrustDialogOpen || isPolicyUpdateDialogOpen || adminSettingsChanged || !!commandConfirmationRequest || !!authConsentRequest || !!permissionConfirmationRequest || !!customDialog || confirmUpdateExtensionRequests.length > 0 || !!loopDetectionConfirmationRequest || isThemeDialogOpen || isSettingsDialogOpen || isModelDialogOpen || isVoiceModelDialogOpen || isAgentConfigDialogOpen || isPermissionsDialogOpen || isAuthenticating || isAuthDialogOpen || isEditorDialogOpen || showPrivacyNotice || showIdeRestartPrompt || !!proQuotaRequest || !!validationRequest || !!overageMenuRequest || !!emptyWalletRequest || isSessionBrowserOpen || authState === "awaiting_api_key_input" /* AwaitingApiKeyInput */ || isAwaitingLoginRestart || !!newAgents;
const hasPendingToolConfirmation = (0, import_react121.useMemo)(
() => isToolAwaitingConfirmation(pendingHistoryItems),
[pendingHistoryItems]
);
const hasConfirmUpdateExtensionRequests = confirmUpdateExtensionRequests.length > 0;
const hasLoopDetectionConfirmationRequest = !!loopDetectionConfirmationRequest;
const hasPendingActionRequired = hasPendingToolConfirmation || !!commandConfirmationRequest || !!authConsentRequest || hasConfirmUpdateExtensionRequests || hasLoopDetectionConfirmationRequest || !!proQuotaRequest || !!validationRequest || !!overageMenuRequest || !!emptyWalletRequest || !!customDialog;
const loadingPhrases = settings.merged.ui.loadingPhrases;
const showStatusTips = loadingPhrases === "tips" || loadingPhrases === "all";
const showStatusWit = loadingPhrases === "witty" || loadingPhrases === "all";
const showLoadingIndicator = (!embeddedShellFocused || isBackgroundTaskVisible) && streamingState === "responding" /* Responding */ && !hasPendingActionRequired;
let estimatedStatusLength = 0;
if (activeHooks.length > 0 && settings.merged.hooksConfig.notifications) {
const hookLabel = activeHooks.length > 1 ? "Executing Hooks" : "Executing Hook";
const hookNames = activeHooks.map(
(h) => h.name + (h.index && h.total && h.total > 1 ? ` (${h.index}/${h.total})` : "")
).join(", ");
estimatedStatusLength = hookLabel.length + hookNames.length + 10;
} else if (showLoadingIndicator) {
const thoughtText = thought?.subject || "Waiting for model...";
estimatedStatusLength = thoughtText.length + 25;
} else if (hasPendingActionRequired) {
estimatedStatusLength = 35;
}
const maxLength = terminalWidth - estimatedStatusLength - 5;
const { elapsedTime, currentLoadingPhrase, currentTip, currentWittyPhrase } = useLoadingIndicator({
streamingState,
shouldShowFocusHint,
retryStatus,
showTips: showStatusTips,
showWit: showStatusWit,
customWittyPhrases: settings.merged.ui.customWittyPhrases,
errorVerbosity: settings.merged.ui.errorVerbosity,
maxLength
});
const allowPlanMode = config.isPlanEnabled() && streamingState === "idle" /* Idle */ && !hasPendingActionRequired;
const showApprovalModeIndicator = useApprovalModeIndicator({
config,
addItem: historyManager.addItem,
onApprovalModeChange: handleApprovalModeChangeWithUiReveal,
isActive: !embeddedShellFocused,
allowPlanMode
});
useRunEventNotifications({
notificationsEnabled,
notificationMethod,
isFocused,
hasReceivedFocusEvent,
streamingState,
hasPendingActionRequired,
pendingHistoryItems,
commandConfirmationRequest,
authConsentRequest,
permissionConfirmationRequest,
hasConfirmUpdateExtensionRequests,
hasLoopDetectionConfirmationRequest
});
const isPassiveShortcutsHelpState = isInputActive && streamingState === "idle" /* Idle */ && !hasPendingActionRequired;
(0, import_react121.useEffect)(() => {
if (shortcutsHelpVisible && !isPassiveShortcutsHelpState) {
setShortcutsHelpVisible(false);
}
}, [
shortcutsHelpVisible,
isPassiveShortcutsHelpState,
setShortcutsHelpVisible
]);
(0, import_react121.useEffect)(() => {
if (!isConfigInitialized || !config.isModelSteeringEnabled() || streamingState !== "idle" /* Idle */ || !isMcpReady || isToolAwaitingConfirmation(pendingHistoryItems)) {
return;
}
const pendingHint = consumePendingHints();
if (!pendingHint) {
return;
}
void submitQuery([{ text: buildUserSteeringHintPrompt(pendingHint) }]);
}, [
config,
historyManager,
isConfigInitialized,
isMcpReady,
streamingState,
submitQuery,
consumePendingHints,
pendingHistoryItems,
pendingHintCount
]);
const allToolCalls = (0, import_react121.useMemo)(
() => getAllToolCalls(pendingHistoryItems),
[pendingHistoryItems]
);
const [geminiMdFileCount, setGeminiMdFileCount] = (0, import_react121.useState)(
config.getGeminiMdFileCount()
);
(0, import_react121.useEffect)(() => {
const handleMemoryChanged = (result) => {
setGeminiMdFileCount(result.fileCount);
};
coreEvents.on(CoreEvent.MemoryChanged, handleMemoryChanged);
return () => {
coreEvents.off(CoreEvent.MemoryChanged, handleMemoryChanged);
};
}, []);
(0, import_react121.useEffect)(() => {
let isMounted = true;
const fetchBannerTexts = async () => {
const [defaultBanner, warningBanner] = await Promise.all([
config.getBannerTextNoCapacityIssues(),
config.getBannerTextCapacityIssues()
]);
if (isMounted) {
setDefaultBannerText(defaultBanner);
setWarningBannerText(warningBanner);
setBannerVisible(true);
}
};
fetchBannerTexts();
return () => {
isMounted = false;
};
}, [config, refreshStatic]);
const inputState = (0, import_react121.useMemo)(
() => ({
buffer,
userMessages: inputHistory,
shellModeActive,
showEscapePrompt,
copyModeEnabled,
inputWidth,
suggestionsWidth
}),
[
buffer,
inputHistory,
shellModeActive,
showEscapePrompt,
copyModeEnabled,
inputWidth,
suggestionsWidth
]
);
const quotaState = (0, import_react121.useMemo)(
() => ({
userTier,
stats: quotaStats,
proQuotaRequest,
validationRequest,
// G1 AI Credits dialog state
overageMenuRequest,
emptyWalletRequest
}),
[
userTier,
quotaStats,
proQuotaRequest,
validationRequest,
overageMenuRequest,
emptyWalletRequest
]
);
const uiState = (0, import_react121.useMemo)(
() => ({
history: historyManager.history,
historyManager,
isThemeDialogOpen,
themeError,
isAuthenticating,
isConfigInitialized,
authError,
accountSuspensionInfo,
isAuthDialogOpen,
isAwaitingApiKeyInput: authState === "awaiting_api_key_input" /* AwaitingApiKeyInput */,
isAwaitingLoginRestart,
loginRestartMessage,
apiKeyDefaultValue,
editorError,
isEditorDialogOpen,
showPrivacyNotice,
mouseMode,
corgiMode,
debugMessage,
quittingMessages,
isSettingsDialogOpen,
isSessionBrowserOpen,
isModelDialogOpen,
isVoiceModelDialogOpen,
isAgentConfigDialogOpen,
selectedAgentName,
selectedAgentDisplayName,
selectedAgentDefinition,
isPermissionsDialogOpen,
permissionsDialogProps,
slashCommands,
pendingSlashCommandHistoryItems,
commandContext,
commandConfirmationRequest,
authConsentRequest,
confirmUpdateExtensionRequests,
loopDetectionConfirmationRequest,
permissionConfirmationRequest,
geminiMdFileCount,
streamingState,
initError,
pendingGeminiHistoryItems,
thought,
isInputActive,
isVoiceModeEnabled,
isResuming,
shouldShowIdePrompt,
isFolderTrustDialogOpen: isFolderTrustDialogOpen ?? false,
folderDiscoveryResults,
isPolicyUpdateDialogOpen,
policyUpdateConfirmationRequest,
isTrustedFolder,
constrainHeight,
showErrorDetails,
showFullTodos,
ideContextState,
renderMarkdown,
ctrlCPressedOnce: ctrlCPressCount >= 1,
ctrlDPressedOnce: ctrlDPressCount >= 1,
shortcutsHelpVisible,
cleanUiDetailsVisible,
isFocused,
elapsedTime,
currentLoadingPhrase,
currentTip,
currentWittyPhrase,
historyRemountKey,
activeHooks,
messageQueue: messageQueue2,
queueErrorMessage,
showApprovalModeIndicator,
allowPlanMode,
currentModel,
contextFileNames,
errorCount,
availableTerminalHeight,
stableControlsHeight,
mainAreaWidth,
staticAreaMaxItemHeight,
staticExtraHeight,
dialogsVisible,
pendingHistoryItems,
nightly,
branchName,
sessionStats,
terminalWidth,
terminalHeight,
mainControlsRef,
rootUiRef,
currentIDE,
updateInfo,
showIdeRestartPrompt,
ideTrustRestartReason,
isRestarting,
extensionsUpdateState,
activePtyId,
backgroundTaskCount,
isBackgroundTaskVisible,
embeddedShellFocused,
showDebugProfiler,
customDialog,
transientMessage,
bannerData,
bannerVisible,
terminalBackgroundColor: config.getTerminalBackground(),
settingsNonce,
backgroundTasks,
activeBackgroundTaskPid,
backgroundTaskHeight,
isBackgroundTaskListOpen,
adminSettingsChanged,
newAgents,
showIsExpandableHint,
hintMode: config.isModelSteeringEnabled() && isToolExecuting(pendingHistoryItems),
hintBuffer: ""
}),
[
isThemeDialogOpen,
themeError,
isAuthenticating,
isConfigInitialized,
authError,
accountSuspensionInfo,
isAuthDialogOpen,
editorError,
isEditorDialogOpen,
showPrivacyNotice,
mouseMode,
corgiMode,
debugMessage,
quittingMessages,
isSettingsDialogOpen,
isSessionBrowserOpen,
isModelDialogOpen,
isVoiceModelDialogOpen,
isAgentConfigDialogOpen,
selectedAgentName,
selectedAgentDisplayName,
selectedAgentDefinition,
isPermissionsDialogOpen,
permissionsDialogProps,
slashCommands,
pendingSlashCommandHistoryItems,
commandContext,
commandConfirmationRequest,
authConsentRequest,
confirmUpdateExtensionRequests,
loopDetectionConfirmationRequest,
permissionConfirmationRequest,
geminiMdFileCount,
streamingState,
initError,
pendingGeminiHistoryItems,
thought,
isInputActive,
isVoiceModeEnabled,
isResuming,
shouldShowIdePrompt,
isFolderTrustDialogOpen,
folderDiscoveryResults,
isPolicyUpdateDialogOpen,
policyUpdateConfirmationRequest,
isTrustedFolder,
constrainHeight,
showErrorDetails,
showFullTodos,
ideContextState,
renderMarkdown,
ctrlCPressCount,
ctrlDPressCount,
shortcutsHelpVisible,
cleanUiDetailsVisible,
isFocused,
elapsedTime,
currentLoadingPhrase,
currentTip,
currentWittyPhrase,
historyRemountKey,
activeHooks,
messageQueue2,
queueErrorMessage,
showApprovalModeIndicator,
allowPlanMode,
contextFileNames,
errorCount,
availableTerminalHeight,
stableControlsHeight,
mainAreaWidth,
staticAreaMaxItemHeight,
staticExtraHeight,
dialogsVisible,
pendingHistoryItems,
nightly,
branchName,
sessionStats,
terminalWidth,
terminalHeight,
mainControlsRef,
rootUiRef,
currentIDE,
updateInfo,
showIdeRestartPrompt,
ideTrustRestartReason,
isRestarting,
currentModel,
extensionsUpdateState,
activePtyId,
backgroundTaskCount,
isBackgroundTaskVisible,
historyManager,
embeddedShellFocused,
showDebugProfiler,
customDialog,
apiKeyDefaultValue,
authState,
isAwaitingLoginRestart,
loginRestartMessage,
transientMessage,
bannerData,
bannerVisible,
config,
settingsNonce,
backgroundTaskHeight,
isBackgroundTaskListOpen,
activeBackgroundTaskPid,
backgroundTasks,
adminSettingsChanged,
newAgents,
showIsExpandableHint
]
);
const exitPrivacyNotice = (0, import_react121.useCallback)(
() => setShowPrivacyNotice(false),
[setShowPrivacyNotice]
);
const uiActions = (0, import_react121.useMemo)(
() => ({
handleThemeSelect,
closeThemeDialog,
handleThemeHighlight,
handleAuthSelect,
setAuthState,
onAuthError,
handleEditorSelect,
exitEditorDialog,
exitPrivacyNotice,
closeSettingsDialog,
closeModelDialog,
openVoiceModelDialog,
closeVoiceModelDialog,
openAgentConfigDialog,
closeAgentConfigDialog,
openPermissionsDialog,
closePermissionsDialog,
setShellModeActive,
vimHandleInput,
handleIdePromptComplete,
handleFolderTrustSelect,
setIsPolicyUpdateDialogOpen,
setConstrainHeight,
onEscapePromptChange: handleEscapePromptChange,
refreshStatic,
handleFinalSubmit,
handleClearScreen,
handleProQuotaChoice,
handleValidationChoice,
// G1 AI Credits handlers
handleOverageMenuChoice,
handleEmptyWalletChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession,
setQueueErrorMessage,
addMessage,
popAllMessages,
handleApiKeySubmit,
handleApiKeyCancel,
setBannerVisible,
setShortcutsHelpVisible,
setCleanUiDetailsVisible,
toggleCleanUiDetailsVisible,
revealCleanUiDetailsTemporarily,
handleWarning,
setEmbeddedShellFocused,
dismissBackgroundTask,
setActiveBackgroundTaskPid,
setIsBackgroundTaskListOpen,
setAuthContext,
dismissLoginRestart: () => {
setAuthContext({});
setAuthState("updating" /* Updating */);
},
onHintInput: () => {
},
onHintBackspace: () => {
},
onHintClear: () => {
},
onHintSubmit: () => {
},
handleRestart: async () => {
if (process12.send) {
const remoteSettings = config.getRemoteAdminSettings();
if (remoteSettings) {
process12.send({
type: "admin-settings-update",
settings: remoteSettings
});
}
}
await relaunchApp();
},
handleNewAgentsSelect: async (choice) => {
if (newAgents && choice === "acknowledge" /* ACKNOWLEDGE */) {
const registry = config.getAgentRegistry();
try {
await Promise.all(
newAgents.map((agent) => registry.acknowledgeAgent(agent))
);
} catch (error) {
debugLogger.error("Failed to acknowledge agents:", error);
historyManager.addItem(
{
type: "error" /* ERROR */,
text: `Failed to acknowledge agents: ${getErrorMessage(error)}`
},
Date.now()
);
}
}
setNewAgents(null);
},
getPreferredEditor,
clearAccountSuspension: () => {
setAccountSuspensionInfo(null);
setAuthState("updating" /* Updating */);
},
setVoiceModeEnabled: (value) => {
setVoiceModeEnabled(value);
}
}),
[
handleThemeSelect,
closeThemeDialog,
handleThemeHighlight,
handleAuthSelect,
setAuthState,
onAuthError,
handleEditorSelect,
exitEditorDialog,
exitPrivacyNotice,
closeSettingsDialog,
closeModelDialog,
openVoiceModelDialog,
closeVoiceModelDialog,
openAgentConfigDialog,
closeAgentConfigDialog,
openPermissionsDialog,
closePermissionsDialog,
setShellModeActive,
vimHandleInput,
handleIdePromptComplete,
handleFolderTrustSelect,
setIsPolicyUpdateDialogOpen,
setConstrainHeight,
handleEscapePromptChange,
refreshStatic,
handleFinalSubmit,
handleClearScreen,
handleProQuotaChoice,
handleValidationChoice,
handleOverageMenuChoice,
handleEmptyWalletChoice,
openSessionBrowser,
closeSessionBrowser,
handleResumeSession,
handleDeleteSession,
setQueueErrorMessage,
addMessage,
popAllMessages,
handleApiKeySubmit,
handleApiKeyCancel,
setBannerVisible,
setShortcutsHelpVisible,
setCleanUiDetailsVisible,
toggleCleanUiDetailsVisible,
revealCleanUiDetailsTemporarily,
handleWarning,
setEmbeddedShellFocused,
dismissBackgroundTask,
setActiveBackgroundTaskPid,
setIsBackgroundTaskListOpen,
setAuthContext,
setAccountSuspensionInfo,
newAgents,
config,
historyManager,
getPreferredEditor,
setVoiceModeEnabled
]
);
return /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(UIStateContext.Provider, { value: uiState, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(QuotaContext.Provider, { value: quotaState, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(InputContext.Provider, { value: inputState, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(UIActionsContext.Provider, { value: uiActions, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(ConfigContext.Provider, { value: config, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(
AppContext.Provider,
{
value: {
version: props.version,
startupWarnings: props.startupWarnings || []
},
children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(
ToolActionsProvider,
{
config,
toolCalls: allToolCalls,
isExpanded,
toggleExpansion,
toggleAllExpansion,
children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(ShellFocusContext.Provider, { value: isFocused, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(MouseProvider, { mouseEventsEnabled: mouseMode, children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(ScrollProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime138.jsx)(App, {}, `app-${forceRerenderKey}`) }) }) })
}
)
}
) }) }) }) }) });
};
// packages/cli/src/interactiveCli.tsx
var import_jsx_runtime139 = __toESM(require_jsx_runtime(), 1);
var SLOW_RENDER_MS = 200;
async function startInteractiveUI(config, settings, startupWarnings, workspaceRoot = process.cwd(), resumedSessionData, initializationResult) {
initializeConsoleStore();
const useAlternateBuffer2 = shouldEnterAlternateScreen(
config.getUseAlternateBuffer(),
config.getScreenReader()
);
const mouseEventsEnabled = useAlternateBuffer2;
if (mouseEventsEnabled) {
enableMouseEvents();
registerCleanup(() => {
disableMouseEvents();
});
}
const { matchers, errors } = await loadKeyMatchers();
errors.forEach((error) => {
coreEvents.emitFeedback("warning", error);
});
const version = await getVersion();
setWindowTitle(basename5(workspaceRoot), settings);
const consolePatcher = new ConsolePatcher({
onNewMessage: (msg) => {
coreEvents.emitConsoleLog(msg.type, msg.content);
},
debugMode: config.getDebugMode()
});
consolePatcher.patch();
const { stdout: inkStdout, stderr: inkStderr } = createWorkingStdio();
const isShpool = !!process.env["SHPOOL_SESSION_NAME"];
const AppWrapper = () => {
useKittyKeyboardProtocol();
return /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(SettingsContext.Provider, { value: settings, children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(KeyMatchersProvider, { value: matchers, children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(KeypressProvider, { config, children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(MouseProvider, { mouseEventsEnabled, children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(TerminalProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(ScrollProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(OverflowProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(SessionStatsProvider, { sessionId: config.getSessionId(), children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(VimModeProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(
AppContainer,
{
config,
startupWarnings,
version,
resumedSessionData,
initializationResult
}
) }) }) }) }) }) }) }) }) });
};
if (isShpool) {
await new Promise((resolve3) => setTimeout(resolve3, 100));
}
const instance = render_default(
process.env["DEBUG"] ? /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(import_react122.default.StrictMode, { children: /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(AppWrapper, {}) }) : /* @__PURE__ */ (0, import_jsx_runtime139.jsx)(AppWrapper, {}),
{
stdout: inkStdout,
stderr: inkStderr,
stdin: process.stdin,
exitOnCtrlC: false,
isScreenReaderEnabled: config.getScreenReader(),
onRender: ({ renderTime }) => {
if (renderTime > SLOW_RENDER_MS) {
recordSlowRender(config, Math.round(renderTime));
}
profiler.reportFrameRendered();
},
standardReactLayoutTiming: useAlternateBuffer2 || config.getUseTerminalBuffer(),
patchConsole: false,
alternateBuffer: useAlternateBuffer2,
terminalBuffer: config.getUseTerminalBuffer(),
renderProcess: config.getUseRenderProcess() && config.getUseTerminalBuffer(),
incrementalRendering: settings.merged.ui.incrementalRendering !== false && useAlternateBuffer2 && !isShpool,
debugRainbow: settings.merged.ui.debugRainbow === true
}
);
let cleanupLineWrapping;
if (useAlternateBuffer2) {
disableLineWrapping();
cleanupLineWrapping = () => enableLineWrapping();
registerCleanup(cleanupLineWrapping);
}
checkForUpdates(settings).then((info) => {
handleAutoUpdate(
info,
settings,
config.getProjectRoot(),
config.getSandboxEnabled()
);
}).catch((err) => {
if (config.getDebugMode()) {
debugLogger.warn("Update check failed:", err);
}
});
const cleanupUnmount = () => instance.unmount();
registerCleanup(cleanupUnmount);
const cleanupTtyCheck = setupTtyCheck();
registerCleanup(cleanupTtyCheck);
const cleanupConsolePatcher = () => consolePatcher.cleanup();
registerCleanup(cleanupConsolePatcher);
try {
await instance.waitUntilExit();
} finally {
try {
removeCleanup(cleanupConsolePatcher);
cleanupConsolePatcher();
} catch (e) {
debugLogger.error("Error cleaning up console patcher:", e);
}
try {
removeCleanup(cleanupUnmount);
instance.unmount();
} catch (e) {
debugLogger.error("Error unmounting Ink instance:", e);
}
try {
removeCleanup(cleanupTtyCheck);
cleanupTtyCheck();
} catch (e) {
debugLogger.error("Error in TTY cleanup:", e);
}
if (cleanupLineWrapping) {
try {
removeCleanup(cleanupLineWrapping);
cleanupLineWrapping();
} catch (e) {
debugLogger.error("Error restoring line wrapping:", e);
}
}
}
}
function setWindowTitle(title, settings) {
if (!settings.merged.ui.hideWindowTitle) {
const windowTitle = computeTerminalTitle({
streamingState: "idle" /* Idle */,
isConfirming: false,
isSilentWorking: false,
folderName: title,
showThoughts: !!settings.merged.ui.showStatusInTitle,
useDynamicTitle: settings.merged.ui.dynamicWindowTitle
});
writeToStdout(`\x1B]0;${windowTitle}\x07`);
process.on("exit", () => {
writeToStdout(`\x1B]0;\x07`);
});
}
}
export {
startInteractiveUI
};
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
/*! Bundled license information:
deep-extend/lib/deep-extend.js:
(*!
* @description Recursive object extending
* @author Viacheslav Lotsmanov <lotsmanov89@gmail.com>
* @license MIT
*
* The MIT License (MIT)
*
* Copyright (c) 2013-2018 Viacheslav Lotsmanov
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*)
ky/distribution/index.js:
(*! MIT License © Sindre Sorhus *)
*/