Expose shell integration command knowledge to extensions

Shell integration in the terminal could enable APIs like being able to listen to commands that run and get their output/exit code.
Rough example:
interface Terminal {
  executeCommand(command): Thenable<TerminalCommandResult>;
}

I came here to request this, and here it is already.
Being in Backlog means it'll get on the work plan eventually?
Cheers, looking forward to this, it's the missing link for my 'debug last command line' extension!

@ClockworkV backlog means it's a valid item and it may be worked on in the future. I'm pretty sure this one will happen within the next year

Proposal:
registerQuickFixProvider(...options: ITerminalQuickFixOptions[]): void;

export interface ITerminalQuickFixOptions {
	commandLineMatcher: string | RegExp;
	outputMatcher?: ITerminalOutputMatcher;
	getQuickFixes: TerminalQuickFixCallback;
	exitStatus?: boolean;
}
export type TerminalQuickFixMatchResult = { commandLineMatch: RegExpMatchArray; outputMatch?: RegExpMatchArray | null };
export type TerminalQuickFixAction = IAction | ITerminalQuickFixCommandAction | ITerminalQuickFixOpenerAction;
export type TerminalQuickFixCallback = (matchResult: TerminalQuickFixMatchResult, command: ITerminalCommand) => TerminalQuickFixAction[] | TerminalQuickFixAction | undefined;

export interface ITerminalQuickFixCommandAction {
	type: 'command';
	command: string;
	addNewLine: boolean;
}
export interface ITerminalQuickFixOpenerAction {
	type: 'opener';
	uri: URI;
}
export interface ITerminalOutputMatcher {
	/**
	 * A string or regex to match against the unwrapped line. If this is a regex with the multiline
	 * flag, it will scan an amount of lines equal to `\n` instances in the regex + 1.
	 */
	lineMatcher: string | RegExp;
	/**
	 * Which side of the output to anchor the {@link offset} and {@link length} against.
	 */
	anchor: 'top' | 'bottom';
	/**
	 * How far from either the top or the bottom of the butter to start matching against.
	 */
	offset: number;
	/**
	 * The number of rows to match against, this should be as small as possible for performance
	 * reasons.
	 */
	length: number;
}

@alexr00 and @lszomoru how does this look to you, as you'll be adopting this for the git extension?

@meganrogge I've forked this off to https://github.com/microsoft/vscode/issues/162950 as I realized this issue is more about general listening to commands

Voting for this issue. The proposed interface would exactly solve a problem I have developing an extension.

Hi @meganrogge and @Tyriar - I've noticed this issue has been on an "iteration plan" in Nov and Oct of 2022. I'm wondering if you think it will be picked up any time soon? Thanks, really appreciate all the hard work!

@mmisiewicz I'll actually likely be looking into a proposed API for this in July

Here's an early proposal:
interface TerminalExecuteCommandEvent {
    terminal: Terminal;
    command: TerminalCommand;
}

interface TerminalCommand {
    commandLine: string;
    // string because we may not know how to convert the cwd reported via shell
    // integration to a URI
    cwd: Uri | string | undefined;
    result: Thenable<TerminalExecuteCommandResult>;
}

interface TerminalExecuteCommandResult {
    exitCode: number;
    output: string;
}

namespace window {
    export const onDidExecuteTerminalCommand: Event<TerminalExecuteCommandEvent>;

    export interface Terminal {
        // Throws when {@link TerminalState.supportsCommandExecution} is false?
        executeCommand(command: string): Thenable<TerminalCommand>;
    }

    export interface TerminalState {
        // Reading this too early would say false, should this start as undefined?
        supportsCommandExecution: boolean;
    }
}

It doesn't look like this will make it in the July release though.

Hi @Tyriar this looks great! It would definitely solve my needs (I only need exit code, not the command output). Would love to use this in August 🤞

Wow I hate to be person to ask this, but what are some scenarios for this, and will all commands executed result in the event being fired? I'm trying to reason about how this would fit with the PowerShell Extension (where we have our own Extension Terminal, that does support shell integration by virtue of copying the same setup script into shell for the ASCII control codes).
First thing that comes to mind is that maybe this would replace our use of workbench.action.terminal.runSelectedText (which is what we call for PowerShell.RunSelection, i.e. select some PowerShell code and run it in the active terminal). Would the command still show up in the active terminal as if it were pasted, or would it run "in the background?"

@andyleejordan provided shell integration is enabled, it would be a more reliable way of running a command as we have logic that will trigger ^C if VS Code is unsure if there's anything in the prompt (always on Windows but improvements are planned).
The biggest thing this gives that people have been after is exit code and output reporting, both of which are impossible currently.

Needs an early discussion in the API sync before we start impl.

Feedback:

Should we have a Shell type that is only active when shell integration is active?

Latest:
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalExecutedCommand {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;
		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;
		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;
		/**
		 * The exit code reported by the shell.
		 */
		exitCode: number | undefined;
		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 */
		output: string | undefined;
	}

	export namespace window {
		/**
		 * An event that is emitted when a terminal with shell integration activated has completed
		 * executing a command.
		 *
		 * Note that this event will not fire if the executed command exits the shell, listen to
		 * {@link onDidCloseTerminal} to handle that case.
		 */
		export const onDidExecuteTerminalCommand: Event<TerminalExecutedCommand>;
	}
}

Related on Stack Overflow:

How to use Terminal API to listen to all terminal commands/output in VS Code?
How to get the output via the VSCode terminal API as string? [duplicate]
How to read active terminal output in vscode extension? (should be closed as a duplicate)
How run a custom command from Integrated terminal in VSCode Extension? (should probably be closed as a duplicate)

@Tyriar Does this (should we) also account for cases where we can stream through as output gets printed?

@karrtikr seems reasonable to me to expose this either as a stream or as an async generator. I can see some use cases such as real time output parsing for test adapters or log parsers.

Gathering interest for Shell Script auto-indent: https://github.com/microsoft/vscode/issues/199003

@FlorentP42 please do not link to random issues to try get more attention on it. It's just spam that causes notifications and wastes time.

@karrtikr @JustinGrote the terminalDataWriteEvent API is essentially the streaming API you're talking about and it has some major performance issues as all data needs to be passed to the extension host (due to API implementation guidelines). We can't really do streaming without sending over escape sequences too which I was explicitly trying to avoid with this one in order to make it more generally useful.
@JustinGrote I think for tests specifically we have special handling of that via test extensions that integrate with the testing view.

@Tyriar: If you have better (less intrusive) ways to get interest on a given ticket, could you please share how you would proceed?

@FlorentP42 social media or friends? Just you shouldn't post off topic stuff to our bug tracker as it's distracting.

@FlorentP42 social media or friends? Just you shouldn't post off topic stuff to our bug tracker as it's distracting.

I do not use any social media, nor do I have friends interested in such topics X) So I assumed people interested in similar VSCode issues would have the most chances to also be interested in the one I posted... Yet it does not sem to be the case here, so I am sorry for the inconvenience.

Hi there,
Wanted to +1 this as it unlocks our ability to work on colorized build output which is the number 2 actively and top-voted issue for the CMake Tools Extension by giving the extension more knowledge of the terminal: https://github.com/microsoft/vscode-cmake-tools/issues/478. Is there any ability for the team to work this to unblock our users high requests for greater visibility of their build?

@sinemakinci1 can you elaborate on how this API would help you? Skimming the issue it seems like you would be after either allowing colors to be controlled in an output channel more finely (just textmate grammars atm?) or the existing Pseudoterminal extension API? This particular issue tracks the ability to be able to listen to when commands execute and get their output, it's nothing to do with adding colors to output?

@Tyriar Secondary to getting ANSI color support in the terminal, our back up request would be that we want to atleast have more of the terminal exposed so that we can potentially build this support ourselves for the extension as a workaround.

