Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 462385c

Browse filesBrowse files
improvement: add allowed-tools configuration UI for Slash Command export
- Added allowedTools property to SlashCommandOptions interface - Added Allowed Tools sub-menu in SlashCommandOptionsDropdown - Uses AVAILABLE_TOOLS (18 tools) for checkbox selection - Only outputs allowed-tools when explicitly configured (omit = default) - Added i18n translations for 5 languages Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent cd603b2 commit 462385c
Copy full SHA for 462385c

12 files changed

+244-11Lines changed: 244 additions & 11 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎src/extension/services/export-service.ts‎

Copy file name to clipboardExpand all lines: src/extension/services/export-service.ts
+7-8Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,7 @@ export function validateClaudeFileFormat(
187187
if (!frontmatterContent.includes('description:')) {
188188
throw new Error('SlashCommand file missing required field: description');
189189
}
190-
if (!frontmatterContent.includes('allowed-tools:')) {
191-
throw new Error('SlashCommand file missing required field: allowed-tools');
192-
}
190+
// Issue #424: allowed-tools is optional (omit = use Claude Code default)
193191
}
194192

195193
// Check that there's content after frontmatter (prompt body)
@@ -518,11 +516,12 @@ function escapeYamlString(value: string, alwaysQuote = false): string {
518516
*/
519517
function generateSlashCommandFile(workflow: Workflow): string {
520518
// YAML frontmatter
521-
const frontmatterLines = [
522-
'---',
523-
`description: ${workflow.description || workflow.name}`,
524-
'allowed-tools: Task,AskUserQuestion',
525-
];
519+
const frontmatterLines = ['---', `description: ${workflow.description || workflow.name}`];
520+
521+
// Issue #424: Add allowed-tools only if explicitly configured (omit = use Claude Code default)
522+
if (workflow.slashCommandOptions?.allowedTools) {
523+
frontmatterLines.push(`allowed-tools: ${workflow.slashCommandOptions.allowedTools}`);
524+
}
526525

527526
// Add model if specified and not 'default'
528527
if (workflow.slashCommandOptions?.model && workflow.slashCommandOptions.model !== 'default') {
Collapse file

‎src/shared/types/workflow-definition.ts‎

Copy file name to clipboardExpand all lines: src/shared/types/workflow-definition.ts
+2Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ export interface SlashCommandOptions {
5555
model?: SlashCommandModel;
5656
/** Hooks configuration for workflow execution */
5757
hooks?: WorkflowHooks;
58+
/** Comma-separated list of allowed tools for Slash Command execution */
59+
allowedTools?: string;
5860
}
5961

6062
// ============================================================================
Collapse file

‎src/webview/src/components/Toolbar.tsx‎

Copy file name to clipboardExpand all lines: src/webview/src/components/Toolbar.tsx
+6-1Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const Toolbar: React.FC<ToolbarProps> = ({
6666
setSlashCommandOptions,
6767
setSlashCommandContext,
6868
setSlashCommandModel,
69+
setSlashCommandAllowedTools,
6970
addHookEntry,
7071
removeHookEntry,
7172
updateHookEntry,
@@ -191,11 +192,12 @@ export const Toolbar: React.FC<ToolbarProps> = ({
191192
setWorkflowName(workflow.name);
192193
// Load description from workflow (default to empty string if not present)
193194
setWorkflowDescription(workflow.description || '');
194-
// Issue #413: Load slashCommandOptions (context, model, hooks) as unified object
195+
// Issue #413: Load slashCommandOptions (context, model, hooks, allowedTools) as unified object
195196
setSlashCommandOptions({
196197
context: workflow.slashCommandOptions?.context ?? 'default',
197198
model: workflow.slashCommandOptions?.model ?? 'default',
198199
hooks: workflow.slashCommandOptions?.hooks,
200+
allowedTools: workflow.slashCommandOptions?.allowedTools,
199201
});
200202
// Set as active workflow to preserve conversation history
201203
setActiveWorkflow(workflow);
@@ -710,6 +712,7 @@ export const Toolbar: React.FC<ToolbarProps> = ({
710712

711713
{/* Options Dropdown (separate with small gap) */}
712714
{/* Issue #413: Hooks are now integrated into SlashCommandOptionsDropdown */}
715+
{/* Issue #424: Allowed Tools configuration added */}
713716
<SlashCommandOptionsDropdown
714717
context={slashCommandOptions.context ?? 'default'}
715718
onContextChange={setSlashCommandContext}
@@ -719,6 +722,8 @@ export const Toolbar: React.FC<ToolbarProps> = ({
719722
onAddHookEntry={addHookEntry}
720723
onRemoveHookEntry={removeHookEntry}
721724
onUpdateHookEntry={updateHookEntry}
725+
allowedTools={slashCommandOptions.allowedTools ?? ''}
726+
onAllowedToolsChange={setSlashCommandAllowedTools}
722727
/>
723728
</div>
724729
</div>
Collapse file

‎src/webview/src/components/toolbar/SlashCommandOptionsDropdown.tsx‎

Copy file name to clipboardExpand all lines: src/webview/src/components/toolbar/SlashCommandOptionsDropdown.tsx
+193-1Lines changed: 193 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
* - model: Specify the model to use for execution (inherit/sonnet/opus/haiku)
66
* - context: fork - Exports with `context: fork` for isolated sub-agent execution (Claude Code v2.1.0+)
77
* - hooks: Configure execution hooks (PreToolUse, PostToolUse, Stop)
8+
* - allowedTools: Configure allowed tools for Slash Command execution
89
*/
910

1011
import * as DropdownMenu from '@radix-ui/react-dropdown-menu';
@@ -15,10 +16,22 @@ import type {
1516
SlashCommandModel,
1617
WorkflowHooks,
1718
} from '@shared/types/workflow-definition';
18-
import { Check, ChevronDown, ChevronLeft, Cpu, GitFork, Plus, Terminal, X } from 'lucide-react';
19+
import {
20+
Check,
21+
ChevronDown,
22+
ChevronLeft,
23+
Cpu,
24+
GitFork,
25+
Plus,
26+
RotateCcw,
27+
Terminal,
28+
Wrench,
29+
X,
30+
} from 'lucide-react';
1931
import { memo, useCallback, useState } from 'react';
2032
import { useTranslation } from '../../i18n/i18n-context';
2133
import type { WebviewTranslationKeys } from '../../i18n/translation-keys';
34+
import { AVAILABLE_TOOLS } from '../../stores/refinement-store';
2235
import { ToolSelectTagInput } from '../common/ToolSelectTagInput';
2336

2437
// Fixed font sizes for dropdown menu (not responsive)
@@ -66,6 +79,20 @@ const HOOK_TYPES: {
6679
},
6780
];
6881

82+
// Default allowed tools for Slash Command (matches export-service.ts default)
83+
const DEFAULT_ALLOWED_TOOLS = 'Task,AskUserQuestion';
84+
85+
// Helper functions for allowed tools conversion
86+
const allowedToolsToArray = (tools: string): string[] =>
87+
tools
88+
? tools
89+
.split(',')
90+
.map((t) => t.trim())
91+
.filter(Boolean)
92+
: [];
93+
94+
const arrayToAllowedTools = (tools: string[]): string => tools.join(',');
95+
6996
// Helper functions for matcher ↔ tags conversion
7097
const matcherToTags = (matcher: string): string[] =>
7198
matcher ? matcher.split('|').filter(Boolean) : [];
@@ -91,6 +118,8 @@ interface SlashCommandOptionsDropdownProps {
91118
onAddHookEntry: (hookType: HookType, matcher: string, command: string, once?: boolean) => void;
92119
onRemoveHookEntry: (hookType: HookType, entryIndex: number) => void;
93120
onUpdateHookEntry: (hookType: HookType, entryIndex: number, entry: Partial<HookEntry>) => void;
121+
allowedTools: string;
122+
onAllowedToolsChange: (tools: string) => void;
94123
}
95124

96125
interface NewEntryState {
@@ -108,6 +137,8 @@ export function SlashCommandOptionsDropdown({
108137
onAddHookEntry,
109138
onRemoveHookEntry,
110139
onUpdateHookEntry,
140+
allowedTools,
141+
onAllowedToolsChange,
111142
}: SlashCommandOptionsDropdownProps) {
112143
const { t } = useTranslation();
113144
const [newEntry, setNewEntry] = useState<Record<HookType, NewEntryState>>({
@@ -127,6 +158,25 @@ export function SlashCommandOptionsDropdown({
127158
(sum, entries) => sum + (entries?.length || 0),
128159
0
129160
);
161+
const selectedTools = allowedToolsToArray(allowedTools);
162+
163+
// Toggle a tool in the allowed tools list
164+
const handleToggleTool = useCallback(
165+
(tool: string) => {
166+
const currentTools = allowedToolsToArray(allowedTools);
167+
if (currentTools.includes(tool)) {
168+
onAllowedToolsChange(arrayToAllowedTools(currentTools.filter((t) => t !== tool)));
169+
} else {
170+
onAllowedToolsChange(arrayToAllowedTools([...currentTools, tool]));
171+
}
172+
},
173+
[allowedTools, onAllowedToolsChange]
174+
);
175+
176+
// Reset to default allowed tools
177+
const handleResetAllowedTools = useCallback(() => {
178+
onAllowedToolsChange(DEFAULT_ALLOWED_TOOLS);
179+
}, [onAllowedToolsChange]);
130180

131181
const handleAddEntry = useCallback(
132182
(hookType: HookType) => {
@@ -516,6 +566,148 @@ export function SlashCommandOptionsDropdown({
516566
</DropdownMenu.SubContent>
517567
</DropdownMenu.Portal>
518568
</DropdownMenu.Sub>
569+
570+
<DropdownMenu.Separator
571+
style={{
572+
height: '1px',
573+
backgroundColor: 'var(--vscode-dropdown-border)',
574+
margin: '4px 0',
575+
}}
576+
/>
577+
578+
{/* Allowed Tools Sub-menu */}
579+
<DropdownMenu.Sub>
580+
<DropdownMenu.SubTrigger
581+
style={{
582+
padding: '8px 12px',
583+
fontSize: `${FONT_SIZES.small}px`,
584+
color: 'var(--vscode-foreground)',
585+
cursor: 'pointer',
586+
display: 'flex',
587+
alignItems: 'center',
588+
justifyContent: 'space-between',
589+
gap: '8px',
590+
outline: 'none',
591+
borderRadius: '2px',
592+
}}
593+
>
594+
<div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
595+
<ChevronLeft size={14} />
596+
<span style={{ color: 'var(--vscode-descriptionForeground)' }}>
597+
{selectedTools.length > 0
598+
? `${selectedTools.length} tools`
599+
: t('toolbar.allowedTools.default')}
600+
</span>
601+
</div>
602+
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
603+
<Wrench size={14} />
604+
<span>{t('toolbar.allowedTools')}</span>
605+
</div>
606+
</DropdownMenu.SubTrigger>
607+
608+
<DropdownMenu.Portal>
609+
<DropdownMenu.SubContent
610+
sideOffset={4}
611+
style={{
612+
backgroundColor: 'var(--vscode-dropdown-background)',
613+
border: '1px solid var(--vscode-dropdown-border)',
614+
borderRadius: '4px',
615+
boxShadow: '0 4px 8px rgba(0, 0, 0, 0.3)',
616+
zIndex: 10000,
617+
minWidth: '180px',
618+
maxHeight: '300px',
619+
overflowY: 'auto',
620+
padding: '4px',
621+
}}
622+
>
623+
{/* Tooltip */}
624+
<div
625+
style={{
626+
padding: '6px 12px',
627+
fontSize: '10px',
628+
color: 'var(--vscode-descriptionForeground)',
629+
lineHeight: '1.4',
630+
borderBottom: '1px solid var(--vscode-dropdown-border)',
631+
marginBottom: '4px',
632+
}}
633+
>
634+
{t('toolbar.allowedTools.tooltip')}
635+
</div>
636+
637+
{/* Tool Checkboxes */}
638+
{AVAILABLE_TOOLS.map((tool) => (
639+
<DropdownMenu.CheckboxItem
640+
key={tool}
641+
checked={selectedTools.includes(tool)}
642+
onSelect={(event) => {
643+
event.preventDefault();
644+
handleToggleTool(tool);
645+
}}
646+
style={{
647+
padding: '6px 12px',
648+
fontSize: `${FONT_SIZES.small}px`,
649+
color: 'var(--vscode-foreground)',
650+
cursor: 'pointer',
651+
display: 'flex',
652+
alignItems: 'center',
653+
gap: '8px',
654+
outline: 'none',
655+
borderRadius: '2px',
656+
position: 'relative',
657+
paddingLeft: '28px',
658+
}}
659+
>
660+
<div
661+
style={{
662+
position: 'absolute',
663+
left: '8px',
664+
width: '12px',
665+
height: '12px',
666+
display: 'flex',
667+
alignItems: 'center',
668+
justifyContent: 'center',
669+
}}
670+
>
671+
<DropdownMenu.ItemIndicator>
672+
<Check size={12} />
673+
</DropdownMenu.ItemIndicator>
674+
</div>
675+
<span style={{ fontFamily: 'monospace' }}>{tool}</span>
676+
</DropdownMenu.CheckboxItem>
677+
))}
678+
679+
<DropdownMenu.Separator
680+
style={{
681+
height: '1px',
682+
backgroundColor: 'var(--vscode-dropdown-border)',
683+
margin: '4px 0',
684+
}}
685+
/>
686+
687+
{/* Reset to Default */}
688+
<DropdownMenu.Item
689+
onSelect={(event) => {
690+
event.preventDefault();
691+
handleResetAllowedTools();
692+
}}
693+
style={{
694+
padding: '6px 12px',
695+
fontSize: `${FONT_SIZES.small}px`,
696+
color: 'var(--vscode-foreground)',
697+
cursor: 'pointer',
698+
display: 'flex',
699+
alignItems: 'center',
700+
gap: '8px',
701+
outline: 'none',
702+
borderRadius: '2px',
703+
}}
704+
>
705+
<RotateCcw size={12} />
706+
<span>{t('toolbar.allowedTools.resetToDefault')}</span>
707+
</DropdownMenu.Item>
708+
</DropdownMenu.SubContent>
709+
</DropdownMenu.Portal>
710+
</DropdownMenu.Sub>
519711
</DropdownMenu.Content>
520712
</DropdownMenu.Portal>
521713
</DropdownMenu.Root>
Collapse file

‎src/webview/src/i18n/translation-keys.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translation-keys.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ export interface WebviewTranslationKeys {
6363
// Toolbar slash command options dropdown
6464
'toolbar.model.tooltip': string;
6565
'toolbar.contextFork.tooltip': string;
66+
'toolbar.allowedTools': string;
67+
'toolbar.allowedTools.tooltip': string;
68+
'toolbar.allowedTools.default': string;
69+
'toolbar.allowedTools.resetToDefault': string;
6670

6771
// Toolbar hooks configuration dropdown
6872
'hooks.title': string;
Collapse file

‎src/webview/src/i18n/translations/en.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translations/en.ts
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ export const enWebviewTranslations: WebviewTranslationKeys = {
6868
// Toolbar slash command options dropdown
6969
'toolbar.model.tooltip': 'Specify the model to use for execution',
7070
'toolbar.contextFork.tooltip': 'Run in isolated sub-agent context (Claude Code v2.1.0+)',
71+
'toolbar.allowedTools': 'Allowed Tools',
72+
'toolbar.allowedTools.tooltip':
73+
'Configure which tools are allowed during Slash Command execution',
74+
'toolbar.allowedTools.default': 'default',
75+
'toolbar.allowedTools.resetToDefault': 'Reset to Default',
7176

7277
// Toolbar hooks configuration dropdown
7378
'hooks.title': 'Hooks',
Collapse file

‎src/webview/src/i18n/translations/ja.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translations/ja.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ export const jaWebviewTranslations: WebviewTranslationKeys = {
6969
'toolbar.model.tooltip': '実行に使用するモデルを指定',
7070
'toolbar.contextFork.tooltip':
7171
'分離されたサブエージェントコンテキストで実行 (Claude Code v2.1.0+)',
72+
'toolbar.allowedTools': '許可ツール',
73+
'toolbar.allowedTools.tooltip': 'スラッシュコマンド実行時に使用可能なツールを設定',
74+
'toolbar.allowedTools.default': 'デフォルト',
75+
'toolbar.allowedTools.resetToDefault': 'デフォルトに戻す',
7276

7377
// Toolbar hooks configuration dropdown
7478
'hooks.title': 'Hooks',
Collapse file

‎src/webview/src/i18n/translations/ko.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translations/ko.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ export const koWebviewTranslations: WebviewTranslationKeys = {
6767
// Toolbar slash command options dropdown
6868
'toolbar.model.tooltip': '실행에 사용할 모델 지정',
6969
'toolbar.contextFork.tooltip': '분리된 서브 에이전트 컨텍스트에서 실행 (Claude Code v2.1.0+)',
70+
'toolbar.allowedTools': '허용된 도구',
71+
'toolbar.allowedTools.tooltip': '슬래시 명령 실행 시 사용 가능한 도구 설정',
72+
'toolbar.allowedTools.default': '기본값',
73+
'toolbar.allowedTools.resetToDefault': '기본값으로 재설정',
7074

7175
// Toolbar hooks configuration dropdown
7276
'hooks.title': 'Hooks',
Collapse file

‎src/webview/src/i18n/translations/zh-CN.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translations/zh-CN.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export const zhCNWebviewTranslations: WebviewTranslationKeys = {
6565
// Toolbar slash command options dropdown
6666
'toolbar.model.tooltip': '指定执行时使用的模型',
6767
'toolbar.contextFork.tooltip': '在隔离的子代理上下文中运行 (Claude Code v2.1.0+)',
68+
'toolbar.allowedTools': '允许的工具',
69+
'toolbar.allowedTools.tooltip': '配置斜杠命令执行时可使用的工具',
70+
'toolbar.allowedTools.default': '默认',
71+
'toolbar.allowedTools.resetToDefault': '重置为默认',
6872

6973
// Toolbar hooks configuration dropdown
7074
'hooks.title': 'Hooks',
Collapse file

‎src/webview/src/i18n/translations/zh-TW.ts‎

Copy file name to clipboardExpand all lines: src/webview/src/i18n/translations/zh-TW.ts
+4Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ export const zhTWWebviewTranslations: WebviewTranslationKeys = {
6565
// Toolbar slash command options dropdown
6666
'toolbar.model.tooltip': '指定執行時使用的模型',
6767
'toolbar.contextFork.tooltip': '在隔離的子代理上下文中運行 (Claude Code v2.1.0+)',
68+
'toolbar.allowedTools': '允許的工具',
69+
'toolbar.allowedTools.tooltip': '配置斜線命令執行時可使用的工具',
70+
'toolbar.allowedTools.default': '預設',
71+
'toolbar.allowedTools.resetToDefault': '重設為預設',
6872

6973
// Toolbar hooks configuration dropdown
7074
'hooks.title': 'Hooks',

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.