Skip to content

Add the built-in tool run_command_in_terminal for AI to execute commands in the connected PowerShell session #398

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

daxian-dbw
Copy link
Member

@daxian-dbw daxian-dbw commented Jul 14, 2025

PR Summary

Allow AIShell to run command in the connected PowerShell session and collect all output and error.

  1. Add Invoke-AICommand cmdlet (alias: airun) to AIShell module. Commands sent from the sidecar AIShell will be executed through this command in the form of airun { <command> }. This command is designed to collect all output and error messages as they are displayed in the terminal, while preserving the streaming behavior as expected.

  2. Add RunCommand and PostResult messages to the protocol.

  3. Update the Channel class in AIShell module to support the OnRunCommand action. We already support posting command to the PowerShell's prompt, but it turns out not easy to make the command be accepted. On Windows, we have to call AcceptLine within an OnIdle event handler and it also requires changes to PSReadLine.

    • AcceptLine only set a flag in PSReadLine to indicate the line was accepted. The flag is checked in InputLoop, however, when PSReadLine is waiting for input, it's blocked in the ReadKey call within InputLoop, so even if the flag is set, InputLoop won't be able to check the flag until after ReadKey call is returned.
    • I need to change PSReadLine a bit: after it finishes handling the OnIdle event, it checks if the _lineAccepted flag is set. If it's set, it means AcceptLine got called within the OnIdle handler, and it throws a LineAcceptedException to break out from ReadKey. I catch this exception in InputLoop to continue with the flag check.
    • However, a problem with this change is: the "readkey thread" is still blocked on Console.ReadKey when the command is returned to PowerShell to execute. On Windows, this could cause minor issues if the command also calls Console.ReadKey -- 2 threads calling Console.ReadKey in parallel, so it's uncertain which will get the next keystroke input. On macOS and Linux, the problem is way much bigger -- any subsequent writing to the terminal may be blocked, because on Unix platforms, reading cursor position will be blocked if another thread is calling Console.ReadKey.
    • So, this approach can only work on Windows. On macOS, we will have to depend on iTerm2, which has a Python API server that allows to send keystrokes to a tab using the Python API, so we could possibly use that for macOS. But Windows Terminal doesn't support that, and thus we will have to use the above approach to accept the command on Windows.
    • On macOS, if the Python API approach works fine, then we could even consider using it for the PostCode action.
  4. Add run_command_in_terminal and get_command_output tools and expose them to agents.

…collect all output and error.

1. Add 'Invoke-AICommand' cmdlet (alias: 'airun') to 'AIShell' module. Commands sent from the side-car AIShell will be executed through this command in the form of `airun { <command> }`. This command is designed to collect all output and error messages as they are displayed in the terminal, while preserving the streaming behavior as expected.

2. Add 'RunCommand' and 'PostResult' messages to the protocol.

3. Update the 'Channel' class in 'AIShell' module to support the 'OnRunCommand' action. We already support posting command to the PowerShell's prompt, but it turns out not easy to make the command be accepted. On Windows, we have to call 'AcceptLine' within an 'OnIdle' event handler and it also requires changes to PsReadLine.
   - 'AcceptLine' only set a flag in PSReadLine to indicate the line was accepted. The flag is checked in 'InputLoop', however, when PSReadLine is waiting for input, it's blocked in the 'ReadKey' call within 'InputLoop', so even if the flag is set, 'InputLoop' won't be able to check it until after 'ReadKey' call is returned.
   - I need to change PSReadLine a bit: After it finishes handling the 'OnIdle' event, it checks if the '_lineAccepted' flag is set. If it's set, it means 'AcceptLine' got called within the 'OnIdle' handler, and it throws a 'LineAcceptedException' to break out from 'ReadKey'. I catch this exception in 'InputLoop' to continue with the flag check.
   - However, a problem with this change is: the "readkey thread" is still blocked on 'Console.ReadKey' when the command is returned to PowerShell to execute. On Windows, this could cause minor issues if the command also calls 'Console.ReadKey' -- 2 threads calling 'Console.ReadKey' in parallel, so it's uncertian which will get the next keystroke input. On macOS and Linux, the problem is way much bigger -- any subsequent writing to the terminal may be blocked, because on non-Windows, reading cursor position will be blocked if another thread is calling 'Console.ReadKey'.
   - So, this approach can only work on Windows. On macOS, we depend on iTerm2, which has a Python API server and it's possible to send keystrokes to a tab using the Python API, so we could use that for macOS. But Windows Terminal doesn't support that, and thus we will have to use the above approach to accept the command on Windows.
   - On macOS, if the Python API approach works fine, then we could even consider using it for the 'PostCode' action.

4. Add '/code run <command>' to test out the 'RunCommand' functionality end-to-end.
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.

1 participant