Can confirm that we do this with Pester Tests extension for our runs, I think @sinemakinci1 is conflating shell integration knowledge with ANSI which are just output characters.

@Tyriar @JustinGrote To fill in some context for @sinemakinci1's questions.
We have two separate asks that would separately help us solve some problems for ensuring that users who run build can get colorized output, either from the output channel or from the terminal.

Supporting ANSI and colorization in the output channel. This, based on your response, seems like it is not supporting any further than textmate grammers. Thanks for this context. We could likely either suggest users to install extensions that already do this, or provide our own.

Support the ask from this issue, the ability to execute a command and get its result. This will help us be able to get result information, both for whether the build succeeded or failed, as well as simply knowing when the build completed. More context on this can also be found here: https://github.com/microsoft/vscode-cmake-tools/issues/2783. We currently can't directly integrate with sending our build to the terminal, we only support it through users creating a tasks.json entry, because we'd need this API in order to get result information from the command.

Does this provide more understanding and context? Thanks!

@gcampbell-msft I'm not too familiar with the tasks extension API but could you contribute a shell task to get the colors working there?
If you want to know when users run a cmake command in any terminal then this request makes sense, bringing colors into the discussion is the main thing that was confusing me 🙂

@Tyriar Yes, I believe that has been our strategy in the past, we have a setting that users can enable that will use tasks.json build tasks so that they can get colorization. However, it looks like at some point this broke for us (we can investigate this), and so we're hoping that this API can be enabled so that we can, instead of relying on keeping a tasks.json entry up-to-date and correct, we can directly invoke a build (or any command) with the extension and automatically use the terminal so that users can get colorization with no effort of their own.

@Tyriar Ah, it seems like it's because we actually attempt to provide a cmake task type, and when users use this, colorization seems not to be there. However, if they switch to a shell task type, colorization is there.
This could be possibly a bug within our own task type, that we should investigate

@gcampbell-msft I'm guessing for a cmake task type you would need to wrap the call in a shell yourself something like bash -c "cmake arg1 arg2"? I'm not sure if you're able to do this via the API so we handle the shell stuff (eg. using the default shell, cmd on Windows, etc.). @meganrogge might have more info?

Is this task to run CMake configuration? or to run build? At least for build, check https://stackoverflow.com/q/9963783/11107541 and see if that makes any difference. Do you have a link to an issue ticket about this I can look at?

However, it looks like at some point this broke for us (we can investigate this)

what broke for you?

@starball5 The task is to run CMake build: https://github.com/microsoft/vscode-cmake-tools/issues/2783#issuecomment-1841819111.
@meganrogge It may not be that it broke, but that the way we are doing tasks right now removes the colors, I think this may be because we are doing our own cmake task type. Here is the relevant issue comment: https://github.com/microsoft/vscode-cmake-tools/issues/2783#issuecomment-1273581199. Thanks.

I have an extension using the onDidWriteTerminalData proposed API. However it is retrieving the output from the terminal of my extension window. I want to retrieve the output of application's terminal (the extension host) that is running my extension. Is this possible? The purpose of this is so that my extension can help debug the extension host's code by viewing the console.log statements.

@milestones95 onDidWriteTerminalData accesses the data send to vscode terminals, console.logs are not put in a vscode terminal but the devtools console. You'd need to use a different approach.

I'm planning on iterating more on this in March. Also considering the idea of stabilizing onDidWriteTerminalData again, instead of worrying about extensions using and disposing of the listeners for performance reasons, perhaps I could flip it around and prevent misuse by rate limiting the event for very busy terminals.

Also considering the idea of stabilizing onDidWriteTerminalData again, instead of worrying about extensions using and disposing of the listeners for performance reasons, perhaps I could flip it around and prevent misuse by rate limiting the event for very busy terminals.

Could this event be per-terminal rather than per-window?

@whitequark that's the easy fix, but unfortunately (fortunately?) a hardline approach is taken to extension API guidelines and since Terminal objects are public objects, the event must be global https://github.com/microsoft/vscode/wiki/Extension-API-guidelines

That is a little cursed but hard to argue with. A solution could be to batch the events so that no more than x events per terminal per second are emitted.

Current state of these proposed APIs:
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalExecutedCommand {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;
		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;
		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;
		/**
		 * The exit code reported by the shell.
		 */
		exitCode: number | undefined;
		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 */
		output: string | undefined;
	}

	export namespace window {
		/**
		 * An event that is emitted when a terminal with shell integration activated has completed
		 * executing a command.
		 *
		 * Note that this event will not fire if the executed command exits the shell, listen to
		 * {@link onDidCloseTerminal} to handle that case.
		 */
		export const onDidExecuteTerminalCommand: Event<TerminalExecutedCommand>;
	}
}

declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/78502
	//
	// This API is still proposed but we don't intent on promoting it to stable due to problems
	// around performance. See #145234 for a more likely API to get stabilized.

	export interface TerminalDataWriteEvent {
		/**
		 * The {@link Terminal} for which the data was written.
		 */
		readonly terminal: Terminal;
		/**
		 * The data being written.
		 */
		readonly data: string;
	}

	namespace window {
		/**
		 * An event which fires when the terminal's child pseudo-device is written to (the shell).
		 * In other words, this provides access to the raw data stream from the process running
		 * within the terminal, including VT sequences.
		 */
		export const onDidWriteTerminalData: Event<TerminalDataWriteEvent>;
	}
}

This would be perfect for my use case, running a command and getting the result!

This would not work for my use case at all, where I would like to detect a string (an URL with a port number) written by a launched command and connect to that port while the command is active. This is something I need for a custom (non-DAP) debugger for a non-software use case.

@whitequark an event/stream to get the data of a command while it's happening would work though right?

@whitequark an event/stream to get the data of a command while it's happening would work though right?

Yep, would work perfectly. Either line-buffered or just chunks of data as they arrive. I would also be OK with specifying a search pattern (a regex perhaps) so that I get an event every time it triggers, which may be a more performant option in some conditions.

@Tyriar Is there a way with this proposed API to know what command you are referring to? I would imagine that we'd need to be able to invoke the command, and then get the result.
Or, is it expected to invoke the command, then listen for event and do your own validation for what task it was?

Here's another shot at the API which I think satisfies all the requirements:

Listen to command execution finished without escape sequences
Listen to all data from start to end, with and without escape sequences
Allow enhanced and more reliable command execution over sendText

This round was made close to the tasks API as they share a lot of similarities. I'll bring it to the API sync on Tuesday, any feedback welcome of course!
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalCommandExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 *
		 * This will be updated on each new line
		 */
		output: string | undefined;

		/**
		 * Raw escape sequences. This may be batched heavily in order to avoid flooding the ext host
		 * connection.
		 */
		rawData: string;

		/**
		 * Fires when {@link rawData} changes.
		 */
		readonly onDidChangeRawData: Event<string>;
	}

	export interface Terminal {
		executeCommand(commandLine: string): Thenable<TerminalCommandExecution>;
	}

	export namespace window {
		export const onDidStartTerminalCommand: Event<TerminalCommandExecution>;
		export const onDidEndTerminalCommand: Event<TerminalCommandExecution>;
	}
}

@gcampbell-msft I think the above gives you what you're after.

		readonly onDidChangeRawData: Event<string>;

Does the raw data ever get truncated, or does it just grow unbounded if the process e.g. writes 1GB of text to terminal instantly?
If it gets truncated, will it be impossible to reliably detect a substring in the output?

@whitequark I think I'll have to limit it somehow, aggressive batching as needed and probably will truncate at some point where it's unreasonable (10s, 100s of mbs?).

will truncate at some point where it's unreasonable (10s, 100s of mbs?).

Repeatedly searching a substring in a 100s of MBs string is going to introduce a lot of CPU load. Probably enough to compete with the UI thread on lower-end hardware, in my experience doing similar things in the browser.

