From 45df27e7499cb0e7b730040c6d080ee453c4fdf4 Mon Sep 17 00:00:00 2001
From: Petr Mironychev <9195189+Palm1r@users.noreply.github.com>
Date: Mon, 13 Oct 2025 18:33:17 +0200
Subject: [PATCH] feat: Add tool for reading issues tab
---
 CMakeLists.txt              |   1 +
 tools/GetIssuesListTool.cpp | 258 ++++++++++++++++++++++++++++++++++++
 tools/GetIssuesListTool.hpp |  64 +++++++++
 tools/ToolsFactory.cpp      |   2 +
 4 files changed, 325 insertions(+)
 create mode 100644 tools/GetIssuesListTool.cpp
 create mode 100644 tools/GetIssuesListTool.hpp
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 06b897f..b23d870 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -119,6 +119,7 @@ add_qtc_plugin(QodeAssist
     tools/ListProjectFilesTool.hpp tools/ListProjectFilesTool.cpp
     tools/ToolsManager.hpp tools/ToolsManager.cpp
     tools/SearchInProjectTool.hpp tools/SearchInProjectTool.cpp
+    tools/GetIssuesListTool.hpp tools/GetIssuesListTool.cpp
     providers/ClaudeMessage.hpp providers/ClaudeMessage.cpp
     providers/OpenAIMessage.hpp providers/OpenAIMessage.cpp
     providers/OllamaMessage.hpp providers/OllamaMessage.cpp
diff --git a/tools/GetIssuesListTool.cpp b/tools/GetIssuesListTool.cpp
new file mode 100644
index 0000000..9487f0b
--- /dev/null
+++ b/tools/GetIssuesListTool.cpp
@@ -0,0 +1,258 @@
+/* 
+ * Copyright (C) 2025 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 "GetIssuesListTool.hpp"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+namespace QodeAssist::Tools {
+
+IssuesTracker &IssuesTracker::instance()
+{
+    static IssuesTracker tracker;
+    return tracker;
+}
+
+IssuesTracker::IssuesTracker(QObject *parent)
+    : QObject(parent)
+{
+    LOG_MESSAGE("IssuesTracker: Initializing tracker");
+
+    auto &hub = ProjectExplorer::taskHub();
+
+    connect(&hub, &ProjectExplorer::TaskHub::taskAdded, this, &IssuesTracker::onTaskAdded);
+    connect(&hub, &ProjectExplorer::TaskHub::taskRemoved, this, &IssuesTracker::onTaskRemoved);
+    connect(&hub, &ProjectExplorer::TaskHub::tasksCleared, this, &IssuesTracker::onTasksCleared);
+
+    LOG_MESSAGE("IssuesTracker: Connected to TaskHub signals");
+}
+
+QList IssuesTracker::getTasks() const
+{
+    QMutexLocker locker(&m_mutex);
+    LOG_MESSAGE(QString("IssuesTracker: getTasks() called, current count: %1").arg(m_tasks.size()));
+    return m_tasks;
+}
+
+void IssuesTracker::onTaskAdded(const ProjectExplorer::Task &task)
+{
+    QMutexLocker locker(&m_mutex);
+    m_tasks.append(task);
+
+    QString typeStr;
+    switch (task.type) {
+    case ProjectExplorer::Task::Error:
+        typeStr = "ERROR";
+        break;
+    case ProjectExplorer::Task::Warning:
+        typeStr = "WARNING";
+        break;
+    default:
+        typeStr = "INFO";
+        break;
+    }
+
+    LOG_MESSAGE(QString("IssuesTracker: Task added [%1] %2 at %3:%4 (total: %5)")
+                    .arg(typeStr)
+                    .arg(task.description())
+                    .arg(task.file.toUrlishString())
+                    .arg(task.line)
+                    .arg(m_tasks.size()));
+}
+
+void IssuesTracker::onTaskRemoved(const ProjectExplorer::Task &task)
+{
+    QMutexLocker locker(&m_mutex);
+    m_tasks.removeOne(task);
+
+    LOG_MESSAGE(QString("IssuesTracker: Task removed: %1 (total: %2)")
+                    .arg(task.description())
+                    .arg(m_tasks.size()));
+}
+
+void IssuesTracker::onTasksCleared(Utils::Id categoryId)
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (categoryId.isValid()) {
+        int beforeCount = m_tasks.size();
+        m_tasks.erase(
+            std::remove_if(
+                m_tasks.begin(),
+                m_tasks.end(),
+                [categoryId](const ProjectExplorer::Task &task) {
+                    return task.category == categoryId;
+                }),
+            m_tasks.end());
+        int removedCount = beforeCount - m_tasks.size();
+
+        LOG_MESSAGE(
+            QString("IssuesTracker: Tasks cleared for category %1, removed %2 tasks (total: %3)")
+                .arg(categoryId.toString())
+                .arg(removedCount)
+                .arg(m_tasks.size()));
+    } else {
+        int clearedCount = m_tasks.size();
+        m_tasks.clear();
+        LOG_MESSAGE(QString("IssuesTracker: All tasks cleared, removed %1 tasks").arg(clearedCount));
+    }
+}
+
+GetIssuesListTool::GetIssuesListTool(QObject *parent)
+    : BaseTool(parent)
+{
+    LOG_MESSAGE("GetIssuesListTool: Initializing tool");
+    IssuesTracker::instance();
+}
+
+QString GetIssuesListTool::name() const
+{
+    return "get_issues_list";
+}
+
+QString GetIssuesListTool::stringName() const
+{
+    return "Getting issues list from Qt Creator";
+}
+
+QString GetIssuesListTool::description() const
+{
+    return "Get list of errors, warnings and other issues from Qt Creator's Issues panel. "
+           "Returns information about compilation errors, static analysis warnings, and other "
+           "diagnostic messages.";
+}
+
+QJsonObject GetIssuesListTool::getDefinition(LLMCore::ToolSchemaFormat format) const
+{
+    QJsonObject definition;
+    definition["type"] = "object";
+
+    QJsonObject properties;
+    properties["severity"] = QJsonObject{
+        {"type", "string"},
+        {"description", "Filter by severity: 'error', 'warning', or 'all'"},
+        {"enum", QJsonArray{"error", "warning", "all"}}};
+
+    definition["properties"] = properties;
+    definition["required"] = QJsonArray();
+
+    switch (format) {
+    case LLMCore::ToolSchemaFormat::OpenAI:
+        return customizeForOpenAI(definition);
+    case LLMCore::ToolSchemaFormat::Claude:
+        return customizeForClaude(definition);
+    case LLMCore::ToolSchemaFormat::Ollama:
+        return customizeForOllama(definition);
+    case LLMCore::ToolSchemaFormat::Google:
+        return customizeForGoogle(definition);
+    }
+
+    return definition;
+}
+
+LLMCore::ToolPermissions GetIssuesListTool::requiredPermissions() const
+{
+    return LLMCore::ToolPermission::FileSystemRead;
+}
+
+QFuture GetIssuesListTool::executeAsync(const QJsonObject &input)
+{
+    return QtConcurrent::run([input]() -> QString {
+        LOG_MESSAGE("GetIssuesListTool: Starting execution");
+
+        QString severityFilter = input.value("severity").toString("all");
+        LOG_MESSAGE(QString("GetIssuesListTool: Severity filter: %1").arg(severityFilter));
+
+        const auto tasks = IssuesTracker::instance().getTasks();
+
+        if (tasks.isEmpty()) {
+            LOG_MESSAGE("GetIssuesListTool: No issues found");
+            return "No issues found in Qt Creator Issues panel.";
+        }
+
+        LOG_MESSAGE(QString("GetIssuesListTool: Processing %1 tasks").arg(tasks.size()));
+
+        QStringList results;
+        results.append(QString("Total issues in panel: %1\n").arg(tasks.size()));
+
+        int errorCount = 0;
+        int warningCount = 0;
+        int processedCount = 0;
+
+        for (const ProjectExplorer::Task &task : tasks) {
+            if (severityFilter == "error" && task.type != ProjectExplorer::Task::Error)
+                continue;
+            if (severityFilter == "warning" && task.type != ProjectExplorer::Task::Warning)
+                continue;
+
+            QString typeStr;
+            switch (task.type) {
+            case ProjectExplorer::Task::Error:
+                typeStr = "ERROR";
+                errorCount++;
+                break;
+            case ProjectExplorer::Task::Warning:
+                typeStr = "WARNING";
+                warningCount++;
+                break;
+            default:
+                typeStr = "INFO";
+                break;
+            }
+
+            QString issueText = QString("[%1] %2").arg(typeStr, task.description());
+
+            if (!task.file.isEmpty()) {
+                issueText += QString("\n  File: %1").arg(task.file.toUrlishString());
+                if (task.line > 0) {
+                    issueText += QString(":%1").arg(task.line);
+                    if (task.column > 0) {
+                        issueText += QString(":%1").arg(task.column);
+                    }
+                }
+            }
+
+            if (!task.category.toString().isEmpty()) {
+                issueText += QString("\n  Category: %1").arg(task.category.toString());
+            }
+
+            results.append(issueText);
+            processedCount++;
+        }
+
+        QString summary = QString("\nSummary: %1 errors, %2 warnings (processed %3 tasks)")
+                              .arg(errorCount)
+                              .arg(warningCount)
+                              .arg(processedCount);
+        results.prepend(summary);
+
+        LOG_MESSAGE(QString("GetIssuesListTool: Execution completed - %1 errors, %2 warnings")
+                        .arg(errorCount)
+                        .arg(warningCount));
+
+        return results.join("\n\n");
+    });
+}
+
+} // namespace QodeAssist::Tools
diff --git a/tools/GetIssuesListTool.hpp b/tools/GetIssuesListTool.hpp
new file mode 100644
index 0000000..ea2c1ef
--- /dev/null
+++ b/tools/GetIssuesListTool.hpp
@@ -0,0 +1,64 @@
+/* 
+ * Copyright (C) 2025 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 .
+ */
+
+#pragma once
+
+#include 
+#include 
+#include 
+#include 
+
+namespace QodeAssist::Tools {
+
+class IssuesTracker : public QObject
+{
+    Q_OBJECT
+public:
+    static IssuesTracker &instance();
+
+    QList getTasks() const;
+
+private:
+    explicit IssuesTracker(QObject *parent = nullptr);
+    ~IssuesTracker() override = default;
+
+    void onTaskAdded(const ProjectExplorer::Task &task);
+    void onTaskRemoved(const ProjectExplorer::Task &task);
+    void onTasksCleared(Utils::Id categoryId);
+
+    QList m_tasks;
+    mutable QMutex m_mutex;
+};
+
+class GetIssuesListTool : public LLMCore::BaseTool
+{
+    Q_OBJECT
+public:
+    explicit GetIssuesListTool(QObject *parent = nullptr);
+
+    QString name() const override;
+    QString stringName() const override;
+    QString description() const override;
+    QJsonObject getDefinition(LLMCore::ToolSchemaFormat format) const override;
+    LLMCore::ToolPermissions requiredPermissions() const override;
+
+    QFuture executeAsync(const QJsonObject &input = QJsonObject()) override;
+};
+
+} // namespace QodeAssist::Tools
diff --git a/tools/ToolsFactory.cpp b/tools/ToolsFactory.cpp
index 6239b9e..4511964 100644
--- a/tools/ToolsFactory.cpp
+++ b/tools/ToolsFactory.cpp
@@ -24,6 +24,7 @@
 #include 
 #include 
 
+#include "GetIssuesListTool.hpp"
 #include "ListProjectFilesTool.hpp"
 #include "ReadProjectFileByNameTool.hpp"
 #include "ReadVisibleFilesTool.hpp"
@@ -43,6 +44,7 @@ void ToolsFactory::registerTools()
     registerTool(new ReadProjectFileByNameTool(this));
     registerTool(new ListProjectFilesTool(this));
     registerTool(new SearchInProjectTool(this));
+    registerTool(new GetIssuesListTool(this));
 
     LOG_MESSAGE(QString("Registered %1 tools").arg(m_tools.size()));
 }