Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Copilot Shell Integration for Bash
# This script is loaded with bash --init-file when starting a terminal.

__copilot_bash_integration_main() {
[ -n "${COPILOT_BASH_INTEGRATION:-}" ] && return
COPILOT_BASH_INTEGRATION=1

bind 'set enable-bracketed-paste on' 2>/dev/null || true
__copilot_prompt_initialized=0

__copilot_precmd() {
__copilot_status=$?
if [ "${__copilot_prompt_initialized:-0}" = "1" ]; then
printf '\033]7775;C;%s\007' "$__copilot_status"
else
__copilot_prompt_initialized=1
fi
printf '\033]7775;A\007'
return "$__copilot_status"
}

__copilot_prompt_end() {
printf '\033]7775;B\007'
}

if [ -z "${__copilot_original_ps1:-}" ]; then
__copilot_original_ps1=${PS1:-'\$ '}
fi

case "$(declare -p PROMPT_COMMAND 2>/dev/null)" in
declare\ -a*|declare\ -A*)
PROMPT_COMMAND=(__copilot_precmd "${PROMPT_COMMAND[@]}")
;;
*)
if [ -n "${PROMPT_COMMAND:-}" ]; then
PROMPT_COMMAND="__copilot_precmd; ${PROMPT_COMMAND}"
else
PROMPT_COMMAND=__copilot_precmd
fi
;;
esac
PS1="${__copilot_original_ps1}"'\[$(__copilot_prompt_end)\]'
}

__copilot_bash_integration_main
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Copilot Shell Integration for Windows PowerShell
# This script is loaded when starting a Copilot terminal.

try {
$global:OutputEncoding = New-Object System.Text.UTF8Encoding $false
[Console]::InputEncoding = $global:OutputEncoding
[Console]::OutputEncoding = $global:OutputEncoding
} catch {
# Some hosts do not expose console encodings during startup.
}