@whitequark oh I mean it's possible an extension will not get some of the data if the command is doing too much. Thinking more about this though, the event itself would probably give the addition, not rawData itself. We may also want to consider rawData being string[] to be nicer on memory and not keep creating giant strings.

@whitequark made some tweaks to better support your problem

@Tyriar Thank you. It would be extremely useful to know when rawData was truncated. I think this can be achieved by a single integer field indicating how many characters were emitted before the first string in the rawData array. This would start at 0 and increased every time it's truncated, making it possible for an extension to unambiguously determine whether it's misisng any output or not.
I believe that output will suffer the same performance issues as rawData, except that making a guarantee that it's updated on each line makes it a lot more severe as now you cannot batch more than a single line.

Are events guaranteed to fire on each character written to the terminal or not? If the answer is no, then the event probably should use a similar mechanism as the previous paragraph describes, for the same reason.

@whitequark good point, added:
		readonly onDidWriteRawData: Event<string>;
		// TODO: Should this be Event<string | { truncatedCount: number }>? Or `onDidTruncateRawData`?
		// How can an extension know when data was truncated for performance reasons?

I believe that output will suffer the same performance issues as rawData, except that making a guarantee that it's updated on each line makes it a lot more severe as now you cannot batch more than a single line.

Added:
/**
 * TODO: Is might be too expensive to maintain this, perhaps it should be Thenable<string> instead?
 */
output: string | undefined;

Here's the cleaned up proposal with some fixes:
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalCommandExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		output: Thenable<string>;

		/**
		 * Raw data with escape sequences. This may be batched heavily in order to avoid flooding the
		 * ext host connection.
		 */
		rawData: string[];
		// NOTE: `string` was considered but this could get very large and could add a lot of memory pressure

		/**
		 * Fires when {@link rawData} changes.
		 */
		readonly onDidWriteRawData: Event<string>;
		// NOTE: `onDidChangeRawData` was considered but it seems more intuitive to fire with a string instead of string[]

		/**
		 * Fires when {@link rawData} was truncated for performance reasons. This can happen when
		 * the process is writing a large amount of data to the terminal very quickly.
		 */
		readonly onDidTruncateRawData: Event<number>;
	}

	export interface Terminal {
		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalCommandExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param commandLine The command line to execute in the terminal.
		 *
		 * @example
		 * const command = term.executeCommand('echo "Hello world"');
		 * // On possible failure fallback to sendText
		 * command.exitCode
		 *   .catch(() => term.sendText('echo "Hello world"'));
		 */
		executeCommand(commandLine: string): TerminalCommandExecution;
	}

	export namespace window {
		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalCommand: Event<TerminalCommandExecution>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalCommand: Event<TerminalCommandExecution>;
	}
}

Feedback from standup was that people don't want to do escaping themselves, so maybe we have another signature like this:
// Would probably be an options object instead
executeCommand(executable: string, args: string, quoting?: ShellQuoting): TerminalCommandExecution;

@Tyriar How would the extension know what shell is currently active in the terminal if they wanted to manual quoting?

why is args a single string and not an array of strings

Yes agreed, I too missed that one.

Anyone still tracks this issue? Something unexpected happens when I use proposed API terminalExecuteCommandEvent.
I followed https://code.visualstudio.com/api/advanced-topics/using-proposed-api#using-a-proposed-api to use terminalExecuteCommandEvent but onDidExecuteTerminalCommand is not triggered. However, it is fine with terminalDataWriteEvent. That is wierd. Any help is appreciated, guys.

extension.ts
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {
	console.log('Congratulations, your extension "proposed-api-sample" is now active!');

	/**
	 * You can use proposed API here. `vscode.` should start auto complete
	 * Proposed API as defined in vscode.proposed.<proposalName>.d.ts.
	 */
	vscode.window.onDidExecuteTerminalCommand(({ terminal, commandLine, cwd, exitCode, output }) => {
		console.log('onDidExecuteTerminalCommand')
		console.log('name', terminal.name)
		console.log('commandLine', commandLine)
		console.log('cwd', cwd)
		console.log('exitCode', exitCode)
		console.log('output', output)
		console.log(`--------------------------------`)
	})

	vscode.window.onDidCloseTerminal((terminal) => {
		console.log('onDidCloseTerminal', terminal.name)
	})
	vscode.window.onDidOpenTerminal((terminal) => {
		console.log('onDidOpenTerminal', terminal.name)
	})

	context.subscriptions.push(vscode.commands.registerCommand('extension.helloWorld', () => {
		vscode.window.showInformationMessage('Hello World!');
	}));

	let terminal: vscode.Terminal
	context.subscriptions.push(vscode.commands.registerCommand("extension.sendText", async () => {
		const cmd = await vscode.window.showInputBox({ placeHolder: "Enter command to run in terminal" })
		if (!cmd) {
			return;
		}
		if (!terminal) {
			terminal = vscode.window.createTerminal({ name: 'test-proposal-api' })
		}
		terminal.show()
		terminal.sendText(cmd)
	}));
}

package.json
{
	"enabledApiProposals": [
		"terminalExecuteCommandEvent"
	],
	"name": "proposed-api-sample",
	"displayName": "proposed-api-sample",
	"description": "Sample showing how to use Proposed API",
	"version": "0.0.1",
	"publisher": "jim-vscode-samples",
	"private": true,
	"license": "MIT",
	"engines": {
		"vscode": "^1.74.0"
	},
	"categories": [
		"Other"
	],
	"activationEvents": [],
	"main": "./dist/extension.js",
	"contributes": {
		"commands": [
			{
				"command": "extension.helloWorld",
				"title": "Hello World"
			},
			{
				"command": "extension.sendText",
				"title": "Send Text to Terminal"
			}
		]
	},
	"scripts": {
		"compile": "tsc -p ./",
		"lint": "eslint \"src/**/*.ts\"",
		"watch": "webpack --watch",
		"compile-tests": "tsc -watch -p ./",
		"download-api": "dts dev",
		"postdownload-api": "dts main",
		"post111install": "npm run download-api",
		"package": "webpack --mode production --devtool hidden-source-map && vsce package"
	},
	"devDependencies": {
		"@types/node": "^16.18.34",
		"@typescript-eslint/eslint-plugin": "^6.7.0",
		"@typescript-eslint/parser": "^6.7.0",
		"@vscode/dts": "^0.4.0",
		"eslint": "^8.26.0",
		"node-loader": "^2.0.0",
		"ts-loader": "^9.5.1",
		"typescript": "^5.2.2",
		"webpack": "^5.90.3",
		"webpack-cli": "^5.1.4"
	}
}

I know why it happened. It turned out that shell integration had to be manually installed https://code.visualstudio.com/docs/terminal/shell-integration#_manual-installation, and changed powershell's execution policy to Unrestricted(Set-ExecutionPolicy Unrestricted with powershell run as administrator, but I don't think it's a good way).

If powershell's execution policy is restricted, script below added to $Profile causes an execution policy error, and onDidExecuteTerminalCommand will not be triggered.

So I will be seeking a way to use onDidExecuteTerminalCommand without changing execution policy.

