Skip to content

Conversation

@catrielmuller
Copy link
Contributor

Context

Resolve #3815

Implementation

  • Command execution realtime updates (Output stream)
  • Action menu (Continue / Abort)
  • Synthetic ask command msg to workaround the inconsistencies of the extension messages

Screenshots

2025-11-21_11-52-07.mp4

How to Test

Execute

for i in {1..5}; do echo "HI" && sleep 2; done

To check if the stream of the command works

@changeset-bot
Copy link

changeset-bot bot commented Nov 21, 2025

🦋 Changeset detected

Latest commit: 5bc6c66

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@kilocode/cli Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot finished reviewing on behalf of catrielmuller November 21, 2025 15:05
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements real-time command execution monitoring with Continue/Abort controls for the CLI. It introduces a synthetic ask message workaround to enable immediate user interaction with running commands before they produce output.

Key changes:

  • Real-time command output streaming with synthetic command_output ask messages created immediately on command start
  • Continue/Abort action menu for running commands with 0ms delay (vs 500ms for other approvals)
  • Message deduplication logic to prevent conflicts between CLI-synthesized and backend-generated asks

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
cli/src/ui/messages/utils/messageCompletion.ts Removed command_output from non-rendering ask types to enable proper streaming display
cli/src/ui/messages/utils/tests/messageCompletion.test.ts Updated test to verify command_output completion requires non-partial state
cli/src/ui/messages/extension/ask/index.ts Added export for new AskCommandOutputMessage component
cli/src/ui/messages/extension/ask/AskCommandOutputMessage.tsx New component displaying running commands with real-time output streaming (max 500 chars)
cli/src/ui/messages/extension/tests/ExtensionMessageRow.test.tsx Removed test for say type command_output (now handled as ask type)
cli/src/ui/messages/extension/SayMessageRouter.tsx Routes command_output to null (handled in AskMessageRouter instead)
cli/src/ui/messages/extension/AskMessageRouter.tsx Routes command_output ask type to new AskCommandOutputMessage component
cli/src/ui/components/CommandInput.tsx Changed placeholder text from "Awaiting approval..." to "Actions available:"
cli/src/ui/components/ApprovalMenu.tsx Removed "[!] Action Required:" header and updated icon logic for approve actions
cli/src/state/hooks/useApprovalHandler.ts Implemented sendTerminalOperation for Continue/Abort with 0ms delay for command_output asks
cli/src/state/atoms/ui.ts Modified lastAskMessageAtom to allow partial command_output asks for real-time interaction
cli/src/state/atoms/extension.ts Added message reconciliation with synthetic ask preservation and deduplication logic
cli/src/state/atoms/effects.ts Implemented commandExecutionStatus handler creating synthetic asks and tracking output updates
cli/src/state/atoms/approval.ts Added Continue/Abort options for command_output asks, preserving selection during streaming
cli/src/state/atoms/tests/effects-command-output.test.ts New tests for synthetic ask creation on command start and completion handling
cli/src/state/atoms/tests/effects-command-output-duplicate.test.ts New tests for duplicate ask filtering and synthetic ask lifecycle
cli/src/services/approvalDecision.ts Added command_output case requiring manual approval (Continue/Abort decision)

Comment on lines +261 to +264
// EXCEPT for command_output messages where we want to preserve selection
// as output streams in (to allow users to abort long-running commands)
const shouldResetSelection = isNewMessage && message?.ask !== "command_output"
if (shouldResetSelection) {
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The variable isNewMessage is computed but only used in a condition that also checks message?.ask !== "command_output". The variable name and the new logic should be aligned. Consider renaming to shouldResetSelection and computing it as isNewMessage && message?.ask !== "command_output" directly, rather than computing it in two steps, to improve code clarity.

Copilot uses AI. Check for mistakes.
output: activeUpdate.output || "",
}),
partial: !activeUpdate.completed,
isAnswered: activeUpdate.completed || false,
Copy link

Copilot AI Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When activeUpdate.completed is true, the message's isAnswered is set to true (line 697). However, based on the comment in effects.ts (line 547: "Still needs user response"), a completed command should have partial: false but isAnswered: false until the user chooses to continue or abort. This logic may cause completed commands to not prompt for user action. Consider changing line 697 to isAnswered: false or isAnswered: msg.isAnswered to preserve the original answered state.

Suggested change
isAnswered: activeUpdate.completed || false,
isAnswered: msg.isAnswered, // kilocode_change

Copilot uses AI. Check for mistakes.
Copy link
Contributor

@pandemicsyn pandemicsyn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm - one question inline but its not so much review feedback but more "help the newbie understand this"


// Complete processing atomically
store.set(completeApprovalProcessingAtom)
} catch (error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok apologies in advanced - potentially dumb question, but sendTerminalOperation sets isAnswered=true and updates the chat message locally, and then calls sendMessage. If sendMessage throws, the catch only clears the processing atom, I think?

Does the chat entry stay isAnswered: true? If so, is that an issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should be test this but, in theory the extension continue pushing the state as isAnswered: false and clearing the completeApprovalProcessingAtom will be force to reupdate the ui showing the approval menu again.
There are multiples race conditions that this code handle on the CLI to deal with the extension message system.

@catrielmuller catrielmuller merged commit 2207b34 into main Nov 24, 2025
12 checks passed
@catrielmuller catrielmuller deleted the catrielmuller/cli-cancel-command-execution branch November 24, 2025 20:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CLI - There is not a way to cancel the command execution or continue

3 participants