if (-not $global:COPILOT_SHELL_INTEGRATION) {
$global:COPILOT_SHELL_INTEGRATION = $true
$global:__copilot_original_prompt = (Get-Command prompt -CommandType Function).ScriptBlock
$global:__copilot_last_history_id = -1

function global:prompt {
$lastSuccess = $?
$lastExitCode = $LASTEXITCODE
$esc = [char]27
$bel = [char]7
$lastHistoryEntry = Get-History -Count 1
$result = ""

if ($global:__copilot_last_history_id -ne -1) {
if ($lastHistoryEntry.Id -eq $global:__copilot_last_history_id) {
$result += "$esc]7775;C$bel"
} else {
if ($lastSuccess) {
$exitCode = 0
} elseif ($null -ne $lastExitCode -and $lastExitCode -ne 0) {
$exitCode = $lastExitCode
} else {
$exitCode = 1
}
$result += "$esc]7775;C;$exitCode$bel"
}
Comment thread
xinyi-gong marked this conversation as resolved.
}

$result += "$esc]7775;A$bel"

if ($global:__copilot_original_prompt) {
$result += $global:__copilot_original_prompt.Invoke()
} else {
$result += "PS $($executionContext.SessionState.Path.CurrentLocation)> "
}

$result += "$esc]7775;B$bel"
$global:__copilot_last_history_id = $lastHistoryEntry.Id
$result
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,25 @@
public interface IRunInTerminalTool {

/**
* Executes a command in the terminal.
* Executes a command in the terminal with an initial working directory.
*
* @param command The command to execute.
* @param isBackground Whether the command should run in the background.
* @param workingDirectory The terminal's initial working directory.
* @return A CompletableFuture that resolves to the output of the command.
*/
public CompletableFuture<String> executeCommand(String command, boolean isBackground);
public CompletableFuture<String> executeCommand(String command, boolean isBackground, String workingDirectory);

/**
* Prepares terminal properties for the command execution.
* Prepares terminal properties for the command execution with an initial working directory.
*
* @param runInBackground Whether the command should run in the background.
* @param executionId The unique identifier for the execution.
* @param workingDirectory The terminal's initial working directory.
* @return A map containing terminal properties.
*/
public Map<String, Object> prepareTerminalProperties(boolean runInBackground, String executionId);
public Map<String, Object> prepareTerminalProperties(boolean runInBackground, String executionId,
String workingDirectory);

/**
* Retrieves the output of a background command execution.
Expand All @@ -41,6 +44,11 @@ public interface IRunInTerminalTool {
*/
public StringBuilder getBackgroundCommandOutput(String executionId);

/**
* Cancels the foreground terminal command if one is currently running.
*/
public void cancelCurrentCommand();

/**
* Sets the terminal icon descriptor for the tool.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,62 @@
*/
public final class ShellIntegrationScripts {

/**
* The marker string output by the shell integration script after each command completes.
* Uses OSC (Operating System Command) escape sequence format: ESC ] 7775 ; marker BEL
* This is invisible in terminal output but can be detected programmatically.
*/
public static final String SHELL_MARKER = "\u001b]7775;C\u0007";
/** OSC namespace used by Copilot shell integration markers. */
public static final String OSC_NAMESPACE = "7775";

/** Marker emitted when the prompt starts. */
public static final String PROMPT_START_MARKER = "\u001b]" + OSC_NAMESPACE + ";A\u0007";

/** Marker emitted when the prompt ends. */
public static final String PROMPT_END_MARKER = "\u001b]" + OSC_NAMESPACE + ";B\u0007";

/** Marker emitted when a command finishes without an exit code. */
public static final String COMMAND_FINISH_MARKER = "\u001b]" + OSC_NAMESPACE + ";C\u0007";

/** Marker prefix emitted when a command finishes with an exit code. */
public static final String COMMAND_FINISH_MARKER_PREFIX = "\u001b]" + OSC_NAMESPACE + ";C;";

/** Pattern matching Copilot OSC markers, including markers that lost ESC/BEL during terminal processing. */
public static final String OSC_MARKER_PATTERN = buildOscMarkerPattern();

private static final String SCRIPTS_PATH = "scripts/";
private static final String SH_SCRIPT = "copilot-sh-integration.sh";
private static final String BASH_SCRIPT = "copilot-bash-integration.sh";
private static final String POWERSHELL_SCRIPT = "copilot-powershell-integration.ps1";

private ShellIntegrationScripts() {
// Utility class
}

private static String buildOscMarkerPattern() {
return "(?:\u001B)?\\]" + OSC_NAMESPACE + ";[ABC](?:;[-]?\\d+)?(?:\u0007|\u001B\\\\)?";
}

/**
* Gets the absolute file path to the Bash integration script.
*
* @return the absolute path to the Bash script, or null if not found
*/
public static String getBashScriptPath() {
return getScriptPath(BASH_SCRIPT);
}

/**
* Gets the absolute file path to the POSIX sh integration script.
* Gets the absolute file path to the PowerShell integration script.
*
* @return the absolute path to the sh script, or null if not found
* @return the absolute path to the PowerShell script, or null if not found
*/
public static String getShScriptPath() {
public static String getPowerShellScriptPath() {
return getScriptPath(POWERSHELL_SCRIPT);
}

private static String getScriptPath(String scriptName) {
try {
Bundle bundle = FrameworkUtil.getBundle(ShellIntegrationScripts.class);
if (bundle == null) {
return null;
}

URL scriptUrl = FileLocator.find(bundle, new Path(SCRIPTS_PATH + SH_SCRIPT));
URL scriptUrl = FileLocator.find(bundle, new Path(SCRIPTS_PATH + scriptName));
if (scriptUrl == null) {
return null;
}
Expand All @@ -60,7 +89,7 @@ public static String getShScriptPath() {
return scriptFile.getAbsolutePath();
}
} catch (IOException e) {
CopilotCore.LOGGER.error("Failed to locate shell integration script: " + SH_SCRIPT, e);
CopilotCore.LOGGER.error("Failed to locate shell integration script: " + scriptName, e);
}
return null;
}
Expand Down
Loading
Loading