Update:
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalCommandExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		output: Thenable<string>;

		/**
		 * Raw data with escape sequences. This may be batched heavily in order to avoid flooding the
		 * ext host connection.
		 */
		rawData: string[];
		// NOTE: `string` was considered but this could get very large and could add a lot of memory pressure

		/**
		 * Fires when {@link rawData} changes.
		 */
		readonly onDidWriteRawData: Event<string>;
		// NOTE: `onDidChangeRawData` was considered but it seems more intuitive to fire with a string instead of string[]

		/**
		 * Fires when {@link rawData} was truncated for performance reasons. This can happen when
		 * the process is writing a large amount of data to the terminal very quickly.
		 */
		readonly onDidTruncateRawData: Event<number>;
	}

	export interface TerminalCommandExecutionOptions {
		// TODO: These could be split into 2 separate interfaces, or 2 separate option interfaces?
		/**
		 * The command line to use.
		 */
		commandLine: string | {
			/**
			 * An executable to use which will be automatically escaped.
			 */
			executable: string;
			/**
			 * Arguments to launch the executable with which will be automatically escaped.
			 */
			args: string[];
		}
	}

	export interface Terminal {
		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalCommandExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param options The options to use for the command.
		 *
		 * @example
		 * const command = term.executeCommand({
		 *   commandLine: 'echo "Hello world"'
		 * });
		 * // Fallback to sendText on possible failure
		 * command.exitCode
		 *   .catch(() => term.sendText('echo "Hello world"'));
		 */
		executeCommand(options: TerminalCommandExecutionOptions): TerminalCommandExecution;
	}

	export namespace window {
		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalCommand: Event<TerminalCommandExecution>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalCommand: Event<TerminalCommandExecution>;
	}
}

Lots of feedback from API sync:

Terminal.executeCommand

Let's move executeCommand and all shell integration-related APIs into a Terminal.shell? or Terminal.shellIntegration? in order to separate the behavior that may not exist from the behavior that will always exist.

TerminalState.isShellIntegrationActivated (#205058)

Instead of this we could have window.onDidActivateTerminalShellIntegration<TerminalShellIntegration>.

TerminalCommandExecution

Rename to TerminalShellExecution?
rawData

The onDidWriteRawData event still breaks the public event API rule. Turns out AsyncIterable is being used for the first time in the proposed LanguageModelChatResponse.stream. This is exactly what we want, resolves the event problems and we could get rid of the events in favor of this:interface TerminalCommandExecution {
	dataStream: AsyncIterable<string>;
}

Given the above AsyncIterable, we could omit rawData completely which avoids the possible memory problems. If we did this then it could be something like startStreaming in order to indicate that it's streaming from that point onwards, ie. you must start listening to the stream immediately after the command executes, and we would start streaming in the (js) task after the command has been executed.
For truncated data, we could move to an object inside the iterable. A concern here was always about creating objects on streamed data, but since we are controlling batching this would be as little as 1 object per data batch.interface TerminalCommandExecution {
	stream: AsyncIterable<{ data: string, truncatedCharacters: number }>;
}

output

For output, this could fundamentally change if we revisited exposing the buffer on Terminal. It would likely turn into a buffer range then and not include the data. Turns out this would also be very useful for Copilot for referencing commands in the terminal.
The extension host has a hard cap on the size of text files that it synchronizes to ~30-50mb. We could have a similar limit to terminal buffer tracking, such as only allowing x lines of scrollback to be tracked, up to a cap.
The pty host is on the remote machine and already has a mirrored version of xterm.js. If we could set up communication between the pty host and the extension host then this would be a great way to synchronize the buffer as it would avoid going over a remote connection twice.

Update after feedback:
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalShellExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		// output: Thenable<string>;
		// TODO: TBD based on terminal buffer exploration.

		/**
		 * A per-extension stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `stream` was called for the
		 * first time, ie. you must call `stream` immediately after the command is executed via
		 * {@link executeCommand} or {@link onDidStartTerminalCommand}`to not miss any data.
		 */
		dataStream: AsyncIterator<TerminalShellExecutionData>;
	}

	export interface TerminalShellExecutionData {
		/**
		 * The data that was written to the terminal.
		 */
		data: string;

		/**
		 * The number of characters that were truncated. This can happen when the process writes a
		 * large amount of data very quickly. If this is non-zero, the data will be the empty
		 * string.
		 */
		truncatedCount: number;
	}

	export interface TerminalShellExecutionOptions {
		// TODO: These could be split into 2 separate interfaces, or 2 separate option interfaces?
		/**
		 * The command line to use.
		 */
		commandLine: string | {
			/**
			 * An executable to use which will be automatically escaped.
			 */
			executable: string;
			/**
			 * Arguments to launch the executable with which will be automatically escaped.
			 */
			args: string[];
		}
	}

	export interface Terminal {
		shellIntegration?: TerminalShellIntegration;
	}

	export interface TerminalShellIntegration {
		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param options The options to use for the command.
		 *
		 * @example
		 * const command = term.executeCommand({
		 *   commandLine: 'echo "Hello world"'
		 * });
		 * // Fallback to sendText on possible failure
		 * command.exitCode
		 *   .catch(() => term.sendText('echo "Hello world"'));
		 */
		executeCommand(options: TerminalShellExecutionOptions): TerminalShellExecution;
	}

	export interface TerminalShellIntegrationEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		terminal: Terminal;
		/**
		 * The shell integration object.
		 */
		shellIntegration: TerminalShellIntegration;
	}

	export namespace window {
		/**
		 * Fires when shell integration activates in a terminal
		 */
		export const onDidActivateTerminalShellIntegration: Event<TerminalShellIntegrationEvent>;

		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalCommand: Event<TerminalShellExecution>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalCommand: Event<TerminalShellExecution>;
	}
}

Rough draft of what a terminal buffer API could look like: https://github.com/microsoft/vscode/issues/207504#issuecomment-1992449065

@Tyriar what happened to the discussion about automatic shell quoting? Does the string signature of commandLine mean no automatic shell quoting, and the object signature mean yes automatic shell quoting?

@starball5 yep, I don't think we need shell quoting. You can pass the command line directly, or let VS Code generate the command line (with quoting) based on the executable type (eg. pwsh arg quoting is a little different to other shells).

Update (from tyriar/apis):
declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalShellExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and the arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The current working directory that was reported by the shell. This will be a {@link Uri}
		 * if the string reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * The output of the command when it has finished executing. This is the plain text shown in
		 * the terminal buffer and does not include raw escape sequences. Depending on the shell
		 * setup, this may include the command line as part of the output.
		 *
		 * *Note* This will be rejected if the terminal is determined to not have shell integration
		 * activated.
		 */
		// output: Thenable<string>;
		// TODO: TBD based on terminal buffer exploration.

		/**
		 * A per-extension stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `stream` was called for the
		 * first time, ie. you must call `dataStream` immediately after the command is executed via
		 * {@link executeCommand} or {@link onDidStartTerminalShellExecution} to not miss any data.
		 *
		 * @example
		 * // Log all data written to the terminal for a command
		 * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' });
		 * for await (const e of command.dataStream) {
		 *   console.log(e.data);
		 *   if (e.truncatedCount) {
		 *     console.warn(`Data was truncated by ${e.truncatedCount} characters`);
		 *   }
		 * }
		 */
		dataStream: AsyncIterator<TerminalShellExecutionData>;
	}

	export interface TerminalShellExecutionData {
		/**
		 * The data that was written to the terminal.
		 */
		data: string;

		/**
		 * The number of characters that were truncated. This can happen when the process writes a
		 * large amount of data very quickly. If this is non-zero, the data will be the empty
		 * string.
		 */
		truncatedCount: number;
	}

	export interface TerminalShellExecutionOptions {
		// TODO: These could be split into 2 separate interfaces, or 2 separate options interfaces?
		/**
		 * The command line to use.
		 */
		commandLine: string | {
			/**
			 * An executable to use.
			 */
			executable: string;
			/**
			 * Arguments to launch the executable with which will be automatically escaped based on
			 * the executable type.
			 */
			args: string[];
		};
	}

	export interface Terminal {
		/**
		 * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered
		 * features for the terminal. This will always be undefined immediately after the terminal
		 * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified
		 * when shell integration is activated for a terminal.
		 */
		shellIntegration?: TerminalShellIntegration;
	}

	export interface TerminalShellIntegration {
		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param options The options to use for the command.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand({
		 *       commandLine: 'echo "Hello world"'
		 *     });
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({ commandLine });
		 *   command.exitCode.then(code => {
		 *     console.log(`Command exited with code ${code}`);
		 *   });
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished
		 * }
		 */
		executeCommand(options: TerminalShellExecutionOptions): TerminalShellExecution;
	}

	export interface TerminalShellIntegrationActivationEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		terminal: Terminal;
		/**
		 * The shell integration object.
		 */
		shellIntegration: TerminalShellIntegration;
	}

	export namespace window {
		// TODO: This could be onDidChange... and also report changes to features:
		//
		// ```ts
		// features: {
		// 	cwdReporting: boolean;
		// 	explicitCommandLineReporting: boolean;
		// };
		// ```
		//
		// Maybe extensions shouldn't be concerned about these features though?

		/**
		 * Fires when shell integration activates in a terminal.
		 */
		export const onDidActivateTerminalShellIntegration: Event<TerminalShellIntegrationActivationEvent>;

		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalShellExecution: Event<TerminalShellExecution>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalShellExecution: Event<TerminalShellExecution>;
	}
}

