mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-08-28 05:27:24 +00:00
feat: add git status to environment details (#9310)
This commit is contained in:
parent
fc19e7620b
commit
3631a02c8b
28 changed files with 410 additions and 14 deletions
|
|
@ -103,6 +103,12 @@ export const globalSettingsSchema = z.object({
|
|||
* @default true
|
||||
*/
|
||||
includeCurrentCost: z.boolean().optional(),
|
||||
/**
|
||||
* Maximum number of git status file entries to include in the environment details.
|
||||
* Set to 0 to disable git status. The header (branch, commits) is always included when > 0.
|
||||
* @default 0
|
||||
*/
|
||||
maxGitStatusFiles: z.number().optional(),
|
||||
|
||||
/**
|
||||
* Whether to include diagnostic messages (errors, warnings) in tool outputs
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { ApiHandler } from "../../../api/index"
|
|||
import { ClineProvider } from "../../webview/ClineProvider"
|
||||
import { RooIgnoreController } from "../../ignore/RooIgnoreController"
|
||||
import { formatResponse } from "../../prompts/responses"
|
||||
import { getGitStatus } from "../../../utils/git"
|
||||
import { Task } from "../../task/Task"
|
||||
|
||||
vi.mock("vscode", () => ({
|
||||
|
|
@ -48,6 +49,7 @@ vi.mock("../../../services/glob/list-files")
|
|||
vi.mock("../../../integrations/terminal/TerminalRegistry")
|
||||
vi.mock("../../../integrations/terminal/Terminal")
|
||||
vi.mock("../../../utils/path")
|
||||
vi.mock("../../../utils/git")
|
||||
vi.mock("../../prompts/responses")
|
||||
|
||||
describe("getEnvironmentDetails", () => {
|
||||
|
|
@ -134,6 +136,7 @@ describe("getEnvironmentDetails", () => {
|
|||
;(TerminalRegistry.getBackgroundTerminals as Mock).mockReturnValue([])
|
||||
;(TerminalRegistry.isProcessHot as Mock).mockReturnValue(false)
|
||||
;(TerminalRegistry.getUnretrievedOutput as Mock).mockReturnValue("")
|
||||
;(getGitStatus as Mock).mockResolvedValue("## main")
|
||||
vi.mocked(pWaitFor).mockResolvedValue(undefined)
|
||||
vi.mocked(delay).mockResolvedValue(undefined)
|
||||
})
|
||||
|
|
@ -143,9 +146,9 @@ describe("getEnvironmentDetails", () => {
|
|||
|
||||
expect(result).toContain("<environment_details>")
|
||||
expect(result).toContain("</environment_details>")
|
||||
expect(result).toContain("# VSCode Visible Files")
|
||||
expect(result).toContain("# VSCode Open Tabs")
|
||||
// Visible Files and Open Tabs headers only appear when there's content
|
||||
expect(result).toContain("# Current Time")
|
||||
expect(result).not.toContain("# Git Status") // Git status is disabled by default (maxGitStatusFiles = 0)
|
||||
expect(result).toContain("# Current Cost")
|
||||
expect(result).toContain("# Current Mode")
|
||||
expect(result).toContain("<model>test-model</model>")
|
||||
|
|
@ -390,4 +393,67 @@ describe("getEnvironmentDetails", () => {
|
|||
const result = await getEnvironmentDetails(cline as Task)
|
||||
expect(result).toContain("REMINDERS")
|
||||
})
|
||||
|
||||
it("should include git status when maxGitStatusFiles > 0", async () => {
|
||||
;(getGitStatus as Mock).mockResolvedValue("## main\nM file1.ts")
|
||||
mockProvider.getState.mockResolvedValue({
|
||||
...mockState,
|
||||
maxGitStatusFiles: 10,
|
||||
})
|
||||
|
||||
const result = await getEnvironmentDetails(mockCline as Task)
|
||||
|
||||
expect(result).toContain("# Git Status")
|
||||
expect(result).toContain("## main")
|
||||
expect(getGitStatus).toHaveBeenCalledWith(mockCwd, 10)
|
||||
})
|
||||
|
||||
it("should NOT include git status when maxGitStatusFiles is 0", async () => {
|
||||
mockProvider.getState.mockResolvedValue({
|
||||
...mockState,
|
||||
maxGitStatusFiles: 0,
|
||||
})
|
||||
|
||||
const result = await getEnvironmentDetails(mockCline as Task)
|
||||
|
||||
expect(result).not.toContain("# Git Status")
|
||||
expect(getGitStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should NOT include git status when maxGitStatusFiles is undefined (defaults to 0)", async () => {
|
||||
mockProvider.getState.mockResolvedValue({
|
||||
...mockState,
|
||||
maxGitStatusFiles: undefined,
|
||||
})
|
||||
|
||||
const result = await getEnvironmentDetails(mockCline as Task)
|
||||
|
||||
expect(result).not.toContain("# Git Status")
|
||||
expect(getGitStatus).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should handle git status returning null gracefully when enabled", async () => {
|
||||
;(getGitStatus as Mock).mockResolvedValue(null)
|
||||
mockProvider.getState.mockResolvedValue({
|
||||
...mockState,
|
||||
maxGitStatusFiles: 10,
|
||||
})
|
||||
|
||||
const result = await getEnvironmentDetails(mockCline as Task)
|
||||
|
||||
expect(result).not.toContain("# Git Status")
|
||||
expect(getGitStatus).toHaveBeenCalledWith(mockCwd, 10)
|
||||
})
|
||||
|
||||
it("should pass maxFiles parameter to getGitStatus", async () => {
|
||||
;(getGitStatus as Mock).mockResolvedValue("## main")
|
||||
mockProvider.getState.mockResolvedValue({
|
||||
...mockState,
|
||||
maxGitStatusFiles: 5,
|
||||
})
|
||||
|
||||
await getEnvironmentDetails(mockCline as Task)
|
||||
|
||||
expect(getGitStatus).toHaveBeenCalledWith(mockCwd, 5)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { TerminalRegistry } from "../../integrations/terminal/TerminalRegistry"
|
|||
import { Terminal } from "../../integrations/terminal/Terminal"
|
||||
import { arePathsEqual } from "../../utils/path"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { getGitStatus } from "../../utils/git"
|
||||
|
||||
import { Task } from "../task/Task"
|
||||
import { formatReminderSection } from "./reminder"
|
||||
|
|
@ -34,8 +35,6 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
|
|||
|
||||
// It could be useful for cline to know if the user went from one or no
|
||||
// file to another between messages, so we always include this context.
|
||||
details += "\n\n# VSCode Visible Files"
|
||||
|
||||
const visibleFilePaths = vscode.window.visibleTextEditors
|
||||
?.map((editor) => editor.document?.uri?.fsPath)
|
||||
.filter(Boolean)
|
||||
|
|
@ -48,12 +47,10 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
|
|||
: visibleFilePaths.map((p) => p.toPosix()).join("\n")
|
||||
|
||||
if (allowedVisibleFiles) {
|
||||
details += "\n\n# VSCode Visible Files"
|
||||
details += `\n${allowedVisibleFiles}`
|
||||
} else {
|
||||
details += "\n(No visible files)"
|
||||
}
|
||||
|
||||
details += "\n\n# VSCode Open Tabs"
|
||||
const { maxOpenTabsContext } = state ?? {}
|
||||
const maxTabs = maxOpenTabsContext ?? 20
|
||||
const openTabPaths = vscode.window.tabGroups.all
|
||||
|
|
@ -70,9 +67,8 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
|
|||
: openTabPaths.map((p) => p.toPosix()).join("\n")
|
||||
|
||||
if (allowedOpenTabs) {
|
||||
details += "\n\n# VSCode Open Tabs"
|
||||
details += `\n${allowedOpenTabs}`
|
||||
} else {
|
||||
details += "\n(No open tabs)"
|
||||
}
|
||||
|
||||
// Get task-specific and background terminals.
|
||||
|
|
@ -191,7 +187,7 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
|
|||
}
|
||||
|
||||
// Get settings for time and cost display
|
||||
const { includeCurrentTime = true, includeCurrentCost = true } = state ?? {}
|
||||
const { includeCurrentTime = true, includeCurrentCost = true, maxGitStatusFiles = 0 } = state ?? {}
|
||||
|
||||
// Add current time information with timezone (if enabled).
|
||||
if (includeCurrentTime) {
|
||||
|
|
@ -205,6 +201,14 @@ export async function getEnvironmentDetails(cline: Task, includeFileDetails: boo
|
|||
details += `\n\n# Current Time\nCurrent time in ISO 8601 UTC format: ${now.toISOString()}\nUser time zone: ${timeZone}, UTC${timeZoneOffsetStr}`
|
||||
}
|
||||
|
||||
// Add git status information (if enabled with maxGitStatusFiles > 0).
|
||||
if (maxGitStatusFiles > 0) {
|
||||
const gitStatus = await getGitStatus(cline.cwd, maxGitStatusFiles)
|
||||
if (gitStatus) {
|
||||
details += `\n\n# Git Status\n${gitStatus}`
|
||||
}
|
||||
}
|
||||
|
||||
// Add context tokens information (if enabled).
|
||||
if (includeCurrentCost) {
|
||||
const { totalCost } = getApiMetrics(cline.clineMessages)
|
||||
|
|
|
|||
|
|
@ -1918,6 +1918,7 @@ export class ClineProvider
|
|||
includeTaskHistoryInEnhance,
|
||||
includeCurrentTime,
|
||||
includeCurrentCost,
|
||||
maxGitStatusFiles,
|
||||
taskSyncEnabled,
|
||||
remoteControlEnabled,
|
||||
openRouterImageApiKey,
|
||||
|
|
@ -2082,6 +2083,7 @@ export class ClineProvider
|
|||
includeTaskHistoryInEnhance: includeTaskHistoryInEnhance ?? true,
|
||||
includeCurrentTime: includeCurrentTime ?? true,
|
||||
includeCurrentCost: includeCurrentCost ?? true,
|
||||
maxGitStatusFiles: maxGitStatusFiles ?? 0,
|
||||
taskSyncEnabled,
|
||||
remoteControlEnabled,
|
||||
openRouterImageApiKey,
|
||||
|
|
@ -2297,6 +2299,7 @@ export class ClineProvider
|
|||
includeTaskHistoryInEnhance: stateValues.includeTaskHistoryInEnhance ?? true,
|
||||
includeCurrentTime: stateValues.includeCurrentTime ?? true,
|
||||
includeCurrentCost: stateValues.includeCurrentCost ?? true,
|
||||
maxGitStatusFiles: stateValues.maxGitStatusFiles ?? 0,
|
||||
taskSyncEnabled,
|
||||
remoteControlEnabled: (() => {
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ export type ExtensionState = Pick<
|
|||
| "reasoningBlockCollapsed"
|
||||
| "includeCurrentTime"
|
||||
| "includeCurrentCost"
|
||||
| "maxGitStatusFiles"
|
||||
> & {
|
||||
version: string
|
||||
clineMessages: ClineMessage[]
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import {
|
|||
extractRepositoryName,
|
||||
getWorkspaceGitInfo,
|
||||
convertGitUrlToHttps,
|
||||
getGitStatus,
|
||||
} from "../git"
|
||||
import { truncateOutput } from "../../integrations/misc/extract-text"
|
||||
|
||||
|
|
@ -834,3 +835,160 @@ describe("getWorkspaceGitInfo", () => {
|
|||
expect(readFileSpy).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
describe("getGitStatus", () => {
|
||||
const cwd = "/test/path"
|
||||
|
||||
beforeEach(() => {
|
||||
vitest.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should return git status output with default maxFiles", async () => {
|
||||
const mockOutput = `## main...origin/main [ahead 2, behind 1]
|
||||
M src/staged-file.ts
|
||||
M src/unstaged-file.ts
|
||||
MM src/both-modified.ts
|
||||
?? src/untracked-file.ts`
|
||||
|
||||
const responses = new Map([
|
||||
["git --version", { stdout: "git version 2.39.2", stderr: "" }],
|
||||
["git rev-parse --git-dir", { stdout: ".git", stderr: "" }],
|
||||
["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }],
|
||||
])
|
||||
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
for (const [cmd, response] of responses) {
|
||||
if (command === cmd) {
|
||||
callback(null, response)
|
||||
return {} as any
|
||||
}
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd, 20)
|
||||
|
||||
expect(result).toBe(mockOutput)
|
||||
})
|
||||
|
||||
it("should show only branch info when maxFiles is 0", async () => {
|
||||
const mockOutput = `## main...origin/main
|
||||
M src/file1.ts
|
||||
?? src/file2.ts`
|
||||
|
||||
const responses = new Map([
|
||||
["git --version", { stdout: "git version 2.39.2", stderr: "" }],
|
||||
["git rev-parse --git-dir", { stdout: ".git", stderr: "" }],
|
||||
["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }],
|
||||
])
|
||||
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
for (const [cmd, response] of responses) {
|
||||
if (command === cmd) {
|
||||
callback(null, response)
|
||||
return {} as any
|
||||
}
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd, 0)
|
||||
|
||||
expect(result).toBe("## main...origin/main")
|
||||
expect(result).not.toContain("M src/file1.ts")
|
||||
expect(result).not.toContain("?? src/file2.ts")
|
||||
})
|
||||
|
||||
it("should return null when git is not installed", async () => {
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
if (command === "git --version") {
|
||||
callback(new Error("git not found"))
|
||||
return {} as any
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should return null when not in a git repository", async () => {
|
||||
const responses = new Map([
|
||||
["git --version", { stdout: "git version 2.39.2", stderr: "" }],
|
||||
["git rev-parse --git-dir", null],
|
||||
])
|
||||
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
const response = responses.get(command)
|
||||
if (response === null) {
|
||||
callback(new Error("not a git repository"))
|
||||
return {} as any
|
||||
} else if (response) {
|
||||
callback(null, response)
|
||||
return {} as any
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
|
||||
it("should respect maxFiles parameter", async () => {
|
||||
const lines = Array.from({ length: 30 }, (_, i) => {
|
||||
if (i === 0) return "## main"
|
||||
return `M file${i}.ts`
|
||||
})
|
||||
const mockOutput = lines.join("\n")
|
||||
|
||||
const responses = new Map([
|
||||
["git --version", { stdout: "git version 2.39.2", stderr: "" }],
|
||||
["git rev-parse --git-dir", { stdout: ".git", stderr: "" }],
|
||||
["git status --porcelain=v1 --branch", { stdout: mockOutput, stderr: "" }],
|
||||
])
|
||||
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
for (const [cmd, response] of responses) {
|
||||
if (command === cmd) {
|
||||
callback(null, response)
|
||||
return {} as any
|
||||
}
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd, 10)
|
||||
|
||||
expect(result).toContain("## main")
|
||||
expect(result).toContain("M file1.ts")
|
||||
expect(result).toContain("... 19 more files")
|
||||
expect(result).not.toContain("file15.ts")
|
||||
})
|
||||
|
||||
it("should return null when status is empty", async () => {
|
||||
const responses = new Map([
|
||||
["git --version", { stdout: "git version 2.39.2", stderr: "" }],
|
||||
["git rev-parse --git-dir", { stdout: ".git", stderr: "" }],
|
||||
["git status --porcelain=v1 --branch", { stdout: "", stderr: "" }],
|
||||
])
|
||||
|
||||
vitest.mocked(exec).mockImplementation((command: string, options: any, callback: any) => {
|
||||
for (const [cmd, response] of responses) {
|
||||
if (command === cmd) {
|
||||
callback(null, response)
|
||||
return {} as any
|
||||
}
|
||||
}
|
||||
callback(new Error("Unexpected command"))
|
||||
return {} as any
|
||||
})
|
||||
|
||||
const result = await getGitStatus(cwd)
|
||||
expect(result).toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -355,3 +355,54 @@ export async function getWorkingState(cwd: string): Promise<string> {
|
|||
return `Failed to get working state: ${error instanceof Error ? error.message : String(error)}`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets git status output with configurable file limit
|
||||
* @param cwd The working directory to check git status in
|
||||
* @param maxFiles Maximum number of file entries to include (0 = disabled)
|
||||
* @returns Git status string or null if not a git repository
|
||||
*/
|
||||
export async function getGitStatus(cwd: string, maxFiles: number = 20): Promise<string | null> {
|
||||
try {
|
||||
const isInstalled = await checkGitInstalled()
|
||||
if (!isInstalled) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isRepo = await checkGitRepo(cwd)
|
||||
if (!isRepo) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Use porcelain v1 format with branch info
|
||||
const { stdout } = await execAsync("git status --porcelain=v1 --branch", { cwd })
|
||||
|
||||
if (!stdout.trim()) {
|
||||
return null
|
||||
}
|
||||
|
||||
const lines = stdout.trim().split("\n")
|
||||
|
||||
// First line is always branch info (e.g., "## main...origin/main")
|
||||
const branchLine = lines[0]
|
||||
const fileLines = lines.slice(1)
|
||||
|
||||
// Build output with branch info and limited file entries
|
||||
const output: string[] = [branchLine]
|
||||
|
||||
if (maxFiles > 0 && fileLines.length > 0) {
|
||||
const filesToShow = fileLines.slice(0, maxFiles)
|
||||
output.push(...filesToShow)
|
||||
|
||||
// Add truncation notice if needed
|
||||
if (fileLines.length > maxFiles) {
|
||||
output.push(`... ${fileLines.length - maxFiles} more files`)
|
||||
}
|
||||
}
|
||||
|
||||
return output.join("\n")
|
||||
} catch (error) {
|
||||
console.error("Error getting git status:", error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
|
|||
writeDelayMs: number
|
||||
includeCurrentTime?: boolean
|
||||
includeCurrentCost?: boolean
|
||||
maxGitStatusFiles?: number
|
||||
setCachedStateField: SetCachedStateField<
|
||||
| "autoCondenseContext"
|
||||
| "autoCondenseContextPercent"
|
||||
|
|
@ -45,6 +46,7 @@ type ContextManagementSettingsProps = HTMLAttributes<HTMLDivElement> & {
|
|||
| "writeDelayMs"
|
||||
| "includeCurrentTime"
|
||||
| "includeCurrentCost"
|
||||
| "maxGitStatusFiles"
|
||||
>
|
||||
}
|
||||
|
||||
|
|
@ -66,6 +68,7 @@ export const ContextManagementSettings = ({
|
|||
writeDelayMs,
|
||||
includeCurrentTime,
|
||||
includeCurrentCost,
|
||||
maxGitStatusFiles,
|
||||
className,
|
||||
...props
|
||||
}: ContextManagementSettingsProps) => {
|
||||
|
|
@ -146,6 +149,26 @@ export const ContextManagementSettings = ({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span className="block font-medium mb-1">
|
||||
{t("settings:contextManagement.maxGitStatusFiles.label")}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
<Slider
|
||||
min={0}
|
||||
max={50}
|
||||
step={1}
|
||||
value={[maxGitStatusFiles ?? 0]}
|
||||
onValueChange={([value]) => setCachedStateField("maxGitStatusFiles", value)}
|
||||
data-testid="max-git-status-files-slider"
|
||||
/>
|
||||
<span className="w-10">{maxGitStatusFiles ?? 0}</span>
|
||||
</div>
|
||||
<div className="text-vscode-descriptionForeground text-sm mt-1">
|
||||
{t("settings:contextManagement.maxGitStatusFiles.description")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<span className="block font-medium mb-1">
|
||||
{t("settings:contextManagement.maxConcurrentFileReads.label")}
|
||||
|
|
|
|||
|
|
@ -203,6 +203,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|||
reasoningBlockCollapsed,
|
||||
includeCurrentTime,
|
||||
includeCurrentCost,
|
||||
maxGitStatusFiles,
|
||||
} = cachedState
|
||||
|
||||
const apiConfiguration = useMemo(() => cachedState.apiConfiguration ?? {}, [cachedState.apiConfiguration])
|
||||
|
|
@ -395,6 +396,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|||
reasoningBlockCollapsed: reasoningBlockCollapsed ?? true,
|
||||
includeCurrentTime: includeCurrentTime ?? true,
|
||||
includeCurrentCost: includeCurrentCost ?? true,
|
||||
maxGitStatusFiles: maxGitStatusFiles ?? 0,
|
||||
profileThresholds,
|
||||
openRouterImageApiKey,
|
||||
openRouterImageGenerationSelectedModel,
|
||||
|
|
@ -764,6 +766,7 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
|
|||
writeDelayMs={writeDelayMs}
|
||||
includeCurrentTime={includeCurrentTime}
|
||||
includeCurrentCost={includeCurrentCost}
|
||||
maxGitStatusFiles={maxGitStatusFiles}
|
||||
setCachedStateField={setCachedStateField}
|
||||
/>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,12 @@ vi.mock("@/hooks/useAppTranslation", () => ({
|
|||
// Mock the UI components
|
||||
vi.mock("@/components/ui", () => ({
|
||||
...vi.importActual("@/components/ui"),
|
||||
Slider: ({ value, onValueChange, "data-testid": dataTestId, disabled }: any) => (
|
||||
Slider: ({ value, onValueChange, "data-testid": dataTestId, disabled, min, max }: any) => (
|
||||
<input
|
||||
type="range"
|
||||
value={value?.[0] ?? 0}
|
||||
min={min}
|
||||
max={max}
|
||||
onChange={(e) => onValueChange([parseFloat(e.target.value)])}
|
||||
onKeyDown={(e) => {
|
||||
const currentValue = value?.[0] ?? 0
|
||||
|
|
@ -455,13 +457,20 @@ describe("ContextManagementSettings", () => {
|
|||
...defaultProps,
|
||||
maxOpenTabsContext: 0,
|
||||
maxWorkspaceFiles: 500,
|
||||
maxGitStatusFiles: 0,
|
||||
setCachedStateField: mockSetCachedStateField,
|
||||
}
|
||||
render(<ContextManagementSettings {...props} />)
|
||||
|
||||
// Check boundary values are displayed
|
||||
expect(screen.getByText("0")).toBeInTheDocument() // min open tabs
|
||||
expect(screen.getByText("500")).toBeInTheDocument() // max workspace files
|
||||
// Check boundary values are displayed by checking the slider values directly
|
||||
const openTabsSlider = screen.getByTestId("open-tabs-limit-slider")
|
||||
expect(openTabsSlider).toHaveValue("0")
|
||||
|
||||
const workspaceFilesSlider = screen.getByTestId("workspace-files-limit-slider")
|
||||
expect(workspaceFilesSlider).toHaveValue("500")
|
||||
|
||||
const gitStatusSlider = screen.getByTestId("max-git-status-files-slider")
|
||||
expect(gitStatusSlider).toHaveValue("0")
|
||||
})
|
||||
|
||||
it("handles undefined optional props gracefully", () => {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ca/settings.json
generated
4
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -626,6 +626,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Inclou el cost actual en el context",
|
||||
"description": "Quan està activat, el cost d'ús actual de l'API s'inclourà a la indicació del sistema. Desactiveu-ho si els models deixen de funcionar per problemes amb el cost."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status màx. fitxers",
|
||||
"description": "Nombre màxim d'entrades de fitxers a incloure en el context d'estat de git. Establiu a 0 per desactivar. La informació de la branca i els commits sempre es mostren quan és > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/de/settings.json
generated
4
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -626,6 +626,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Aktuelle Kosten in den Kontext einbeziehen",
|
||||
"description": "Wenn aktiviert, werden die aktuellen API-Nutzungskosten in den System-Prompt aufgenommen. Deaktiviere diese Option, wenn Modelle aufgrund von Kostenbedenken die Arbeit einstellen."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git-Status max. Dateien",
|
||||
"description": "Maximale Anzahl von Dateieinträgen, die in den Git-Status-Kontext aufgenommen werden sollen. Auf 0 setzen, um zu deaktivieren. Branch-Informationen und Commits werden immer angezeigt, wenn > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
|
|
@ -631,6 +631,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Include current cost in context",
|
||||
"description": "When enabled, the current API usage cost will be included in the system prompt. Disable this if models are stopping work due to cost concerns."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status max files",
|
||||
"description": "Maximum number of file entries to include in git status context. Set to 0 to disable. Branch info is always shown when > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/es/settings.json
generated
4
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -626,6 +626,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Incluir costo actual en el contexto",
|
||||
"description": "Cuando está habilitado, el costo de uso actual de la API se incluirá en el prompt del sistema. Deshabilítelo si los modelos dejan de funcionar por problemas de costos."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status máx. archivos",
|
||||
"description": "Número máximo de entradas de archivo para incluir en el contexto de estado de git. Establézcalo en 0 para deshabilitar. La información de la rama y los commits siempre se muestran cuando es > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/fr/settings.json
generated
4
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -626,6 +626,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Inclure le coût actuel dans le contexte",
|
||||
"description": "Lorsque cette option est activée, le coût d'utilisation actuel de l'API sera inclus dans le prompt système. Désactivez cette option si les modèles cessent de fonctionner en raison de problèmes de coût."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status max fichiers",
|
||||
"description": "Nombre maximum de fichiers à inclure dans le contexte de statut git. Mettre à 0 pour désactiver. Les informations de branche et les commits sont toujours affichés si > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/hi/settings.json
generated
4
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "संदर्भ में वर्तमान लागत शामिल करें",
|
||||
"description": "सक्षम होने पर, वर्तमान एपीआई उपयोग लागत सिस्टम प्रॉम्प्ट में शामिल की जाएगी। यदि मॉडल लागत संबंधी चिंताओं के कारण काम करना बंद कर देते हैं तो इसे अक्षम करें।"
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "गिट स्थिति अधिकतम फ़ाइलें",
|
||||
"description": "गिट स्थिति संदर्भ में शामिल करने के लिए फ़ाइल प्रविष्टियों की अधिकतम संख्या। अक्षम करने के लिए 0 पर सेट करें। शाखा जानकारी और कमिट हमेशा दिखाए जाते हैं जब > 0 होता है।"
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/id/settings.json
generated
4
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -631,6 +631,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Sertakan biaya saat ini dalam konteks",
|
||||
"description": "Ketika diaktifkan, biaya penggunaan API saat ini akan disertakan dalam prompt sistem. Nonaktifkan ini jika model berhenti bekerja karena masalah biaya."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status maks file",
|
||||
"description": "Jumlah maksimum entri file untuk disertakan dalam konteks status git. Atur ke 0 untuk menonaktifkan. Info cabang dan commit selalu ditampilkan saat > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/it/settings.json
generated
4
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Includi il costo corrente nel contesto",
|
||||
"description": "Se abilitato, il costo di utilizzo corrente dell'API verrà incluso nel prompt di sistema. Disabilita questa opzione se i modelli smettono di funzionare a causa di problemi di costo."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status max file",
|
||||
"description": "Numero massimo di voci di file da includere nel contesto dello stato di git. Imposta a 0 per disabilitare. Le informazioni sul ramo e sui commit vengono sempre mostrate quando > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ja/settings.json
generated
4
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "現在のコストをコンテキストに含める",
|
||||
"description": "有効にすると、現在のAPI使用コストがシステムプロンプトに含まれます。モデルがコストに関する懸念で動作を停止する場合は無効にしてください。"
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Gitステータス最大ファイル数",
|
||||
"description": "gitステータスコンテキストに含めるファイルエントリの最大数。無効にするには0に設定します。ブランチ情報とコミットは、> 0の場合に常に表示されます。"
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ko/settings.json
generated
4
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "컨텍스트에 현재 비용 포함",
|
||||
"description": "활성화하면 현재 API 사용 비용이 시스템 프롬프트에 포함됩니다. 비용 문제로 모델이 작동을 멈추면 비활성화하세요."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git 상태 최대 파일",
|
||||
"description": "git 상태 컨텍스트에 포함할 최대 파일 항목 수입니다. 비활성화하려면 0으로 설정하세요. 분기 정보와 커밋은 > 0일 때 항상 표시됩니다."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/nl/settings.json
generated
4
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Huidige kosten opnemen in context",
|
||||
"description": "Indien ingeschakeld, worden de huidige API-gebruikskosten opgenomen in de systeemprompt. Schakel dit uit als modellen stoppen met werken vanwege kostenproblemen."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status max bestanden",
|
||||
"description": "Maximum aantal bestandsvermeldingen dat in de git-statuscontext moet worden opgenomen. Stel in op 0 om uit te schakelen. Branch-info en commits worden altijd getoond wanneer > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pl/settings.json
generated
4
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Uwzględnij bieżący koszt w kontekście",
|
||||
"description": "Gdy włączone, bieżący koszt użycia API zostanie uwzględniony w promptcie systemowym. Wyłącz, jeśli modele przestają działać z powodu problemów z kosztami."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status maks. plików",
|
||||
"description": "Maksymalna liczba wpisów plików do uwzględnienia w kontekście statusu git. Ustaw na 0, aby wyłączyć. Informacje o gałęzi i zatwierdzenia są zawsze pokazywane, gdy > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Incluir custo atual no contexto",
|
||||
"description": "Quando ativado, o custo de uso atual da API será incluído no prompt do sistema. Desative se os modelos pararem de funcionar por problemas de custo."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status máx. arquivos",
|
||||
"description": "Número máximo de entradas de arquivo a serem incluídas no contexto de status do git. Defina como 0 para desativar. Informações sobre o branch e os commits são sempre exibidos quando > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ru/settings.json
generated
4
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Включить текущую стоимость в контекст",
|
||||
"description": "Если включено, текущая стоимость использования API будет включена в системную подсказку. Отключите, если модели прекращают работу из-за проблем со стоимостью."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git статус макс. файлов",
|
||||
"description": "Максимальное количество записей файлов для включения в контекст статуса git. Установите значение 0, чтобы отключить. Информация о ветке и коммитах всегда отображается, если значение > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/tr/settings.json
generated
4
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Mevcut maliyeti bağlama dahil et",
|
||||
"description": "Etkinleştirildiğinde, mevcut API kullanım maliyeti sistem istemine dahil edilecektir. Modeller maliyet endişeleri nedeniyle çalışmayı durdurursa bunu devre dışı bırakın."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git durumu maks. dosya",
|
||||
"description": "Git durum bağlamına dahil edilecek maksimum dosya girişi sayısı. Devre dışı bırakmak için 0 olarak ayarlayın. Dal bilgisi ve commit'ler > 0 olduğunda her zaman gösterilir."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/vi/settings.json
generated
4
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "Bao gồm chi phí hiện tại trong ngữ cảnh",
|
||||
"description": "Khi được bật, chi phí sử dụng API hiện tại sẽ được bao gồm trong lời nhắc hệ thống. Tắt nếu các mô hình ngừng hoạt động do lo ngại về chi phí."
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git status tệp tối đa",
|
||||
"description": "Số lượng mục tệp tối đa để bao gồm trong ngữ cảnh trạng thái git. Đặt thành 0 để tắt. Thông tin nhánh và các commit luôn được hiển thị khi > 0."
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "在上下文中包含当前成本",
|
||||
"description": "启用后,当前 API 使用成本将包含在系统提示中。如果模型因成本问题停止工作,请禁用此选项。"
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git 状态最大文件数",
|
||||
"description": "git状态上下文中包含的最大文件条目数。设为0禁用。分支信息和提交在>0时始终显示。"
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -627,6 +627,10 @@
|
|||
"includeCurrentCost": {
|
||||
"label": "在上下文中包含目前成本",
|
||||
"description": "啟用後,目前 API 使用成本將包含在系統提示中。如果模型因成本問題停止工作,請停用此選項。"
|
||||
},
|
||||
"maxGitStatusFiles": {
|
||||
"label": "Git 狀態最大檔案數",
|
||||
"description": "git狀態上下文中包含的最大檔案條目數。設為0禁用。分支資訊和提交在>0時始終顯示。"
|
||||
}
|
||||
},
|
||||
"terminal": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue