mirror of
https://github.com/openai/codex.git
synced 2026-04-30 19:32:04 +03:00
feat: shell command explanation option (#173)
# Shell Command Explanation Option ## Description This PR adds an option to explain shell commands when the user is prompted to approve them (Fixes #110). When reviewing a shell command, users can now select "Explain this command" to get a detailed explanation of what the command does before deciding whether to approve or reject it. ## Changes - Added a new "EXPLAIN" option to the `ReviewDecision` enum - Updated the command review UI to include an "Explain this command (x)" option - Implemented the logic to send the command to the LLM for explanation using the same model as the agent - Added a display for the explanation in the command review UI - Updated all relevant components to pass the explanation through the component tree ## Benefits - Improves user understanding of shell commands before approving them - Reduces the risk of approving potentially harmful commands - Enhances the educational aspect of the tool, helping users learn about shell commands - Maintains the same workflow with minimal UI changes ## Testing - Manually tested the explanation feature with various shell commands - Verified that the explanation is displayed correctly in the UI - Confirmed that the user can still approve or reject the command after viewing the explanation ## Screenshots  ## Additional Notes The explanation is generated using the same model as the agent, ensuring consistency in the quality and style of explanations. --------- Signed-off-by: crazywolf132 <crazywolf132@gmail.com>
This commit is contained in:
@@ -15,11 +15,24 @@ const DEFAULT_DENY_MESSAGE =
|
||||
export function TerminalChatCommandReview({
|
||||
confirmationPrompt,
|
||||
onReviewCommand,
|
||||
explanation: propExplanation,
|
||||
}: {
|
||||
confirmationPrompt: React.ReactNode;
|
||||
onReviewCommand: (decision: ReviewDecision, customMessage?: string) => void;
|
||||
explanation?: string;
|
||||
}): React.ReactElement {
|
||||
const [mode, setMode] = React.useState<"select" | "input">("select");
|
||||
const [mode, setMode] = React.useState<"select" | "input" | "explanation">(
|
||||
"select",
|
||||
);
|
||||
const [explanation, setExplanation] = React.useState<string>("");
|
||||
|
||||
// If the component receives an explanation prop, update the state
|
||||
React.useEffect(() => {
|
||||
if (propExplanation) {
|
||||
setExplanation(propExplanation);
|
||||
setMode("explanation");
|
||||
}
|
||||
}, [propExplanation]);
|
||||
const [msg, setMsg] = React.useState<string>("");
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
@@ -72,6 +85,10 @@ export function TerminalChatCommandReview({
|
||||
}
|
||||
|
||||
opts.push(
|
||||
{
|
||||
label: "Explain this command (x)",
|
||||
value: ReviewDecision.EXPLAIN,
|
||||
},
|
||||
{
|
||||
label: "Edit or give feedback (e)",
|
||||
value: "edit",
|
||||
@@ -93,6 +110,8 @@ export function TerminalChatCommandReview({
|
||||
if (mode === "select") {
|
||||
if (input === "y") {
|
||||
onReviewCommand(ReviewDecision.YES);
|
||||
} else if (input === "x") {
|
||||
onReviewCommand(ReviewDecision.EXPLAIN);
|
||||
} else if (input === "e") {
|
||||
setMode("input");
|
||||
} else if (input === "n") {
|
||||
@@ -105,6 +124,11 @@ export function TerminalChatCommandReview({
|
||||
} else if (key.escape) {
|
||||
onReviewCommand(ReviewDecision.NO_EXIT);
|
||||
}
|
||||
} else if (mode === "explanation") {
|
||||
// When in explanation mode, any key returns to select mode
|
||||
if (key.return || key.escape || input === "x") {
|
||||
setMode("select");
|
||||
}
|
||||
} else {
|
||||
// text entry mode
|
||||
if (key.return) {
|
||||
@@ -125,7 +149,44 @@ export function TerminalChatCommandReview({
|
||||
<Box flexDirection="column" gap={1} borderStyle="round" marginTop={1}>
|
||||
{confirmationPrompt}
|
||||
<Box flexDirection="column" gap={1}>
|
||||
{mode === "select" ? (
|
||||
{mode === "explanation" ? (
|
||||
<>
|
||||
<Text bold color="yellow">
|
||||
Command Explanation:
|
||||
</Text>
|
||||
<Box paddingX={2} flexDirection="column" gap={1}>
|
||||
{explanation ? (
|
||||
<>
|
||||
{explanation.split("\n").map((line, i) => {
|
||||
// Check if it's an error message
|
||||
if (
|
||||
explanation.startsWith("Unable to generate explanation")
|
||||
) {
|
||||
return (
|
||||
<Text key={i} bold color="red">
|
||||
{line}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
// Apply different styling to headings (numbered items)
|
||||
else if (line.match(/^\d+\.\s+/)) {
|
||||
return (
|
||||
<Text key={i} bold color="cyan">
|
||||
{line}
|
||||
</Text>
|
||||
);
|
||||
} else {
|
||||
return <Text key={i}>{line}</Text>;
|
||||
}
|
||||
})}
|
||||
</>
|
||||
) : (
|
||||
<Text dimColor>Loading explanation...</Text>
|
||||
)}
|
||||
<Text dimColor>Press any key to return to options</Text>
|
||||
</Box>
|
||||
</>
|
||||
) : mode === "select" ? (
|
||||
<>
|
||||
<Text>Allow command?</Text>
|
||||
<Box paddingX={2} flexDirection="column" gap={1}>
|
||||
@@ -141,7 +202,7 @@ export function TerminalChatCommandReview({
|
||||
/>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
) : mode === "input" ? (
|
||||
<>
|
||||
<Text>Give the model feedback (↵ to submit):</Text>
|
||||
<Box borderStyle="round">
|
||||
@@ -165,7 +226,7 @@ export function TerminalChatCommandReview({
|
||||
</Box>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
) : null}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user