Feedback from API sync:

Overall good direction
Add 2 overloads for executeCommand and don't call the args options as they are not optional. This also allows overload-specific examples
Switch to onDidChangeTerminalShellIntegration to future proof us
TerminalShellExecution.output is no longer part of this, instead moved into https://github.com/microsoft/vscode/issues/207504
Will start implementing it!

Switch to onDidChangeTerminalShellIntegration to future proof us

I was thinking that this may also be useful now if "terminal.integrated.shellIntegration.enabled" is toggled.

cwdReporting: boolean;

I recall there has been a few cases when we're interested in cwd of a user, so it could potentially be a useful feature to include. I can look for such instances in the Python extension to back this.

I was thinking that this may also be useful now if "terminal.integrated.shellIntegration.enabled" is toggled.

terminal.integrated.shellIntegration.enabled doesn't necessarily mean shell integration will work or not, just whether VS Code tried to inject the script.

I recall there has been a few cases when we're interested in cwd of a user, so it could potentially be a useful feature to include. I can look for such instances in the Python extension to back this.

Good point, I'll think about adding a cwd property to

Update:

2 executeCommand signatures
Removed the TerminalShellExecutionData and possibility of truncated data, so it's now dataStream: AsyncIterator<string>. It looks like we'll likely implement the buffer API which will mean that truncation no longer needs to happen. dataStream will be the way to guarantee getting all data in the terminal since output may be trimmed.
onDidActivateTerminalShellIntegration -> onDidChangeTerminalShellIntegration
Added TerminalShellIntegration.cwd, when this value changes onDidChangeTerminalShellIntegration will fire

declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	export interface TerminalShellExecution {
		/**
		 * The {@link Terminal} the command was executed in.
		 */
		terminal: Terminal;

		/**
		 * The full command line that was executed, including both the command and arguments.
		 */
		commandLine: string | undefined;

		/**
		 * The working directory that was reported by the shell when this command executed. This
		 * will be a {@link Uri} if the string reported by the shell can reliably be mapped to the
		 * connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * The exit code reported by the shell.
		 */
		exitCode: Thenable<number | undefined>;

		/**
		 * A per-extension stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `stream` was called for the
		 * first time, ie. you must call `dataStream` immediately after the command is executed via
		 * {@link executeCommand} or {@link onDidStartTerminalShellExecution} to not miss any data.
		 *
		 * @example
		 * // Log all data written to the terminal for a command
		 * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' });
		 * for await (const data of command.dataStream) {
		 *   console.log(data);
		 * }
		 */
		dataStream: AsyncIterator<string>;
	}

	export interface Terminal {
		/**
		 * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered
		 * features for the terminal. This will always be undefined immediately after the terminal
		 * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified
		 * when shell integration is activated for a terminal.
		 */
		shellIntegration?: TerminalShellIntegration;
	}

	export interface TerminalShellIntegration {
		// TODO: Is this fine to share the TerminalShellIntegrationChangeEvent event?
		/**
		 * The current working directory of the terminal. This will be a {@link Uri} if the string
		 * reported by the shell can reliably be mapped to the connected machine.
		 */
		cwd: Uri | string | undefined;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param commandLine The command line to execute, this is the exact text that will be sent
		 * to the terminal.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand('echo "Hello world"');
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({ commandLine });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(commandLine: string): TerminalShellExecution;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param command A command to run.
		 * @param args Arguments to launch the executable with which will be automatically escaped
		 * based on the executable type.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand({
		 *       command: 'echo',
		 *       args: ['Hello world']
		 *     });
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({
		 *     command: 'echo',
		 *     args: ['Hello world']
		 *   });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(executable: string, args: string[]): TerminalShellExecution;
	}

	export interface TerminalShellIntegrationChangeEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		terminal: Terminal;
		/**
		 * The shell integration object.
		 */
		shellIntegration: TerminalShellIntegration;
	}

	export namespace window {
		/**
		 * Fires when shell integration activates or one of its properties changes in a terminal.
		 */
		export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>;

		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalShellExecution: Event<TerminalShellExecution>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalShellExecution: Event<TerminalShellExecution>;
	}
}

This sounds terminal specific:
export namespace window {
		/**
		 * Fires when shell integration activates or one of its properties changes in a terminal.
		 */
		export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>;
		...

Curious, why doesn't these live under the Terminal scope? As a consumer, I only care about a specific terminal, and once that is done, I can dispose the event rather than listening for changes to all terminals.

shellIntegration?: TerminalShellIntegration;

As per API guidelines, I would expect a producer object to be:
shellIntegration: TerminalShellIntegration | undefined;

Curious, why doesn't these live under the Terminal scope?

@karrtikr because of this; global objects must have global events.

As per API guidelines, I would expect a producer object to be:

Done!

TODOs added in https://github.com/microsoft/vscode/pull/209548

Latest:
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	// TODO: Add missing docs
	// TODO: Review and polish up all docs
	export interface TerminalShellExecution {
		/**
		 * The full command line that was executed, including both the command and arguments. The
		 * {@link TerminalShellExecutionCommandLineConfidence confidence} of this value depends on
		 * the specific shell's shell integration implementation. This value may become more
		 * accurate after {@link onDidEndTerminalShellExecution} is fired.
		 */
		readonly commandLine: TerminalShellExecutionCommandLine;

		/**
		 * The working directory that was reported by the shell when this command executed. This
		 * will be a {@link Uri} if the path reported by the shell can reliably be mapped to the
		 * connected machine. This requires the shell integration to support working directory
		 * reporting.
		 */
		readonly cwd: Uri | undefined;

		/**
		 * Creates a stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `stream` was called for the
		 * first time, ie. you must call `dataStream` immediately after the command is executed via
		 * {@link executeCommand} or {@link onDidStartTerminalShellExecution} to not miss any data.
		 *
		 * @example
		 * // Log all data written to the terminal for a command
		 * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' });
		 * const stream = command.readData();
		 * for await (const data of stream) {
		 *   console.log(data);
		 * }
		 */
		// TODO: read? "data" typically means Uint8Array. What's the encoding of the string? Usage here will typically be checking for substrings
		// TODO: dispose function?
		readData(): AsyncIterable<string>;
	}

	/**
	 * A command line that was executed in a terminal.
	 */
	export interface TerminalShellExecutionCommandLine {
		/**
		 * The full command line that was executed, including both the command and its arguments.
		 */
		value: string;

		/**
		 * Whether the command line value came from a trusted source and is therefore safe to
		 * execute without user additional confirmation, such as a notification that asks "Do you
		 * want to execute (command)?".
		 *
		 * This is false when the command line was reported explicitly by the shell integration
		 * script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence}),
		 * but did not include a nonce for verification.
		 */
		isTrusted: boolean;

		/**
		 * The confidence of the command line value which is determined by how the value was
		 * obtained. This depends upon the implementation of the shell integration script.
		 */
		confidence: TerminalShellExecutionCommandLineConfidence;
	}

