mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 12:21:13 -05:00
Merge pull request #253 from RooVetGit/cat_pager
Improvements to terminal output when executing commands
This commit is contained in:
5
.changeset/early-dodos-know.md
Normal file
5
.changeset/early-dodos-know.md
Normal file
@@ -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
|
||||||
@@ -20,6 +20,7 @@ A fork of Cline, an autonomous coding agent, with some additional experimental f
|
|||||||
- Per-tool MCP auto-approval
|
- Per-tool MCP auto-approval
|
||||||
- Enable/disable MCP servers
|
- Enable/disable MCP servers
|
||||||
- Configurable delay after auto-writes to allow diagnostics to detect potential problems
|
- 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
|
- Runs alongside the original Cline
|
||||||
|
|
||||||
## Disclaimer
|
## Disclaimer
|
||||||
|
|||||||
@@ -721,9 +721,9 @@ export class Cline {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = ""
|
let lines: string[] = []
|
||||||
process.on("line", (line) => {
|
process.on("line", (line) => {
|
||||||
result += line + "\n"
|
lines.push(line)
|
||||||
if (!didContinue) {
|
if (!didContinue) {
|
||||||
sendCommandOutput(line)
|
sendCommandOutput(line)
|
||||||
} else {
|
} 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
|
let completed = false
|
||||||
process.once("completed", () => {
|
process.once("completed", () => {
|
||||||
completed = true
|
completed = true
|
||||||
@@ -749,7 +765,8 @@ export class Cline {
|
|||||||
// grouping command_output messages despite any gaps anyways)
|
// grouping command_output messages despite any gaps anyways)
|
||||||
await delay(50)
|
await delay(50)
|
||||||
|
|
||||||
result = result.trim()
|
const output = await getFormattedOutput()
|
||||||
|
const result = output.trim()
|
||||||
|
|
||||||
if (userFeedback) {
|
if (userFeedback) {
|
||||||
await this.say("user_feedback", userFeedback.text, userFeedback.images)
|
await this.say("user_feedback", userFeedback.text, userFeedback.images)
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ type GlobalStateKey =
|
|||||||
| "fuzzyMatchThreshold"
|
| "fuzzyMatchThreshold"
|
||||||
| "preferredLanguage" // Language setting for Cline's communication
|
| "preferredLanguage" // Language setting for Cline's communication
|
||||||
| "writeDelayMs"
|
| "writeDelayMs"
|
||||||
|
| "terminalOutputLineLimit"
|
||||||
|
|
||||||
export const GlobalFileNames = {
|
export const GlobalFileNames = {
|
||||||
apiConversationHistory: "api_conversation_history.json",
|
apiConversationHistory: "api_conversation_history.json",
|
||||||
@@ -642,6 +643,10 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
await this.updateGlobalState("writeDelayMs", message.value)
|
await this.updateGlobalState("writeDelayMs", message.value)
|
||||||
await this.postStateToWebview()
|
await this.postStateToWebview()
|
||||||
break
|
break
|
||||||
|
case "terminalOutputLineLimit":
|
||||||
|
await this.updateGlobalState("terminalOutputLineLimit", message.value)
|
||||||
|
await this.postStateToWebview()
|
||||||
|
break
|
||||||
case "deleteMessage": {
|
case "deleteMessage": {
|
||||||
const answer = await vscode.window.showInformationMessage(
|
const answer = await vscode.window.showInformationMessage(
|
||||||
"Are you sure you want to delete this message and all subsequent messages?",
|
"Are you sure you want to delete this message and all subsequent messages?",
|
||||||
@@ -1046,6 +1051,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
screenshotQuality,
|
screenshotQuality,
|
||||||
preferredLanguage,
|
preferredLanguage,
|
||||||
writeDelayMs,
|
writeDelayMs,
|
||||||
|
terminalOutputLineLimit,
|
||||||
} = await this.getState()
|
} = await this.getState()
|
||||||
|
|
||||||
const allowedCommands = vscode.workspace
|
const allowedCommands = vscode.workspace
|
||||||
@@ -1075,6 +1081,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
screenshotQuality: screenshotQuality ?? 75,
|
screenshotQuality: screenshotQuality ?? 75,
|
||||||
preferredLanguage: preferredLanguage ?? 'English',
|
preferredLanguage: preferredLanguage ?? 'English',
|
||||||
writeDelayMs: writeDelayMs ?? 1000,
|
writeDelayMs: writeDelayMs ?? 1000,
|
||||||
|
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1174,6 +1181,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
preferredLanguage,
|
preferredLanguage,
|
||||||
writeDelayMs,
|
writeDelayMs,
|
||||||
screenshotQuality,
|
screenshotQuality,
|
||||||
|
terminalOutputLineLimit,
|
||||||
] = await Promise.all([
|
] = await Promise.all([
|
||||||
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
this.getGlobalState("apiProvider") as Promise<ApiProvider | undefined>,
|
||||||
this.getGlobalState("apiModelId") as Promise<string | 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("preferredLanguage") as Promise<string | undefined>,
|
||||||
this.getGlobalState("writeDelayMs") as Promise<number | undefined>,
|
this.getGlobalState("writeDelayMs") as Promise<number | undefined>,
|
||||||
this.getGlobalState("screenshotQuality") as Promise<number | undefined>,
|
this.getGlobalState("screenshotQuality") as Promise<number | undefined>,
|
||||||
|
this.getGlobalState("terminalOutputLineLimit") as Promise<number | undefined>,
|
||||||
])
|
])
|
||||||
|
|
||||||
let apiProvider: ApiProvider
|
let apiProvider: ApiProvider
|
||||||
@@ -1279,6 +1288,7 @@ export class ClineProvider implements vscode.WebviewViewProvider {
|
|||||||
screenshotQuality: screenshotQuality ?? 75,
|
screenshotQuality: screenshotQuality ?? 75,
|
||||||
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
|
fuzzyMatchThreshold: fuzzyMatchThreshold ?? 1.0,
|
||||||
writeDelayMs: writeDelayMs ?? 1000,
|
writeDelayMs: writeDelayMs ?? 1000,
|
||||||
|
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
|
||||||
preferredLanguage: preferredLanguage ?? (() => {
|
preferredLanguage: preferredLanguage ?? (() => {
|
||||||
// Get VSCode's locale setting
|
// Get VSCode's locale setting
|
||||||
const vscodeLang = vscode.env.language;
|
const vscodeLang = vscode.env.language;
|
||||||
|
|||||||
@@ -16,8 +16,11 @@ export class TerminalRegistry {
|
|||||||
static createTerminal(cwd?: string | vscode.Uri | undefined): TerminalInfo {
|
static createTerminal(cwd?: string | vscode.Uri | undefined): TerminalInfo {
|
||||||
const terminal = vscode.window.createTerminal({
|
const terminal = vscode.window.createTerminal({
|
||||||
cwd,
|
cwd,
|
||||||
name: "Cline",
|
name: "Roo Cline",
|
||||||
iconPath: new vscode.ThemeIcon("robot"),
|
iconPath: new vscode.ThemeIcon("rocket"),
|
||||||
|
env: {
|
||||||
|
PAGER: "cat"
|
||||||
|
}
|
||||||
})
|
})
|
||||||
const newInfo: TerminalInfo = {
|
const newInfo: TerminalInfo = {
|
||||||
terminal,
|
terminal,
|
||||||
|
|||||||
37
src/integrations/terminal/__tests__/TerminalRegistry.test.ts
Normal file
37
src/integrations/terminal/__tests__/TerminalRegistry.test.ts
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import * as vscode from "vscode"
|
||||||
|
import { TerminalRegistry } from "../TerminalRegistry"
|
||||||
|
|
||||||
|
// Mock vscode.window.createTerminal
|
||||||
|
const mockCreateTerminal = jest.fn()
|
||||||
|
jest.mock("vscode", () => ({
|
||||||
|
window: {
|
||||||
|
createTerminal: (...args: any[]) => {
|
||||||
|
mockCreateTerminal(...args)
|
||||||
|
return {
|
||||||
|
exitStatus: undefined,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ThemeIcon: jest.fn(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
describe("TerminalRegistry", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
mockCreateTerminal.mockClear()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe("createTerminal", () => {
|
||||||
|
it("creates terminal with PAGER set to cat", () => {
|
||||||
|
TerminalRegistry.createTerminal("/test/path")
|
||||||
|
|
||||||
|
expect(mockCreateTerminal).toHaveBeenCalledWith({
|
||||||
|
cwd: "/test/path",
|
||||||
|
name: "Roo Cline",
|
||||||
|
iconPath: expect.any(Object),
|
||||||
|
env: {
|
||||||
|
PAGER: "cat"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -61,6 +61,7 @@ export interface ExtensionState {
|
|||||||
fuzzyMatchThreshold?: number
|
fuzzyMatchThreshold?: number
|
||||||
preferredLanguage: string
|
preferredLanguage: string
|
||||||
writeDelayMs: number
|
writeDelayMs: number
|
||||||
|
terminalOutputLineLimit?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ClineMessage {
|
export interface ClineMessage {
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ export interface WebviewMessage {
|
|||||||
| "enhancedPrompt"
|
| "enhancedPrompt"
|
||||||
| "draggedImages"
|
| "draggedImages"
|
||||||
| "deleteMessage"
|
| "deleteMessage"
|
||||||
|
| "terminalOutputLineLimit"
|
||||||
text?: string
|
text?: string
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
askResponse?: ClineAskResponse
|
askResponse?: ClineAskResponse
|
||||||
|
|||||||
@@ -46,6 +46,8 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
setWriteDelayMs,
|
setWriteDelayMs,
|
||||||
screenshotQuality,
|
screenshotQuality,
|
||||||
setScreenshotQuality,
|
setScreenshotQuality,
|
||||||
|
terminalOutputLineLimit,
|
||||||
|
setTerminalOutputLineLimit,
|
||||||
} = useExtensionState()
|
} = useExtensionState()
|
||||||
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
const [apiErrorMessage, setApiErrorMessage] = useState<string | undefined>(undefined)
|
||||||
const [modelIdErrorMessage, setModelIdErrorMessage] = 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: "preferredLanguage", text: preferredLanguage })
|
||||||
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
|
vscode.postMessage({ type: "writeDelayMs", value: writeDelayMs })
|
||||||
vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
|
vscode.postMessage({ type: "screenshotQuality", value: screenshotQuality ?? 75 })
|
||||||
|
vscode.postMessage({ type: "terminalOutputLineLimit", value: terminalOutputLineLimit ?? 500 })
|
||||||
onDone()
|
onDone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -210,6 +213,31 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</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 }}>
|
<div style={{ marginBottom: 5 }}>
|
||||||
<VSCodeCheckbox checked={diffEnabled} onChange={(e: any) => setDiffEnabled(e.target.checked)}>
|
<VSCodeCheckbox checked={diffEnabled} onChange={(e: any) => setDiffEnabled(e.target.checked)}>
|
||||||
<span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
|
<span style={{ fontWeight: "500" }}>Enable editing through diffs</span>
|
||||||
@@ -431,7 +459,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
<div style={{ marginBottom: 10 }}>
|
<div style={{ marginBottom: 10 }}>
|
||||||
<div style={{ marginBottom: 15 }}>
|
<div style={{ marginBottom: 15 }}>
|
||||||
<h3 style={{ color: "var(--vscode-foreground)", margin: 0, marginBottom: 15 }}>Browser Settings</h3>
|
<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
|
<select
|
||||||
value={browserViewportSize}
|
value={browserViewportSize}
|
||||||
onChange={(e) => setBrowserViewportSize(e.target.value)}
|
onChange={(e) => setBrowserViewportSize(e.target.value)}
|
||||||
@@ -460,7 +488,7 @@ const SettingsView = ({ onDone }: SettingsViewProps) => {
|
|||||||
|
|
||||||
<div style={{ marginBottom: 15 }}>
|
<div style={{ marginBottom: 15 }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: '5px' }}>
|
<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
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
min="1"
|
min="1"
|
||||||
|
|||||||
@@ -39,6 +39,8 @@ export interface ExtensionStateContextType extends ExtensionState {
|
|||||||
setWriteDelayMs: (value: number) => void
|
setWriteDelayMs: (value: number) => void
|
||||||
screenshotQuality?: number
|
screenshotQuality?: number
|
||||||
setScreenshotQuality: (value: number) => void
|
setScreenshotQuality: (value: number) => void
|
||||||
|
terminalOutputLineLimit?: number
|
||||||
|
setTerminalOutputLineLimit: (value: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
|
export const ExtensionStateContext = createContext<ExtensionStateContextType | undefined>(undefined)
|
||||||
@@ -58,6 +60,7 @@ export const ExtensionStateContextProvider: React.FC<{ children: React.ReactNode
|
|||||||
writeDelayMs: 1000,
|
writeDelayMs: 1000,
|
||||||
browserViewportSize: "900x600",
|
browserViewportSize: "900x600",
|
||||||
screenshotQuality: 75,
|
screenshotQuality: 75,
|
||||||
|
terminalOutputLineLimit: 500,
|
||||||
})
|
})
|
||||||
const [didHydrateState, setDidHydrateState] = useState(false)
|
const [didHydrateState, setDidHydrateState] = useState(false)
|
||||||
const [showWelcome, setShowWelcome] = 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 })),
|
setPreferredLanguage: (value) => setState((prevState) => ({ ...prevState, preferredLanguage: value })),
|
||||||
setWriteDelayMs: (value) => setState((prevState) => ({ ...prevState, writeDelayMs: value })),
|
setWriteDelayMs: (value) => setState((prevState) => ({ ...prevState, writeDelayMs: value })),
|
||||||
setScreenshotQuality: (value) => setState((prevState) => ({ ...prevState, screenshotQuality: value })),
|
setScreenshotQuality: (value) => setState((prevState) => ({ ...prevState, screenshotQuality: value })),
|
||||||
|
setTerminalOutputLineLimit: (value) => setState((prevState) => ({ ...prevState, terminalOutputLineLimit: value })),
|
||||||
}
|
}
|
||||||
|
|
||||||
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
return <ExtensionStateContext.Provider value={contextValue}>{children}</ExtensionStateContext.Provider>
|
||||||
|
|||||||
Reference in New Issue
Block a user