/*
* Copyright (C) 2026 Petr Mironychev
*
* This file is part of QodeAssist.
*
* QodeAssist is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* QodeAssist is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with QodeAssist. If not, see .
*/
#include "ReadFileTool.hpp"
#include "FileSearchUtils.hpp"
#include
#include
#include
#include
#include
#include
#include
#include
#include
namespace QodeAssist::Tools {
ReadFileTool::ReadFileTool(QObject *parent)
: BaseTool(parent)
{}
QString ReadFileTool::id() const
{
return "read_file";
}
QString ReadFileTool::displayName() const
{
return "Reading file";
}
QString ReadFileTool::description() const
{
return "Read the text content of a file. Accepts either an absolute path or a path "
"relative to the project root. Reading files outside the active project requires "
"the 'Allow file access outside project' option in settings. Use `find_file` first "
"if you only know a partial filename.";
}
QJsonObject ReadFileTool::parametersSchema() const
{
QJsonObject properties;
properties["file_path"] = QJsonObject{
{"type", "string"},
{"description",
"Absolute path (e.g. /path/to/file.cpp) or project-relative path (e.g. src/main.cpp). "
"Relative paths are resolved against the root of the active project."}};
QJsonObject definition;
definition["type"] = "object";
definition["properties"] = properties;
definition["required"] = QJsonArray{"file_path"};
return definition;
}
QFuture ReadFileTool::executeAsync(const QJsonObject &input)
{
return QtConcurrent::run([input]() -> LLMQore::ToolResult {
QString rawPath = input["file_path"].toString().trimmed();
if (rawPath.isEmpty()) {
throw LLMQore::ToolInvalidArgument(
"'file_path' parameter is required and cannot be empty");
}
QFileInfo pathInfo(rawPath);
QString absolutePath;
if (pathInfo.isAbsolute()) {
absolutePath = rawPath;
} else {
QString projectRoot = Context::ProjectUtils::getProjectRoot();
if (projectRoot.isEmpty()) {
throw LLMQore::ToolRuntimeError(
QString("Cannot resolve relative path '%1': no project is open. "
"Provide an absolute path or open a project.")
.arg(rawPath));
}
absolutePath = QDir(projectRoot).absoluteFilePath(rawPath);
LOG_MESSAGE(QString("ReadFileTool: Resolved relative path '%1' to '%2'")
.arg(rawPath, absolutePath));
}
QFileInfo finalInfo(absolutePath);
if (!finalInfo.exists() || !finalInfo.isFile()) {
throw LLMQore::ToolRuntimeError(QString("File does not exist: %1").arg(absolutePath));
}
QString content = FileSearchUtils::readFileContent(absolutePath);
if (content.isNull()) {
throw LLMQore::ToolRuntimeError(
QString("Cannot read file '%1'. It may be outside the project scope "
"(enable 'Allow file access outside project' in settings), unreadable, "
"or use an unsupported encoding.")
.arg(absolutePath));
}
QString result = QString("File: %1\n\n%2").arg(absolutePath, content);
return LLMQore::ToolResult::text(result);
});
}
} // namespace QodeAssist::Tools