	/**
	 * The confidence of a {@link TerminalShellExecutionCommandLine} value.
	 */
	enum TerminalShellExecutionCommandLineConfidence {
		/**
		 * The command line value confidence is low. This means that the value was read from the
		 * terminal buffer using markers reported by the shell integration script. Additionally one
		 * of the following conditions will be met:
		 *
		 * - The command started on the very left-most column which is unusual, or
		 * - The command is multi-line which is more difficult to accurately detect due to line
		 *   continuation characters and right prompts.
		 * - Command line markers were not reported by the shell integration script.
		 */
		Low = 0,

		/**
		 * The command line value confidence is medium. This means that the value was read from the
		 * terminal buffer using markers reported by the shell integration script. The command is
		 * single-line and does not start on the very left-most column (which is unusual).
		 */
		Medium = 1,

		/**
		 * The command line value confidence is high. This means that the value was explicitly send
		 * from the shell integration script.
		 */
		High = 2
	}

	export interface Terminal {
		/**
		 * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered
		 * features for the terminal. This will always be undefined immediately after the terminal
		 * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified
		 * when shell integration is activated for a terminal.
		 *
		 * Note that this object may remain undefined if shell integation never activates. For
		 * example Command Prompt does not support shell integration and a user's shell setup could
		 * conflict with the automatic shell integration activation.
		 */
		readonly shellIntegration: TerminalShellIntegration | undefined;
	}

	export interface TerminalShellIntegration {
		/**
		 * The current working directory of the terminal. This will be a {@link Uri} if the path
		 * reported by the shell can reliably be mapped to the connected machine.
		 */
		readonly cwd: Uri | undefined;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * @param commandLine The command line to execute, this is the exact text that will be sent
		 * to the terminal.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand('echo "Hello world"');
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({ commandLine });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(commandLine: string): TerminalShellExecution;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param command A command to run.
		 * @param args Arguments to launch the executable with which will be automatically escaped
		 * based on the executable type.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand({
		 *       command: 'echo',
		 *       args: ['Hello world']
		 *     });
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({
		 *     command: 'echo',
		 *     args: ['Hello world']
		 *   });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(executable: string, args: string[]): TerminalShellExecution;
	}

	export interface TerminalShellIntegrationChangeEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;
	}

	export interface TerminalShellExecutionStartEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;

		/**
		 * The terminal shell execution that has ended.
		 */
		readonly execution: TerminalShellExecution;
	}

	export interface TerminalShellExecutionEndEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;

		/**
		 * The terminal shell execution that has ended.
		 */
		readonly execution: TerminalShellExecution;

		/**
		 * The exit code reported by the shell. `undefined` means the shell did not report an exit
		 * code or the shell reported a command started before the command finished.
		 */
		readonly exitCode: number | undefined;
	}

	export namespace window {
		/**
		 * Fires when shell integration activates or one of its properties changes in a terminal.
		 */
		export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>;

		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent>;
	}
}

Very close to the final API! We will wait for some feedback from users before finalizing so moving back to proposed.
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

declare module 'vscode' {

	// https://github.com/microsoft/vscode/issues/145234

	/**
	 * A command that was executed in a terminal.
	 */
	export interface TerminalShellExecution {
		/**
		 * The command line that was executed. The {@link TerminalShellExecutionCommandLineConfidence confidence}
		 * of this value depends on the specific shell's shell integration implementation. This
		 * value may become more accurate after {@link window.onDidEndTerminalShellExecution} is
		 * fired.
		 *
		 * @example
		 * // Log the details of the command line on start and end
		 * window.onDidStartTerminalShellExecution(event => {
		 *   const commandLine = event.execution.commandLine;
		 *   console.log(`Command started\n${summarizeCommandLine(commandLine)}`);
		 * });
		 * window.onDidEndTerminalShellExecution(event => {
		 *   const commandLine = event.execution.commandLine;
		 *   console.log(`Command ended\n${summarizeCommandLine(commandLine)}`);
		 * });
		 * function summarizeCommandLine(commandLine: TerminalShellExecutionCommandLine) {
		 *   return [
		 *     `  Command line: ${command.ommandLine.value}`,
		 *     `  Confidence: ${command.ommandLine.confidence}`,
		 *     `  Trusted: ${command.ommandLine.isTrusted}
		 *   ].join('\n');
		 * }
		 */
		readonly commandLine: TerminalShellExecutionCommandLine;

		/**
		 * The working directory that was reported by the shell when this command executed. This
		 * {@link Uri} may represent a file on another machine (eg. ssh into another machine). This
		 * requires the shell integration to support working directory reporting.
		 */
		readonly cwd: Uri | undefined;

		/**
		 * Creates a stream of raw data (including escape sequences) that is written to the
		 * terminal. This will only include data that was written after `readData` was called for
		 * the first time, ie. you must call `readData` immediately after the command is executed
		 * via {@link TerminalShellIntegration.executeCommand} or
		 * {@link window.onDidStartTerminalShellExecution} to not miss any data.
		 *
		 * @example
		 * // Log all data written to the terminal for a command
		 * const command = term.shellIntegration.executeCommand({ commandLine: 'echo "Hello world"' });
		 * const stream = command.read();
		 * for await (const data of stream) {
		 *   console.log(data);
		 * }
		 */
		read(): AsyncIterable<string>;
	}

	/**
	 * A command line that was executed in a terminal.
	 */
	export interface TerminalShellExecutionCommandLine {
		/**
		 * The full command line that was executed, including both the command and its arguments.
		 */
		value: string;

		/**
		 * Whether the command line value came from a trusted source and is therefore safe to
		 * execute without user additional confirmation, such as a notification that asks "Do you
		 * want to execute (command)?". This verification is likely only needed if you are going to
		 * execute the command again.
		 *
		 * This is `true` only when the command line was reported explicitly by the shell
		 * integration script (ie. {@link TerminalShellExecutionCommandLineConfidence.High high confidence})
		 * and it used a nonce for verification.
		 */
		isTrusted: boolean;

		/**
		 * The confidence of the command line value which is determined by how the value was
		 * obtained. This depends upon the implementation of the shell integration script.
		 */
		confidence: TerminalShellExecutionCommandLineConfidence;
	}

	/**
	 * The confidence of a {@link TerminalShellExecutionCommandLine} value.
	 */
	enum TerminalShellExecutionCommandLineConfidence {
		/**
		 * The command line value confidence is low. This means that the value was read from the
		 * terminal buffer using markers reported by the shell integration script. Additionally one
		 * of the following conditions will be met:
		 *
		 * - The command started on the very left-most column which is unusual, or
		 * - The command is multi-line which is more difficult to accurately detect due to line
		 *   continuation characters and right prompts.
		 * - Command line markers were not reported by the shell integration script.
		 */
		Low = 0,

		/**
		 * The command line value confidence is medium. This means that the value was read from the
		 * terminal buffer using markers reported by the shell integration script. The command is
		 * single-line and does not start on the very left-most column (which is unusual).
		 */
		Medium = 1,

		/**
		 * The command line value confidence is high. This means that the value was explicitly sent
		 * from the shell integration script or the command was executed via the
		 * {@link TerminalShellIntegration.executeCommand} API.
		 */
		High = 2
	}

	export interface Terminal {
		/**
		 * An object that contains [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered
		 * features for the terminal. This will always be `undefined` immediately after the terminal
		 * is created. Listen to {@link window.onDidActivateTerminalShellIntegration} to be notified
		 * when shell integration is activated for a terminal.
		 *
		 * Note that this object may remain undefined if shell integation never activates. For
		 * example Command Prompt does not support shell integration and a user's shell setup could
		 * conflict with the automatic shell integration activation.
		 */
		readonly shellIntegration: TerminalShellIntegration | undefined;
	}

