Explorar el Código

Setting for number of terminal lines to return from commands

Matt Rubens hace 1 año
padre
commit
c8b8eff147

+ 5 - 0
.changeset/early-dodos-know.md

@@ -0,0 +1,5 @@
+---
+"roo-cline": patch
+---
+
+Add a setting to control the number of terminal output lines to pass to the model when executing commands

+ 1 - 0
README.md

@@ -20,6 +20,7 @@ A fork of Cline, an autonomous coding agent, with some additional experimental f
 - Per-tool MCP auto-approval
 - Enable/disable MCP servers
 - Configurable delay after auto-writes to allow diagnostics to detect potential problems
+- Control the number of terminal output lines to pass to the model when executing commands
 - Runs alongside the original Cline
 
 ## Disclaimer

+ 20 - 3
src/core/Cline.ts

@@ -721,9 +721,9 @@ export class Cline {
 			}
 		}
 
-		let result = ""
+		let lines: string[] = []
 		process.on("line", (line) => {
-			result += line + "\n"
+			lines.push(line)
 			if (!didContinue) {
 				sendCommandOutput(line)
 			} else {
@@ -731,6 +731,22 @@ export class Cline {
 			}
 		})
 
+		const getFormattedOutput = async () => {
+			const { terminalOutputLineLimit } = await this.providerRef.deref()?.getState() ?? {}
+			const limit = terminalOutputLineLimit ?? 0
+			
+			if (limit > 0 && lines.length > limit) {
+				const beforeLimit = Math.floor(limit * 0.2) // 20% of lines before
+				const afterLimit = limit - beforeLimit // remaining 80% after
+				return [
+					...lines.slice(0, beforeLimit),
+					`\n[...${lines.length - limit} lines omitted...]\n`,
+					...lines.slice(-afterLimit)
+				].join('\n')
+			}
+			return lines.join('\n')
+		}
+
 		let completed = false
 		process.once("completed", () => {
 			completed = true
@@ -749,7 +765,8 @@ export class Cline {
 		// grouping command_output messages despite any gaps anyways)
 		await delay(50)
 
-		result = result.trim()
+		const output = await getFormattedOutput()
+		const result = output.trim()
 
 		if (userFeedback) {
 			await this.say("user_feedback", userFeedback.text, userFeedback.images)

+ 10 - 0
src/core/webview/ClineProvider.ts

@@ -76,6 +76,7 @@ type GlobalStateKey =
 	| "fuzzyMatchThreshold"
 	| "preferredLanguage" // Language setting for Cline's communication
 	| "writeDelayMs"
+	| "terminalOutputLineLimit"
 
 export const GlobalFileNames = {
 	apiConversationHistory: "api_conversation_history.json",
@@ -642,6 +643,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 						await this.updateGlobalState("writeDelayMs", message.value)
 						await this.postStateToWebview()
 						break
+					case "terminalOutputLineLimit":
+						await this.updateGlobalState("terminalOutputLineLimit", message.value)
+						await this.postStateToWebview()
+						break
 					case "deleteMessage": {
 						const answer = await vscode.window.showInformationMessage(
 							"Are you sure you want to delete this message and all subsequent messages?",
@@ -1046,6 +1051,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			screenshotQuality,
 			preferredLanguage,
 			writeDelayMs,
+			terminalOutputLineLimit,
 		} = await this.getState()
 		
 		const allowedCommands = vscode.workspace
@@ -1075,6 +1081,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			screenshotQuality: screenshotQuality ?? 75,
 			preferredLanguage: preferredLanguage ?? 'English',
 			writeDelayMs: writeDelayMs ?? 1000,
+			terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
 		}
 	}
 
@@ -1174,6 +1181,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			preferredLanguage,
 			writeDelayMs,
 			screenshotQuality,
+			terminalOutputLineLimit,
 		] = await Promise.all([
 			this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
 			this.getGlobalState("apiModelId") as Promise<string | undefined>,
@@ -1218,6 +1226,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			this.getGlobalState("preferredLanguage") as Promise<string | undefined>,
 			this.getGlobalState("writeDelayMs") as Promise<number | undefined>,
 			this.getGlobalState("screenshotQuality") as Promise<number | undefined>,
+			this.getGlobalState("terminalOutputLineLimit") as Promise<number | undefined>,
 		])
 
 		let apiProvider: ApiProvider
@@ -1279,6 +1288,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
 			screenshotQuality: screenshotQuality ?? 75,
 			fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
 			writeDelayMs: writeDelayMs ?? 1000,
+			terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
 			preferredLanguage: preferredLanguage ?? (() => {
 				// Get VSCode's locale setting
 				const vscodeLang = vscode.env.language;

+ 1 - 0
src/shared/ExtensionMessage.ts

@@ -61,6 +61,7 @@ export interface ExtensionState {
 	fuzzyMatchThreshold?: number
 	preferredLanguage: string
 	writeDelayMs: number
+	terminalOutputLineLimit?: number
 }
 
 export interface ClineMessage {

+ 1 - 0
src/shared/WebviewMessage.ts

@@ -48,6 +48,7 @@ export interface WebviewMessage {
 		| "enhancedPrompt"
 		| "draggedImages"
 		| "deleteMessage"
+		| "terminalOutputLineLimit"
 	text?: string
 	disabled?: boolean
 	askResponse?: ClineAskResponse

+ 30 - 2
webview-ui/src/components/settings/SettingsView.tsx

@@ -46,6 +46,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
 		setWriteDelayMs,
 		screenshotQuality,
 		setScreenshotQuality,
+		terminalOutputLineLimit,
+		setTerminalOutputLineLimit,
 	} = useExtensionState()
 	const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
 	const [modelIdErrorMessage, setModelIdErrorMessage] = useState<string | undefined>(undefined)
@@ -76,6 +78,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
 			vscode.postMessage({ type: "preferredLanguage", text: preferredLanguage })
 			vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
 			vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
+			vscode.postMessage({ type: "terminalOutputLineLimit", value: terminalOutputLineLimit ?? 500 })
 			onDone()
 		}
 	}
@@ -210,6 +213,31 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
 					</p>
 				</div>
 
+				<div style={{ marginBottom: 5 }}>
+					<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
+						<span style={{ fontWeight: "500", minWidth: '150px' }}>Terminal output limit</span>
+						<input
+							type="range"
+							min="100"
+							max="5000"
+							step="100"
+							value={terminalOutputLineLimit ?? 500}
+							onChange={(e) => setTerminalOutputLineLimit(parseInt(e.target.value))}
+							style={{
+								flexGrow: 1,
+								accentColor: 'var(--vscode-button-background)',
+								height: '2px'
+							}}
+						/>
+						<span style={{ minWidth: '45px', textAlign: 'left' }}>
+							{terminalOutputLineLimit ?? 500}
+						</span>
+					</div>
+					<p style={{ fontSize: "12px", marginTop: "5px", color: "var(--vscode-descriptionForeground)" }}>
+						Maximum number of lines to include in terminal output when executing commands. When exceeded lines will be removed from the middle, saving tokens.
+					</p>
+				</div>
+
 				<div style={{ marginBottom: 5 }}>
 					<VSCodeCheckbox checked={diffEnabled} onChange={(e: any) => setDiffEnabled(e.target.checked)}>
 						<span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
@@ -431,7 +459,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
 					<div style={{ marginBottom: 10 }}>
 						<div style={{ marginBottom: 15 }}>
 							<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Browser Settings</h3>
-							<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport Size</label>
+							<label style={{ fontWeight: "500", display: "block", marginBottom: 5 }}>Viewport size</label>
 							<select
 								value={browserViewportSize}
 								onChange={(e) => setBrowserViewportSize(e.target.value)}
@@ -460,7 +488,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
 
 						<div style={{ marginBottom: 15 }}>
 							<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
-								<span style={{ fontWeight: "500", minWidth: '100px' }}>Screenshot Quality</span>
+								<span style={{ fontWeight: "500", minWidth: '100px' }}>Screenshot quality</span>
 								<input
 									type="range"
 									min="1"

+ 4 - 0
webview-ui/src/context/ExtensionStateContext.tsx

@@ -39,6 +39,8 @@ export interface ExtensionStateContextType extends ExtensionState {
 	setWriteDelayMs: (value: number) => void
 	screenshotQuality?: number
 	setScreenshotQuality: (value: number) => void
+	terminalOutputLineLimit?: number
+	setTerminalOutputLineLimit: (value: number) => void
 }
 
 export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
@@ -58,6 +60,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		writeDelayMs: 1000,
 		browserViewportSize: "900x600",
 		screenshotQuality: 75,
+		terminalOutputLineLimit: 500,
 	})
 	const [didHydrateState, setDidHydrateState] = useState(false)
 	const [showWelcome, setShowWelcome] = useState(false)
@@ -176,6 +179,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
 		setPreferredLanguage: (value) => setState((prevState) => ({ ...prevState, preferredLanguage: value })),
 		setWriteDelayMs: (value) => setState((prevState) => ({ ...prevState, writeDelayMs: value })),
 		setScreenshotQuality: (value) => setState((prevState) => ({ ...prevState, screenshotQuality: value })),
+		setTerminalOutputLineLimit: (value) => setState((prevState) => ({ ...prevState, terminalOutputLineLimit: value })),
 	}
 
 	return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>