mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-19 23:05:51 +00:00
更新大量 tsx 原始文件; 已经迁移 login panel; 部分 (#121)
* style(B1-1): 格式化 ink/buddy/cli/context/screens/tasks/services/keybindings/state (43 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 修复了 Box.tsx 和 ScrollBox.tsx 中无效的 global.d.ts import。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-2): 格式化 commands (79 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-3): 格式化 components/messages,permissions,mcp,sandbox,shell (104 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-4): 格式化 components/PromptInput,FeedbackSurvey,tasks,agents,skills,design-system,wizard (73 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-5): 格式化 components其余 + hooks + tools (232 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * style(B1-6): 格式化 main/entrypoints/utils/moreright (21 files) 纯格式化:移除分号、React Compiler import、import 多行展开。 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: 更新 README,新增 Run.ps1/TODO.md,删除 V6.md - README.md: 大幅重写,更详细版本历史和配置示例 - Run.ps1: 新增 Windows 启动脚本 - TODO.md: 新增包完成清单 - V6.md: 删除(架构重构规划已不适用) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: 修复以前的问题 * fix: 修复 login 面板的问题 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,43 +1,48 @@
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { Box, Text, useTheme } from '../../../ink.js';
|
||||
import { useKeybinding } from '../../../keybindings/useKeybinding.js';
|
||||
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js';
|
||||
import { type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS, logEvent } from '../../../services/analytics/index.js';
|
||||
import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js';
|
||||
import { getDestructiveCommandWarning } from '../../../tools/PowerShellTool/destructiveCommandWarning.js';
|
||||
import { PowerShellTool } from '../../../tools/PowerShellTool/PowerShellTool.js';
|
||||
import { isAllowlistedCommand } from '../../../tools/PowerShellTool/readOnlyValidation.js';
|
||||
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js';
|
||||
import { getCompoundCommandPrefixesStatic } from '../../../utils/powershell/staticPrefix.js';
|
||||
import { Select } from '../../CustomSelect/select.js';
|
||||
import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js';
|
||||
import { PermissionDecisionDebugInfo } from '../PermissionDecisionDebugInfo.js';
|
||||
import { PermissionDialog } from '../PermissionDialog.js';
|
||||
import { PermissionExplainerContent, usePermissionExplainerUI } from '../PermissionExplanation.js';
|
||||
import type { PermissionRequestProps } from '../PermissionRequest.js';
|
||||
import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js';
|
||||
import { useShellPermissionFeedback } from '../useShellPermissionFeedback.js';
|
||||
import { logUnaryPermissionEvent } from '../utils.js';
|
||||
import { powershellToolUseOptions } from './powershellToolUseOptions.js';
|
||||
export function PowerShellPermissionRequest(props: PermissionRequestProps): React.ReactNode {
|
||||
const {
|
||||
toolUseConfirm,
|
||||
toolUseContext,
|
||||
onDone,
|
||||
onReject,
|
||||
workerBadge
|
||||
} = props;
|
||||
const {
|
||||
command,
|
||||
description
|
||||
} = PowerShellTool.inputSchema.parse(toolUseConfirm.input);
|
||||
const [theme] = useTheme();
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { Box, Text, useTheme } from '../../../ink.js'
|
||||
import { useKeybinding } from '../../../keybindings/useKeybinding.js'
|
||||
import { getFeatureValue_CACHED_MAY_BE_STALE } from '../../../services/analytics/growthbook.js'
|
||||
import {
|
||||
type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
|
||||
logEvent,
|
||||
} from '../../../services/analytics/index.js'
|
||||
import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js'
|
||||
import { getDestructiveCommandWarning } from '../../../tools/PowerShellTool/destructiveCommandWarning.js'
|
||||
import { PowerShellTool } from '../../../tools/PowerShellTool/PowerShellTool.js'
|
||||
import { isAllowlistedCommand } from '../../../tools/PowerShellTool/readOnlyValidation.js'
|
||||
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'
|
||||
import { getCompoundCommandPrefixesStatic } from '../../../utils/powershell/staticPrefix.js'
|
||||
import { Select } from '../../CustomSelect/select.js'
|
||||
import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'
|
||||
import { PermissionDecisionDebugInfo } from '../PermissionDecisionDebugInfo.js'
|
||||
import { PermissionDialog } from '../PermissionDialog.js'
|
||||
import {
|
||||
PermissionExplainerContent,
|
||||
usePermissionExplainerUI,
|
||||
} from '../PermissionExplanation.js'
|
||||
import type { PermissionRequestProps } from '../PermissionRequest.js'
|
||||
import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'
|
||||
import { useShellPermissionFeedback } from '../useShellPermissionFeedback.js'
|
||||
import { logUnaryPermissionEvent } from '../utils.js'
|
||||
import { powershellToolUseOptions } from './powershellToolUseOptions.js'
|
||||
|
||||
export function PowerShellPermissionRequest(
|
||||
props: PermissionRequestProps,
|
||||
): React.ReactNode {
|
||||
const { toolUseConfirm, toolUseContext, onDone, onReject, workerBadge } =
|
||||
props
|
||||
|
||||
const { command, description } = PowerShellTool.inputSchema.parse(
|
||||
toolUseConfirm.input,
|
||||
)
|
||||
|
||||
const [theme] = useTheme()
|
||||
const explainerState = usePermissionExplainerUI({
|
||||
toolName: toolUseConfirm.tool.name,
|
||||
toolInput: toolUseConfirm.input,
|
||||
toolDescription: toolUseConfirm.description,
|
||||
messages: toolUseContext.messages
|
||||
});
|
||||
messages: toolUseContext.messages,
|
||||
})
|
||||
const {
|
||||
yesInputMode,
|
||||
noInputMode,
|
||||
@@ -50,15 +55,21 @@ export function PowerShellPermissionRequest(props: PermissionRequestProps): Reac
|
||||
focusedOption,
|
||||
handleInputModeToggle,
|
||||
handleReject,
|
||||
handleFocus
|
||||
handleFocus,
|
||||
} = useShellPermissionFeedback({
|
||||
toolUseConfirm,
|
||||
onDone,
|
||||
onReject,
|
||||
explainerVisible: explainerState.visible
|
||||
});
|
||||
const destructiveWarning = getFeatureValue_CACHED_MAY_BE_STALE('tengu_destructive_command_warning', false) ? getDestructiveCommandWarning(command) : null;
|
||||
const [showPermissionDebug, setShowPermissionDebug] = useState(false);
|
||||
explainerVisible: explainerState.visible,
|
||||
})
|
||||
const destructiveWarning = getFeatureValue_CACHED_MAY_BE_STALE(
|
||||
'tengu_destructive_command_warning',
|
||||
false,
|
||||
)
|
||||
? getDestructiveCommandWarning(command)
|
||||
: null
|
||||
|
||||
const [showPermissionDebug, setShowPermissionDebug] = useState(false)
|
||||
|
||||
// Editable prefix — compute static prefix locally (no LLM call).
|
||||
// Initialize synchronously to the raw command for single-line commands so
|
||||
@@ -69,166 +80,233 @@ export function PowerShellPermissionRequest(props: PermissionRequestProps): Reac
|
||||
// corpus shows 14 multiline rules, zero match twice). For compound commands,
|
||||
// computes a prefix per subcommand, excluding subcommands that are already
|
||||
// auto-allowed (read-only).
|
||||
const [editablePrefix, setEditablePrefix] = useState<string | undefined>(command.includes('\n') ? undefined : command);
|
||||
const hasUserEditedPrefix = useRef(false);
|
||||
const [editablePrefix, setEditablePrefix] = useState<string | undefined>(
|
||||
command.includes('\n') ? undefined : command,
|
||||
)
|
||||
const hasUserEditedPrefix = useRef(false)
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
let cancelled = false
|
||||
// Filter receives ParsedCommandElement — isAllowlistedCommand works from
|
||||
// element.name/nameType/args directly. isReadOnlyCommand(text) would need
|
||||
// to reparse (pwsh.exe spawn per subcommand) and returns false without the
|
||||
// full parsed AST, making the filter a no-op.
|
||||
getCompoundCommandPrefixesStatic(command, element => isAllowlistedCommand(element, element.text)).then(prefixes => {
|
||||
if (cancelled || hasUserEditedPrefix.current) return;
|
||||
if (prefixes.length > 0) {
|
||||
setEditablePrefix(`${prefixes[0]}:*`);
|
||||
}
|
||||
}).catch(() => {});
|
||||
getCompoundCommandPrefixesStatic(command, element =>
|
||||
isAllowlistedCommand(element, element.text),
|
||||
)
|
||||
.then(prefixes => {
|
||||
if (cancelled || hasUserEditedPrefix.current) return
|
||||
if (prefixes.length > 0) {
|
||||
setEditablePrefix(`${prefixes[0]}:*`)
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
cancelled = true
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [command]);
|
||||
}, [command])
|
||||
|
||||
const onEditablePrefixChange = useCallback((value: string) => {
|
||||
hasUserEditedPrefix.current = true;
|
||||
setEditablePrefix(value);
|
||||
}, []);
|
||||
const unaryEvent = useMemo<UnaryEvent>(() => ({
|
||||
completion_type: 'tool_use_single',
|
||||
language_name: 'none'
|
||||
}), []);
|
||||
usePermissionRequestLogging(toolUseConfirm, unaryEvent);
|
||||
const options = useMemo(() => powershellToolUseOptions({
|
||||
suggestions: toolUseConfirm.permissionResult.behavior === 'ask' ? toolUseConfirm.permissionResult.suggestions : undefined,
|
||||
onRejectFeedbackChange: setRejectFeedback,
|
||||
onAcceptFeedbackChange: setAcceptFeedback,
|
||||
yesInputMode,
|
||||
noInputMode,
|
||||
editablePrefix,
|
||||
onEditablePrefixChange
|
||||
}), [toolUseConfirm, yesInputMode, noInputMode, editablePrefix, onEditablePrefixChange]);
|
||||
hasUserEditedPrefix.current = true
|
||||
setEditablePrefix(value)
|
||||
}, [])
|
||||
|
||||
const unaryEvent = useMemo<UnaryEvent>(
|
||||
() => ({ completion_type: 'tool_use_single', language_name: 'none' }),
|
||||
[],
|
||||
)
|
||||
|
||||
usePermissionRequestLogging(toolUseConfirm, unaryEvent)
|
||||
|
||||
const options = useMemo(
|
||||
() =>
|
||||
powershellToolUseOptions({
|
||||
suggestions:
|
||||
toolUseConfirm.permissionResult.behavior === 'ask'
|
||||
? toolUseConfirm.permissionResult.suggestions
|
||||
: undefined,
|
||||
onRejectFeedbackChange: setRejectFeedback,
|
||||
onAcceptFeedbackChange: setAcceptFeedback,
|
||||
yesInputMode,
|
||||
noInputMode,
|
||||
editablePrefix,
|
||||
onEditablePrefixChange,
|
||||
}),
|
||||
[
|
||||
toolUseConfirm,
|
||||
yesInputMode,
|
||||
noInputMode,
|
||||
editablePrefix,
|
||||
onEditablePrefixChange,
|
||||
],
|
||||
)
|
||||
|
||||
// Toggle permission debug info with keybinding
|
||||
const handleToggleDebug = useCallback(() => {
|
||||
setShowPermissionDebug(prev => !prev);
|
||||
}, []);
|
||||
setShowPermissionDebug(prev => !prev)
|
||||
}, [])
|
||||
useKeybinding('permission:toggleDebug', handleToggleDebug, {
|
||||
context: 'Confirmation'
|
||||
});
|
||||
context: 'Confirmation',
|
||||
})
|
||||
|
||||
function onSelect(value: string) {
|
||||
// Map options to numeric values for analytics (strings not allowed in logEvent)
|
||||
const optionIndex: Record<string, number> = {
|
||||
yes: 1,
|
||||
'yes-apply-suggestions': 2,
|
||||
'yes-prefix-edited': 2,
|
||||
no: 3
|
||||
};
|
||||
no: 3,
|
||||
}
|
||||
logEvent('tengu_permission_request_option_selected', {
|
||||
option_index: optionIndex[value],
|
||||
explainer_visible: explainerState.visible
|
||||
});
|
||||
const toolNameForAnalytics = sanitizeToolNameForAnalytics(toolUseConfirm.tool.name) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS;
|
||||
explainer_visible: explainerState.visible,
|
||||
})
|
||||
|
||||
const toolNameForAnalytics = sanitizeToolNameForAnalytics(
|
||||
toolUseConfirm.tool.name,
|
||||
) as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS
|
||||
|
||||
if (value === 'yes-prefix-edited') {
|
||||
const trimmedPrefix = (editablePrefix ?? '').trim();
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept');
|
||||
const trimmedPrefix = (editablePrefix ?? '').trim()
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept')
|
||||
if (!trimmedPrefix) {
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, []);
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, [])
|
||||
} else {
|
||||
const prefixUpdates: PermissionUpdate[] = [{
|
||||
type: 'addRules',
|
||||
rules: [{
|
||||
toolName: PowerShellTool.name,
|
||||
ruleContent: trimmedPrefix
|
||||
}],
|
||||
behavior: 'allow',
|
||||
destination: 'localSettings'
|
||||
}];
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, prefixUpdates);
|
||||
const prefixUpdates: PermissionUpdate[] = [
|
||||
{
|
||||
type: 'addRules',
|
||||
rules: [
|
||||
{
|
||||
toolName: PowerShellTool.name,
|
||||
ruleContent: trimmedPrefix,
|
||||
},
|
||||
],
|
||||
behavior: 'allow',
|
||||
destination: 'localSettings',
|
||||
},
|
||||
]
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, prefixUpdates)
|
||||
}
|
||||
onDone();
|
||||
return;
|
||||
onDone()
|
||||
return
|
||||
}
|
||||
|
||||
switch (value) {
|
||||
case 'yes':
|
||||
{
|
||||
const trimmedFeedback = acceptFeedback.trim();
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept');
|
||||
// Log accept submission with feedback context
|
||||
logEvent('tengu_accept_submitted', {
|
||||
toolName: toolNameForAnalytics,
|
||||
isMcp: toolUseConfirm.tool.isMcp ?? false,
|
||||
has_instructions: !!trimmedFeedback,
|
||||
instructions_length: trimmedFeedback.length,
|
||||
entered_feedback_mode: yesFeedbackModeEntered
|
||||
});
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, [], trimmedFeedback || undefined);
|
||||
onDone();
|
||||
break;
|
||||
}
|
||||
case 'yes-apply-suggestions':
|
||||
{
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept');
|
||||
// Extract suggestions if present (works for both 'ask' and 'passthrough' behaviors)
|
||||
const permissionUpdates = 'suggestions' in toolUseConfirm.permissionResult ? toolUseConfirm.permissionResult.suggestions || [] : [];
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, permissionUpdates);
|
||||
onDone();
|
||||
break;
|
||||
}
|
||||
case 'no':
|
||||
{
|
||||
const trimmedFeedback = rejectFeedback.trim();
|
||||
case 'yes': {
|
||||
const trimmedFeedback = acceptFeedback.trim()
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept')
|
||||
// Log accept submission with feedback context
|
||||
logEvent('tengu_accept_submitted', {
|
||||
toolName: toolNameForAnalytics,
|
||||
isMcp: toolUseConfirm.tool.isMcp ?? false,
|
||||
has_instructions: !!trimmedFeedback,
|
||||
instructions_length: trimmedFeedback.length,
|
||||
entered_feedback_mode: yesFeedbackModeEntered,
|
||||
})
|
||||
toolUseConfirm.onAllow(
|
||||
toolUseConfirm.input,
|
||||
[],
|
||||
trimmedFeedback || undefined,
|
||||
)
|
||||
onDone()
|
||||
break
|
||||
}
|
||||
case 'yes-apply-suggestions': {
|
||||
logUnaryPermissionEvent('tool_use_single', toolUseConfirm, 'accept')
|
||||
// Extract suggestions if present (works for both 'ask' and 'passthrough' behaviors)
|
||||
const permissionUpdates =
|
||||
'suggestions' in toolUseConfirm.permissionResult
|
||||
? toolUseConfirm.permissionResult.suggestions || []
|
||||
: []
|
||||
toolUseConfirm.onAllow(toolUseConfirm.input, permissionUpdates)
|
||||
onDone()
|
||||
break
|
||||
}
|
||||
case 'no': {
|
||||
const trimmedFeedback = rejectFeedback.trim()
|
||||
|
||||
// Log reject submission with feedback context
|
||||
logEvent('tengu_reject_submitted', {
|
||||
toolName: toolNameForAnalytics,
|
||||
isMcp: toolUseConfirm.tool.isMcp ?? false,
|
||||
has_instructions: !!trimmedFeedback,
|
||||
instructions_length: trimmedFeedback.length,
|
||||
entered_feedback_mode: noFeedbackModeEntered
|
||||
});
|
||||
// Log reject submission with feedback context
|
||||
logEvent('tengu_reject_submitted', {
|
||||
toolName: toolNameForAnalytics,
|
||||
isMcp: toolUseConfirm.tool.isMcp ?? false,
|
||||
has_instructions: !!trimmedFeedback,
|
||||
instructions_length: trimmedFeedback.length,
|
||||
entered_feedback_mode: noFeedbackModeEntered,
|
||||
})
|
||||
|
||||
// Process rejection (with or without feedback)
|
||||
handleReject(trimmedFeedback || undefined);
|
||||
break;
|
||||
}
|
||||
// Process rejection (with or without feedback)
|
||||
handleReject(trimmedFeedback || undefined)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return <PermissionDialog workerBadge={workerBadge} title="PowerShell command">
|
||||
|
||||
return (
|
||||
<PermissionDialog workerBadge={workerBadge} title="PowerShell command">
|
||||
<Box flexDirection="column" paddingX={2} paddingY={1}>
|
||||
<Text dimColor={explainerState.visible}>
|
||||
{PowerShellTool.renderToolUseMessage({
|
||||
command,
|
||||
description
|
||||
}, {
|
||||
theme,
|
||||
verbose: true
|
||||
} // always show the full command
|
||||
)}
|
||||
{PowerShellTool.renderToolUseMessage(
|
||||
{ command, description },
|
||||
{ theme, verbose: true }, // always show the full command
|
||||
)}
|
||||
</Text>
|
||||
{!explainerState.visible && <Text dimColor>{toolUseConfirm.description}</Text>}
|
||||
<PermissionExplainerContent visible={explainerState.visible} promise={explainerState.promise} />
|
||||
{!explainerState.visible && (
|
||||
<Text dimColor>{toolUseConfirm.description}</Text>
|
||||
)}
|
||||
<PermissionExplainerContent
|
||||
visible={explainerState.visible}
|
||||
promise={explainerState.promise}
|
||||
/>
|
||||
</Box>
|
||||
{showPermissionDebug ? <>
|
||||
<PermissionDecisionDebugInfo permissionResult={toolUseConfirm.permissionResult} toolName="PowerShell" />
|
||||
{toolUseContext.options.debug && <Box justifyContent="flex-end" marginTop={1}>
|
||||
{showPermissionDebug ? (
|
||||
<>
|
||||
<PermissionDecisionDebugInfo
|
||||
permissionResult={toolUseConfirm.permissionResult}
|
||||
toolName="PowerShell"
|
||||
/>
|
||||
{toolUseContext.options.debug && (
|
||||
<Box justifyContent="flex-end" marginTop={1}>
|
||||
<Text dimColor>Ctrl-D to hide debug info</Text>
|
||||
</Box>}
|
||||
</> : <>
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Box flexDirection="column">
|
||||
<PermissionRuleExplanation permissionResult={toolUseConfirm.permissionResult} toolType="command" />
|
||||
{destructiveWarning && <Box marginBottom={1}>
|
||||
<PermissionRuleExplanation
|
||||
permissionResult={toolUseConfirm.permissionResult}
|
||||
toolType="command"
|
||||
/>
|
||||
{destructiveWarning && (
|
||||
<Box marginBottom={1}>
|
||||
<Text color="warning">{destructiveWarning}</Text>
|
||||
</Box>}
|
||||
</Box>
|
||||
)}
|
||||
<Text>Do you want to proceed?</Text>
|
||||
<Select options={options} inlineDescriptions onChange={onSelect} onCancel={() => handleReject()} onFocus={handleFocus} onInputModeToggle={handleInputModeToggle} />
|
||||
<Select
|
||||
options={options}
|
||||
inlineDescriptions
|
||||
onChange={onSelect}
|
||||
onCancel={() => handleReject()}
|
||||
onFocus={handleFocus}
|
||||
onInputModeToggle={handleInputModeToggle}
|
||||
/>
|
||||
</Box>
|
||||
<Box justifyContent="space-between" marginTop={1}>
|
||||
<Text dimColor>
|
||||
Esc to cancel
|
||||
{(focusedOption === 'yes' && !yesInputMode || focusedOption === 'no' && !noInputMode) && ' · Tab to amend'}
|
||||
{explainerState.enabled && ` · ctrl+e to ${explainerState.visible ? 'hide' : 'explain'}`}
|
||||
{((focusedOption === 'yes' && !yesInputMode) ||
|
||||
(focusedOption === 'no' && !noInputMode)) &&
|
||||
' · Tab to amend'}
|
||||
{explainerState.enabled &&
|
||||
` · ctrl+e to ${explainerState.visible ? 'hide' : 'explain'}`}
|
||||
</Text>
|
||||
{toolUseContext.options.debug && <Text dimColor>Ctrl+d to show debug info</Text>}
|
||||
{toolUseContext.options.debug && (
|
||||
<Text dimColor>Ctrl+d to show debug info</Text>
|
||||
)}
|
||||
</Box>
|
||||
</>}
|
||||
</PermissionDialog>;
|
||||
</>
|
||||
)}
|
||||
</PermissionDialog>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { POWERSHELL_TOOL_NAME } from '../../../tools/PowerShellTool/toolName.js';
|
||||
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js';
|
||||
import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js';
|
||||
import type { OptionWithDescription } from '../../CustomSelect/select.js';
|
||||
import { generateShellSuggestionsLabel } from '../shellPermissionHelpers.js';
|
||||
export type PowerShellToolUseOption = 'yes' | 'yes-apply-suggestions' | 'yes-prefix-edited' | 'no';
|
||||
import { POWERSHELL_TOOL_NAME } from '../../../tools/PowerShellTool/toolName.js'
|
||||
import type { PermissionUpdate } from '../../../utils/permissions/PermissionUpdateSchema.js'
|
||||
import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js'
|
||||
import type { OptionWithDescription } from '../../CustomSelect/select.js'
|
||||
import { generateShellSuggestionsLabel } from '../shellPermissionHelpers.js'
|
||||
|
||||
export type PowerShellToolUseOption =
|
||||
| 'yes'
|
||||
| 'yes-apply-suggestions'
|
||||
| 'yes-prefix-edited'
|
||||
| 'no'
|
||||
|
||||
export function powershellToolUseOptions({
|
||||
suggestions = [],
|
||||
onRejectFeedbackChange,
|
||||
@@ -11,17 +17,18 @@ export function powershellToolUseOptions({
|
||||
yesInputMode = false,
|
||||
noInputMode = false,
|
||||
editablePrefix,
|
||||
onEditablePrefixChange
|
||||
onEditablePrefixChange,
|
||||
}: {
|
||||
suggestions?: PermissionUpdate[];
|
||||
onRejectFeedbackChange: (value: string) => void;
|
||||
onAcceptFeedbackChange: (value: string) => void;
|
||||
yesInputMode?: boolean;
|
||||
noInputMode?: boolean;
|
||||
editablePrefix?: string;
|
||||
onEditablePrefixChange?: (value: string) => void;
|
||||
suggestions?: PermissionUpdate[]
|
||||
onRejectFeedbackChange: (value: string) => void
|
||||
onAcceptFeedbackChange: (value: string) => void
|
||||
yesInputMode?: boolean
|
||||
noInputMode?: boolean
|
||||
editablePrefix?: string
|
||||
onEditablePrefixChange?: (value: string) => void
|
||||
}): OptionWithDescription<PowerShellToolUseOption>[] {
|
||||
const options: OptionWithDescription<PowerShellToolUseOption>[] = [];
|
||||
const options: OptionWithDescription<PowerShellToolUseOption>[] = []
|
||||
|
||||
if (yesInputMode) {
|
||||
options.push({
|
||||
type: 'input',
|
||||
@@ -29,13 +36,13 @@ export function powershellToolUseOptions({
|
||||
value: 'yes',
|
||||
placeholder: 'and tell Claude what to do next',
|
||||
onChange: onAcceptFeedbackChange,
|
||||
allowEmptySubmitToCancel: true
|
||||
});
|
||||
allowEmptySubmitToCancel: true,
|
||||
})
|
||||
} else {
|
||||
options.push({
|
||||
label: 'Yes',
|
||||
value: 'yes'
|
||||
});
|
||||
value: 'yes',
|
||||
})
|
||||
}
|
||||
|
||||
// Note: No sandbox toggle for PowerShell - sandbox is not supported on Windows
|
||||
@@ -47,8 +54,17 @@ export function powershellToolUseOptions({
|
||||
// directory permissions or Read-tool rules, so fall back to the label when
|
||||
// those are present.
|
||||
if (shouldShowAlwaysAllowOptions() && suggestions.length > 0) {
|
||||
const hasNonPowerShellSuggestions = suggestions.some(s => s.type === 'addDirectories' || s.type === 'addRules' && s.rules?.some(r => r.toolName !== POWERSHELL_TOOL_NAME));
|
||||
if (editablePrefix !== undefined && onEditablePrefixChange && !hasNonPowerShellSuggestions) {
|
||||
const hasNonPowerShellSuggestions = suggestions.some(
|
||||
s =>
|
||||
s.type === 'addDirectories' ||
|
||||
(s.type === 'addRules' &&
|
||||
s.rules?.some(r => r.toolName !== POWERSHELL_TOOL_NAME)),
|
||||
)
|
||||
if (
|
||||
editablePrefix !== undefined &&
|
||||
onEditablePrefixChange &&
|
||||
!hasNonPowerShellSuggestions
|
||||
) {
|
||||
options.push({
|
||||
type: 'input',
|
||||
label: 'Yes, and don\u2019t ask again for',
|
||||
@@ -59,18 +75,22 @@ export function powershellToolUseOptions({
|
||||
allowEmptySubmitToCancel: true,
|
||||
showLabelWithValue: true,
|
||||
labelValueSeparator: ': ',
|
||||
resetCursorOnUpdate: true
|
||||
});
|
||||
resetCursorOnUpdate: true,
|
||||
})
|
||||
} else {
|
||||
const label = generateShellSuggestionsLabel(suggestions, POWERSHELL_TOOL_NAME);
|
||||
const label = generateShellSuggestionsLabel(
|
||||
suggestions,
|
||||
POWERSHELL_TOOL_NAME,
|
||||
)
|
||||
if (label) {
|
||||
options.push({
|
||||
label,
|
||||
value: 'yes-apply-suggestions'
|
||||
});
|
||||
value: 'yes-apply-suggestions',
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (noInputMode) {
|
||||
options.push({
|
||||
type: 'input',
|
||||
@@ -78,13 +98,14 @@ export function powershellToolUseOptions({
|
||||
value: 'no',
|
||||
placeholder: 'and tell Claude what to do differently',
|
||||
onChange: onRejectFeedbackChange,
|
||||
allowEmptySubmitToCancel: true
|
||||
});
|
||||
allowEmptySubmitToCancel: true,
|
||||
})
|
||||
} else {
|
||||
options.push({
|
||||
label: 'No',
|
||||
value: 'no'
|
||||
});
|
||||
value: 'no',
|
||||
})
|
||||
}
|
||||
return options;
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user