	/**
	 * [Shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)-powered capabilities owned by a terminal.
	 */
	export interface TerminalShellIntegration {
		/**
		 * The current working directory of the terminal. This {@link Uri} may represent a file on
		 * another machine (eg. ssh into another machine). This requires the shell integration to
		 * support working directory reporting.
		 */
		readonly cwd: Uri | undefined;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * @param commandLine The command line to execute, this is the exact text that will be sent
		 * to the terminal.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand('echo "Hello world"');
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({ commandLine });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(commandLine: string): TerminalShellExecution;

		/**
		 * Execute a command, sending ^C as necessary to interrupt any running command if needed.
		 *
		 * *Note* This is not guaranteed to work as [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration)
		 * must be activated. Check whether {@link TerminalShellExecution.exitCode} is rejected to
		 * verify whether it was successful.
		 *
		 * @param command A command to run.
		 * @param args Arguments to launch the executable with which will be automatically escaped
		 * based on the executable type.
		 *
		 * @example
		 * // Execute a command in a terminal immediately after being created
		 * const myTerm = window.createTerminal();
		 * window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
		 *   if (terminal === myTerm) {
		 *     const command = shellIntegration.executeCommand({
		 *       command: 'echo',
		 *       args: ['Hello world']
		 *     });
		 *     const code = await command.exitCode;
		 *     console.log(`Command exited with code ${code}`);
		 *   }
		 * }));
		 * // Fallback to sendText if there is no shell integration within 3 seconds of launching
		 * setTimeout(() => {
		 *   if (!myTerm.shellIntegration) {
		 *     myTerm.sendText('echo "Hello world"');
		 *     // Without shell integration, we can't know when the command has finished or what the
		 *     // exit code was.
		 *   }
		 * }, 3000);
		 *
		 * @example
		 * // Send command to terminal that has been alive for a while
		 * const commandLine = 'echo "Hello world"';
		 * if (term.shellIntegration) {
		 *   const command = term.shellIntegration.executeCommand({
		 *     command: 'echo',
		 *     args: ['Hello world']
		 *   });
		 *   const code = await command.exitCode;
		 *   console.log(`Command exited with code ${code}`);
		 * } else {
		 *   term.sendText(commandLine);
		 *   // Without shell integration, we can't know when the command has finished or what the
		 *   // exit code was.
		 * }
		 */
		executeCommand(executable: string, args: string[]): TerminalShellExecution;
	}

	export interface TerminalShellIntegrationChangeEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;
	}

	export interface TerminalShellExecutionStartEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;

		/**
		 * The terminal shell execution that has ended.
		 */
		readonly execution: TerminalShellExecution;
	}

	export interface TerminalShellExecutionEndEvent {
		/**
		 * The terminal that shell integration has been activated in.
		 */
		readonly terminal: Terminal;

		/**
		 * The shell integration object.
		 */
		readonly shellIntegration: TerminalShellIntegration;

		/**
		 * The terminal shell execution that has ended.
		 */
		readonly execution: TerminalShellExecution;

		/**
		 * The exit code reported by the shell. `undefined` means the shell did not report an exit
		 * code or the shell reported a command started before the command finished.
		 */
		readonly exitCode: number | undefined;
	}

	export namespace window {
		/**
		 * Fires when shell integration activates or one of its properties changes in a terminal.
		 */
		export const onDidChangeTerminalShellIntegration: Event<TerminalShellIntegrationChangeEvent>;

		/**
		 * This will be fired when a terminal command is started. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidStartTerminalShellExecution: Event<TerminalShellExecutionStartEvent>;

		/**
		 * This will be fired when a terminal command is ended. This event will fire only when
		 * [shell integration](https://code.visualstudio.com/docs/terminal/shell-integration) is
		 * activated for the terminal.
		 */
		export const onDidEndTerminalShellExecution: Event<TerminalShellExecutionEndEvent>;
	}
}

I've reviewed the API and it appears entirely sufficient for my goals (scanning the terminal output for a URI that an extension spawning the terminal will then connect to).

@Tyriar I hope to test this out soon to see if it meets our needs! Our needs are simplistic, we want to be able to run a command in the terminal and get the return code of that command. Reading the API, I think this can be done, but I plan to test it in the coming days.

@gcampbell-msft yep should be very easy for that by listening to window.onDidEndTerminalShellExecution :+1:

@Tyriar This would require invoking the command and then statically waiting for this handler, right?
Is there / was there consideration of an async method where we can directly invoke a command in the terminal and wait for it to complete?

@gcampbell-msft yes you can use Terminal.shellIntegration?.executeCommand to execute a command and track its progress. For just listening you would use window.onDid(Start|End)TerminalShellExecution.execution.(commandLine|exitCode)

@Tyriar Is this able to be tested through the enabledApiProposals yet? What is the key?

@Tyriar In order to create a Terminal, would I need to grab one that has already been created? Or should I be creating one myself?

@Tyriar I'm curious on your suggested way, with this API, to create your own terminal and then use the shellIntegration to run the command, since this is event driven rather than all async and await?

@Tyriar Also, I notice that there is no way to guarantee that this works because the shell integration could possibly be turned off by the user. Is there a way around this? Another way to integrate with the shell? Etc.

@gcampbell-msft there's an example in the jsdoc for executing a command immediately after creating a terminal with a fallback:
// Execute a command in a terminal immediately after being created
const myTerm = window.createTerminal();
window.onDidActivateTerminalShellIntegration(async ({ terminal, shellIntegration }) => {
  if (terminal === myTerm) {
    const command = shellIntegration.executeCommand('echo "Hello world"');
    const code = await command.exitCode;
    console.log(`Command exited with code ${code}`);
  }
}));
// Fallback to sendText if there is no shell integration within 3 seconds of launching
setTimeout(() => {
  if (!myTerm.shellIntegration) {
    myTerm.sendText('echo "Hello world"');
    // Without shell integration, we can't know when the command has finished or what the
    // exit code was.
  }
}, 3000);

You would also want to dispose of the window.onDidActivateTerminalShellIntegration listener when you're done with it.

@Tyriar Understood. That makes sense. My general feedback is that this is somewhat complex for someone who wants to invoke a command and simply get the exit code. It definitely makes it possible, by creating a variable outside of the scope and then updating it in the handlers, but in a perfect world we would be able to do this process in a simple async await pattern.

@gcampbell-msft you can wrap it on your end, there are too many things that can go wrong for us to expose some reliable method to always do this. For example you could assume shell integration is active, and if it doesn't work then just point the user at troubleshooting docs. If you do that (which I'd recommend) then it would be quite simple.

@Tyriar Got it, that makes sense. Is there a specific documentation on the VS Code site that has documentation on when shellIntegration won't be on, and what troubleshooting there is? Is it just this page? https://code.visualstudio.com/docs/terminal/shell-integration

@gcampbell-msft yes just that page. There's a bit of information about it at the bottom. It's hard to give general advice because they're are so many different setups

