mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-11 10:30:46 -07:00
refactor(cli): remove unsafe type assertions in commands (Phase 5)
Replaces 'as Type' assertions with runtime type guards and safe property access in CLI commands (extensions, skills, mcp, hooks). Fixes #19733
This commit is contained in:
@@ -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,14 @@ 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');
|
||||
}
|
||||
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();
|
||||
},
|
||||
|
||||
@@ -12,10 +12,6 @@ import { loadSettings, SettingScope } from '../../config/settings.js';
|
||||
import { exitCli } from '../utils.js';
|
||||
import stripJsonComments from 'strip-json-comments';
|
||||
|
||||
interface MigrateArgs {
|
||||
fromClaude: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mapping from Claude Code event names to Gemini event names
|
||||
*/
|
||||
@@ -62,16 +58,19 @@ function transformMatcher(matcher: string | undefined): string | undefined {
|
||||
return transformed;
|
||||
}
|
||||
|
||||
function isRecord(obj: unknown): obj is Record<string, unknown> {
|
||||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 +105,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 +127,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 +176,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 = 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 +190,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 = 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 +258,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,56 @@ 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'];
|
||||
const existingArgs = Array.isArray(args) ? 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();
|
||||
},
|
||||
|
||||
@@ -64,6 +64,10 @@ import {
|
||||
} from '../components/LogoutConfirmationDialog.js';
|
||||
import { runExitCleanup } from '../../utils/cleanup.js';
|
||||
|
||||
function isRecord(obj: unknown): obj is Record<string, unknown> {
|
||||
return typeof obj === 'object' && obj !== null && !Array.isArray(obj);
|
||||
}
|
||||
|
||||
interface SlashCommandProcessorActions {
|
||||
openAuthDialog: () => void;
|
||||
openThemeDialog: () => void;
|
||||
@@ -501,17 +505,14 @@ 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;
|
||||
if (
|
||||
!props ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
!isRecord(props) ||
|
||||
typeof props['name'] !== 'string' ||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
typeof props['displayName'] !== 'string' ||
|
||||
!props['definition']
|
||||
) {
|
||||
throw new Error(
|
||||
) { throw new Error(
|
||||
'Received invalid properties for agentConfig dialog action.',
|
||||
);
|
||||
}
|
||||
@@ -524,12 +525,15 @@ export const useSlashCommandProcessor = (
|
||||
);
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user