fix: /policy to display policies according to mode (#16772)

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Allen Hutchison <adh@google.com>
This commit is contained in:
Ishaan Gupta
2026-01-22 03:03:57 +05:30
committed by GitHub
parent addecf2c74
commit 1033550f78
2 changed files with 62 additions and 23 deletions
@@ -62,7 +62,7 @@ describe('policiesCommand', () => {
); );
}); });
it('should list active policies in correct format', async () => { it('should list policies grouped by mode', async () => {
const mockRules = [ const mockRules = [
{ {
decision: PolicyDecision.DENY, decision: PolicyDecision.DENY,
@@ -99,13 +99,18 @@ describe('policiesCommand', () => {
const call = vi.mocked(mockContext.ui.addItem).mock.calls[0]; const call = vi.mocked(mockContext.ui.addItem).mock.calls[0];
const content = (call[0] as { text: string }).text; const content = (call[0] as { text: string }).text;
expect(content).toContain('### Normal Mode Policies');
expect(content).toContain( expect(content).toContain(
'1. **DENY** tool: `dangerousTool` [Priority: 10]', '### Auto Edit Mode Policies (combined with normal mode policies)',
); );
expect(content).toContain( expect(content).toContain(
'2. **ALLOW** all tools (args match: `safe`) [Source: `test.toml`]', '### Yolo Mode Policies (combined with normal mode policies)',
); );
expect(content).toContain('3. **ASK_USER** all tools'); expect(content).toContain(
'**DENY** tool: `dangerousTool` [Priority: 10]',
);
expect(content).toContain('**ALLOW** all tools (args match: `safe`)');
expect(content).toContain('**ASK_USER** all tools');
}); });
}); });
}); });
+53 -19
View File
@@ -4,12 +4,46 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
import { ApprovalMode, type PolicyRule } from '@google/gemini-cli-core';
import { CommandKind, type SlashCommand } from './types.js'; import { CommandKind, type SlashCommand } from './types.js';
import { MessageType } from '../types.js'; import { MessageType } from '../types.js';
interface CategorizedRules {
normal: PolicyRule[];
autoEdit: PolicyRule[];
yolo: PolicyRule[];
}
const categorizeRulesByMode = (
rules: readonly PolicyRule[],
): CategorizedRules => {
const result: CategorizedRules = {
normal: [],
autoEdit: [],
yolo: [],
};
const ALL_MODES = Object.values(ApprovalMode);
rules.forEach((rule) => {
const modes = rule.modes?.length ? rule.modes : ALL_MODES;
const modeSet = new Set(modes);
if (modeSet.has(ApprovalMode.DEFAULT)) result.normal.push(rule);
if (modeSet.has(ApprovalMode.AUTO_EDIT)) result.autoEdit.push(rule);
if (modeSet.has(ApprovalMode.YOLO)) result.yolo.push(rule);
});
return result;
};
const formatRule = (rule: PolicyRule, i: number) =>
`${i + 1}. **${rule.decision.toUpperCase()}** ${rule.toolName ? `tool: \`${rule.toolName}\`` : 'all tools'}` +
(rule.argsPattern ? ` (args match: \`${rule.argsPattern.source}\`)` : '') +
(rule.priority !== undefined ? ` [Priority: ${rule.priority}]` : '');
const formatSection = (title: string, rules: PolicyRule[]) =>
`### ${title}\n${rules.length ? rules.map(formatRule).join('\n') : '_No policies._'}\n\n`;
const listPoliciesCommand: SlashCommand = { const listPoliciesCommand: SlashCommand = {
name: 'list', name: 'list',
description: 'List all active policies', description: 'List all active policies grouped by mode',
kind: CommandKind.BUILT_IN, kind: CommandKind.BUILT_IN,
autoExecute: true, autoExecute: true,
action: async (context) => { action: async (context) => {
@@ -39,25 +73,25 @@ const listPoliciesCommand: SlashCommand = {
return; return;
} }
const categorized = categorizeRulesByMode(rules);
const normalRulesSet = new Set(categorized.normal);
const uniqueAutoEdit = categorized.autoEdit.filter(
(rule) => !normalRulesSet.has(rule),
);
const uniqueYolo = categorized.yolo.filter(
(rule) => !normalRulesSet.has(rule),
);
let content = '**Active Policies**\n\n'; let content = '**Active Policies**\n\n';
rules.forEach((rule, index) => { content += formatSection('Normal Mode Policies', categorized.normal);
content += `${index + 1}. **${rule.decision.toUpperCase()}**`; content += formatSection(
if (rule.toolName) { 'Auto Edit Mode Policies (combined with normal mode policies)',
content += ` tool: \`${rule.toolName}\``; uniqueAutoEdit,
} else { );
content += ` all tools`; content += formatSection(
} 'Yolo Mode Policies (combined with normal mode policies)',
if (rule.argsPattern) { uniqueYolo,
content += ` (args match: \`${rule.argsPattern.source}\`)`; );
}
if (rule.priority !== undefined) {
content += ` [Priority: ${rule.priority}]`;
}
if (rule.source) {
content += ` [Source: \`${rule.source}\`]`;
}
content += '\n';
});
context.ui.addItem( context.ui.addItem(
{ {