Hi @Tyriar, can you please confirm, if as part of this feature, allowing access to the active terminal selected text is going to be available / planned to work in the short term? (related issue https://github.com/microsoft/vscode/issues/188173).
One of the use cases could be for extensions using a command as part of terminal/context to access the selected text from the terminal to perform things like AI Suggestions, Output sharing, etc.
Thank you 🙏🏼

@degrammer selection access will be done as a part of the buffer access api, not this one. That's been but on hold for a while as I have some competing priorities and want to think about the API shape in the background a bit.
We definitely do want to finalize an api for selection eventually as GH copilot uses the current proposal, unfortunately to name it future proof we need to tie it in with the much more complex buffer proposal.

Moving to July for Python to adopt first and give feedback before finalizing.

Is there any way to receive a stream of raw data from a terminal that does not have shell integration? For example if I create a terminal with any command of my choice by setting shellPath to argv[0] and shellArgs to argv[1...].
Since there's only one command that could plausibly be run in that terminal it's not necessary to try and guess which command the output comes from. The current API doesn't seem to offer anything to cover this use case, and I think it would be too limiting for my purpose.

@whitequark no, this is only for when shell integration is enabled. There is the related proposal for buffer access which allows reading the buffer which would probably cover your use case. https://github.com/microsoft/vscode/issues/207504

@Tyriar Thank you, this really helps!

I have a quick question. What is the difference between this API and vscode.proposed.terminalExecuteCommandEvent.d.ts? My current use case is grabbing the output from an executed command, and I am using vscode.proposed.terminalExecuteCommandEvent.d.ts to do so. I suppose we can also use read() from vscode.proposed.terminalShellIntegration.d.ts to read the output stream but feel free to correct me. I wonder which one would you think have a higher chance for public release. Before these two APIs I remembered having to work around with the old vscode.proposed.terminalDataWriteEvent.d.ts, so I truly appreciate the current more stable approaches you have made available. Thanks!

@itstreefam terminalExecuteCommandEvent is old and will be removed, terminalDataWriteEvent is unlikely to get finalized and may be replaced eventually with improvements upon terminalShellIntegration. terminalShellIntegration should be finalized very soon, I just need a fix a few bugs with it.

Plan:

Fix the 2 bugs that have been found, these are impl-related, not API shape related https://github.com/microsoft/vscode/issues/221405, https://github.com/microsoft/vscode/issues/221399
Get final sign off from @jrieken
Finalize for August


hero attributes check for bond monitor

feel free to close this if you fix it another way

Whats changed here and what is refactoring? Can we have a PR for the changes?

removed all the refactoring
I like to do it in small chunks to make the code base a little cleaner over time :<


feat: populate generator field if edge function is from a config file

Related to https://github.com/netlify/pod-compute/issues/423
But I wonder if this solution makes sense. Can we make sure this represents autogenerated functions correctly?

Can we make sure this represents autogenerated functions correctly?

There are two ways to define a generated function:

Write a manifest.json file in .netlify/edge-functions
Use in-source configuration to define the function config options from inside the function file

I think your solution will only catch the first case. To catch both cases, I think we'll need to tell Edge Bundler where generated functions are located. This could be a generatedFunctionsPath property, similarly to what we have (or had?) in zip-it-and-ship-it.


Add XML string parsing support

I found there isn't support for parsing XML string. Could you please add support for it?
I don't want to download and then pass the stix.xml file to
STIXPackage.from_xml('stix.xml')

instead, I want to pass stix text(str) data directly to to_xml and process further.
Could you please show me a way, if it's already available? I am unable to finger it out.
Thanks.

Hi @MrAsimZahid thanks for the question. to_xml() can take "a file, file-like object, etree._Element, or etree._ElementTree instance" as input.
You can turn a string into a file-like object by putting it in a StringIO. Such as in this example from our tests:
from io import StringIO

def test_valid(self):
    valid = """
    <stix:STIX_Package xmlns:stix="http://stix.mitre.org/stix-1"
        version="1.2" id="example:Package-1">
    </stix:STIX_Package>
    """

    parser = EntityParser()
    package = parser.parse_xml(StringIO(valid))

Thank you @clenk. It worked perfectly.


User handle group lifetime

Can u setup an possibility like permission lifetime for users group lifetime?
E.g. "player123" is in the group "testers". The leader of "testers" wants that "player123" get's an temporary lifetime in the group.
I think about clan-members test phase, sub-benefits.
Hope it's easy to integrate ;]
Greetz,
Hannes

This is already possible.
pex user <user> group add <group> [world] [lifetime]

Sadly it doesn't work :/
I have all permissions. Tried /pex user yololetsplay group add tester "" 3600.
Using the sponge version of the plugin, is that the problem, like it's another command?
I get "Too many arguments!".
Greetz,
Hannes

PEX 2.0 doesn't currently have support for timed permissions.

Can u add this soon? ;]

Ahhhh!  Another person trying to get help without telling us their version, and me assuming 1.x was the question.

Well, using your newest build of https://ci.yawk.at/view/PermissionsEx/job/PermissionsEx/ : P
Sry, didn't knowed there are more then just for sponge.


fix(tooltip): decouple removal logic from change detection

Currently the logic in the tooltip that removes it from the DOM is run either if the trigger is destroyed or the exit animation has finished. The problem is that if the trigger is detached from change detection, but hasn't been destroyed, the exit animation will never run and the element won't be cleaned up. These changes switch to using CSS animations and manipulating the DOM node directly to trigger the animation.
Fixes #19365.

Caretaker note: these changes have the potential to break people's tests. We should determine whether to move forward, based on how many targets it breaks.

Looks like it needs a rebase to presubmit

I pushed one more change to help address the concerns from #15990 by binding the internal animationend event outside the NgZone. I'm also bumping it to a P2, because this has come up a few times while triaging issues.

FYI this has 24 failing targets, it would be possible to get in, but would take some effort

I'm guessing their tests would have to be updated in the same way as ours: to dispatch a fake animationend event instead of flushing the animations through Angular, or to switch to use test harnesses.

Please rebase

@andrewseguin I finally got this back into a mergeable state. It took some time, because all of the tooltip code was moved around as a part of the MDC implementation. I've also ported this fix over to the MDC-based tooltip.

It seems that there's something not being properly flushed when using the harness. There's a test that looks roughly like this:
  const tooltip1 = await getHarness(MatTooltipHarness.with({selector: 'tt1'}));
  await (await tooltip1.host()).hover();
  expect(await tooltip1.isOpen()).toEqual(true);
  await (await tooltip1.host()).mouseAway();
  const tooltip2 = await getHarness(MatTooltipHarness.with({selector: 'tt2'}));
  await (await tooltip2.host()).hover();
  expect(await tooltip2.isOpen()).toEqual(true);
  await (await tooltip2.host()).mouseAway();

For the second tooltip the isOpen check fails, if I comment out the tooltip1 stuff then it passes

I've rebased and added some logic to the test harnesses to simulate the animationend events.


fix typo for building example: skipTest --> skipTests

 Thank you for your submission, we really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.You have signed the CLA already but the status is still pending? Let us recheck it.


Update sbt-wartremover to 3.1.6

About this PR
📦 Updates org.wartremover:sbt-wartremover from 2.4.13 to 3.1.6 ⚠
📜 GitHub Release Notes - Version Diff
Usage
✅ Please merge!
I'll automatically update this PR to resolve conflicts as long as you don't change it yourself.
If you'd like to skip this version, you can just close this PR. If you have any feedback, just mention me in the comments below.
Configure Scala Steward for your repository with a .scala-steward.conf file.
Have a fantastic day writing Scala!

⚙ Adjust future updates
Add this to your .scala-steward.conf file to ignore future updates of this dependency:
updates.ignore = [ { groupId = "org.wartremover", artifactId = "sbt-wartremover" } ]

Or, add this to slow down future updates of this dependency:
dependencyOverrides = [{
  pullRequests = { frequency = "30 days" },
  dependency = { groupId = "org.wartremover", artifactId = "sbt-wartremover" }
}]

labels: sbt-plugin-update, early-semver-major, semver-spec-major, commit-count:1

Superseded by #259.


Redirect unauthenticated user in 'Auth'

Shoudn't we add the abillty to redirect unauthenticated user right in the 'auth' somehow?
Like in Laravel:
https://github.com/laravel/docs/blob/5.7/authentication.md#redirecting-unauthenticated-users
Because it very often situation, when you just need to redirect user who try to get some routes in 'admin' group, but with default 'auth' behavior you can only get session error and need to create another middleware.
Sorry if I get something wrong about 'auth' and there is a better solution.

Closing since no answer from issue reporter.


Dropdowns always open down

Vanilla dropdowns open either up or down based on the space available:

Space below

No space below

This breaks with the theme applied:

Space below

No space below

On clicking the dropdown again, everything shifts upwards to make more space for it:

I guess that's a bot feature you're showing here, if you mind sending a public one that have dropdowns like that, it would be cool