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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -351,10 +351,29 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
}

// Initialize out-of-scope variables that need to recieve persistent global state values
this.getState().then(({ soundEnabled, terminalShellIntegrationTimeout }) => {
setSoundEnabled(soundEnabled ?? false)
Terminal.setShellIntegrationTimeout(terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT)
})
this.getState().then(
({
soundEnabled,
terminalShellIntegrationTimeout,
terminalCommandDelay,
terminalZshClearEolMark,
terminalZshOhMy,
terminalZshP10k,
terminalPowershellCounter,
terminalZdotdir,
}) => {
setSoundEnabled(soundEnabled ?? false)
Terminal.setShellIntegrationTimeout(
terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT,
)
Terminal.setCommandDelay(terminalCommandDelay ?? 0)
Terminal.setTerminalZshClearEolMark(terminalZshClearEolMark ?? true)
Terminal.setTerminalZshOhMy(terminalZshOhMy ?? false)
Terminal.setTerminalZshP10k(terminalZshP10k ?? false)
Terminal.setPowershellCounter(terminalPowershellCounter ?? false)
Terminal.setTerminalZdotdir(terminalZdotdir ?? false)
},
)

// Initialize tts enabled state
this.getState().then(({ ttsEnabled }) => {
Expand Down Expand Up @@ -1197,6 +1216,12 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
writeDelayMs,
terminalOutputLineLimit,
terminalShellIntegrationTimeout,
terminalCommandDelay,
terminalPowershellCounter,
terminalZshClearEolMark,
terminalZshOhMy,
terminalZshP10k,
terminalZdotdir,
fuzzyMatchThreshold,
mcpEnabled,
enableMcpServerCreation,
Expand Down Expand Up @@ -1264,6 +1289,12 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
writeDelayMs: writeDelayMs ?? 1000,
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
terminalShellIntegrationTimeout: terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT,
terminalCommandDelay: terminalCommandDelay ?? 0,
terminalPowershellCounter: terminalPowershellCounter ?? false,
terminalZshClearEolMark: terminalZshClearEolMark ?? true,
terminalZshOhMy: terminalZshOhMy ?? false,
terminalZshP10k: terminalZshP10k ?? false,
terminalZdotdir: terminalZdotdir ?? false,
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
mcpEnabled: mcpEnabled ?? true,
enableMcpServerCreation: enableMcpServerCreation ?? true,
Expand Down Expand Up @@ -1350,6 +1381,12 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
terminalOutputLineLimit: stateValues.terminalOutputLineLimit ?? 500,
terminalShellIntegrationTimeout:
stateValues.terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT,
terminalCommandDelay: stateValues.terminalCommandDelay ?? 0,
terminalPowershellCounter: stateValues.terminalPowershellCounter ?? false,
terminalZshClearEolMark: stateValues.terminalZshClearEolMark ?? true,
terminalZshOhMy: stateValues.terminalZshOhMy ?? false,
terminalZshP10k: stateValues.terminalZshP10k ?? false,
terminalZdotdir: stateValues.terminalZdotdir ?? false,
mode: stateValues.mode ?? defaultModeSlug,
language: stateValues.language ?? formatLanguage(vscode.env.language),
mcpEnabled: stateValues.mcpEnabled ?? true,
Expand Down
42 changes: 42 additions & 0 deletions src/core/webview/webviewMessageHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,48 @@ export const webviewMessageHandler = async (provider: ClineProvider, message: We
Terminal.setShellIntegrationTimeout(message.value)
}
break
case "terminalCommandDelay":
await updateGlobalState("terminalCommandDelay", message.value)
await provider.postStateToWebview()
if (message.value !== undefined) {
Terminal.setCommandDelay(message.value)
}
break
case "terminalPowershellCounter":
await updateGlobalState("terminalPowershellCounter", message.bool)
await provider.postStateToWebview()
if (message.bool !== undefined) {
Terminal.setPowershellCounter(message.bool)
}
break
case "terminalZshClearEolMark":
await updateGlobalState("terminalZshClearEolMark", message.bool)
await provider.postStateToWebview()
if (message.bool !== undefined) {
Terminal.setTerminalZshClearEolMark(message.bool)
}
break
case "terminalZshOhMy":
await updateGlobalState("terminalZshOhMy", message.bool)
await provider.postStateToWebview()
if (message.bool !== undefined) {
Terminal.setTerminalZshOhMy(message.bool)
}
break
case "terminalZshP10k":
await updateGlobalState("terminalZshP10k", message.bool)
await provider.postStateToWebview()
if (message.bool !== undefined) {
Terminal.setTerminalZshP10k(message.bool)
}
break
case "terminalZdotdir":
Copy link
Contributor

Choose a reason for hiding this comment

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

For consistency with other terminal Zsh-related cases, consider renaming 'terminalZdotdir' to 'terminalZshDotdir' (or another consistent variant) unless the current naming is intentional.

await updateGlobalState("terminalZdotdir", message.bool)
await provider.postStateToWebview()
if (message.bool !== undefined) {
Terminal.setTerminalZdotdir(message.bool)
}
break
case "mode":
await provider.handleModeSwitch(message.text as Mode)
break
Expand Down
6 changes: 6 additions & 0 deletions src/exports/roo-code.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,12 @@ type GlobalSettings = {
maxReadFileLine?: number | undefined
terminalOutputLineLimit?: number | undefined
terminalShellIntegrationTimeout?: number | undefined
terminalCommandDelay?: number | undefined
terminalPowershellCounter?: boolean | undefined
terminalZshClearEolMark?: boolean | undefined
terminalZshOhMy?: boolean | undefined
terminalZshP10k?: boolean | undefined
terminalZdotdir?: boolean | undefined
rateLimitSeconds?: number | undefined
diffEnabled?: boolean | undefined
fuzzyMatchThreshold?: number | undefined
Expand Down
6 changes: 6 additions & 0 deletions src/exports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,12 @@ type GlobalSettings = {
maxReadFileLine?: number | undefined
terminalOutputLineLimit?: number | undefined
terminalShellIntegrationTimeout?: number | undefined
terminalCommandDelay?: number | undefined
terminalPowershellCounter?: boolean | undefined
terminalZshClearEolMark?: boolean | undefined
terminalZshOhMy?: boolean | undefined
terminalZshP10k?: boolean | undefined
terminalZdotdir?: boolean | undefined
rateLimitSeconds?: number | undefined
diffEnabled?: boolean | undefined
fuzzyMatchThreshold?: number | undefined
Expand Down
114 changes: 114 additions & 0 deletions src/integrations/terminal/Terminal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,19 @@ import * as vscode from "vscode"
import pWaitFor from "p-wait-for"
import { ExitCodeDetails, mergePromise, TerminalProcess, TerminalProcessResultPromise } from "./TerminalProcess"
import { truncateOutput, applyRunLengthEncoding } from "../misc/extract-text"
// Import TerminalRegistry here to avoid circular dependencies
const { TerminalRegistry } = require("./TerminalRegistry")

export const TERMINAL_SHELL_INTEGRATION_TIMEOUT = 5000

export class Terminal {
private static shellIntegrationTimeout: number = TERMINAL_SHELL_INTEGRATION_TIMEOUT
private static commandDelay: number = 0
private static powershellCounter: boolean = false
private static terminalZshClearEolMark: boolean = true
private static terminalZshOhMy: boolean = false
private static terminalZshP10k: boolean = false
private static terminalZdotdir: boolean = false

public terminal: vscode.Terminal
public busy: boolean
Expand Down Expand Up @@ -180,10 +188,16 @@ export class Terminal {
// Wait for shell integration before executing the command
pWaitFor(() => this.terminal.shellIntegration !== undefined, { timeout: Terminal.shellIntegrationTimeout })
.then(() => {
// Clean up temporary directory if shell integration is available, zsh did its job:
TerminalRegistry.zshCleanupTmpDir(this.id)

// Run the command in the terminal
process.run(command)
})
.catch(() => {
console.log(`[Terminal ${this.id}] Shell integration not available. Command execution aborted.`)
// Clean up temporary directory if shell integration is not available
TerminalRegistry.zshCleanupTmpDir(this.id)
process.emit(
"no_shell_integration",
`Shell integration initialization sequence '\\x1b]633;A' was not received within ${Terminal.shellIntegrationTimeout / 1000}s. Shell integration has been disabled for this terminal instance. Increase the timeout in the settings if necessary.`,
Expand Down Expand Up @@ -256,7 +270,107 @@ export class Terminal {
Terminal.shellIntegrationTimeout = timeoutMs
}

public static getShellIntegrationTimeout(): number {
return Terminal.shellIntegrationTimeout
}

/**
* Sets the command delay in milliseconds
* @param delayMs The delay in milliseconds
*/
public static setCommandDelay(delayMs: number): void {
Terminal.commandDelay = delayMs
}

/**
* Gets the command delay in milliseconds
* @returns The command delay in milliseconds
*/
public static getCommandDelay(): number {
return Terminal.commandDelay
}

/**
* Sets whether to use the PowerShell counter workaround
* @param enabled Whether to enable the PowerShell counter workaround
*/
public static setPowershellCounter(enabled: boolean): void {
Terminal.powershellCounter = enabled
}

/**
* Gets whether to use the PowerShell counter workaround
* @returns Whether the PowerShell counter workaround is enabled
*/
public static getPowershellCounter(): boolean {
return Terminal.powershellCounter
}

/**
* Sets whether to clear the ZSH EOL mark
* @param enabled Whether to clear the ZSH EOL mark
*/
public static setTerminalZshClearEolMark(enabled: boolean): void {
Terminal.terminalZshClearEolMark = enabled
}

/**
* Gets whether to clear the ZSH EOL mark
* @returns Whether the ZSH EOL mark clearing is enabled
*/
public static getTerminalZshClearEolMark(): boolean {
return Terminal.terminalZshClearEolMark
}

/**
* Sets whether to enable Oh My Zsh shell integration
* @param enabled Whether to enable Oh My Zsh shell integration
*/
public static setTerminalZshOhMy(enabled: boolean): void {
Terminal.terminalZshOhMy = enabled
}

/**
* Gets whether Oh My Zsh shell integration is enabled
* @returns Whether Oh My Zsh shell integration is enabled
*/
public static getTerminalZshOhMy(): boolean {
return Terminal.terminalZshOhMy
}

/**
* Sets whether to enable Powerlevel10k shell integration
* @param enabled Whether to enable Powerlevel10k shell integration
*/
public static setTerminalZshP10k(enabled: boolean): void {
Terminal.terminalZshP10k = enabled
}

/**
* Gets whether Powerlevel10k shell integration is enabled
* @returns Whether Powerlevel10k shell integration is enabled
*/
public static getTerminalZshP10k(): boolean {
return Terminal.terminalZshP10k
}

public static compressTerminalOutput(input: string, lineLimit: number): string {
return truncateOutput(applyRunLengthEncoding(input), lineLimit)
}

/**
* Sets whether to enable ZDOTDIR handling for zsh
* @param enabled Whether to enable ZDOTDIR handling
*/
public static setTerminalZdotdir(enabled: boolean): void {
Terminal.terminalZdotdir = enabled
}

/**
* Gets whether ZDOTDIR handling is enabled
* @returns Whether ZDOTDIR handling is enabled
*/
public static getTerminalZdotdir(): boolean {
return Terminal.terminalZdotdir
}
}
32 changes: 24 additions & 8 deletions src/integrations/terminal/TerminalProcess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ export interface ExitCodeDetails {
coreDumpPossible?: boolean
}
import { Terminal } from "./Terminal"
import { TerminalRegistry } from "./TerminalRegistry"

export interface TerminalProcessEvents {
line: [line: string]
Expand Down Expand Up @@ -140,7 +139,10 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
this.once("no_shell_integration", () => {
if (this.terminalInfo) {
console.log(`no_shell_integration received for terminal ${this.terminalInfo.id}`)
TerminalRegistry.removeTerminal(this.terminalInfo.id)
this.emit("completed", "<no shell integration>")
this.terminalInfo.busy = false
this.terminalInfo.setActiveStream(undefined)
this.continue()
}
})
}
Expand Down Expand Up @@ -254,12 +256,16 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
// Emit no_shell_integration event with descriptive message
this.emit(
"no_shell_integration",
"VSCE shell integration stream did not start within 3 seconds. Terminal problem?",
`VSCE shell integration stream did not start within ${Terminal.getShellIntegrationTimeout() / 1000} seconds. Terminal problem?`,
)

// Reject with descriptive error
reject(new Error("VSCE shell integration stream did not start within 3 seconds."))
}, 3000)
reject(
new Error(
`VSCE shell integration stream did not start within ${Terminal.getShellIntegrationTimeout() / 1000} seconds.`,
),
)
}, Terminal.getShellIntegrationTimeout())

// Clean up timeout if stream becomes available
this.once("stream_available", (stream: AsyncIterable<string>) => {
Expand All @@ -284,9 +290,19 @@ export class TerminalProcess extends EventEmitter<TerminalProcessEvents> {
(defaultWindowsShellProfile === null ||
(defaultWindowsShellProfile as string)?.toLowerCase().includes("powershell"))
if (isPowerShell) {
terminal.shellIntegration.executeCommand(
`${command} ; "(Roo/PS Workaround: ${this.terminalInfo.cmdCounter++})" > $null; start-sleep -milliseconds 150`,
)
let commandToExecute = command

// Only add the PowerShell counter workaround if enabled
if (Terminal.getPowershellCounter()) {
commandToExecute += ` ; "(Roo/PS Workaround: ${this.terminalInfo.cmdCounter++})" > $null`
}

// Only add the sleep command if the command delay is greater than 0
if (Terminal.getCommandDelay() > 0) {
commandToExecute += ` ; start-sleep -milliseconds ${Terminal.getCommandDelay()}`
}

terminal.shellIntegration.executeCommand(commandToExecute)
} else {
terminal.shellIntegration.executeCommand(command)
}
Expand Down
Loading