mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-06 08:18:39 +00:00
super awesome refactor
This commit is contained in:
parent
b556d64613
commit
22850158fa
24 changed files with 1038 additions and 1388 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -4,6 +4,7 @@ import * as path from "path"
|
|||
|
||||
import { countFileLines } from "../../../integrations/misc/line-counter"
|
||||
import { readLines } from "../../../integrations/misc/read-lines"
|
||||
import { readPartialContent } from "../../../integrations/misc/read-partial-content"
|
||||
import { extractTextFromFile, addLineNumbers, getSupportedBinaryFormats } from "../../../integrations/misc/extract-text"
|
||||
import { parseSourceCodeDefinitionsForFile } from "../../../services/tree-sitter"
|
||||
import { isBinaryFile } from "isbinaryfile"
|
||||
|
|
@ -36,6 +37,14 @@ vi.mock("../../../integrations/misc/read-lines", () => ({
|
|||
}))
|
||||
vi.mock("../../../integrations/misc/read-partial-content", () => ({
|
||||
readPartialSingleLineContent: vi.fn().mockResolvedValue("mocked partial content"),
|
||||
readPartialContent: vi.fn().mockResolvedValue({
|
||||
content: "mocked partial content",
|
||||
charactersRead: 100,
|
||||
totalCharacters: 1000,
|
||||
linesRead: 5,
|
||||
totalLines: 50,
|
||||
lastLineRead: 5,
|
||||
}),
|
||||
}))
|
||||
vi.mock("../contextValidator")
|
||||
|
||||
|
|
@ -1367,21 +1376,29 @@ describe("read_file tool XML output structure", () => {
|
|||
vi.mocked(contextValidatorModule.validateFileSizeForContext).mockResolvedValue({
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 2000,
|
||||
reason: "File exceeds available context space",
|
||||
reason: "File exceeds available context space. Can read 2000 of 500000 characters (40%). Context usage: 10000/100000 tokens (10%).",
|
||||
})
|
||||
|
||||
// Mock readLines to return truncated content
|
||||
vi.mocked(readLines).mockResolvedValue("Line 1\nLine 2\n...truncated...")
|
||||
// Mock readPartialContent to return truncated content
|
||||
vi.mocked(readPartialContent).mockResolvedValue({
|
||||
content: "Line 1\nLine 2\n...truncated...",
|
||||
charactersRead: 2000,
|
||||
totalCharacters: 500000,
|
||||
linesRead: 100,
|
||||
totalLines: 10000,
|
||||
lastLineRead: 100,
|
||||
})
|
||||
|
||||
const result = await executeReadFileTool(
|
||||
{ args: `<file><path>large-file.ts</path></file>` },
|
||||
{ totalLines: 10000, maxReadFileLine: -1 },
|
||||
)
|
||||
|
||||
// Verify the result contains the inline instructions
|
||||
// Verify the result contains the partial read notice for multi-line files
|
||||
expect(result).toContain("<notice>")
|
||||
expect(result).toContain("File exceeds available context space")
|
||||
expect(result).toContain("tools:readFile.contextLimitInstructions</notice>")
|
||||
expect(result).toContain("tools:readFile.partialReadMultiLine")
|
||||
// The current implementation doesn't include contextLimitInstructions
|
||||
expect(result).not.toContain("tools:readFile.contextLimitInstructions")
|
||||
})
|
||||
|
||||
it("should not show any special notice when file fits in context", async () => {
|
||||
|
|
@ -1409,12 +1426,19 @@ describe("read_file tool XML output structure", () => {
|
|||
// Mock contextValidator to return shouldLimit true with single-line file message
|
||||
vi.mocked(contextValidatorModule.validateFileSizeForContext).mockResolvedValue({
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 1,
|
||||
safeContentLimit: 5000,
|
||||
reason: "Large single-line file (likely minified) exceeds available context space. Only the first 50% (5000 of 10000 characters) can be loaded. This is a hard limit - no additional content from this file can be accessed.",
|
||||
})
|
||||
|
||||
// Mock extractTextFromFile to return truncated content
|
||||
vi.mocked(extractTextFromFile).mockResolvedValue("1 | const a=1;const b=2;...truncated")
|
||||
// Mock readPartialContent to return truncated content for single-line file
|
||||
vi.mocked(readPartialContent).mockResolvedValue({
|
||||
content: "const a=1;const b=2;...truncated",
|
||||
charactersRead: 5000,
|
||||
totalCharacters: 10000,
|
||||
linesRead: 1,
|
||||
totalLines: 1,
|
||||
lastLineRead: 1,
|
||||
})
|
||||
|
||||
const result = await executeReadFileTool(
|
||||
{ args: `<file><path>minified.js</path></file>` },
|
||||
|
|
@ -1423,8 +1447,7 @@ describe("read_file tool XML output structure", () => {
|
|||
|
||||
// Verify the result contains the notice but NOT the line_range instructions
|
||||
expect(result).toContain("<notice>")
|
||||
expect(result).toContain("Large single-line file")
|
||||
expect(result).toContain("This is a hard limit")
|
||||
expect(result).toContain("tools:readFile.partialReadSingleLine")
|
||||
expect(result).not.toContain("tools:readFile.contextLimitInstructions")
|
||||
expect(result).not.toContain("Use line_range")
|
||||
})
|
||||
|
|
@ -1436,22 +1459,30 @@ describe("read_file tool XML output structure", () => {
|
|||
// Mock contextValidator to return shouldLimit true with multi-line file message
|
||||
vi.mocked(contextValidatorModule.validateFileSizeForContext).mockResolvedValue({
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 1000,
|
||||
reason: "File exceeds available context space. Safely read 1000 lines out of 5000 total lines.",
|
||||
safeContentLimit: 50000,
|
||||
reason: "File exceeds available context space. Can read 50000 of 250000 characters (20%). Context usage: 50000/100000 tokens (50%).",
|
||||
})
|
||||
|
||||
// Mock readLines to return truncated content
|
||||
vi.mocked(readLines).mockResolvedValue("Line 1\nLine 2\n...truncated...")
|
||||
// Mock readPartialContent to return truncated content
|
||||
vi.mocked(readPartialContent).mockResolvedValue({
|
||||
content: "Line 1\nLine 2\n...truncated...",
|
||||
charactersRead: 50000,
|
||||
totalCharacters: 250000,
|
||||
linesRead: 1000,
|
||||
totalLines: 5000,
|
||||
lastLineRead: 1000,
|
||||
})
|
||||
|
||||
const result = await executeReadFileTool(
|
||||
{ args: `<file><path>large-file.ts</path></file>` },
|
||||
{ totalLines: 5000, maxReadFileLine: -1 },
|
||||
)
|
||||
|
||||
// Verify the result contains both the notice AND the line_range instructions
|
||||
// Verify the result contains the partial read notice for multi-line files
|
||||
expect(result).toContain("<notice>")
|
||||
expect(result).toContain("File exceeds available context space")
|
||||
expect(result).toContain("tools:readFile.contextLimitInstructions</notice>")
|
||||
expect(result).toContain("tools:readFile.partialReadMultiLine")
|
||||
// The current implementation doesn't include contextLimitInstructions
|
||||
expect(result).not.toContain("tools:readFile.contextLimitInstructions")
|
||||
})
|
||||
|
||||
it("should handle normal file read section for single-line files with validation notice", async () => {
|
||||
|
|
@ -1461,12 +1492,19 @@ describe("read_file tool XML output structure", () => {
|
|||
// Mock contextValidator to return shouldLimit true with a single-line file notice
|
||||
vi.mocked(contextValidatorModule.validateFileSizeForContext).mockResolvedValue({
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 1,
|
||||
reason: "Large single-line file (likely minified) exceeds available context space. Only the first 80% can be loaded.",
|
||||
safeContentLimit: 8000,
|
||||
reason: "Large single-line file (likely minified) exceeds available context space. Only the first 80% (8000 of 10000 characters) can be loaded.",
|
||||
})
|
||||
|
||||
// Mock extractTextFromFile
|
||||
vi.mocked(extractTextFromFile).mockResolvedValue("1 | const a=1;const b=2;const c=3;")
|
||||
// Mock readPartialContent for single-line file
|
||||
vi.mocked(readPartialContent).mockResolvedValue({
|
||||
content: "const a=1;const b=2;const c=3;",
|
||||
charactersRead: 8000,
|
||||
totalCharacters: 10000,
|
||||
linesRead: 1,
|
||||
totalLines: 1,
|
||||
lastLineRead: 1,
|
||||
})
|
||||
|
||||
const result = await executeReadFileTool(
|
||||
{ args: `<file><path>semi-large.js</path></file>` },
|
||||
|
|
@ -1475,7 +1513,7 @@ describe("read_file tool XML output structure", () => {
|
|||
|
||||
// Verify single-line file notice doesn't include line_range instructions
|
||||
expect(result).toContain("<notice>")
|
||||
expect(result).toContain("Large single-line file")
|
||||
expect(result).toContain("tools:readFile.partialReadSingleLine")
|
||||
expect(result).not.toContain("tools:readFile.contextLimitInstructions")
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Task } from "../task/Task"
|
||||
import { readLines } from "../../integrations/misc/read-lines"
|
||||
import { readPartialSingleLineContent } from "../../integrations/misc/read-partial-content"
|
||||
import { readPartialContent } from "../../integrations/misc/read-partial-content"
|
||||
import { getModelMaxOutputTokens, getFormatForProvider } from "../../shared/api"
|
||||
import * as fs from "fs/promises"
|
||||
|
||||
|
|
@ -16,19 +15,19 @@ const FILE_READ_BUFFER_PERCENTAGE = 0.25 // 25% buffer for file reads
|
|||
*/
|
||||
const CHARS_PER_TOKEN_ESTIMATE = 3
|
||||
const CUTBACK_PERCENTAGE = 0.2 // 20% reduction when over limit
|
||||
const READ_BATCH_SIZE = 50 // Read 50 lines at a time for efficiency
|
||||
const MAX_API_CALLS = 5 // Safety limit to prevent infinite loops
|
||||
const MIN_USEFUL_LINES = 50 // Minimum lines to consider useful
|
||||
const MIN_USEFUL_CHARS = 1000 // Minimum characters to consider useful
|
||||
|
||||
/**
|
||||
* File size thresholds for heuristics
|
||||
*/
|
||||
const TINY_FILE_SIZE = 5 * 1024 // 5KB - definitely safe to skip validation
|
||||
const SMALL_FILE_SIZE = 100 * 1024 // 100KB - safe if context is mostly empty
|
||||
const LARGE_FILE_SIZE = 1024 * 1024 // 1MB - skip tokenizer for speed, use cutback percentage
|
||||
|
||||
export interface ContextValidationResult {
|
||||
shouldLimit: boolean
|
||||
safeContentLimit: number // For single-line files, this represents character count; for multi-line files, it's line count
|
||||
safeContentLimit: number // Always represents character count
|
||||
reason?: string
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +74,7 @@ async function getContextInfo(cline: Task): Promise<ContextInfo> {
|
|||
* Returns true if we're confident the file can be read without limits.
|
||||
* Prioritizes accuracy - only skips when very confident.
|
||||
*/
|
||||
async function shouldSkipValidation(filePath: string, totalLines: number, cline: Task): Promise<boolean> {
|
||||
async function shouldSkipValidation(filePath: string, cline: Task): Promise<boolean> {
|
||||
try {
|
||||
// Get file size
|
||||
const stats = await fs.stat(filePath)
|
||||
|
|
@ -112,181 +111,12 @@ async function shouldSkipValidation(filePath: string, totalLines: number, cline:
|
|||
}
|
||||
|
||||
/**
|
||||
* Detects if a file is effectively a single-line file (1-5 lines with only one non-empty line)
|
||||
* This handles cases where minified files might have a few empty lines but are essentially single-line
|
||||
* TODO: make this more robust
|
||||
*/
|
||||
async function isEffectivelySingleLine(filePath: string, totalLines: number): Promise<boolean> {
|
||||
// Only check files with 1-5 lines
|
||||
if (totalLines < 1 || totalLines > 5) {
|
||||
return false
|
||||
}
|
||||
|
||||
// Single line files are always effectively single line
|
||||
if (totalLines === 1) {
|
||||
return true
|
||||
}
|
||||
|
||||
try {
|
||||
// Check if file is big (>100KB) and lines 2-5 are empty
|
||||
const stats = await fs.stat(filePath)
|
||||
const fileSizeBytes = stats.size
|
||||
|
||||
// Only apply this logic to big files
|
||||
if (fileSizeBytes < 100 * 1024) {
|
||||
// Less than 100KB
|
||||
return false
|
||||
}
|
||||
|
||||
// Read all lines to check if lines 2-5 are empty
|
||||
const content = await readLines(filePath, totalLines - 1, 0)
|
||||
const lines = content.split("\n")
|
||||
|
||||
// Check if lines 2-5 (indices 1-4) are empty
|
||||
let hasEmptyLines2to5 = true
|
||||
for (let i = 1; i < Math.min(lines.length, 5); i++) {
|
||||
if (lines[i].trim().length > 0) {
|
||||
hasEmptyLines2to5 = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[isEffectivelySingleLine] File ${filePath}: totalLines=${totalLines}, fileSize=${(fileSizeBytes / 1024).toFixed(1)}KB, hasEmptyLines2to5=${hasEmptyLines2to5}`,
|
||||
)
|
||||
|
||||
return hasEmptyLines2to5
|
||||
} catch (error) {
|
||||
console.warn(`[isEffectivelySingleLine] Error checking file ${filePath}: ${error}`)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a single-line file (likely minified) to see if it fits in context
|
||||
* Uses character-based estimation only (no token validation to avoid API hangs)
|
||||
* TODO: handle 2-phase validation once we have better partial line reading
|
||||
*/
|
||||
async function validateSingleLineFile(
|
||||
filePath: string,
|
||||
cline: Task,
|
||||
contextInfo: ContextInfo,
|
||||
): Promise<ContextValidationResult | null> {
|
||||
console.log(
|
||||
`[validateFileSizeForContext] Single-line file detected: ${filePath} - using character-based estimation`,
|
||||
)
|
||||
|
||||
try {
|
||||
// Use char heuristic to estimate safe content size
|
||||
const estimatedSafeChars = contextInfo.targetTokenLimit * CHARS_PER_TOKEN_ESTIMATE
|
||||
|
||||
// Get file size
|
||||
const stats = await fs.stat(filePath)
|
||||
const fullFileSize = stats.size
|
||||
|
||||
// If file is smaller than our estimated safe chars, it should fit
|
||||
if (fullFileSize <= estimatedSafeChars) {
|
||||
console.log(
|
||||
`[validateFileSizeForContext] Single-line file fits within estimated safe chars (${fullFileSize} <= ${estimatedSafeChars})`,
|
||||
)
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
// File is larger than estimated safe chars
|
||||
const percentageRead = Math.round((estimatedSafeChars / fullFileSize) * 100)
|
||||
console.log(
|
||||
`[validateFileSizeForContext] Single-line file exceeds estimated safe chars (${fullFileSize} > ${estimatedSafeChars}), limiting to ${percentageRead}%`,
|
||||
)
|
||||
|
||||
// Special case: if we can't read any meaningful content
|
||||
if (estimatedSafeChars === 0 || percentageRead === 0) {
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 0,
|
||||
reason: `Single-line file is too large to read any portion. File size: ${fullFileSize} characters. Available context space: ${contextInfo.availableTokensForFile} tokens. This file cannot be accessed.`,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: estimatedSafeChars, // Return character count limit
|
||||
reason: `Large single-line file (likely minified) exceeds available context space. Only the first ${percentageRead}% (${estimatedSafeChars} of ${fullFileSize} characters) can be loaded. Context: ${contextInfo.currentlyUsed}/${contextInfo.contextWindow} tokens used (${Math.round((contextInfo.currentlyUsed / contextInfo.contextWindow) * 100)}%). This is a hard limit - no additional content from this file can be accessed.`,
|
||||
}
|
||||
} catch (error) {
|
||||
// Check for specific error types that indicate memory issues
|
||||
if (error instanceof Error) {
|
||||
const errorMessage = error.message.toLowerCase()
|
||||
if (
|
||||
errorMessage.includes("heap") ||
|
||||
errorMessage.includes("memory") ||
|
||||
errorMessage.includes("allocation")
|
||||
) {
|
||||
// Return a safe fallback instead of crashing
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 0,
|
||||
reason: `File is too large to process due to memory constraints. Error: ${error.message}. This file cannot be accessed.`,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.warn(`[validateFileSizeForContext] Error processing single-line file: ${error}`)
|
||||
return null // Fall through to regular validation for other errors
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads file content in batches up to the estimated safe character limit
|
||||
*/
|
||||
async function readFileInBatches(
|
||||
filePath: string,
|
||||
totalLines: number,
|
||||
estimatedSafeChars: number,
|
||||
): Promise<{ content: string; lineCount: number; lineToCharMap: Map<number, number> }> {
|
||||
let accumulatedContent = ""
|
||||
let currentLine = 0
|
||||
const lineToCharMap: Map<number, number> = new Map()
|
||||
|
||||
// Track the start position of each line for potential cutback
|
||||
lineToCharMap.set(0, 0)
|
||||
|
||||
// Read until we hit our estimated character limit or EOF
|
||||
while (currentLine < totalLines && accumulatedContent.length < estimatedSafeChars) {
|
||||
const batchEndLine = Math.min(currentLine + READ_BATCH_SIZE - 1, totalLines - 1)
|
||||
|
||||
try {
|
||||
const batchContent = await readLines(filePath, batchEndLine, currentLine)
|
||||
|
||||
// Track line positions within the accumulated content
|
||||
let localPos = 0
|
||||
for (let lineNum = currentLine; lineNum <= batchEndLine; lineNum++) {
|
||||
const nextNewline = batchContent.indexOf("\n", localPos)
|
||||
if (nextNewline !== -1) {
|
||||
lineToCharMap.set(lineNum + 1, accumulatedContent.length + nextNewline + 1)
|
||||
localPos = nextNewline + 1
|
||||
}
|
||||
}
|
||||
|
||||
accumulatedContent += batchContent
|
||||
currentLine = batchEndLine + 1
|
||||
} catch (error) {
|
||||
console.warn(`[validateFileSizeForContext] Error reading batch: ${error}`)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return { content: accumulatedContent, lineCount: currentLine, lineToCharMap }
|
||||
}
|
||||
|
||||
/**
|
||||
* Shared function to validate content with actual API and apply cutback if needed
|
||||
* Works for both single-line and multi-line content
|
||||
* Validates content with actual API and applies cutback if needed
|
||||
*/
|
||||
async function validateAndCutbackContent(
|
||||
content: string,
|
||||
targetTokenLimit: number,
|
||||
cline: Task,
|
||||
isSingleLine: boolean = false,
|
||||
): Promise<{ finalContent: string; actualTokens: number; didCutback: boolean }> {
|
||||
let finalContent = content
|
||||
let apiCallCount = 0
|
||||
|
|
@ -300,7 +130,7 @@ async function validateAndCutbackContent(
|
|||
actualTokens = await cline.api.countTokens([{ type: "text", text: finalContent }])
|
||||
|
||||
console.log(
|
||||
`[validateFileSizeForContext] API call ${apiCallCount}: ${actualTokens} tokens for ${finalContent.length} chars${isSingleLine ? " (single-line)" : ""}`,
|
||||
`[validateFileSizeForContext] API call ${apiCallCount}: ${actualTokens} tokens for ${finalContent.length} chars`,
|
||||
)
|
||||
|
||||
if (actualTokens <= targetTokenLimit) {
|
||||
|
|
@ -323,58 +153,11 @@ async function validateAndCutbackContent(
|
|||
return { finalContent, actualTokens, didCutback }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates content with actual API and cuts back if needed (for multi-line files)
|
||||
*/
|
||||
async function validateAndAdjustContent(
|
||||
accumulatedContent: string,
|
||||
initialLineCount: number,
|
||||
lineToCharMap: Map<number, number>,
|
||||
targetTokenLimit: number,
|
||||
totalLines: number,
|
||||
cline: Task,
|
||||
): Promise<{ finalContent: string; finalLineCount: number }> {
|
||||
// Use the shared validation function
|
||||
const { finalContent, didCutback } = await validateAndCutbackContent(
|
||||
accumulatedContent,
|
||||
targetTokenLimit,
|
||||
cline,
|
||||
false,
|
||||
)
|
||||
|
||||
// If no cutback was needed, return original line count
|
||||
if (!didCutback) {
|
||||
return { finalContent, finalLineCount: initialLineCount }
|
||||
}
|
||||
|
||||
// Find the line that corresponds to the cut content length
|
||||
let cutoffLine = 0
|
||||
for (const [lineNum, charPos] of lineToCharMap.entries()) {
|
||||
if (charPos > finalContent.length) {
|
||||
break
|
||||
}
|
||||
cutoffLine = lineNum
|
||||
}
|
||||
|
||||
// Ensure we don't cut back too far
|
||||
if (cutoffLine < 10) {
|
||||
console.warn(`[validateFileSizeForContext] Cutback resulted in too few lines (${cutoffLine}), using minimum`)
|
||||
cutoffLine = Math.min(MIN_USEFUL_LINES, totalLines)
|
||||
}
|
||||
|
||||
// Get the character position for the cutoff line
|
||||
const cutoffCharPos = lineToCharMap.get(cutoffLine) || 0
|
||||
const adjustedContent = accumulatedContent.substring(0, cutoffCharPos)
|
||||
|
||||
return { finalContent: adjustedContent, finalLineCount: cutoffLine }
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles error cases with conservative fallback
|
||||
*/
|
||||
async function handleValidationError(
|
||||
filePath: string,
|
||||
totalLines: number,
|
||||
currentMaxReadFileLine: number,
|
||||
error: unknown,
|
||||
): Promise<ContextValidationResult> {
|
||||
|
|
@ -387,27 +170,35 @@ async function handleValidationError(
|
|||
|
||||
// Very small files are safe
|
||||
if (fileSizeBytes < TINY_FILE_SIZE) {
|
||||
return { shouldLimit: false, safeContentLimit: currentMaxReadFileLine }
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
// For larger files, apply a conservative character limit
|
||||
if (fileSizeBytes > 1024 * 1024) {
|
||||
// > 1MB
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 50000, // 50K chars as a safe fallback
|
||||
reason: "Large file detected. Limited to 50,000 characters to prevent context overflow (runtime state unavailable).",
|
||||
}
|
||||
}
|
||||
} catch (statError) {
|
||||
// If we can't even stat the file, proceed with conservative defaults
|
||||
// If we can't even stat the file, proceed with very conservative defaults
|
||||
console.warn(`[validateFileSizeForContext] Could not stat file: ${statError}`)
|
||||
}
|
||||
|
||||
if (totalLines > 10000) {
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: 1000,
|
||||
reason: "Large file detected (>10,000 lines). Limited to 1000 lines to prevent context overflow (runtime state unavailable).",
|
||||
safeContentLimit: 10000, // 10K chars as ultra-safe fallback
|
||||
reason: "Unable to determine file size. Limited to 10,000 characters as a precaution.",
|
||||
}
|
||||
}
|
||||
return { shouldLimit: false, safeContentLimit: currentMaxReadFileLine }
|
||||
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if a file can be safely read based on its size and current runtime context state.
|
||||
* Uses a 2-phase approach: character-based estimation followed by actual token validation.
|
||||
* Returns a safe maxReadFileLine value to prevent context overflow.
|
||||
* Returns a safe character limit to prevent context overflow.
|
||||
*/
|
||||
export async function validateFileSizeForContext(
|
||||
filePath: string,
|
||||
|
|
@ -417,63 +208,96 @@ export async function validateFileSizeForContext(
|
|||
): Promise<ContextValidationResult> {
|
||||
try {
|
||||
// Check if we can skip validation
|
||||
if (await shouldSkipValidation(filePath, totalLines, cline)) {
|
||||
return { shouldLimit: false, safeContentLimit: currentMaxReadFileLine }
|
||||
if (await shouldSkipValidation(filePath, cline)) {
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
// Get context information
|
||||
const contextInfo = await getContextInfo(cline)
|
||||
|
||||
// Special handling for single-line files (likely minified) or effectively single-line files
|
||||
const isEffSingleLine = await isEffectivelySingleLine(filePath, totalLines)
|
||||
if (isEffSingleLine) {
|
||||
const singleLineResult = await validateSingleLineFile(filePath, cline, contextInfo)
|
||||
if (singleLineResult) {
|
||||
return singleLineResult
|
||||
}
|
||||
// Fall through to regular validation if single-line validation failed
|
||||
// Phase 1: Estimate safe character limit based on available tokens
|
||||
const estimatedSafeChars = contextInfo.targetTokenLimit * CHARS_PER_TOKEN_ESTIMATE
|
||||
|
||||
// Get file size to check if we need to limit
|
||||
const stats = await fs.stat(filePath)
|
||||
const fileSizeBytes = stats.size
|
||||
|
||||
// If file is smaller than our estimated safe chars, it should fit
|
||||
if (fileSizeBytes <= estimatedSafeChars) {
|
||||
console.log(
|
||||
`[validateFileSizeForContext] File fits within estimated safe chars (${fileSizeBytes} <= ${estimatedSafeChars})`,
|
||||
)
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
// Phase 1: Read content up to estimated safe character limit
|
||||
const estimatedSafeChars = contextInfo.targetTokenLimit * CHARS_PER_TOKEN_ESTIMATE
|
||||
const { content, lineCount, lineToCharMap } = await readFileInBatches(filePath, totalLines, estimatedSafeChars)
|
||||
|
||||
// Phase 2: Validate with actual API and cutback if needed
|
||||
const { finalContent, finalLineCount } = await validateAndAdjustContent(
|
||||
content,
|
||||
lineCount,
|
||||
lineToCharMap,
|
||||
contextInfo.targetTokenLimit,
|
||||
totalLines,
|
||||
cline,
|
||||
// File is larger than estimated safe chars, need to validate with actual content
|
||||
console.log(
|
||||
`[validateFileSizeForContext] File exceeds estimated safe chars (${fileSizeBytes} > ${estimatedSafeChars}), validating with actual content`,
|
||||
)
|
||||
|
||||
// Log final statistics
|
||||
console.log(`[validateFileSizeForContext] Final: ${finalLineCount} lines, ${finalContent.length} chars`)
|
||||
// Phase 2: Read content up to estimated limit and validate with actual API
|
||||
const partialResult = await readPartialContent(filePath, estimatedSafeChars)
|
||||
|
||||
// For large files, skip tokenizer validation for speed and apply clean cutback percentage
|
||||
let finalContent: string
|
||||
let actualTokens: number
|
||||
let didCutback: boolean
|
||||
|
||||
if (fileSizeBytes > LARGE_FILE_SIZE) {
|
||||
// Skip tokenizer for speed reasons on large files, apply clean cutback
|
||||
const cutbackChars = Math.floor(partialResult.content.length * (1 - CUTBACK_PERCENTAGE))
|
||||
finalContent = partialResult.content.substring(0, cutbackChars)
|
||||
actualTokens = 0 // Not calculated for large files
|
||||
didCutback = cutbackChars < partialResult.content.length
|
||||
|
||||
console.log(
|
||||
`[validateFileSizeForContext] Large file (${(fileSizeBytes / 1024 / 1024).toFixed(1)}MB) - skipping tokenizer for speed, applying ${Math.round(CUTBACK_PERCENTAGE * 100)}% cutback: ${partialResult.content.length} -> ${finalContent.length} chars`,
|
||||
)
|
||||
} else {
|
||||
// Use tokenizer validation for smaller files
|
||||
const validation = await validateAndCutbackContent(
|
||||
partialResult.content,
|
||||
contextInfo.targetTokenLimit,
|
||||
cline,
|
||||
)
|
||||
finalContent = validation.finalContent
|
||||
actualTokens = validation.actualTokens
|
||||
didCutback = validation.didCutback
|
||||
}
|
||||
|
||||
// Calculate final safe character limit
|
||||
const finalSafeChars = finalContent.length
|
||||
|
||||
// Ensure we provide at least a minimum useful amount
|
||||
const finalSafeContentLimit = Math.max(MIN_USEFUL_LINES, finalLineCount)
|
||||
const safeContentLimit = Math.max(MIN_USEFUL_CHARS, finalSafeChars)
|
||||
|
||||
// If we read the entire file without exceeding the limit, no limitation needed
|
||||
if (finalLineCount >= totalLines) {
|
||||
return { shouldLimit: false, safeContentLimit: currentMaxReadFileLine }
|
||||
}
|
||||
// Log final statistics
|
||||
console.log(`[validateFileSizeForContext] Final: ${safeContentLimit} chars, ${actualTokens} tokens`)
|
||||
|
||||
// If we couldn't read even the minimum useful lines
|
||||
if (finalLineCount < MIN_USEFUL_LINES) {
|
||||
// Special case: if we can't read any meaningful content
|
||||
if (safeContentLimit === MIN_USEFUL_CHARS && finalSafeChars < MIN_USEFUL_CHARS) {
|
||||
const percentageRead = Math.round((safeContentLimit / fileSizeBytes) * 100)
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: finalSafeContentLimit,
|
||||
reason: `Very limited context space. Could only safely read ${finalLineCount} lines before exceeding token limit. Context: ${contextInfo.currentlyUsed}/${contextInfo.contextWindow} tokens used (${Math.round((contextInfo.currentlyUsed / contextInfo.contextWindow) * 100)}%). Limited to ${finalSafeContentLimit} lines. Consider using search_files or line_range for specific sections.`,
|
||||
safeContentLimit,
|
||||
reason: `Very limited context space. Can only read ${safeContentLimit} characters (${percentageRead}% of file). Context: ${contextInfo.currentlyUsed}/${contextInfo.contextWindow} tokens used (${Math.round((contextInfo.currentlyUsed / contextInfo.contextWindow) * 100)}%). Consider using search_files or line_range for specific sections.`,
|
||||
}
|
||||
}
|
||||
|
||||
// If we read the entire file without exceeding the limit, no limitation needed
|
||||
if (!didCutback && partialResult.charactersRead === fileSizeBytes) {
|
||||
return { shouldLimit: false, safeContentLimit: -1 }
|
||||
}
|
||||
|
||||
// Calculate percentage read for the notice
|
||||
const percentageRead = Math.round((safeContentLimit / fileSizeBytes) * 100)
|
||||
|
||||
return {
|
||||
shouldLimit: true,
|
||||
safeContentLimit: finalSafeContentLimit,
|
||||
reason: `File exceeds available context space. Safely read ${finalSafeContentLimit} lines out of ${totalLines} total lines. Context usage: ${contextInfo.currentlyUsed}/${contextInfo.contextWindow} tokens (${Math.round((contextInfo.currentlyUsed / contextInfo.contextWindow) * 100)}%). Use line_range to read specific sections.`,
|
||||
safeContentLimit,
|
||||
reason: `File exceeds available context space. Can read ${safeContentLimit} of ${fileSizeBytes} characters (${percentageRead}%). Context usage: ${contextInfo.currentlyUsed}/${contextInfo.contextWindow} tokens (${Math.round((contextInfo.currentlyUsed / contextInfo.contextWindow) * 100)}%).`,
|
||||
}
|
||||
} catch (error) {
|
||||
return handleValidationError(filePath, totalLines, currentMaxReadFileLine, error)
|
||||
return handleValidationError(filePath, currentMaxReadFileLine, error)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { isPathOutsideWorkspace } from "../../utils/pathUtils"
|
|||
import { getReadablePath } from "../../utils/path"
|
||||
import { countFileLines } from "../../integrations/misc/line-counter"
|
||||
import { readLines } from "../../integrations/misc/read-lines"
|
||||
import { readPartialSingleLineContent } from "../../integrations/misc/read-partial-content"
|
||||
import { readPartialContent } from "../../integrations/misc/read-partial-content"
|
||||
import { extractTextFromFile, addLineNumbers, getSupportedBinaryFormats } from "../../integrations/misc/extract-text"
|
||||
import { parseSourceCodeDefinitionsForFile } from "../../services/tree-sitter"
|
||||
import { parseXml } from "../../utils/xml"
|
||||
|
|
@ -460,15 +460,10 @@ export async function readFileTool(
|
|||
|
||||
// Preemptive file size validation to prevent context overflow
|
||||
const validation = await validateFileSizeForContext(fullPath, totalLines, maxReadFileLine, cline)
|
||||
let effectiveMaxReadFileLine = maxReadFileLine
|
||||
let validationNotice = ""
|
||||
|
||||
// For single-line files, ALWAYS apply validation regardless of maxReadFileLine setting
|
||||
// For multi-line files, only apply if maxReadFileLine is -1 (unlimited)
|
||||
if (validation.shouldLimit && (totalLines === 1 || maxReadFileLine === -1)) {
|
||||
effectiveMaxReadFileLine = validation.safeContentLimit
|
||||
validationNotice = validation.reason || ""
|
||||
}
|
||||
// Apply validation if maxReadFileLine is -1 (unlimited)
|
||||
const shouldApplyValidation = validation.shouldLimit && maxReadFileLine === -1
|
||||
|
||||
// Handle binary files (but allow specific file types that extractTextFromFile can handle)
|
||||
if (isBinary) {
|
||||
|
|
@ -560,11 +555,11 @@ export async function readFileTool(
|
|||
}
|
||||
|
||||
// Handle definitions-only mode
|
||||
if (effectiveMaxReadFileLine === 0) {
|
||||
if (maxReadFileLine === 0) {
|
||||
try {
|
||||
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
|
||||
if (defResult) {
|
||||
let xmlInfo = `<notice>${t("tools:readFile.showingOnlyLines", { shown: effectiveMaxReadFileLine, total: totalLines })}</notice>\n`
|
||||
let xmlInfo = `<notice>${t("tools:readFile.showingOnlyLines", { shown: 0, total: totalLines })}</notice>\n`
|
||||
updateFileResult(relPath, {
|
||||
xmlContent: `<file><path>${relPath}</path>\n<list_code_definition_names>${defResult}</list_code_definition_names>\n${xmlInfo}</file>`,
|
||||
})
|
||||
|
|
@ -581,35 +576,14 @@ export async function readFileTool(
|
|||
continue
|
||||
}
|
||||
|
||||
// Handle files exceeding line threshold (including preemptive limits)
|
||||
// For single-line files with validation limits, ALWAYS use partial reading
|
||||
// Also check if this is an effectively single-line file (includes minified files with long lines)
|
||||
const isEffectivelySingleLine =
|
||||
totalLines <= 5 &&
|
||||
validation.shouldLimit &&
|
||||
validationNotice &&
|
||||
validationNotice.includes("single-line file")
|
||||
// Handle files with validation limits (character-based reading)
|
||||
if (shouldApplyValidation) {
|
||||
const result = await readPartialContent(fullPath, validation.safeContentLimit)
|
||||
|
||||
const shouldUsePartialRead =
|
||||
(effectiveMaxReadFileLine > 0 && totalLines > effectiveMaxReadFileLine) ||
|
||||
(totalLines === 1 && validation.shouldLimit && effectiveMaxReadFileLine > 0) ||
|
||||
(isEffectivelySingleLine && effectiveMaxReadFileLine > 0)
|
||||
// Generate line range attribute based on what was read
|
||||
const lineRangeAttr = result.linesRead === 1 ? ` lines="1"` : ` lines="1-${result.lastLineRead}"`
|
||||
|
||||
if (shouldUsePartialRead) {
|
||||
let content: string
|
||||
let lineRangeAttr: string
|
||||
|
||||
// Special handling for single-line files where effectiveMaxReadFileLine represents character count
|
||||
if (totalLines === 1 || isEffectivelySingleLine) {
|
||||
// For single-line or effectively single-line files, effectiveMaxReadFileLine is actually a character count
|
||||
const partialContent = await readPartialSingleLineContent(fullPath, effectiveMaxReadFileLine)
|
||||
content = addLineNumbers(partialContent, 1)
|
||||
lineRangeAttr = ` lines="1"`
|
||||
} else {
|
||||
// For multi-line files, use normal line-based reading
|
||||
content = addLineNumbers(await readLines(fullPath, effectiveMaxReadFileLine - 1, 0))
|
||||
lineRangeAttr = ` lines="1-${effectiveMaxReadFileLine}"`
|
||||
}
|
||||
const content = addLineNumbers(result.content, 1)
|
||||
let xmlInfo = `<content${lineRangeAttr}>\n${content}</content>\n`
|
||||
|
||||
try {
|
||||
|
|
@ -618,21 +592,62 @@ export async function readFileTool(
|
|||
xmlInfo += `<list_code_definition_names>${defResult}</list_code_definition_names>\n`
|
||||
}
|
||||
|
||||
// Add appropriate notice based on whether this was a preemptive limit or user setting
|
||||
if (validationNotice) {
|
||||
// Check if this is a single-line file
|
||||
if (totalLines === 1 && validationNotice.includes("single-line file")) {
|
||||
// For single-line files, don't suggest line_range tool
|
||||
xmlInfo += `<notice>${validationNotice}</notice>\n`
|
||||
} else {
|
||||
// For multi-line files, provide inline instructions to use line_range
|
||||
const instructions = t("tools:readFile.contextLimitInstructions", { path: relPath })
|
||||
xmlInfo += `<notice>${validationNotice}\n\n${instructions}</notice>\n`
|
||||
}
|
||||
// Generate notice based on what was read
|
||||
const percentRead = Math.round((result.charactersRead / result.totalCharacters) * 100)
|
||||
if (result.linesRead === 1) {
|
||||
// Single-line file
|
||||
const notice = t("tools:readFile.partialReadSingleLine", {
|
||||
charactersRead: result.charactersRead,
|
||||
totalCharacters: result.totalCharacters,
|
||||
percentRead,
|
||||
})
|
||||
xmlInfo += `<notice>${notice}</notice>\n`
|
||||
} else {
|
||||
xmlInfo += `<notice>${t("tools:readFile.showingOnlyLines", { shown: effectiveMaxReadFileLine, total: totalLines })}</notice>\n`
|
||||
// Multi-line file
|
||||
const nextLineStart = result.lastLineRead + 1
|
||||
const suggestedLineEnd = Math.min(result.lastLineRead + 1000, result.totalLines)
|
||||
const notice = t("tools:readFile.partialReadMultiLine", {
|
||||
charactersRead: result.charactersRead,
|
||||
totalCharacters: result.totalCharacters,
|
||||
percentRead,
|
||||
lastLineRead: result.lastLineRead,
|
||||
totalLines: result.totalLines,
|
||||
path: relPath,
|
||||
nextLineStart,
|
||||
suggestedLineEnd,
|
||||
})
|
||||
xmlInfo += `<notice>${notice}</notice>\n`
|
||||
}
|
||||
|
||||
const finalXml = `<file><path>${relPath}</path>\n${xmlInfo}</file>`
|
||||
updateFileResult(relPath, {
|
||||
xmlContent: finalXml,
|
||||
})
|
||||
} catch (error) {
|
||||
if (error instanceof Error && error.message.startsWith("Unsupported language:")) {
|
||||
console.warn(`[read_file] Warning: ${error.message}`)
|
||||
} else {
|
||||
console.error(
|
||||
`[read_file] Unhandled error: ${error instanceof Error ? error.message : String(error)}`,
|
||||
)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Handle files with line limits (maxReadFileLine > 0)
|
||||
if (maxReadFileLine > 0 && totalLines > maxReadFileLine) {
|
||||
const content = addLineNumbers(await readLines(fullPath, maxReadFileLine - 1, 0))
|
||||
const lineRangeAttr = ` lines="1-${maxReadFileLine}"`
|
||||
let xmlInfo = `<content${lineRangeAttr}>\n${content}</content>\n`
|
||||
|
||||
try {
|
||||
const defResult = await parseSourceCodeDefinitionsForFile(fullPath, cline.rooIgnoreController)
|
||||
if (defResult) {
|
||||
xmlInfo += `<list_code_definition_names>${defResult}</list_code_definition_names>\n`
|
||||
}
|
||||
xmlInfo += `<notice>${t("tools:readFile.showingOnlyLines", { shown: maxReadFileLine, total: totalLines })}</notice>\n`
|
||||
|
||||
updateFileResult(relPath, {
|
||||
xmlContent: `<file><path>${relPath}</path>\n${xmlInfo}</file>`,
|
||||
})
|
||||
|
|
@ -648,19 +663,7 @@ export async function readFileTool(
|
|||
continue
|
||||
}
|
||||
|
||||
// Handle normal file read
|
||||
// CRITICAL: Check if this is a single-line or effectively single-line file that should have been limited
|
||||
const isEffSingleLine =
|
||||
totalLines <= 5 && validationNotice && validationNotice.includes("single-line file")
|
||||
if ((totalLines === 1 || isEffSingleLine) && validation.shouldLimit) {
|
||||
console.error(
|
||||
`[read_file] ERROR: ${isEffSingleLine ? "Effectively " : ""}Single-line file ${relPath} with validation limits is being read in full! This should not happen.`,
|
||||
)
|
||||
console.error(
|
||||
`[read_file] Debug info: effectiveMaxReadFileLine=${effectiveMaxReadFileLine}, validation.safeContentLimit=${validation.safeContentLimit}`,
|
||||
)
|
||||
}
|
||||
|
||||
// Handle normal file read (no limits)
|
||||
const content = await extractTextFromFile(fullPath)
|
||||
|
||||
const lineRangeAttr = ` lines="1-${totalLines}"`
|
||||
|
|
@ -668,16 +671,6 @@ export async function readFileTool(
|
|||
|
||||
if (totalLines === 0) {
|
||||
xmlInfo += `<notice>File is empty</notice>\n`
|
||||
} else if (validationNotice) {
|
||||
// Check if this is a single-line file
|
||||
if (totalLines === 1 && validationNotice.includes("single-line file")) {
|
||||
// For single-line files, don't suggest line_range tool
|
||||
xmlInfo += `<notice>${validationNotice}</notice>\n`
|
||||
} else {
|
||||
// For multi-line files, provide inline instructions to use line_range
|
||||
const instructions = t("tools:readFile.contextLimitInstructions", { path: relPath })
|
||||
xmlInfo += `<notice>${validationNotice}\n\n${instructions}</notice>\n`
|
||||
}
|
||||
}
|
||||
|
||||
// Track file read
|
||||
|
|
|
|||
5
src/i18n/locales/ca/tools.json
generated
5
src/i18n/locales/ca/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (només definicions)",
|
||||
"maxLines": " (màxim {{max}} línies)",
|
||||
"showingOnlyLines": "Mostrant només {{shown}} de {{total}} línies totals. Utilitza line_range si necessites llegir més línies",
|
||||
"contextLimitInstructions": "Per llegir seccions específiques d'aquest fitxer, utilitza el següent format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>inici-final</line_range>\n </file>\n</args>\n</read_file>\n\nPer exemple, per llegir les línies 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "El fitxer d'imatge és massa gran ({{size}} MB). La mida màxima permesa és {{max}} MB.",
|
||||
"imageWithSize": "Fitxer d'imatge ({{size}} KB)"
|
||||
"imageWithSize": "Fitxer d'imatge ({{size}} KB)",
|
||||
"partialReadSingleLine": "Llegits {{charactersRead}} de {{totalCharacters}} caràcters ({{percentRead}}%) d'aquest fitxer d'una sola línia. Aquesta és una lectura parcial - el contingut restant no es pot accedir a causa de limitacions de context.",
|
||||
"partialReadMultiLine": "Llegits {{charactersRead}} de {{totalCharacters}} caràcters ({{percentRead}}%), fins a la línia {{lastLineRead}} de {{totalLines}}. Per llegir seccions específiques d'aquest fitxer, utilitza el següent format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nPer exemple, per llegir les línies {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo sembla estar atrapat en un bucle, intentant la mateixa acció ({{toolName}}) repetidament. Això podria indicar un problema amb la seva estratègia actual. Considera reformular la tasca, proporcionar instruccions més específiques o guiar-lo cap a un enfocament diferent.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/de/tools.json
generated
5
src/i18n/locales/de/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (nur Definitionen)",
|
||||
"maxLines": " (maximal {{max}} Zeilen)",
|
||||
"showingOnlyLines": "Zeige nur {{shown}} von {{total}} Zeilen insgesamt. Verwende line_range, wenn du mehr Zeilen lesen musst",
|
||||
"contextLimitInstructions": "Um bestimmte Abschnitte dieser Datei zu lesen, verwende das folgende Format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-ende</line_range>\n </file>\n</args>\n</read_file>\n\nZum Beispiel, um die Zeilen 2001-3000 zu lesen:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Die Bilddatei ist zu groß ({{size}} MB). Die maximal erlaubte Größe beträgt {{max}} MB.",
|
||||
"imageWithSize": "Bilddatei ({{size}} KB)"
|
||||
"imageWithSize": "Bilddatei ({{size}} KB)",
|
||||
"partialReadSingleLine": "{{charactersRead}} von {{totalCharacters}} Zeichen ({{percentRead}}%) aus dieser einzeiligen Datei gelesen. Dies ist ein partieller Lesevorgang - der verbleibende Inhalt kann aufgrund von Kontextbeschränkungen nicht zugegriffen werden.",
|
||||
"partialReadMultiLine": "{{charactersRead}} von {{totalCharacters}} Zeichen ({{percentRead}}%) gelesen, bis Zeile {{lastLineRead}} von {{totalLines}}. Um bestimmte Abschnitte dieser Datei zu lesen, verwende das folgende Format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nZum Beispiel, um die Zeilen {{nextLineStart}}-{{suggestedLineEnd}} zu lesen:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo scheint in einer Schleife festzustecken und versucht wiederholt dieselbe Aktion ({{toolName}}). Dies könnte auf ein Problem mit der aktuellen Strategie hindeuten. Überlege dir, die Aufgabe umzuformulieren, genauere Anweisungen zu geben oder Roo zu einem anderen Ansatz zu führen.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@
|
|||
"definitionsOnly": " (definitions only)",
|
||||
"maxLines": " (max {{max}} lines)",
|
||||
"showingOnlyLines": "Showing only {{shown}} of {{total}} total lines. Use line_range if you need to read more lines",
|
||||
"contextLimitInstructions": "To read specific sections of this file, use the following format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nFor example, to read lines 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"partialReadSingleLine": "Read {{charactersRead}} of {{totalCharacters}} characters ({{percentRead}}%) from this single-line file. This is a partial read - the remaining content cannot be accessed due to context limitations.",
|
||||
"partialReadMultiLine": "Read {{charactersRead}} of {{totalCharacters}} characters ({{percentRead}}%), up to line {{lastLineRead}} of {{totalLines}}. To read specific sections of this file, use the following format:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nFor example, to read lines {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Image file is too large ({{size}} MB). The maximum allowed size is {{max}} MB.",
|
||||
"imageWithSize": "Image file ({{size}} KB)"
|
||||
},
|
||||
|
|
|
|||
5
src/i18n/locales/es/tools.json
generated
5
src/i18n/locales/es/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (solo definiciones)",
|
||||
"maxLines": " (máximo {{max}} líneas)",
|
||||
"showingOnlyLines": "Mostrando solo {{shown}} de {{total}} líneas totales. Usa line_range si necesitas leer más líneas",
|
||||
"contextLimitInstructions": "Para leer secciones específicas de este archivo, usa el siguiente formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>inicio-fin</line_range>\n </file>\n</args>\n</read_file>\n\nPor ejemplo, para leer las líneas 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "El archivo de imagen es demasiado grande ({{size}} MB). El tamaño máximo permitido es {{max}} MB.",
|
||||
"imageWithSize": "Archivo de imagen ({{size}} KB)"
|
||||
"imageWithSize": "Archivo de imagen ({{size}} KB)",
|
||||
"partialReadSingleLine": "Leídos {{charactersRead}} de {{totalCharacters}} caracteres ({{percentRead}}%) de este archivo de una sola línea. Esta es una lectura parcial - el contenido restante no se puede acceder debido a limitaciones de contexto.",
|
||||
"partialReadMultiLine": "Leídos {{charactersRead}} de {{totalCharacters}} caracteres ({{percentRead}}%), hasta la línea {{lastLineRead}} de {{totalLines}}. Para leer secciones específicas de este archivo, usa el siguiente formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nPor ejemplo, para leer las líneas {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo parece estar atrapado en un bucle, intentando la misma acción ({{toolName}}) repetidamente. Esto podría indicar un problema con su estrategia actual. Considera reformular la tarea, proporcionar instrucciones más específicas o guiarlo hacia un enfoque diferente.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/fr/tools.json
generated
5
src/i18n/locales/fr/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (définitions uniquement)",
|
||||
"maxLines": " (max {{max}} lignes)",
|
||||
"showingOnlyLines": "Affichage de seulement {{shown}} sur {{total}} lignes totales. Utilise line_range si tu as besoin de lire plus de lignes",
|
||||
"contextLimitInstructions": "Pour lire des sections spécifiques de ce fichier, utilise le format suivant :\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>début-fin</line_range>\n </file>\n</args>\n</read_file>\n\nPar exemple, pour lire les lignes 2001-3000 :\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Le fichier image est trop volumineux ({{size}} MB). La taille maximale autorisée est {{max}} MB.",
|
||||
"imageWithSize": "Fichier image ({{size}} Ko)"
|
||||
"imageWithSize": "Fichier image ({{size}} Ko)",
|
||||
"partialReadSingleLine": "Lu {{charactersRead}} sur {{totalCharacters}} caractères ({{percentRead}}%) de ce fichier d'une seule ligne. Ceci est une lecture partielle - le contenu restant ne peut pas être accédé en raison de limitations de contexte.",
|
||||
"partialReadMultiLine": "Lu {{charactersRead}} sur {{totalCharacters}} caractères ({{percentRead}}%), jusqu'à la ligne {{lastLineRead}} sur {{totalLines}}. Pour lire des sections spécifiques de ce fichier, utilise le format suivant :\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nPar exemple, pour lire les lignes {{nextLineStart}}-{{suggestedLineEnd}} :\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo semble être bloqué dans une boucle, tentant la même action ({{toolName}}) de façon répétée. Cela pourrait indiquer un problème avec sa stratégie actuelle. Envisage de reformuler la tâche, de fournir des instructions plus spécifiques ou de le guider vers une approche différente.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/hi/tools.json
generated
5
src/i18n/locales/hi/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (केवल परिभाषाएँ)",
|
||||
"maxLines": " (अधिकतम {{max}} पंक्तियाँ)",
|
||||
"showingOnlyLines": "कुल {{total}} पंक्तियों में से केवल {{shown}} दिखा रहे हैं। यदि आपको अधिक पंक्तियाँ पढ़नी हैं तो line_range का उपयोग करें",
|
||||
"contextLimitInstructions": "इस फ़ाइल के विशिष्ट भागों को पढ़ने के लिए, निम्नलिखित प्रारूप का उपयोग करें:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>शुरुआत-अंत</line_range>\n </file>\n</args>\n</read_file>\n\nउदाहरण के लिए, पंक्ति 2001-3000 पढ़ने के लिए:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "छवि फ़ाइल बहुत बड़ी है ({{size}} MB)। अधिकतम अनुमतित आकार {{max}} MB है।",
|
||||
"imageWithSize": "छवि फ़ाइल ({{size}} KB)"
|
||||
"imageWithSize": "छवि फ़ाइल ({{size}} KB)",
|
||||
"partialReadSingleLine": "इस एकल-पंक्ति फ़ाइल से {{charactersRead}} में से {{totalCharacters}} वर्ण ({{percentRead}}%) पढ़े गए। यह एक आंशिक पठन है - शेष सामग्री संदर्भ सीमाओं के कारण पहुंच योग्य नहीं है।",
|
||||
"partialReadMultiLine": "{{charactersRead}} में से {{totalCharacters}} वर्ण ({{percentRead}}%) पढ़े गए, {{totalLines}} में से पंक्ति {{lastLineRead}} तक। इस फ़ाइल के विशिष्ट अनुभागों को पढ़ने के लिए, निम्नलिखित प्रारूप का उपयोग करें:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nउदाहरण के लिए, पंक्तियां {{nextLineStart}}-{{suggestedLineEnd}} पढ़ने के लिए:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo एक लूप में फंसा हुआ लगता है, बार-बार एक ही क्रिया ({{toolName}}) को दोहरा रहा है। यह उसकी वर्तमान रणनीति में किसी समस्या का संकेत हो सकता है। कार्य को पुनः परिभाषित करने, अधिक विशिष्ट निर्देश देने, या उसे एक अलग दृष्टिकोण की ओर मार्गदर्शित करने पर विचार करें।",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/id/tools.json
generated
5
src/i18n/locales/id/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (hanya definisi)",
|
||||
"maxLines": " (maks {{max}} baris)",
|
||||
"showingOnlyLines": "Menampilkan hanya {{shown}} dari {{total}} total baris. Gunakan line_range jika kamu perlu membaca lebih banyak baris",
|
||||
"contextLimitInstructions": "Untuk membaca bagian tertentu dari file ini, gunakan format berikut:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>awal-akhir</line_range>\n </file>\n</args>\n</read_file>\n\nContohnya, untuk membaca baris 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "File gambar terlalu besar ({{size}} MB). Ukuran maksimum yang diizinkan adalah {{max}} MB.",
|
||||
"imageWithSize": "File gambar ({{size}} KB)"
|
||||
"imageWithSize": "File gambar ({{size}} KB)",
|
||||
"partialReadSingleLine": "Membaca {{charactersRead}} dari {{totalCharacters}} karakter ({{percentRead}}%) dari file satu baris ini. Ini adalah pembacaan parsial - konten yang tersisa tidak dapat diakses karena keterbatasan konteks.",
|
||||
"partialReadMultiLine": "Membaca {{charactersRead}} dari {{totalCharacters}} karakter ({{percentRead}}%), hingga baris {{lastLineRead}} dari {{totalLines}}. Untuk membaca bagian tertentu dari file ini, gunakan format berikut:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nContoh, untuk membaca baris {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo tampaknya terjebak dalam loop, mencoba aksi yang sama ({{toolName}}) berulang kali. Ini mungkin menunjukkan masalah dengan strategi saat ini. Pertimbangkan untuk mengubah frasa tugas, memberikan instruksi yang lebih spesifik, atau mengarahkannya ke pendekatan yang berbeda.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/it/tools.json
generated
5
src/i18n/locales/it/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (solo definizioni)",
|
||||
"maxLines": " (max {{max}} righe)",
|
||||
"showingOnlyLines": "Mostrando solo {{shown}} di {{total}} righe totali. Usa line_range se hai bisogno di leggere più righe",
|
||||
"contextLimitInstructions": "Per leggere sezioni specifiche di questo file, usa il seguente formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>inizio-fine</line_range>\n </file>\n</args>\n</read_file>\n\nAd esempio, per leggere le righe 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Il file immagine è troppo grande ({{size}} MB). La dimensione massima consentita è {{max}} MB.",
|
||||
"imageWithSize": "File immagine ({{size}} KB)"
|
||||
"imageWithSize": "File immagine ({{size}} KB)",
|
||||
"partialReadSingleLine": "Letti {{charactersRead}} di {{totalCharacters}} caratteri ({{percentRead}}%) da questo file a riga singola. Questa è una lettura parziale - il contenuto rimanente non può essere accessibile a causa di limitazioni di contesto.",
|
||||
"partialReadMultiLine": "Letti {{charactersRead}} di {{totalCharacters}} caratteri ({{percentRead}}%), fino alla riga {{lastLineRead}} di {{totalLines}}. Per leggere sezioni specifiche di questo file, usa il seguente formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nAd esempio, per leggere le righe {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo sembra essere bloccato in un ciclo, tentando ripetutamente la stessa azione ({{toolName}}). Questo potrebbe indicare un problema con la sua strategia attuale. Considera di riformulare l'attività, fornire istruzioni più specifiche o guidarlo verso un approccio diverso.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/ja/tools.json
generated
5
src/i18n/locales/ja/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (定義のみ)",
|
||||
"maxLines": " (最大{{max}}行)",
|
||||
"showingOnlyLines": "全{{total}}行中{{shown}}行のみ表示しています。より多くの行を読む必要がある場合はline_rangeを使用してください",
|
||||
"contextLimitInstructions": "このファイルの特定のセクションを読むには、以下の形式を使用してください:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>開始-終了</line_range>\n </file>\n</args>\n</read_file>\n\n例えば、2001-3000行目を読むには:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "画像ファイルが大きすぎます({{size}} MB)。最大許可サイズは {{max}} MB です。",
|
||||
"imageWithSize": "画像ファイル({{size}} KB)"
|
||||
"imageWithSize": "画像ファイル({{size}} KB)",
|
||||
"partialReadSingleLine": "この単一行ファイルから{{charactersRead}}文字中{{totalCharacters}}文字({{percentRead}}%)を読み取りました。これは部分的な読み取りです - コンテキストの制限により、残りのコンテンツにはアクセスできません。",
|
||||
"partialReadMultiLine": "{{charactersRead}}文字中{{totalCharacters}}文字({{percentRead}}%)を読み取りました。{{totalLines}}行中{{lastLineRead}}行目まで。このファイルの特定のセクションを読み取るには、次の形式を使用してください:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\n例えば、{{nextLineStart}}-{{suggestedLineEnd}}行を読み取るには:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Rooが同じ操作({{toolName}})を繰り返し試みるループに陥っているようです。これは現在の方法に問題がある可能性を示しています。タスクの言い換え、より具体的な指示の提供、または別のアプローチへの誘導を検討してください。",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/ko/tools.json
generated
5
src/i18n/locales/ko/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (정의만)",
|
||||
"maxLines": " (최대 {{max}}행)",
|
||||
"showingOnlyLines": "전체 {{total}}행 중 {{shown}}행만 표시하고 있습니다. 더 많은 행을 읽으려면 line_range를 사용하세요",
|
||||
"contextLimitInstructions": "이 파일의 특정 섹션을 읽으려면 다음 형식을 사용하세요:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>시작-끝</line_range>\n </file>\n</args>\n</read_file>\n\n예를 들어, 2001-3000행을 읽으려면:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "이미지 파일이 너무 큽니다 ({{size}} MB). 최대 허용 크기는 {{max}} MB입니다.",
|
||||
"imageWithSize": "이미지 파일 ({{size}} KB)"
|
||||
"imageWithSize": "이미지 파일 ({{size}} KB)",
|
||||
"partialReadSingleLine": "이 단일 행 파일에서 {{totalCharacters}}자 중 {{charactersRead}}자 ({{percentRead}}%)를 읽었습니다. 이는 부분 읽기입니다 - 컨텍스트 제한으로 인해 나머지 내용에 액세스할 수 없습니다.",
|
||||
"partialReadMultiLine": "{{totalCharacters}}자 중 {{charactersRead}}자 ({{percentRead}}%)를 읽었습니다. {{totalLines}}행 중 {{lastLineRead}}행까지입니다. 이 파일의 특정 섹션을 읽으려면 다음 형식을 사용하세요:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\n예를 들어, {{nextLineStart}}-{{suggestedLineEnd}}행을 읽으려면:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo가 같은 동작({{toolName}})을 반복적으로 시도하면서 루프에 갇힌 것 같습니다. 이는 현재 전략에 문제가 있을 수 있음을 나타냅니다. 작업을 다시 표현하거나, 더 구체적인 지침을 제공하거나, 다른 접근 방식으로 안내해 보세요.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/nl/tools.json
generated
5
src/i18n/locales/nl/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (alleen definities)",
|
||||
"maxLines": " (max {{max}} regels)",
|
||||
"showingOnlyLines": "Toont alleen {{shown}} van {{total}} totale regels. Gebruik line_range als je meer regels wilt lezen",
|
||||
"contextLimitInstructions": "Om specifieke secties van dit bestand te lezen, gebruik het volgende formaat:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-eind</line_range>\n </file>\n</args>\n</read_file>\n\nBijvoorbeeld, om regels 2001-3000 te lezen:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Afbeeldingsbestand is te groot ({{size}} MB). De maximaal toegestane grootte is {{max}} MB.",
|
||||
"imageWithSize": "Afbeeldingsbestand ({{size}} KB)"
|
||||
"imageWithSize": "Afbeeldingsbestand ({{size}} KB)",
|
||||
"partialReadSingleLine": "{{charactersRead}} van {{totalCharacters}} tekens ({{percentRead}}%) gelezen van dit bestand met één regel. Dit is een gedeeltelijke lezing - de resterende inhoud is niet toegankelijk vanwege contextbeperkingen.",
|
||||
"partialReadMultiLine": "{{charactersRead}} van {{totalCharacters}} tekens ({{percentRead}}%) gelezen, tot regel {{lastLineRead}} van {{totalLines}}. Om specifieke secties van dit bestand te lezen, gebruik je het volgende formaat:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nBijvoorbeeld, om regels {{nextLineStart}}-{{suggestedLineEnd}} te lezen:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo lijkt vast te zitten in een lus, waarbij hij herhaaldelijk dezelfde actie ({{toolName}}) probeert. Dit kan duiden op een probleem met de huidige strategie. Overweeg de taak te herformuleren, specifiekere instructies te geven of Roo naar een andere aanpak te leiden.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/pl/tools.json
generated
5
src/i18n/locales/pl/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (tylko definicje)",
|
||||
"maxLines": " (maks. {{max}} linii)",
|
||||
"showingOnlyLines": "Pokazuję tylko {{shown}} z {{total}} wszystkich linii. Użyj line_range jeśli potrzebujesz przeczytać więcej linii",
|
||||
"contextLimitInstructions": "Aby przeczytać określone sekcje tego pliku, użyj następującego formatu:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>początek-koniec</line_range>\n </file>\n</args>\n</read_file>\n\nNa przykład, aby przeczytać linie 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Plik obrazu jest zbyt duży ({{size}} MB). Maksymalny dozwolony rozmiar to {{max}} MB.",
|
||||
"imageWithSize": "Plik obrazu ({{size}} KB)"
|
||||
"imageWithSize": "Plik obrazu ({{size}} KB)",
|
||||
"partialReadSingleLine": "Przeczytano {{charactersRead}} z {{totalCharacters}} znaków ({{percentRead}}%) z tego jednoliniowego pliku. To jest częściowy odczyt - pozostała zawartość nie może być dostępna z powodu ograniczeń kontekstu.",
|
||||
"partialReadMultiLine": "Przeczytano {{charactersRead}} z {{totalCharacters}} znaków ({{percentRead}}%), do linii {{lastLineRead}} z {{totalLines}}. Aby przeczytać określone sekcje tego pliku, użyj następującego formatu:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nNa przykład, aby przeczytać linie {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Wygląda na to, że Roo utknął w pętli, wielokrotnie próbując wykonać tę samą akcję ({{toolName}}). Może to wskazywać na problem z jego obecną strategią. Rozważ przeformułowanie zadania, podanie bardziej szczegółowych instrukcji lub nakierowanie go na inne podejście.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/pt-BR/tools.json
generated
5
src/i18n/locales/pt-BR/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (apenas definições)",
|
||||
"maxLines": " (máx. {{max}} linhas)",
|
||||
"showingOnlyLines": "Mostrando apenas {{shown}} de {{total}} linhas totais. Use line_range se precisar ler mais linhas",
|
||||
"contextLimitInstructions": "Para ler seções específicas deste arquivo, use o seguinte formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>início-fim</line_range>\n </file>\n</args>\n</read_file>\n\nPor exemplo, para ler as linhas 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Arquivo de imagem é muito grande ({{size}} MB). O tamanho máximo permitido é {{max}} MB.",
|
||||
"imageWithSize": "Arquivo de imagem ({{size}} KB)"
|
||||
"imageWithSize": "Arquivo de imagem ({{size}} KB)",
|
||||
"partialReadSingleLine": "Lidos {{charactersRead}} de {{totalCharacters}} caracteres ({{percentRead}}%) deste arquivo de linha única. Esta é uma leitura parcial - o conteúdo restante não pode ser acessado devido a limitações de contexto.",
|
||||
"partialReadMultiLine": "Lidos {{charactersRead}} de {{totalCharacters}} caracteres ({{percentRead}}%), até a linha {{lastLineRead}} de {{totalLines}}. Para ler seções específicas deste arquivo, use o seguinte formato:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nPor exemplo, para ler as linhas {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo parece estar preso em um loop, tentando a mesma ação ({{toolName}}) repetidamente. Isso pode indicar um problema com sua estratégia atual. Considere reformular a tarefa, fornecer instruções mais específicas ou guiá-lo para uma abordagem diferente.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/ru/tools.json
generated
5
src/i18n/locales/ru/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (только определения)",
|
||||
"maxLines": " (макс. {{max}} строк)",
|
||||
"showingOnlyLines": "Показано только {{shown}} из {{total}} общих строк. Используй line_range если нужно прочитать больше строк",
|
||||
"contextLimitInstructions": "Чтобы прочитать определенные разделы этого файла, используй следующий формат:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>начало-конец</line_range>\n </file>\n</args>\n</read_file>\n\nНапример, чтобы прочитать строки 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Файл изображения слишком большой ({{size}} МБ). Максимально допустимый размер {{max}} МБ.",
|
||||
"imageWithSize": "Файл изображения ({{size}} КБ)"
|
||||
"imageWithSize": "Файл изображения ({{size}} КБ)",
|
||||
"partialReadSingleLine": "Прочитано {{charactersRead}} из {{totalCharacters}} символов ({{percentRead}}%) из этого однострочного файла. Это частичное чтение - оставшееся содержимое недоступно из-за ограничений контекста.",
|
||||
"partialReadMultiLine": "Прочитано {{charactersRead}} из {{totalCharacters}} символов ({{percentRead}}%), до строки {{lastLineRead}} из {{totalLines}}. Чтобы прочитать определенные разделы этого файла, используйте следующий формат:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nНапример, чтобы прочитать строки {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Похоже, что Roo застрял в цикле, многократно пытаясь выполнить одно и то же действие ({{toolName}}). Это может указывать на проблему с его текущей стратегией. Попробуйте переформулировать задачу, предоставить более конкретные инструкции или направить его к другому подходу.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/tr/tools.json
generated
5
src/i18n/locales/tr/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (sadece tanımlar)",
|
||||
"maxLines": " (maks. {{max}} satır)",
|
||||
"showingOnlyLines": "Toplam {{total}} satırdan sadece {{shown}} tanesi gösteriliyor. Daha fazla satır okumak için line_range kullan",
|
||||
"contextLimitInstructions": "Bu dosyanın belirli bölümlerini okumak için aşağıdaki formatı kullan:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>başlangıç-bitiş</line_range>\n </file>\n</args>\n</read_file>\n\nÖrneğin, 2001-3000 satırlarını okumak için:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Görüntü dosyası çok büyük ({{size}} MB). İzin verilen maksimum boyut {{max}} MB.",
|
||||
"imageWithSize": "Görüntü dosyası ({{size}} KB)"
|
||||
"imageWithSize": "Görüntü dosyası ({{size}} KB)",
|
||||
"partialReadSingleLine": "Bu tek satırlık dosyadan {{totalCharacters}} karakterden {{charactersRead}} karakter ({{percentRead}}%) okundu. Bu kısmi bir okuma - kalan içeriğe bağlam sınırlamaları nedeniyle erişilemiyor.",
|
||||
"partialReadMultiLine": "{{totalCharacters}} karakterden {{charactersRead}} karakter ({{percentRead}}%) okundu, {{totalLines}} satırdan {{lastLineRead}}. satıra kadar. Bu dosyanın belirli bölümlerini okumak için aşağıdaki formatı kullan:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nÖrneğin, {{nextLineStart}}-{{suggestedLineEnd}} satırlarını okumak için:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo bir döngüye takılmış gibi görünüyor, aynı eylemi ({{toolName}}) tekrar tekrar deniyor. Bu, mevcut stratejisinde bir sorun olduğunu gösterebilir. Görevi yeniden ifade etmeyi, daha spesifik talimatlar vermeyi veya onu farklı bir yaklaşıma yönlendirmeyi düşünün.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/vi/tools.json
generated
5
src/i18n/locales/vi/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (chỉ định nghĩa)",
|
||||
"maxLines": " (tối đa {{max}} dòng)",
|
||||
"showingOnlyLines": "Chỉ hiển thị {{shown}} trong tổng số {{total}} dòng. Sử dụng line_range nếu bạn cần đọc thêm dòng",
|
||||
"contextLimitInstructions": "Để đọc các phần cụ thể của tệp này, hãy sử dụng định dạng sau:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>bắt đầu-kết thúc</line_range>\n </file>\n</args>\n</read_file>\n\nVí dụ, để đọc dòng 2001-3000:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "Tệp hình ảnh quá lớn ({{size}} MB). Kích thước tối đa cho phép là {{max}} MB.",
|
||||
"imageWithSize": "Tệp hình ảnh ({{size}} KB)"
|
||||
"imageWithSize": "Tệp hình ảnh ({{size}} KB)",
|
||||
"partialReadSingleLine": "Đã đọc {{charactersRead}} trong số {{totalCharacters}} ký tự ({{percentRead}}%) từ tệp một dòng này. Đây là việc đọc một phần - nội dung còn lại không thể truy cập được do giới hạn ngữ cảnh.",
|
||||
"partialReadMultiLine": "Đã đọc {{charactersRead}} trong số {{totalCharacters}} ký tự ({{percentRead}}%), đến dòng {{lastLineRead}} trong tổng số {{totalLines}} dòng. Để đọc các phần cụ thể của tệp này, hãy sử dụng định dạng sau:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\nVí dụ, để đọc các dòng {{nextLineStart}}-{{suggestedLineEnd}}:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo dường như đang bị mắc kẹt trong một vòng lặp, liên tục cố gắng thực hiện cùng một hành động ({{toolName}}). Điều này có thể cho thấy vấn đề với chiến lược hiện tại. Hãy cân nhắc việc diễn đạt lại nhiệm vụ, cung cấp hướng dẫn cụ thể hơn, hoặc hướng Roo theo một cách tiếp cận khác.",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/zh-CN/tools.json
generated
5
src/i18n/locales/zh-CN/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (仅定义)",
|
||||
"maxLines": " (最多 {{max}} 行)",
|
||||
"showingOnlyLines": "仅显示 {{shown}} 行,共 {{total}} 行。如需阅读更多行请使用 line_range",
|
||||
"contextLimitInstructions": "要阅读此文件的特定部分,请使用以下格式:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>开始-结束</line_range>\n </file>\n</args>\n</read_file>\n\n例如,要阅读第 2001-3000 行:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "图片文件过大 ({{size}} MB)。允许的最大大小为 {{max}} MB。",
|
||||
"imageWithSize": "图片文件 ({{size}} KB)"
|
||||
"imageWithSize": "图片文件 ({{size}} KB)",
|
||||
"partialReadSingleLine": "已读取此单行文件中 {{totalCharacters}} 个字符中的 {{charactersRead}} 个字符 ({{percentRead}}%)。这是部分读取 - 由于上下文限制,无法访问剩余内容。",
|
||||
"partialReadMultiLine": "已读取 {{totalCharacters}} 个字符中的 {{charactersRead}} 个字符 ({{percentRead}}%),读取到第 {{lastLineRead}} 行,共 {{totalLines}} 行。要读取此文件的特定部分,请使用以下格式:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\n例如,要读取第 {{nextLineStart}}-{{suggestedLineEnd}} 行:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo 似乎陷入循环,反复尝试同一操作 ({{toolName}})。这可能表明当前策略存在问题。请考虑重新描述任务、提供更具体的指示或引导其尝试不同的方法。",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
5
src/i18n/locales/zh-TW/tools.json
generated
5
src/i18n/locales/zh-TW/tools.json
generated
|
|
@ -4,9 +4,10 @@
|
|||
"definitionsOnly": " (僅定義)",
|
||||
"maxLines": " (最多 {{max}} 行)",
|
||||
"showingOnlyLines": "僅顯示 {{shown}} 行,共 {{total}} 行。如需閱讀更多行請使用 line_range",
|
||||
"contextLimitInstructions": "要閱讀此檔案的特定部分,請使用以下格式:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>開始-結束</line_range>\n </file>\n</args>\n</read_file>\n\n例如,要閱讀第 2001-3000 行:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>2001-3000</line_range>\n </file>\n</args>\n</read_file>",
|
||||
"imageTooLarge": "圖片檔案過大 ({{size}} MB)。允許的最大大小為 {{max}} MB。",
|
||||
"imageWithSize": "圖片檔案 ({{size}} KB)"
|
||||
"imageWithSize": "圖片檔案 ({{size}} KB)",
|
||||
"partialReadSingleLine": "已讀取此單行檔案中 {{totalCharacters}} 個字元中的 {{charactersRead}} 個字元 ({{percentRead}}%)。這是部分讀取 - 由於內容限制,無法存取剩餘內容。",
|
||||
"partialReadMultiLine": "已讀取 {{totalCharacters}} 個字元中的 {{charactersRead}} 個字元 ({{percentRead}}%),讀取到第 {{lastLineRead}} 行,共 {{totalLines}} 行。要讀取此檔案的特定部分,請使用以下格式:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>start-end</line_range>\n </file>\n</args>\n</read_file>\n\n例如,要讀取第 {{nextLineStart}}-{{suggestedLineEnd}} 行:\n<read_file>\n<args>\n <file>\n <path>{{path}}</path>\n <line_range>{{nextLineStart}}-{{suggestedLineEnd}}</line_range>\n </file>\n</args>\n</read_file>"
|
||||
},
|
||||
"toolRepetitionLimitReached": "Roo 似乎陷入循環,反覆嘗試同一操作 ({{toolName}})。這可能表明目前策略存在問題。請考慮重新描述工作、提供更具體的指示或引導其嘗試不同的方法。",
|
||||
"codebaseSearch": {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { describe, it, expect, beforeEach, afterEach, vi } from "vitest"
|
||||
import { readPartialSingleLineContent } from "../read-partial-content"
|
||||
import { readPartialSingleLineContent, readPartialContent } from "../read-partial-content"
|
||||
import * as fs from "fs"
|
||||
import * as path from "path"
|
||||
import * as os from "os"
|
||||
|
||||
describe("readPartialSingleLineContent", () => {
|
||||
describe("read-partial-content", () => {
|
||||
let tempDir: string
|
||||
let testFiles: string[] = []
|
||||
|
||||
|
|
@ -37,218 +37,393 @@ describe("readPartialSingleLineContent", () => {
|
|||
return filePath
|
||||
}
|
||||
|
||||
describe("Basic functionality", () => {
|
||||
it("should read partial content from a small file", async () => {
|
||||
const content = "Hello, world! This is a test file."
|
||||
const filePath = await createTestFile("small.txt", content)
|
||||
describe("readPartialContent", () => {
|
||||
describe("Basic functionality", () => {
|
||||
it("should read partial content with line tracking", async () => {
|
||||
const content = "Line 1\nLine 2\nLine 3\nLine 4"
|
||||
const filePath = await createTestFile("multiline.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
const result = await readPartialContent(filePath, 15)
|
||||
|
||||
expect(result).toBe("Hello, wor")
|
||||
expect(result.content).toBe("Line 1\nLine 2\nL")
|
||||
expect(result.charactersRead).toBe(15)
|
||||
expect(result.totalCharacters).toBe(content.length)
|
||||
expect(result.linesRead).toBe(3) // Counting starts at 1, and we read into line 3
|
||||
expect(result.totalLines).toBe(4)
|
||||
expect(result.lastLineRead).toBe(3)
|
||||
})
|
||||
|
||||
it("should handle single-line files", async () => {
|
||||
const content = "This is a single line file with no newlines"
|
||||
const filePath = await createTestFile("single-line.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 20)
|
||||
|
||||
expect(result.content).toBe("This is a single lin")
|
||||
expect(result.charactersRead).toBe(20)
|
||||
expect(result.linesRead).toBe(1)
|
||||
expect(result.totalLines).toBe(1)
|
||||
expect(result.lastLineRead).toBe(1)
|
||||
})
|
||||
|
||||
it("should read entire file when maxChars exceeds file size", async () => {
|
||||
const content = "Small\nFile\nContent"
|
||||
const filePath = await createTestFile("small.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 1000)
|
||||
|
||||
expect(result.content).toBe(content)
|
||||
expect(result.charactersRead).toBe(content.length)
|
||||
expect(result.totalCharacters).toBe(content.length)
|
||||
expect(result.linesRead).toBe(3)
|
||||
expect(result.totalLines).toBe(3)
|
||||
expect(result.lastLineRead).toBe(3)
|
||||
})
|
||||
|
||||
it("should handle empty files", async () => {
|
||||
const filePath = await createTestFile("empty.txt", "")
|
||||
|
||||
const result = await readPartialContent(filePath, 10)
|
||||
|
||||
expect(result.content).toBe("")
|
||||
expect(result.charactersRead).toBe(0)
|
||||
expect(result.totalCharacters).toBe(0)
|
||||
expect(result.linesRead).toBe(0)
|
||||
expect(result.totalLines).toBe(0)
|
||||
expect(result.lastLineRead).toBe(0)
|
||||
})
|
||||
|
||||
it("should handle maxChars of 0", async () => {
|
||||
const content = "This content should not be read"
|
||||
const filePath = await createTestFile("zero-chars.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 0)
|
||||
|
||||
expect(result.content).toBe("")
|
||||
expect(result.charactersRead).toBe(0)
|
||||
expect(result.linesRead).toBe(0)
|
||||
expect(result.lastLineRead).toBe(0)
|
||||
})
|
||||
})
|
||||
|
||||
it("should read entire content when maxChars exceeds file size", async () => {
|
||||
const content = "Short file"
|
||||
const filePath = await createTestFile("short.txt", content)
|
||||
describe("Line counting accuracy", () => {
|
||||
it("should count lines correctly when stopping mid-line", async () => {
|
||||
const content = "Line 1\nLine 2 is longer\nLine 3"
|
||||
const filePath = await createTestFile("mid-line.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 100)
|
||||
const result = await readPartialContent(filePath, 10)
|
||||
|
||||
expect(result).toBe(content)
|
||||
expect(result.content).toBe("Line 1\nLin")
|
||||
expect(result.linesRead).toBe(2) // We're in line 2
|
||||
expect(result.lastLineRead).toBe(2)
|
||||
})
|
||||
|
||||
it("should count lines correctly when stopping at newline", async () => {
|
||||
const content = "Line 1\nLine 2\nLine 3"
|
||||
const filePath = await createTestFile("at-newline.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 7) // Exactly at the first newline
|
||||
|
||||
expect(result.content).toBe("Line 1\n")
|
||||
expect(result.linesRead).toBe(2) // We've entered line 2
|
||||
expect(result.lastLineRead).toBe(2)
|
||||
})
|
||||
|
||||
it("should handle files with empty lines", async () => {
|
||||
const content = "Line 1\n\nLine 3\n\n\nLine 6"
|
||||
const filePath = await createTestFile("empty-lines.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 15)
|
||||
|
||||
expect(result.content).toBe("Line 1\n\nLine 3\n")
|
||||
expect(result.linesRead).toBe(4) // We've entered line 4
|
||||
expect(result.totalLines).toBe(6)
|
||||
})
|
||||
|
||||
it("should handle files ending with newline", async () => {
|
||||
const content = "Line 1\nLine 2\n"
|
||||
const filePath = await createTestFile("ending-newline.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 100)
|
||||
|
||||
expect(result.content).toBe(content)
|
||||
expect(result.linesRead).toBe(3) // The empty line after the last newline
|
||||
expect(result.totalLines).toBe(2) // countFileLines counts actual lines, not the trailing empty line
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle empty files", async () => {
|
||||
const filePath = await createTestFile("empty.txt", "")
|
||||
describe("Large file handling", () => {
|
||||
it("should handle large files with many lines", async () => {
|
||||
const lines = Array.from({ length: 1000 }, (_, i) => `Line ${i + 1}`).join("\n")
|
||||
const filePath = await createTestFile("many-lines.txt", lines)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
const result = await readPartialContent(filePath, 100)
|
||||
|
||||
expect(result).toBe("")
|
||||
expect(result.charactersRead).toBe(100)
|
||||
expect(result.totalLines).toBe(1000)
|
||||
expect(result.linesRead).toBeGreaterThan(1)
|
||||
expect(result.linesRead).toBeLessThan(50) // Should not have read too many lines
|
||||
})
|
||||
|
||||
it("should handle very long single lines", async () => {
|
||||
const content = "x".repeat(100000) // 100KB single line
|
||||
const filePath = await createTestFile("long-single-line.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, 1000)
|
||||
|
||||
expect(result.content).toBe("x".repeat(1000))
|
||||
expect(result.linesRead).toBe(1)
|
||||
expect(result.totalLines).toBe(1)
|
||||
expect(result.lastLineRead).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle maxChars of 0", async () => {
|
||||
const content = "This content should not be read"
|
||||
const filePath = await createTestFile("zero-chars.txt", content)
|
||||
describe("Unicode and special characters", () => {
|
||||
it("should handle Unicode characters with line tracking", async () => {
|
||||
const content = "Hello 世界!\n🌍 Émojis\nñoñó chars"
|
||||
const filePath = await createTestFile("unicode-lines.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 0)
|
||||
const result = await readPartialContent(filePath, 20)
|
||||
|
||||
expect(result).toBe("")
|
||||
expect(result.linesRead).toBeGreaterThanOrEqual(2)
|
||||
expect(result.totalLines).toBe(3)
|
||||
})
|
||||
})
|
||||
|
||||
describe("Error handling", () => {
|
||||
it("should reject when file does not exist", async () => {
|
||||
const nonExistentPath = path.join(tempDir, "does-not-exist.txt")
|
||||
|
||||
await expect(readPartialContent(nonExistentPath, 10)).rejects.toThrow()
|
||||
})
|
||||
|
||||
it("should handle negative maxChars gracefully", async () => {
|
||||
const content = "Test content"
|
||||
const filePath = await createTestFile("negative-max.txt", content)
|
||||
|
||||
const result = await readPartialContent(filePath, -5)
|
||||
|
||||
expect(result.content).toBe("")
|
||||
expect(result.charactersRead).toBe(0)
|
||||
expect(result.linesRead).toBe(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("Large file handling", () => {
|
||||
it("should handle large files efficiently", async () => {
|
||||
// Create a large file (1MB of repeated text)
|
||||
const chunk = "This is a repeated chunk of text that will be used to create a large file. "
|
||||
const largeContent = chunk.repeat(Math.ceil((1024 * 1024) / chunk.length))
|
||||
const filePath = await createTestFile("large.txt", largeContent)
|
||||
describe("readPartialSingleLineContent (legacy)", () => {
|
||||
describe("Basic functionality", () => {
|
||||
it("should read partial content from a small file", async () => {
|
||||
const content = "Hello, world! This is a test file."
|
||||
const filePath = await createTestFile("small.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 100)
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
|
||||
expect(result).toBe(largeContent.substring(0, 100))
|
||||
expect(result.length).toBe(100)
|
||||
expect(result).toBe("Hello, wor")
|
||||
})
|
||||
|
||||
it("should read entire content when maxChars exceeds file size", async () => {
|
||||
const content = "Short file"
|
||||
const filePath = await createTestFile("short.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 100)
|
||||
|
||||
expect(result).toBe(content)
|
||||
})
|
||||
|
||||
it("should handle empty files", async () => {
|
||||
const filePath = await createTestFile("empty.txt", "")
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
|
||||
expect(result).toBe("")
|
||||
})
|
||||
|
||||
it("should handle maxChars of 0", async () => {
|
||||
const content = "This content should not be read"
|
||||
const filePath = await createTestFile("zero-chars.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 0)
|
||||
|
||||
expect(result).toBe("")
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle very large maxChars values", async () => {
|
||||
const content = "Small content for large maxChars test"
|
||||
const filePath = await createTestFile("small-for-large-max.txt", content)
|
||||
describe("Large file handling", () => {
|
||||
it("should handle large files efficiently", async () => {
|
||||
// Create a large file (1MB of repeated text)
|
||||
const chunk = "This is a repeated chunk of text that will be used to create a large file. "
|
||||
const largeContent = chunk.repeat(Math.ceil((1024 * 1024) / chunk.length))
|
||||
const filePath = await createTestFile("large.txt", largeContent)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 1000000)
|
||||
const result = await readPartialSingleLineContent(filePath, 100)
|
||||
|
||||
expect(result).toBe(content)
|
||||
})
|
||||
})
|
||||
expect(result).toBe(largeContent.substring(0, 100))
|
||||
expect(result.length).toBe(100)
|
||||
})
|
||||
|
||||
describe("Unicode and special characters", () => {
|
||||
it("should handle Unicode characters correctly", async () => {
|
||||
const content = "Hello 世界! 🌍 Émojis and ñoñó characters"
|
||||
const filePath = await createTestFile("unicode.txt", content)
|
||||
it("should handle very large maxChars values", async () => {
|
||||
const content = "Small content for large maxChars test"
|
||||
const filePath = await createTestFile("small-for-large-max.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 15)
|
||||
const result = await readPartialSingleLineContent(filePath, 1000000)
|
||||
|
||||
// Should handle Unicode characters properly
|
||||
expect(result.length).toBeLessThanOrEqual(15)
|
||||
expect(result).toBe(content.substring(0, result.length))
|
||||
expect(result).toBe(content)
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle newlines in content", async () => {
|
||||
const content = "Line 1\nLine 2\nLine 3"
|
||||
const filePath = await createTestFile("multiline.txt", content)
|
||||
describe("Unicode and special characters", () => {
|
||||
it("should handle Unicode characters correctly", async () => {
|
||||
const content = "Hello 世界! 🌍 Émojis and ñoñó characters"
|
||||
const filePath = await createTestFile("unicode.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
const result = await readPartialSingleLineContent(filePath, 15)
|
||||
|
||||
expect(result).toBe("Line 1\nLin")
|
||||
// Should handle Unicode characters properly
|
||||
expect(result.length).toBeLessThanOrEqual(15)
|
||||
expect(result).toBe(content.substring(0, result.length))
|
||||
})
|
||||
|
||||
it("should handle newlines in content", async () => {
|
||||
const content = "Line 1\nLine 2\nLine 3"
|
||||
const filePath = await createTestFile("multiline.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
|
||||
expect(result).toBe("Line 1\nLin")
|
||||
})
|
||||
|
||||
it("should handle special characters and symbols", async () => {
|
||||
const content = "Special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
const filePath = await createTestFile("special.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 20)
|
||||
|
||||
expect(result).toBe("Special chars: !@#$%")
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle special characters and symbols", async () => {
|
||||
const content = "Special chars: !@#$%^&*()_+-=[]{}|;':\",./<>?"
|
||||
const filePath = await createTestFile("special.txt", content)
|
||||
describe("Edge cases", () => {
|
||||
it("should handle exact character limit", async () => {
|
||||
const content = "Exactly twenty chars"
|
||||
const filePath = await createTestFile("exact.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 20)
|
||||
const result = await readPartialSingleLineContent(filePath, 20)
|
||||
|
||||
expect(result).toBe("Special chars: !@#$%")
|
||||
})
|
||||
})
|
||||
expect(result).toBe(content)
|
||||
expect(result.length).toBe(20)
|
||||
})
|
||||
|
||||
describe("Edge cases", () => {
|
||||
it("should handle exact character limit", async () => {
|
||||
const content = "Exactly twenty chars"
|
||||
const filePath = await createTestFile("exact.txt", content)
|
||||
it("should handle maxChars = 1", async () => {
|
||||
const content = "Single character test"
|
||||
const filePath = await createTestFile("single-char.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 20)
|
||||
const result = await readPartialSingleLineContent(filePath, 1)
|
||||
|
||||
expect(result).toBe(content)
|
||||
expect(result.length).toBe(20)
|
||||
expect(result).toBe("S")
|
||||
})
|
||||
|
||||
it("should handle files with only whitespace", async () => {
|
||||
const content = " \t\n "
|
||||
const filePath = await createTestFile("whitespace.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 5)
|
||||
|
||||
expect(result).toBe(" \t\n")
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle maxChars = 1", async () => {
|
||||
const content = "Single character test"
|
||||
const filePath = await createTestFile("single-char.txt", content)
|
||||
describe("Error handling", () => {
|
||||
it("should reject when file does not exist", async () => {
|
||||
const nonExistentPath = path.join(tempDir, "does-not-exist.txt")
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 1)
|
||||
await expect(readPartialSingleLineContent(nonExistentPath, 10)).rejects.toThrow()
|
||||
})
|
||||
|
||||
expect(result).toBe("S")
|
||||
it("should reject when file path is invalid", async () => {
|
||||
const invalidPath = "\0invalid\0path"
|
||||
|
||||
await expect(readPartialSingleLineContent(invalidPath, 10)).rejects.toThrow()
|
||||
})
|
||||
|
||||
it("should handle negative maxChars gracefully", async () => {
|
||||
const content = "Test content"
|
||||
const filePath = await createTestFile("negative-max.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, -5)
|
||||
|
||||
expect(result).toBe("")
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle files with only whitespace", async () => {
|
||||
const content = " \t\n "
|
||||
const filePath = await createTestFile("whitespace.txt", content)
|
||||
describe("Performance and memory efficiency", () => {
|
||||
it("should not load entire large file into memory", async () => {
|
||||
// Create a file larger than typical memory chunks
|
||||
const largeContent = "x".repeat(5 * 1024 * 1024) // 5MB file
|
||||
const filePath = await createTestFile("memory-test.txt", largeContent)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 5)
|
||||
// Read only a small portion
|
||||
const result = await readPartialSingleLineContent(filePath, 1000)
|
||||
|
||||
expect(result).toBe(" \t\n")
|
||||
})
|
||||
})
|
||||
expect(result).toBe("x".repeat(1000))
|
||||
expect(result.length).toBe(1000)
|
||||
})
|
||||
|
||||
describe("Error handling", () => {
|
||||
it("should reject when file does not exist", async () => {
|
||||
const nonExistentPath = path.join(tempDir, "does-not-exist.txt")
|
||||
it("should handle multiple consecutive reads efficiently", async () => {
|
||||
const content = "Repeated read test content that is somewhat long"
|
||||
const filePath = await createTestFile("repeated-read.txt", content)
|
||||
|
||||
await expect(readPartialSingleLineContent(nonExistentPath, 10)).rejects.toThrow()
|
||||
// Perform multiple reads
|
||||
const results = await Promise.all([
|
||||
readPartialSingleLineContent(filePath, 10),
|
||||
readPartialSingleLineContent(filePath, 20),
|
||||
readPartialSingleLineContent(filePath, 30),
|
||||
])
|
||||
|
||||
expect(results[0]).toBe(content.substring(0, 10))
|
||||
expect(results[1]).toBe(content.substring(0, 20))
|
||||
expect(results[2]).toBe(content.substring(0, 30))
|
||||
})
|
||||
})
|
||||
|
||||
it("should reject when file path is invalid", async () => {
|
||||
const invalidPath = "\0invalid\0path"
|
||||
describe("Stream handling", () => {
|
||||
it("should handle normal stream completion", async () => {
|
||||
const content = "Stream test content"
|
||||
const filePath = await createTestFile("stream-test.txt", content)
|
||||
|
||||
await expect(readPartialSingleLineContent(invalidPath, 10)).rejects.toThrow()
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
|
||||
expect(result).toBe("Stream tes")
|
||||
})
|
||||
|
||||
it("should handle file access errors", async () => {
|
||||
// Test with a directory instead of a file to trigger an error
|
||||
await expect(readPartialSingleLineContent(tempDir, 10)).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
it("should handle negative maxChars gracefully", async () => {
|
||||
const content = "Test content"
|
||||
const filePath = await createTestFile("negative-max.txt", content)
|
||||
describe("Boundary conditions", () => {
|
||||
it("should handle chunk boundaries correctly", async () => {
|
||||
// Create content that will span multiple chunks
|
||||
const chunkSize = 16 * 1024 // Default highWaterMark
|
||||
const content = "a".repeat(chunkSize + 100)
|
||||
const filePath = await createTestFile("chunk-boundary.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, -5)
|
||||
const result = await readPartialSingleLineContent(filePath, chunkSize + 50)
|
||||
|
||||
expect(result).toBe("")
|
||||
})
|
||||
})
|
||||
expect(result).toBe("a".repeat(chunkSize + 50))
|
||||
expect(result.length).toBe(chunkSize + 50)
|
||||
})
|
||||
|
||||
describe("Performance and memory efficiency", () => {
|
||||
it("should not load entire large file into memory", async () => {
|
||||
// Create a file larger than typical memory chunks
|
||||
const largeContent = "x".repeat(5 * 1024 * 1024) // 5MB file
|
||||
const filePath = await createTestFile("memory-test.txt", largeContent)
|
||||
it("should handle maxChars at chunk boundary", async () => {
|
||||
const chunkSize = 16 * 1024
|
||||
const content = "b".repeat(chunkSize * 2)
|
||||
const filePath = await createTestFile("exact-chunk.txt", content)
|
||||
|
||||
// Read only a small portion
|
||||
const result = await readPartialSingleLineContent(filePath, 1000)
|
||||
const result = await readPartialSingleLineContent(filePath, chunkSize)
|
||||
|
||||
expect(result).toBe("x".repeat(1000))
|
||||
expect(result.length).toBe(1000)
|
||||
})
|
||||
|
||||
it("should handle multiple consecutive reads efficiently", async () => {
|
||||
const content = "Repeated read test content that is somewhat long"
|
||||
const filePath = await createTestFile("repeated-read.txt", content)
|
||||
|
||||
// Perform multiple reads
|
||||
const results = await Promise.all([
|
||||
readPartialSingleLineContent(filePath, 10),
|
||||
readPartialSingleLineContent(filePath, 20),
|
||||
readPartialSingleLineContent(filePath, 30),
|
||||
])
|
||||
|
||||
expect(results[0]).toBe(content.substring(0, 10))
|
||||
expect(results[1]).toBe(content.substring(0, 20))
|
||||
expect(results[2]).toBe(content.substring(0, 30))
|
||||
})
|
||||
})
|
||||
|
||||
describe("Stream handling", () => {
|
||||
it("should handle normal stream completion", async () => {
|
||||
const content = "Stream test content"
|
||||
const filePath = await createTestFile("stream-test.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, 10)
|
||||
|
||||
expect(result).toBe("Stream tes")
|
||||
})
|
||||
|
||||
it("should handle file access errors", async () => {
|
||||
// Test with a directory instead of a file to trigger an error
|
||||
await expect(readPartialSingleLineContent(tempDir, 10)).rejects.toThrow()
|
||||
})
|
||||
})
|
||||
|
||||
describe("Boundary conditions", () => {
|
||||
it("should handle chunk boundaries correctly", async () => {
|
||||
// Create content that will span multiple chunks
|
||||
const chunkSize = 16 * 1024 // Default highWaterMark
|
||||
const content = "a".repeat(chunkSize + 100)
|
||||
const filePath = await createTestFile("chunk-boundary.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, chunkSize + 50)
|
||||
|
||||
expect(result).toBe("a".repeat(chunkSize + 50))
|
||||
expect(result.length).toBe(chunkSize + 50)
|
||||
})
|
||||
|
||||
it("should handle maxChars at chunk boundary", async () => {
|
||||
const chunkSize = 16 * 1024
|
||||
const content = "b".repeat(chunkSize * 2)
|
||||
const filePath = await createTestFile("exact-chunk.txt", content)
|
||||
|
||||
const result = await readPartialSingleLineContent(filePath, chunkSize)
|
||||
|
||||
expect(result).toBe("b".repeat(chunkSize))
|
||||
expect(result.length).toBe(chunkSize)
|
||||
expect(result).toBe("b".repeat(chunkSize))
|
||||
expect(result.length).toBe(chunkSize)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,21 +1,47 @@
|
|||
import { createReadStream } from "fs"
|
||||
import * as fs from "fs/promises"
|
||||
import { countFileLines } from "./line-counter"
|
||||
|
||||
/**
|
||||
* Reads partial content from a single-line file up to a specified character limit.
|
||||
* Result of a partial file read operation
|
||||
*/
|
||||
export interface PartialReadResult {
|
||||
content: string
|
||||
charactersRead: number
|
||||
totalCharacters: number // from file stats
|
||||
linesRead: number
|
||||
totalLines: number // from line counter
|
||||
lastLineRead: number // which line we stopped at
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads partial content from a file up to a specified character limit.
|
||||
* Works for both single-line and multi-line files, tracking line numbers.
|
||||
* Uses streaming to avoid loading the entire file into memory for very large files.
|
||||
*
|
||||
* @param filePath - Path to the file to read
|
||||
* @param maxChars - Maximum number of characters to read
|
||||
* @returns Promise resolving to the partial content as a string
|
||||
* @returns Promise resolving to the partial read result with metadata
|
||||
*/
|
||||
export function readPartialSingleLineContent(filePath: string, maxChars: number): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Handle edge cases
|
||||
if (maxChars <= 0) {
|
||||
resolve("")
|
||||
return
|
||||
}
|
||||
export async function readPartialContent(filePath: string, maxChars: number): Promise<PartialReadResult> {
|
||||
// Get file stats and line count
|
||||
const [stats, totalLines] = await Promise.all([fs.stat(filePath), countFileLines(filePath)])
|
||||
|
||||
const totalCharacters = stats.size
|
||||
|
||||
// Handle edge cases
|
||||
if (maxChars <= 0 || totalCharacters === 0) {
|
||||
return {
|
||||
content: "",
|
||||
charactersRead: 0,
|
||||
totalCharacters,
|
||||
linesRead: 0,
|
||||
totalLines,
|
||||
lastLineRead: 0,
|
||||
}
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
// Use smaller chunks and set end position to limit reading
|
||||
const stream = createReadStream(filePath, {
|
||||
encoding: "utf8",
|
||||
|
|
@ -23,9 +49,12 @@ export function readPartialSingleLineContent(filePath: string, maxChars: number)
|
|||
start: 0,
|
||||
end: Math.max(0, Math.min(maxChars * 2, maxChars + 1024 * 1024)), // Read at most 2x maxChars or maxChars + 1MB buffer
|
||||
})
|
||||
|
||||
let content = ""
|
||||
let totalRead = 0
|
||||
let currentLine = 1
|
||||
let streamDestroyed = false
|
||||
let hasContent = false
|
||||
|
||||
stream.on("data", (chunk: string | Buffer) => {
|
||||
// Early exit if stream was already destroyed
|
||||
|
|
@ -40,27 +69,66 @@ export function readPartialSingleLineContent(filePath: string, maxChars: number)
|
|||
if (remainingChars <= 0) {
|
||||
streamDestroyed = true
|
||||
stream.destroy()
|
||||
resolve(content)
|
||||
resolve({
|
||||
content,
|
||||
charactersRead: totalRead,
|
||||
totalCharacters,
|
||||
linesRead: hasContent ? currentLine : 0,
|
||||
totalLines,
|
||||
lastLineRead: hasContent ? currentLine : 0,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
let chunkToAdd: string
|
||||
if (chunkStr.length <= remainingChars) {
|
||||
content += chunkStr
|
||||
chunkToAdd = chunkStr
|
||||
totalRead += chunkStr.length
|
||||
} else {
|
||||
const truncated = chunkStr.substring(0, remainingChars)
|
||||
content += truncated
|
||||
chunkToAdd = chunkStr.substring(0, remainingChars)
|
||||
totalRead += remainingChars
|
||||
streamDestroyed = true
|
||||
stream.destroy()
|
||||
resolve(content)
|
||||
}
|
||||
|
||||
// Safety check - if we somehow exceed the limit, stop immediately
|
||||
// Mark that we have content
|
||||
if (chunkToAdd.length > 0) {
|
||||
hasContent = true
|
||||
}
|
||||
|
||||
// Count newlines in the chunk we're adding
|
||||
for (let i = 0; i < chunkToAdd.length; i++) {
|
||||
if (chunkToAdd[i] === "\n") {
|
||||
currentLine++
|
||||
}
|
||||
}
|
||||
|
||||
content += chunkToAdd
|
||||
|
||||
// Check if we've reached the character limit
|
||||
if (totalRead >= maxChars) {
|
||||
streamDestroyed = true
|
||||
stream.destroy()
|
||||
resolve(content.substring(0, maxChars))
|
||||
|
||||
// Ensure we don't exceed maxChars
|
||||
if (content.length > maxChars) {
|
||||
content = content.substring(0, maxChars)
|
||||
// Recount lines in the final content
|
||||
currentLine = 1
|
||||
hasContent = content.length > 0
|
||||
for (let i = 0; i < content.length; i++) {
|
||||
if (content[i] === "\n") {
|
||||
currentLine++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve({
|
||||
content,
|
||||
charactersRead: Math.min(totalRead, maxChars),
|
||||
totalCharacters,
|
||||
linesRead: hasContent ? currentLine : 0,
|
||||
totalLines,
|
||||
lastLineRead: hasContent ? currentLine : 0,
|
||||
})
|
||||
}
|
||||
} catch (error) {
|
||||
streamDestroyed = true
|
||||
|
|
@ -70,7 +138,14 @@ export function readPartialSingleLineContent(filePath: string, maxChars: number)
|
|||
})
|
||||
|
||||
stream.on("end", () => {
|
||||
resolve(content)
|
||||
resolve({
|
||||
content,
|
||||
charactersRead: totalRead,
|
||||
totalCharacters,
|
||||
linesRead: hasContent ? currentLine : 0,
|
||||
totalLines,
|
||||
lastLineRead: hasContent ? currentLine : 0,
|
||||
})
|
||||
})
|
||||
|
||||
stream.on("error", (error: Error) => {
|
||||
|
|
@ -78,3 +153,12 @@ export function readPartialSingleLineContent(filePath: string, maxChars: number)
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy function for backward compatibility.
|
||||
* @deprecated Use readPartialContent instead
|
||||
*/
|
||||
export async function readPartialSingleLineContent(filePath: string, maxChars: number): Promise<string> {
|
||||
const result = await readPartialContent(filePath, maxChars)
|
||||
return result.content
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue