| 94 | } |
| 95 | |
| 96 | QFuture<LLMQore::ToolResult> EditFileTool::executeAsync(const QJsonObject &input) |
| 97 | { |
| 98 | return QtConcurrent::run([this, input]() -> LLMQore::ToolResult { |
| 99 | QString filename = input["filename"].toString().trimmed(); |
| 100 | QString oldContent = input["old_content"].toString(); |
| 101 | QString newContent = input["new_content"].toString(); |
| 102 | QString requestId = input["_request_id"].toString(); |
| 103 | |
| 104 | if (filename.isEmpty()) { |
| 105 | throw LLMQore::ToolInvalidArgument("'filename' parameter is required and cannot be empty"); |
| 106 | } |
| 107 | |
| 108 | if (newContent.isEmpty()) { |
| 109 | throw LLMQore::ToolInvalidArgument("'new_content' parameter is required and cannot be empty"); |
| 110 | } |
| 111 | |
| 112 | |
| 113 | QFileInfo fileInfo(filename); |
| 114 | QString filePath; |
| 115 | |
| 116 | if (fileInfo.isAbsolute()) { |
| 117 | filePath = filename; |
| 118 | } else { |
| 119 | QString projectRoot = Context::ProjectUtils::getProjectRoot(); |
| 120 | if (projectRoot.isEmpty()) { |
| 121 | throw LLMQore::ToolRuntimeError( |
| 122 | QString("Cannot resolve relative path '%1': no project is open. " |
| 123 | "Please provide an absolute path or open a project.") |
| 124 | .arg(filename)); |
| 125 | } |
| 126 | |
| 127 | filePath = QDir(projectRoot).absoluteFilePath(filename); |
| 128 | LOG_MESSAGE(QString("EditFileTool: Resolved relative path '%1' to '%2'") |
| 129 | .arg(filename, filePath)); |
| 130 | } |
| 131 | |
| 132 | QFile file(filePath); |
| 133 | if (!file.exists()) { |
| 134 | throw LLMQore::ToolRuntimeError(QString("File does not exist: %1").arg(filePath)); |
| 135 | } |
| 136 | |
| 137 | QFileInfo finalFileInfo(filePath); |
| 138 | if (!finalFileInfo.isWritable()) { |
| 139 | throw LLMQore::ToolRuntimeError( |
| 140 | QString("File is not writable (read-only or permission denied): %1").arg(filePath)); |
| 141 | } |
| 142 | |
| 143 | bool isInProject = Context::ProjectUtils::isFileInProject(filePath); |
| 144 | if (!isInProject) { |
| 145 | const auto &settings = Settings::toolsSettings(); |
| 146 | if (!settings.allowAccessOutsideProject()) { |
| 147 | const QString projectRoot = Context::ProjectUtils::getProjectRoot(); |
| 148 | const QString hint = projectRoot.isEmpty() |
| 149 | ? QStringLiteral( |
| 150 | "No project is currently open. Open a project in Qt Creator or " |
| 151 | "enable 'Allow file access outside project' in QodeAssist settings.") |
| 152 | : QString( |
| 153 | "Retry with a path under the active project root: '%1'. The build " |
nothing calls this directly
no test coverage detected