mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-08-02 13:11:03 -07:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9c82e316e | |||
| f7bf22c711 | |||
| d458dbbfb9 | |||
| 112a4195bd | |||
| 5605670d6d |
@@ -5,7 +5,7 @@
|
||||
*/
|
||||
|
||||
import type { CommandModule } from 'yargs';
|
||||
import type { ExtensionSettingScope } from '../../config/extensions/extensionSettings.js';
|
||||
import { ExtensionSettingScope } from '../../config/extensions/extensionSettings.js';
|
||||
import {
|
||||
configureAllExtensions,
|
||||
configureExtension,
|
||||
@@ -64,6 +64,10 @@ export const configureCommand: CommandModule<object, ConfigureArgs> = {
|
||||
}
|
||||
|
||||
const extensionManager = await getExtensionManager();
|
||||
const effectiveScope =
|
||||
scope === 'workspace'
|
||||
? ExtensionSettingScope.WORKSPACE
|
||||
: ExtensionSettingScope.USER;
|
||||
|
||||
// Case 1: Configure specific setting for an extension
|
||||
if (name && setting) {
|
||||
@@ -71,26 +75,16 @@ export const configureCommand: CommandModule<object, ConfigureArgs> = {
|
||||
extensionManager,
|
||||
name,
|
||||
setting,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
effectiveScope,
|
||||
);
|
||||
}
|
||||
// Case 2: Configure all settings for an extension
|
||||
else if (name) {
|
||||
await configureExtension(
|
||||
extensionManager,
|
||||
name,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
await configureExtension(extensionManager, name, effectiveScope);
|
||||
}
|
||||
// Case 3: Configure all extensions
|
||||
else {
|
||||
await configureAllExtensions(
|
||||
extensionManager,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope as ExtensionSettingScope,
|
||||
);
|
||||
await configureAllExtensions(extensionManager, effectiveScope);
|
||||
}
|
||||
|
||||
await exitCli();
|
||||
|
||||
@@ -77,11 +77,11 @@ export const disableCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const scope = argv['scope'];
|
||||
await handleDisable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
name: typeof name === 'string' ? name : '',
|
||||
scope: typeof scope === 'string' ? scope : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -104,11 +104,11 @@ export const enableCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const scope = argv['scope'];
|
||||
await handleEnable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
name: typeof name === 'string' ? name : '',
|
||||
scope: typeof scope === 'string' ? scope : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -209,19 +209,23 @@ export const installCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const source = argv['source'];
|
||||
const ref = argv['ref'];
|
||||
const autoUpdate = argv['auto-update'];
|
||||
const allowPreRelease = argv['pre-release'];
|
||||
const consent = argv['consent'];
|
||||
|
||||
await handleInstall({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
source: argv['source'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
ref: argv['ref'] as string | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
autoUpdate: argv['auto-update'] as boolean | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
allowPreRelease: argv['pre-release'] as boolean | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
consent: argv['consent'] as boolean | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
skipSettings: argv['skip-settings'] as boolean | undefined,
|
||||
source: typeof source === 'string' ? source : '',
|
||||
ref: typeof ref === 'string' ? ref : undefined,
|
||||
autoUpdate: typeof autoUpdate === 'boolean' ? autoUpdate : undefined,
|
||||
allowPreRelease:
|
||||
typeof allowPreRelease === 'boolean' ? allowPreRelease : undefined,
|
||||
consent: typeof consent === 'boolean' ? consent : undefined,
|
||||
skipSettings:
|
||||
typeof argv['skip-settings'] === 'boolean'
|
||||
? argv['skip-settings']
|
||||
: undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -78,11 +78,11 @@ export const linkCommand: CommandModule = {
|
||||
})
|
||||
.check((_) => true),
|
||||
handler: async (argv) => {
|
||||
const path = argv['path'];
|
||||
const consent = argv['consent'];
|
||||
await handleLink({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
path: argv['path'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
consent: argv['consent'] as boolean | undefined,
|
||||
path: typeof path === 'string' ? path : '',
|
||||
consent: typeof consent === 'boolean' ? consent : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -60,9 +60,12 @@ export const listCommand: CommandModule = {
|
||||
default: 'text',
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const outputFormat = argv['output-format'];
|
||||
await handleList({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
outputFormat: argv['output-format'] as 'text' | 'json',
|
||||
outputFormat:
|
||||
outputFormat === 'text' || outputFormat === 'json'
|
||||
? outputFormat
|
||||
: undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -97,11 +97,11 @@ export const newCommand: CommandModule = {
|
||||
});
|
||||
},
|
||||
handler: async (args) => {
|
||||
const path = args['path'];
|
||||
const template = args['template'];
|
||||
await handleNew({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
path: args['path'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
template: args['template'] as string | undefined,
|
||||
path: typeof path === 'string' ? path : '',
|
||||
template: typeof template === 'string' ? template : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -91,11 +91,16 @@ export const uninstallCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const names = argv['names'];
|
||||
let namesArray: string[] = [];
|
||||
if (Array.isArray(names)) {
|
||||
namesArray = names.filter((n): n is string => typeof n === 'string');
|
||||
} else if (typeof names === 'string') {
|
||||
namesArray = [names];
|
||||
}
|
||||
await handleUninstall({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
names: argv['names'] as string[] | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
all: argv['all'] as boolean,
|
||||
names: namesArray,
|
||||
all: typeof argv['all'] === 'boolean' ? argv['all'] : false,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -157,11 +157,11 @@ export const updateCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const all = argv['all'];
|
||||
await handleUpdate({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
all: argv['all'] as boolean | undefined,
|
||||
name: typeof name === 'string' ? name : undefined,
|
||||
all: typeof all === 'boolean' ? all : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -98,9 +98,9 @@ export const validateCommand: CommandModule = {
|
||||
demandOption: true,
|
||||
}),
|
||||
handler: async (args) => {
|
||||
const path = args['path'];
|
||||
await handleValidate({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
path: args['path'] as string,
|
||||
path: typeof path === 'string' ? path : '',
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -11,10 +11,7 @@ import { debugLogger, getErrorMessage } from '@google/gemini-cli-core';
|
||||
import { loadSettings, SettingScope } from '../../config/settings.js';
|
||||
import { exitCli } from '../utils.js';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
|
||||
interface MigrateArgs {
|
||||
fromClaude: boolean;
|
||||
}
|
||||
import { isRecord } from '../../utils/typeGuards.js';
|
||||
|
||||
/**
|
||||
* Mapping from Claude Code event names to Gemini event names
|
||||
@@ -66,12 +63,11 @@ function transformMatcher(matcher: string | undefined): string | undefined {
|
||||
* Migrate a Claude Code hook configuration to Gemini format
|
||||
*/
|
||||
function migrateClaudeHook(claudeHook: unknown): unknown {
|
||||
if (!claudeHook || typeof claudeHook !== 'object') {
|
||||
if (!isRecord(claudeHook)) {
|
||||
return claudeHook;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const hook = claudeHook as Record<string, unknown>;
|
||||
const hook = claudeHook;
|
||||
const migrated: Record<string, unknown> = {};
|
||||
|
||||
// Map command field
|
||||
@@ -106,18 +102,15 @@ function migrateClaudeHook(claudeHook: unknown): unknown {
|
||||
* Migrate Claude Code hooks configuration to Gemini format
|
||||
*/
|
||||
function migrateClaudeHooks(claudeConfig: unknown): Record<string, unknown> {
|
||||
if (!claudeConfig || typeof claudeConfig !== 'object') {
|
||||
if (!isRecord(claudeConfig)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const config = claudeConfig as Record<string, unknown>;
|
||||
const geminiHooks: Record<string, unknown> = {};
|
||||
|
||||
// Check if there's a hooks section
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const hooksSection = config['hooks'] as Record<string, unknown> | undefined;
|
||||
if (!hooksSection || typeof hooksSection !== 'object') {
|
||||
const hooksSection = claudeConfig['hooks'];
|
||||
if (!isRecord(hooksSection)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -131,31 +124,25 @@ function migrateClaudeHooks(claudeConfig: unknown): Record<string, unknown> {
|
||||
|
||||
// Migrate each hook definition
|
||||
const migratedDefinitions = eventConfig.map((def: unknown) => {
|
||||
if (!def || typeof def !== 'object') {
|
||||
if (!isRecord(def)) {
|
||||
return def;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const definition = def as Record<string, unknown>;
|
||||
const migratedDef: Record<string, unknown> = {};
|
||||
|
||||
// Transform matcher
|
||||
if (
|
||||
'matcher' in definition &&
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof definition['matcher'] === 'string'
|
||||
) {
|
||||
migratedDef['matcher'] = transformMatcher(definition['matcher']);
|
||||
if ('matcher' in def && typeof def['matcher'] === 'string') {
|
||||
migratedDef['matcher'] = transformMatcher(def['matcher']);
|
||||
}
|
||||
|
||||
// Copy sequential flag
|
||||
if ('sequential' in definition) {
|
||||
migratedDef['sequential'] = definition['sequential'];
|
||||
if ('sequential' in def) {
|
||||
migratedDef['sequential'] = def['sequential'];
|
||||
}
|
||||
|
||||
// Migrate hooks array
|
||||
if ('hooks' in definition && Array.isArray(definition['hooks'])) {
|
||||
migratedDef['hooks'] = definition['hooks'].map(migrateClaudeHook);
|
||||
if ('hooks' in def && Array.isArray(def['hooks'])) {
|
||||
migratedDef['hooks'] = def['hooks'].map(migrateClaudeHook);
|
||||
}
|
||||
|
||||
return migratedDef;
|
||||
@@ -186,11 +173,11 @@ export async function handleMigrateFromClaude() {
|
||||
sourceFile = claudeLocalSettingsPath;
|
||||
try {
|
||||
const content = fs.readFileSync(claudeLocalSettingsPath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
claudeSettings = JSON.parse(stripJsonComments(content)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const parsed: unknown = JSON.parse(stripJsonComments(content));
|
||||
if (isRecord(parsed)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
claudeSettings = parsed as any;
|
||||
}
|
||||
} catch (error) {
|
||||
debugLogger.error(
|
||||
`Error reading ${claudeLocalSettingsPath}: ${getErrorMessage(error)}`,
|
||||
@@ -200,11 +187,11 @@ export async function handleMigrateFromClaude() {
|
||||
sourceFile = claudeSettingsPath;
|
||||
try {
|
||||
const content = fs.readFileSync(claudeSettingsPath, 'utf-8');
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
claudeSettings = JSON.parse(stripJsonComments(content)) as Record<
|
||||
string,
|
||||
unknown
|
||||
>;
|
||||
const parsed: unknown = JSON.parse(stripJsonComments(content));
|
||||
if (isRecord(parsed)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-type-assertion
|
||||
claudeSettings = parsed as any;
|
||||
}
|
||||
} catch (error) {
|
||||
debugLogger.error(
|
||||
`Error reading ${claudeSettingsPath}: ${getErrorMessage(error)}`,
|
||||
@@ -268,9 +255,8 @@ export const migrateCommand: CommandModule = {
|
||||
default: false,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const args = argv as unknown as MigrateArgs;
|
||||
if (args.fromClaude) {
|
||||
const fromClaude = argv['from-claude'];
|
||||
if (typeof fromClaude === 'boolean' && fromClaude) {
|
||||
await handleMigrateFromClaude();
|
||||
} else {
|
||||
debugLogger.log(
|
||||
|
||||
@@ -211,40 +211,64 @@ export const addCommand: CommandModule = {
|
||||
})
|
||||
.middleware((argv) => {
|
||||
// Handle -- separator args as server args if present
|
||||
if (argv['--']) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const existingArgs = (argv['args'] as Array<string | number>) || [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
argv['args'] = [...existingArgs, ...(argv['--'] as string[])];
|
||||
if (argv['--'] && Array.isArray(argv['--'])) {
|
||||
const args = argv['args'];
|
||||
let existingArgs: Array<string | number> = [];
|
||||
if (Array.isArray(args)) {
|
||||
existingArgs = args.filter(
|
||||
(a): a is string | number =>
|
||||
typeof a === 'string' || typeof a === 'number',
|
||||
);
|
||||
} else if (typeof args === 'string' || typeof args === 'number') {
|
||||
existingArgs = [args];
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
argv['args'] = [...existingArgs, ...argv['--']];
|
||||
}
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const commandOrUrl = argv['commandOrUrl'];
|
||||
const args = argv['args'];
|
||||
const scope = argv['scope'];
|
||||
const transport = argv['transport'];
|
||||
const env = argv['env'];
|
||||
const header = argv['header'];
|
||||
const timeout = argv['timeout'];
|
||||
const trust = argv['trust'];
|
||||
const description = argv['description'];
|
||||
const includeTools = argv['includeTools'];
|
||||
const excludeTools = argv['excludeTools'];
|
||||
|
||||
const argsVal = Array.isArray(args)
|
||||
? args.filter(
|
||||
(a): a is string | number =>
|
||||
typeof a === 'string' || typeof a === 'number',
|
||||
)
|
||||
: undefined;
|
||||
|
||||
await addMcpServer(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
argv['commandOrUrl'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
argv['args'] as Array<string | number>,
|
||||
typeof name === 'string' ? name : '',
|
||||
typeof commandOrUrl === 'string' ? commandOrUrl : '',
|
||||
argsVal,
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
transport: argv['transport'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
env: argv['env'] as string[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
header: argv['header'] as string[],
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
timeout: argv['timeout'] as number | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
trust: argv['trust'] as boolean | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
description: argv['description'] as string | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
includeTools: argv['includeTools'] as string[] | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
excludeTools: argv['excludeTools'] as string[] | undefined,
|
||||
scope: typeof scope === 'string' ? scope : 'project',
|
||||
transport: typeof transport === 'string' ? transport : 'stdio',
|
||||
env: Array.isArray(env)
|
||||
? env.filter((e): e is string => typeof e === 'string')
|
||||
: undefined,
|
||||
header: Array.isArray(header)
|
||||
? header.filter((h): h is string => typeof h === 'string')
|
||||
: undefined,
|
||||
timeout: typeof timeout === 'number' ? timeout : undefined,
|
||||
trust: typeof trust === 'boolean' ? trust : undefined,
|
||||
description: typeof description === 'string' ? description : undefined,
|
||||
includeTools: Array.isArray(includeTools)
|
||||
? includeTools.filter((t): t is string => typeof t === 'string')
|
||||
: undefined,
|
||||
excludeTools: Array.isArray(excludeTools)
|
||||
? excludeTools.filter((t): t is string => typeof t === 'string')
|
||||
: undefined,
|
||||
},
|
||||
);
|
||||
await exitCli();
|
||||
|
||||
@@ -112,7 +112,12 @@ export const enableCommand: CommandModule<object, Args> = {
|
||||
default: false,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
await handleEnable(argv as Args);
|
||||
const name = argv['name'];
|
||||
const session = argv['session'];
|
||||
await handleEnable({
|
||||
name: typeof name === 'string' ? name : '',
|
||||
session: typeof session === 'boolean' ? session : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
};
|
||||
@@ -133,7 +138,12 @@ export const disableCommand: CommandModule<object, Args> = {
|
||||
default: false,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
await handleDisable(argv as Args);
|
||||
const name = argv['name'];
|
||||
const session = argv['session'];
|
||||
await handleDisable({
|
||||
name: typeof name === 'string' ? name : '',
|
||||
session: typeof session === 'boolean' ? session : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -55,10 +55,10 @@ export const removeCommand: CommandModule = {
|
||||
choices: ['user', 'project'],
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
await removeMcpServer(argv['name'] as string, {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as string,
|
||||
const name = argv['name'];
|
||||
const scope = argv['scope'];
|
||||
await removeMcpServer(typeof name === 'string' ? name : '', {
|
||||
scope: typeof scope === 'string' ? scope : 'project',
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -48,13 +48,13 @@ export const disableCommand: CommandModule = {
|
||||
choices: ['user', 'workspace'],
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const scope =
|
||||
argv['scope'] === 'workspace'
|
||||
? SettingScope.Workspace
|
||||
: SettingScope.User;
|
||||
await handleDisable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
name: typeof name === 'string' ? name : '',
|
||||
scope,
|
||||
});
|
||||
await exitCli();
|
||||
|
||||
@@ -39,9 +39,9 @@ export const enableCommand: CommandModule = {
|
||||
demandOption: true,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
await handleEnable({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
name: typeof name === 'string' ? name : '',
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -104,15 +104,16 @@ export const installCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const source = argv['source'];
|
||||
const scope = argv['scope'];
|
||||
const path = argv['path'];
|
||||
const consent = argv['consent'];
|
||||
|
||||
await handleInstall({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
source: argv['source'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as 'user' | 'workspace',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
path: argv['path'] as string | undefined,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
consent: argv['consent'] as boolean | undefined,
|
||||
source: typeof source === 'string' ? source : '',
|
||||
scope: scope === 'user' || scope === 'workspace' ? scope : undefined,
|
||||
path: typeof path === 'string' ? path : undefined,
|
||||
consent: typeof consent === 'boolean' ? consent : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -82,13 +82,14 @@ export const linkCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const path = argv['path'];
|
||||
const scope = argv['scope'];
|
||||
const consent = argv['consent'];
|
||||
|
||||
await handleLink({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
path: argv['path'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as 'user' | 'workspace',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
consent: argv['consent'] as boolean | undefined,
|
||||
path: typeof path === 'string' ? path : '',
|
||||
scope: scope === 'user' || scope === 'workspace' ? scope : undefined,
|
||||
consent: typeof consent === 'boolean' ? consent : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -20,7 +20,7 @@ export async function handleList(args: { all?: boolean }) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
{
|
||||
debug: false,
|
||||
} as Partial<CliArgs> as CliArgs,
|
||||
} as unknown as CliArgs,
|
||||
{ cwd: workspaceDir },
|
||||
);
|
||||
|
||||
@@ -72,8 +72,8 @@ export const listCommand: CommandModule = {
|
||||
default: false,
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
await handleList({ all: argv['all'] as boolean });
|
||||
const all = argv['all'];
|
||||
await handleList({ all: typeof all === 'boolean' ? all : false });
|
||||
await exitCli();
|
||||
},
|
||||
};
|
||||
|
||||
@@ -62,11 +62,12 @@ export const uninstallCommand: CommandModule = {
|
||||
return true;
|
||||
}),
|
||||
handler: async (argv) => {
|
||||
const name = argv['name'];
|
||||
const scope = argv['scope'];
|
||||
|
||||
await handleUninstall({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
name: argv['name'] as string,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
scope: argv['scope'] as 'user' | 'workspace',
|
||||
name: typeof name === 'string' ? name : '',
|
||||
scope: scope === 'user' || scope === 'workspace' ? scope : undefined,
|
||||
});
|
||||
await exitCli();
|
||||
},
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
type SlashCommand,
|
||||
type CommandContext,
|
||||
} from './types.js';
|
||||
import { MessageType, type HistoryItem } from '../types.js';
|
||||
import { MessageType, type HistoryItemWithoutId } from '../types.js';
|
||||
import {
|
||||
refreshServerHierarchicalMemory,
|
||||
type Config,
|
||||
@@ -29,10 +29,7 @@ import * as fs from 'node:fs';
|
||||
|
||||
async function finishAddingDirectories(
|
||||
config: Config,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
addItem: (itemData: HistoryItemWithoutId, baseTimestamp?: number) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) {
|
||||
|
||||
@@ -209,8 +209,8 @@ async function restartAction(
|
||||
|
||||
const s = extensionsToRestart.length > 1 ? 's' : '';
|
||||
|
||||
const reloadingMessage = {
|
||||
type: MessageType.INFO,
|
||||
const reloadingMessage: HistoryItemInfo = {
|
||||
type: 'info',
|
||||
text: `Reloading ${extensionsToRestart.length} extension${s}...`,
|
||||
color: theme.text.primary,
|
||||
};
|
||||
@@ -472,8 +472,7 @@ async function installAction(
|
||||
args: string,
|
||||
requestConsentOverride?: (consent: string) => Promise<boolean>,
|
||||
) {
|
||||
const extensionLoader =
|
||||
context.services.agentContext?.config.getExtensionLoader();
|
||||
const extensionLoader = context.services.config?.getExtensionLoader();
|
||||
if (!(extensionLoader instanceof ExtensionManager)) {
|
||||
debugLogger.error(
|
||||
`Cannot ${context.invocation?.name} extensions in this environment`,
|
||||
|
||||
@@ -16,7 +16,7 @@ import { useKeypress } from '../hooks/useKeypress.js';
|
||||
import { loadTrustedFolders, TrustLevel } from '../../config/trustedFolders.js';
|
||||
import { expandHomeDir } from '../utils/directoryUtils.js';
|
||||
import * as path from 'node:path';
|
||||
import { MessageType, type HistoryItem } from '../types.js';
|
||||
import { MessageType, type HistoryItemWithoutId } from '../types.js';
|
||||
import { type Config } from '@google/gemini-cli-core';
|
||||
|
||||
export enum MultiFolderTrustChoice {
|
||||
@@ -32,18 +32,12 @@ export interface MultiFolderTrustDialogProps {
|
||||
errors: string[];
|
||||
finishAddingDirectories: (
|
||||
config: Config,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
addItem: (itemData: HistoryItemWithoutId, baseTimestamp?: number) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) => Promise<void>;
|
||||
config: Config;
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number;
|
||||
addItem: (itemData: HistoryItemWithoutId, baseTimestamp?: number) => number;
|
||||
}
|
||||
|
||||
export const MultiFolderTrustDialog: React.FC<MultiFolderTrustDialogProps> = ({
|
||||
|
||||
@@ -63,6 +63,7 @@ import {
|
||||
LogoutChoice,
|
||||
} from '../components/LogoutConfirmationDialog.js';
|
||||
import { runExitCleanup } from '../../utils/cleanup.js';
|
||||
import { isRecord } from '../../utils/typeGuards.js';
|
||||
|
||||
interface SlashCommandProcessorActions {
|
||||
openAuthDialog: () => void;
|
||||
@@ -501,35 +502,41 @@ export const useSlashCommandProcessor = (
|
||||
actions.openModelDialog();
|
||||
return { type: 'handled' };
|
||||
case 'agentConfig': {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const props = result.props as Record<string, unknown>;
|
||||
const props = result.props;
|
||||
const name = isRecord(props) ? props['name'] : undefined;
|
||||
const displayName = isRecord(props)
|
||||
? props['displayName']
|
||||
: undefined;
|
||||
const definition = isRecord(props)
|
||||
? props['definition']
|
||||
: undefined;
|
||||
|
||||
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']
|
||||
typeof name !== 'string' ||
|
||||
typeof displayName !== 'string' ||
|
||||
!definition
|
||||
) {
|
||||
throw new Error(
|
||||
'Received invalid properties for agentConfig dialog action.',
|
||||
);
|
||||
}
|
||||
|
||||
actions.openAgentConfigDialog(
|
||||
props['name'],
|
||||
props['displayName'],
|
||||
name,
|
||||
displayName,
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
props['definition'] as AgentDefinition,
|
||||
definition as AgentDefinition,
|
||||
);
|
||||
return { type: 'handled' };
|
||||
}
|
||||
case 'permissions':
|
||||
case 'permissions': {
|
||||
const props = result.props;
|
||||
actions.openPermissionsDialog(
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
result.props as { targetDirectory?: string },
|
||||
isRecord(props)
|
||||
? (props as { targetDirectory?: string })
|
||||
: undefined,
|
||||
);
|
||||
return { type: 'handled' };
|
||||
}
|
||||
case 'help':
|
||||
return { type: 'handled' };
|
||||
default: {
|
||||
|
||||
@@ -92,7 +92,6 @@ import {
|
||||
type TrackedCompletedToolCall,
|
||||
type TrackedCancelledToolCall,
|
||||
type TrackedWaitingToolCall,
|
||||
type TrackedExecutingToolCall,
|
||||
} from './useToolScheduler.js';
|
||||
import { theme } from '../semantic-colors.js';
|
||||
import { getToolGroupBorderAppearance } from '../utils/borderStyles.js';
|
||||
@@ -102,6 +101,10 @@ import { useSessionStats } from '../contexts/SessionContext.js';
|
||||
import { useKeypress } from './useKeypress.js';
|
||||
import type { LoadedSettings } from '../../config/settings.js';
|
||||
|
||||
function isRecord(obj: unknown): obj is Record<string, unknown> {
|
||||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
type ToolResponseWithParts = ToolCallResponseInfo & {
|
||||
llmContent?: PartListUnion;
|
||||
};
|
||||
@@ -842,9 +845,11 @@ export const useGeminiStream = (
|
||||
|
||||
// If it is a shell command, we update the status to Canceled and clear the output
|
||||
// to avoid artifacts, then add it to history immediately.
|
||||
if (isShellCommand) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const toolGroup = pendingHistoryItemRef.current as HistoryItemToolGroup;
|
||||
if (
|
||||
isShellCommand &&
|
||||
pendingHistoryItemRef.current.type === 'tool_group'
|
||||
) {
|
||||
const toolGroup = pendingHistoryItemRef.current;
|
||||
const updatedTools = toolGroup.tools.map((tool) => {
|
||||
if (tool.name === SHELL_COMMAND_NAME) {
|
||||
return {
|
||||
@@ -855,7 +860,7 @@ export const useGeminiStream = (
|
||||
}
|
||||
return tool;
|
||||
});
|
||||
addItem({ ...toolGroup, tools: updatedTools } as HistoryItemWithoutId);
|
||||
addItem({ ...toolGroup, tools: updatedTools });
|
||||
} else {
|
||||
addItem(pendingHistoryItemRef.current);
|
||||
}
|
||||
@@ -1069,11 +1074,15 @@ export const useGeminiStream = (
|
||||
const splitPoint = findLastSafeSplitPoint(newGeminiMessageBuffer);
|
||||
if (splitPoint === newGeminiMessageBuffer.length) {
|
||||
// Update the existing message with accumulated content
|
||||
setPendingHistoryItem((item) => ({
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
type: item?.type as 'gemini' | 'gemini_content',
|
||||
text: newGeminiMessageBuffer,
|
||||
}));
|
||||
setPendingHistoryItem((item) => {
|
||||
if (item?.type === 'gemini' || item?.type === 'gemini_content') {
|
||||
return {
|
||||
type: item.type,
|
||||
text: newGeminiMessageBuffer,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
} else {
|
||||
// This indicates that we need to split up this Gemini Message.
|
||||
// Splitting a message is primarily a performance consideration. There is a
|
||||
@@ -1086,16 +1095,16 @@ export const useGeminiStream = (
|
||||
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 as
|
||||
| 'gemini'
|
||||
| 'gemini_content',
|
||||
text: beforeText,
|
||||
},
|
||||
userMessageTimestamp,
|
||||
);
|
||||
const type = pendingHistoryItemRef.current?.type;
|
||||
if (type === 'gemini' || type === 'gemini_content') {
|
||||
addItem(
|
||||
{
|
||||
type,
|
||||
text: beforeText,
|
||||
},
|
||||
userMessageTimestamp,
|
||||
);
|
||||
}
|
||||
}
|
||||
setPendingHistoryItem({ type: 'gemini_content', text: afterText });
|
||||
newGeminiMessageBuffer = afterText;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { describe, it, expect } from 'vitest';
|
||||
import { act } from 'react';
|
||||
import { renderHook } from '../../test-utils/render.js';
|
||||
import { useHistory } from './useHistoryManager.js';
|
||||
import type { HistoryItem } from '../types.js';
|
||||
import type { HistoryItem, HistoryItemWithoutId } from '../types.js';
|
||||
|
||||
describe('useHistoryManager', () => {
|
||||
it('should initialize with an empty history', async () => {
|
||||
@@ -19,7 +19,7 @@ describe('useHistoryManager', () => {
|
||||
it('should add an item to history with a unique ID', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData: Omit<HistoryItem, 'id'> = {
|
||||
const itemData: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Hello',
|
||||
};
|
||||
@@ -92,11 +92,11 @@ describe('useHistoryManager', () => {
|
||||
it('should generate unique IDs for items added with the same base timestamp', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData1: Omit<HistoryItem, 'id'> = {
|
||||
const itemData1: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'First',
|
||||
};
|
||||
const itemData2: Omit<HistoryItem, 'id'> = {
|
||||
const itemData2: HistoryItemWithoutId = {
|
||||
type: 'gemini', // Replaced HistoryItemType.Gemini
|
||||
text: 'Second',
|
||||
};
|
||||
@@ -120,7 +120,7 @@ describe('useHistoryManager', () => {
|
||||
it('should update an existing history item', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const initialItem: Omit<HistoryItem, 'id'> = {
|
||||
const initialItem: HistoryItemWithoutId = {
|
||||
type: 'gemini', // Replaced HistoryItemType.Gemini
|
||||
text: 'Initial content',
|
||||
};
|
||||
@@ -146,7 +146,7 @@ describe('useHistoryManager', () => {
|
||||
it('should not change history if updateHistoryItem is called with a nonexistent ID', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData: Omit<HistoryItem, 'id'> = {
|
||||
const itemData: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Hello',
|
||||
};
|
||||
@@ -167,11 +167,11 @@ describe('useHistoryManager', () => {
|
||||
it('should clear the history', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData1: Omit<HistoryItem, 'id'> = {
|
||||
const itemData1: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'First',
|
||||
};
|
||||
const itemData2: Omit<HistoryItem, 'id'> = {
|
||||
const itemData2: HistoryItemWithoutId = {
|
||||
type: 'gemini', // Replaced HistoryItemType.Gemini
|
||||
text: 'Second',
|
||||
};
|
||||
@@ -193,19 +193,19 @@ describe('useHistoryManager', () => {
|
||||
it('should not add consecutive duplicate user messages', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData1: Omit<HistoryItem, 'id'> = {
|
||||
const itemData1: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Duplicate message',
|
||||
};
|
||||
const itemData2: Omit<HistoryItem, 'id'> = {
|
||||
const itemData2: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Duplicate message',
|
||||
};
|
||||
const itemData3: Omit<HistoryItem, 'id'> = {
|
||||
const itemData3: HistoryItemWithoutId = {
|
||||
type: 'gemini', // Replaced HistoryItemType.Gemini
|
||||
text: 'Gemini response',
|
||||
};
|
||||
const itemData4: Omit<HistoryItem, 'id'> = {
|
||||
const itemData4: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Another user message',
|
||||
};
|
||||
@@ -226,15 +226,15 @@ describe('useHistoryManager', () => {
|
||||
it('should add duplicate user messages if they are not consecutive', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const timestamp = Date.now();
|
||||
const itemData1: Omit<HistoryItem, 'id'> = {
|
||||
const itemData1: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Message 1',
|
||||
};
|
||||
const itemData2: Omit<HistoryItem, 'id'> = {
|
||||
const itemData2: HistoryItemWithoutId = {
|
||||
type: 'gemini', // Replaced HistoryItemType.Gemini
|
||||
text: 'Gemini response',
|
||||
};
|
||||
const itemData3: Omit<HistoryItem, 'id'> = {
|
||||
const itemData3: HistoryItemWithoutId = {
|
||||
type: 'user', // Replaced HistoryItemType.User
|
||||
text: 'Message 1', // Duplicate text, but not consecutive
|
||||
};
|
||||
@@ -254,7 +254,7 @@ describe('useHistoryManager', () => {
|
||||
it('should use Date.now() as default baseTimestamp if not provided', async () => {
|
||||
const { result } = await renderHook(() => useHistory());
|
||||
const before = Date.now();
|
||||
const itemData: Omit<HistoryItem, 'id'> = {
|
||||
const itemData: HistoryItemWithoutId = {
|
||||
type: 'user',
|
||||
text: 'Default timestamp test',
|
||||
};
|
||||
|
||||
@@ -5,24 +5,24 @@
|
||||
*/
|
||||
|
||||
import { useState, useRef, useCallback, useMemo } from 'react';
|
||||
import type { HistoryItem } from '../types.js';
|
||||
import type { HistoryItem, HistoryItemWithoutId } from '../types.js';
|
||||
import type { ChatRecordingService } from '@google/gemini-cli-core/src/services/chatRecordingService.js';
|
||||
|
||||
// Type for the updater function passed to updateHistoryItem
|
||||
type HistoryItemUpdater = (
|
||||
prevItem: HistoryItem,
|
||||
) => Partial<Omit<HistoryItem, 'id'>>;
|
||||
) => Partial<HistoryItemWithoutId>;
|
||||
|
||||
export interface UseHistoryManagerReturn {
|
||||
history: HistoryItem[];
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
itemData: HistoryItemWithoutId,
|
||||
baseTimestamp?: number,
|
||||
isResuming?: boolean,
|
||||
) => number; // Returns the generated ID
|
||||
updateItem: (
|
||||
id: number,
|
||||
updates: Partial<Omit<HistoryItem, 'id'>> | HistoryItemUpdater,
|
||||
updates: Partial<HistoryItemWithoutId> | HistoryItemUpdater,
|
||||
) => void;
|
||||
clearItems: () => void;
|
||||
loadHistory: (newHistory: HistoryItem[]) => void;
|
||||
@@ -63,13 +63,12 @@ export function useHistory({
|
||||
// Adds a new item to the history state with a unique ID.
|
||||
const addItem = useCallback(
|
||||
(
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
itemData: HistoryItemWithoutId,
|
||||
baseTimestamp: number = Date.now(),
|
||||
isResuming: boolean = false,
|
||||
): number => {
|
||||
const id = getNextMessageId(baseTimestamp);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const newItem: HistoryItem = { ...itemData, id } as HistoryItem;
|
||||
const newItem: HistoryItem = { ...itemData, id };
|
||||
|
||||
setHistory((prevHistory) => {
|
||||
if (prevHistory.length > 0) {
|
||||
@@ -138,7 +137,7 @@ export function useHistory({
|
||||
const updateItem = useCallback(
|
||||
(
|
||||
id: number,
|
||||
updates: Partial<Omit<HistoryItem, 'id'>> | HistoryItemUpdater,
|
||||
updates: Partial<HistoryItemWithoutId> | HistoryItemUpdater,
|
||||
) => {
|
||||
setHistory((prevHistory) =>
|
||||
prevHistory.map((item) => {
|
||||
|
||||
@@ -14,14 +14,11 @@ import {
|
||||
} from '@google/gemini-cli-core';
|
||||
import { MultiFolderTrustDialog } from '../components/MultiFolderTrustDialog.js';
|
||||
import type { UseHistoryManagerReturn } from './useHistoryManager.js';
|
||||
import { MessageType, type HistoryItem } from '../types.js';
|
||||
import { MessageType, type HistoryItemWithoutId } from '../types.js';
|
||||
|
||||
async function finishAddingDirectories(
|
||||
config: Config,
|
||||
addItem: (
|
||||
itemData: Omit<HistoryItem, 'id'>,
|
||||
baseTimestamp?: number,
|
||||
) => number,
|
||||
addItem: (itemData: HistoryItemWithoutId, baseTimestamp?: number) => number,
|
||||
added: string[],
|
||||
errors: string[],
|
||||
) {
|
||||
|
||||
@@ -8,7 +8,7 @@ import type { UpdateObject } from '../ui/utils/updateCheck.js';
|
||||
import type { LoadedSettings } from '../config/settings.js';
|
||||
import { getInstallationInfo, PackageManager } from './installationInfo.js';
|
||||
import { updateEventEmitter } from './updateEventEmitter.js';
|
||||
import { MessageType, type HistoryItem } from '../ui/types.js';
|
||||
import { MessageType, type HistoryItemWithoutId } from '../ui/types.js';
|
||||
import { spawnWrapper } from './spawnWrapper.js';
|
||||
import type { spawn } from 'node:child_process';
|
||||
import { debugLogger } from '@google/gemini-cli-core';
|
||||
@@ -115,7 +115,6 @@ export function handleAutoUpdate(
|
||||
}
|
||||
updateEventEmitter.emit('update-received', {
|
||||
...info,
|
||||
message: combinedMessage,
|
||||
isUpdating: true,
|
||||
});
|
||||
if (_updateInProgress) {
|
||||
@@ -163,7 +162,7 @@ export function handleAutoUpdate(
|
||||
}
|
||||
|
||||
export function setUpdateHandler(
|
||||
addItem: (item: Omit<HistoryItem, 'id'>, timestamp: number) => void,
|
||||
addItem: (item: HistoryItemWithoutId, timestamp: number) => void,
|
||||
setUpdateInfo: (info: UpdateObject | null) => void,
|
||||
) {
|
||||
let successfullyInstalled = false;
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* Type guard to check if a value is a non-null object and not an array.
|
||||
*/
|
||||
export function isRecord(obj: unknown): obj is Record<string, unknown> {
|
||||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
Reference in New Issue
Block a user