mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-15 14:23:02 -07:00
feat(cli): wire active extension context into slash command routing
Extracts the extension context from slash commands based on their registered metadata and sets it as the active context in the Config before execution. This enables the backend to dynamically route plan directories based on the extension that owns the invoked command.
This commit is contained in:
@@ -3711,7 +3711,8 @@ describe('loadCliConfig mcpEnabled', () => {
|
||||
]);
|
||||
|
||||
const config = await loadCliConfig(settings, 'test-session', argv);
|
||||
expect(config.storage.getPlansDir()).toContain('ext-plans-dir');
|
||||
config.setActiveExtensionContext('ext-plan');
|
||||
expect(config.getPlansDir()).toContain('ext-plans-dir');
|
||||
});
|
||||
|
||||
it('should NOT use plan directory from active extension when user has specified one', async () => {
|
||||
|
||||
@@ -609,9 +609,12 @@ export async function loadCliConfig(
|
||||
});
|
||||
await extensionManager.loadExtensions();
|
||||
|
||||
const extensionPlanSettings = extensionManager
|
||||
.getExtensions()
|
||||
.find((ext) => ext.isActive && ext.plan?.directory)?.plan;
|
||||
const extensionPlanDirs: Record<string, string> = {};
|
||||
for (const ext of extensionManager.getExtensions()) {
|
||||
if (ext.isActive && ext.plan?.directory) {
|
||||
extensionPlanDirs[ext.name] = ext.plan.directory;
|
||||
}
|
||||
}
|
||||
|
||||
const experimentalJitContext = settings.experimental.jitContext;
|
||||
|
||||
@@ -969,9 +972,8 @@ export async function loadCliConfig(
|
||||
plan: settings.general?.plan?.enabled ?? true,
|
||||
tracker: settings.experimental?.taskTracker,
|
||||
directWebFetch: settings.experimental?.directWebFetch,
|
||||
planSettings: settings.general?.plan?.directory
|
||||
? settings.general.plan
|
||||
: (extensionPlanSettings ?? settings.general?.plan),
|
||||
planSettings: settings.general?.plan,
|
||||
extensionPlanDirs,
|
||||
enableEventDrivenScheduler: true,
|
||||
skillsSupport: settings.skills?.enabled ?? true,
|
||||
disabledSkills: settings.skills?.disabled,
|
||||
|
||||
@@ -1308,6 +1308,17 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
}
|
||||
}
|
||||
|
||||
const parsedCommand = parseSlashCommand(
|
||||
submittedValue,
|
||||
slashCommands ?? [],
|
||||
);
|
||||
|
||||
if (parsedCommand.extensionContext && config) {
|
||||
if (config.hasExtensionPlanDir(parsedCommand.extensionContext)) {
|
||||
config.setActiveExtensionContext(parsedCommand.extensionContext);
|
||||
}
|
||||
}
|
||||
|
||||
const isSlash = isSlashCommand(submittedValue.trim());
|
||||
const isIdle = streamingState === StreamingState.Idle;
|
||||
const isAgentRunning =
|
||||
@@ -1315,10 +1326,7 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
isToolExecuting(pendingHistoryItems);
|
||||
|
||||
if (isSlash && isAgentRunning) {
|
||||
const { commandToExecute } = parseSlashCommand(
|
||||
submittedValue,
|
||||
slashCommands ?? [],
|
||||
);
|
||||
const commandToExecute = parsedCommand.commandToExecute;
|
||||
if (commandToExecute?.isSafeConcurrent) {
|
||||
void handleSlashCommand(submittedValue);
|
||||
addInput(submittedValue);
|
||||
|
||||
@@ -45,6 +45,7 @@ describe('clearCommand', () => {
|
||||
fireSessionEndEvent: vi.fn().mockResolvedValue(undefined),
|
||||
fireSessionStartEvent: vi.fn().mockResolvedValue(undefined),
|
||||
}),
|
||||
setActiveExtensionContext: vi.fn(),
|
||||
injectionService: {
|
||||
clear: mockHintClear,
|
||||
},
|
||||
|
||||
@@ -30,8 +30,9 @@ export const clearCommand: SlashCommand = {
|
||||
await hookSystem.fireSessionEndEvent(SessionEndReason.Clear);
|
||||
}
|
||||
|
||||
// Reset user steering hints
|
||||
// Reset user steering hints and extension context
|
||||
config?.injectionService.clear();
|
||||
config?.setActiveExtensionContext(undefined);
|
||||
|
||||
// Start a new conversation recording with a new session ID
|
||||
// We MUST do this before calling resetChat() so the new ChatRecordingService
|
||||
|
||||
@@ -10,6 +10,7 @@ export type ParsedSlashCommand = {
|
||||
commandToExecute: SlashCommand | undefined;
|
||||
args: string;
|
||||
canonicalPath: string[];
|
||||
extensionContext?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -69,6 +70,8 @@ export const parseSlashCommand = (
|
||||
|
||||
const args = parts.slice(pathIndex).join(' ');
|
||||
|
||||
const extensionContext = commandToExecute?.extensionName;
|
||||
|
||||
// Backtrack if the matched (sub)command doesn't take arguments but some were provided,
|
||||
// AND the parent command is capable of handling them.
|
||||
if (
|
||||
@@ -82,8 +85,9 @@ export const parseSlashCommand = (
|
||||
commandToExecute: parentCommand,
|
||||
args: parts.slice(pathIndex - 1).join(' '),
|
||||
canonicalPath: canonicalPath.slice(0, -1),
|
||||
extensionContext: parentCommand.extensionName,
|
||||
};
|
||||
}
|
||||
|
||||
return { commandToExecute, args, canonicalPath };
|
||||
return { commandToExecute, args, canonicalPath, extensionContext };
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user