@quantumai/quantum-cli-core
Version:
Quantum CLI Core - Multi-LLM Collaboration System
103 lines • 4.75 kB
JavaScript
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import path from 'path';
import { SchemaValidator } from '../utils/schemaValidator.js';
import { makeRelative, shortenPath } from '../utils/paths.js';
import { BaseTool } from './tools.js';
import { isWithinRoot, processSingleFileContent, getSpecificMimeType, } from '../utils/fileUtils.js';
import { recordFileOperationMetric, FileOperation, } from '../telemetry/metrics.js';
/**
* Implementation of the ReadFile tool logic
*/
export class ReadFileTool extends BaseTool {
rootDirectory;
config;
static Name = 'read_file';
constructor(rootDirectory, config) {
super(ReadFileTool.Name, 'ReadFile', 'Reads and returns the content of a specified file from the local filesystem. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.', {
properties: {
absolute_path: {
description: "The absolute path to the file to read (e.g., '/home/user/project/file.txt'). Relative paths are not supported. You must provide an absolute path.",
type: 'string',
pattern: '^/',
},
offset: {
description: "Optional: For text files, the 0-based line number to start reading from. Requires 'limit' to be set. Use for paginating through large files.",
type: 'number',
},
limit: {
description: "Optional: For text files, maximum number of lines to read. Use with 'offset' to paginate through large files. If omitted, reads the entire file (if feasible, up to a default limit).",
type: 'number',
},
},
required: ['absolute_path'],
type: 'object',
});
this.rootDirectory = rootDirectory;
this.config = config;
this.rootDirectory = path.resolve(rootDirectory);
}
validateToolParams(params) {
if (this.schema.parameters &&
!SchemaValidator.validate(this.schema.parameters, params)) {
return 'Parameters failed schema validation.';
}
const filePath = params.absolute_path;
if (!path.isAbsolute(filePath)) {
return `File path must be absolute, but was relative: ${filePath}. You must provide an absolute path.`;
}
if (!isWithinRoot(filePath, this.rootDirectory)) {
return `File path must be within the root directory (${this.rootDirectory}): ${filePath}`;
}
if (params.offset !== undefined && params.offset < 0) {
return 'Offset must be a non-negative number';
}
if (params.limit !== undefined && params.limit <= 0) {
return 'Limit must be a positive number';
}
const fileService = this.config.getFileService();
if (fileService.shouldGeminiIgnoreFile(params.absolute_path)) {
const relativePath = makeRelative(params.absolute_path, this.rootDirectory);
return `File path '${shortenPath(relativePath)}' is ignored by .quantumignore pattern(s).`;
}
return null;
}
getDescription(params) {
if (!params ||
typeof params.absolute_path !== 'string' ||
params.absolute_path.trim() === '') {
return `Path unavailable`;
}
const relativePath = makeRelative(params.absolute_path, this.rootDirectory);
return shortenPath(relativePath);
}
async execute(params, _signal) {
const validationError = this.validateToolParams(params);
if (validationError) {
return {
llmContent: `Error: Invalid parameters provided. Reason: ${validationError}`,
returnDisplay: validationError,
};
}
const result = await processSingleFileContent(params.absolute_path, this.rootDirectory, params.offset, params.limit);
if (result.error) {
return {
llmContent: result.error, // The detailed error for LLM
returnDisplay: result.returnDisplay, // User-friendly error
};
}
const lines = typeof result.llmContent === 'string'
? result.llmContent.split('\n').length
: undefined;
const mimetype = getSpecificMimeType(params.absolute_path);
recordFileOperationMetric(this.config, FileOperation.READ, lines, mimetype, path.extname(params.absolute_path));
return {
llmContent: result.llmContent,
returnDisplay: result.returnDisplay,
};
}
}
//# sourceMappingURL=read-file.js.map