Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .claude/agents/build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
name: build
description: Compile the project using cmake and ninja. Use when code changes need to be built or when checking for compilation errors.
tools: Bash
model: Haiku
---

Build the project from the project root.

If the build directory doesn't exist yet, run:
mkdir -p build && cmake -GNinja -S . -B build

Then always run:
ninja -C build

Report only errors and warnings. If the build succeeds, say so briefly.
73 changes: 73 additions & 0 deletions .claude/agents/code-reviewer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
name: code-reviewer
description: Reviews Qt/C++ code for quality, safety, and best practices
tools: Read, Glob, Grep
model: sonnet
---

You are a senior Qt/C++ code reviewer. When invoked, analyze the code and provide
specific, actionable feedback tailored to Qt and modern C++ best practices.

Evaluate:

### Qt-specific best practices
- Correct use of signals and slots (including new-style connections)
- QObject ownership and parent-child memory management
- Threading correctness (QThread, moveToThread, signal/slot thread safety)
- UI responsiveness (avoid blocking the main thread)
- Correct use of Qt macros (Q_OBJECT, Q_PROPERTY, etc.)
- Resource management (QScopedPointer, QSharedPointer, parent ownership)

### C++ quality
- RAII and memory safety (avoid raw `new/delete` where possible)
- Const-correctness and references
- Copy/move semantics
- Clear naming and class design

### Correctness
- Potential bugs (null dereferences, lifetime issues, race conditions)
- Signal-slot connection errors (wrong signatures, missing connections)
- Misuse of Qt APIs

### Performance
- Unnecessary copies (especially with QString, QVector, etc.)
- Inefficient signal emissions or event handling
- UI or thread bottlenecks

### Security & robustness
- Input validation
- Unsafe string or file handling
- Thread-safety issues

### Testability
- Separation of UI and logic
- Ability to unit test (e.g., avoiding tight coupling to QWidget)
- Use of dependency injection where appropriate

---

Format your response as:

## Summary
Brief overall assessment

## Critical Issues
- [Critical] Issue description + why it matters + how to fix

## Warnings
- [Warning] Issue description + suggested fix

## Suggestions
- [Suggestion] Improvements or best practices

## Qt-Specific Notes
- Qt idioms or framework-specific recommendations

---

Guidelines:

- Highlight ownership issues explicitly (who owns what)
- Call out threading mistakes clearly (they are often subtle and critical)
- Suggest concrete code improvements where helpful
- If context is incomplete, state assumptions clearly
18 changes: 18 additions & 0 deletions .claude/agents/quality.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
name: quality
description: Run pre-commit quality checks (clang-format, clang-tidy, clazy) on source files. Use after making code changes before finishing a task.
tools: Bash
model: Haiku
---

Run quality checks from the project root.

For a specific file (faster):
clang-format -i <file>
./scripts/run_clang_tidy.sh <file>
./scripts/run_clazy.sh <file>

For all files (slow):
./scripts/run_precommit.sh

Report only violations with file, line, and error message. If all checks pass, say so briefly.
11 changes: 11 additions & 0 deletions .claude/agents/test-runner.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
name: test-runner
description: Run the test suite with ctest. Use after making code changes to verify correctness. The project must be built first.
tools: Bash
model: Haiku
---

Run the test suite from the project root:
ctest --test-dir build --output-on-failure

Report only failing tests with their error messages. If all tests pass, say so briefly with the count.
12 changes: 9 additions & 3 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ clang-format -i src/path/to/file.cpp

```text
src/
├── communication/ Modbus protocol layer (ModbusPoll, ModbusMaster, ModbusConnection)
├── communication/ Communication (using external adapters)
├── models/ Data models (SettingsModel, GraphDataModel, DiagnosticModel, Device, Connection)
├── datahandling/ Expression parsing, graph data processing
├── importexport/ CSV export, MBS project files, MBC device config import
Expand Down Expand Up @@ -82,5 +82,11 @@ Enforced by `.clang-format` (Mozilla-based, C++20):

## Development

- Use a subagent to run the test suite and report only the failing tests with their error messages.
- Use a subagent to run the quality checks (clang, clazy) and report only the violations with their error messages.
Several sub-agents are defined in `.claude/agents/` to keep build/test/lint output out of the main context:

- **`@agent-build`** - runs cmake + ninja; reports only errors and warnings.
- **`@agent-test-runner`** - runs ctest; reports only failing tests with their error messages.
- **`@agent-quality`** - runs clang-format, clang-tidy, and clazy; reports only violations.
- **`@agent-code-reviewer`** - reviews code for quality, safety, and best practices; provides specific, actionable feedback.

Always use these agents rather than running the commands directly. After making source file changes: build, then run tests, then run quality checks - all must pass before the work is done.
32 changes: 13 additions & 19 deletions src/dialogs/devicesettings.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
#include "devicesettings.h"
#include "ui_devicesettings.h"

#include "customwidgets/addabletabwidget.h"
#include "customwidgets/deviceform.h"
#include "models/device.h"

#include <QLabel>
#include <QToolButton>
#include <QVBoxLayout>

DeviceSettings::DeviceSettings(SettingsModel* pSettingsModel, QWidget* parent)
: QWidget(parent), _pUi(new Ui::DeviceSettings), _pSettingsModel(pSettingsModel)
: QWidget(parent), _pDeviceTabs(new AddableTabWidget(this)), _pSettingsModel(pSettingsModel)
{
_pUi->setupUi(this);
QVBoxLayout* layout = new QVBoxLayout(this);
layout->addWidget(_pDeviceTabs);

connect(_pUi->deviceTabs, &AddableTabWidget::tabClosed, this, &DeviceSettings::handleCloseTab,
Qt::DirectConnection);
connect(_pUi->deviceTabs, &AddableTabWidget::addTabRequested, this, &DeviceSettings::handleAddTab);
connect(_pDeviceTabs, &AddableTabWidget::tabClosed, this, &DeviceSettings::handleCloseTab, Qt::DirectConnection);
connect(_pDeviceTabs, &AddableTabWidget::addTabRequested, this, &DeviceSettings::handleAddTab);

QList<QWidget*> pages;
QStringList names;
Expand All @@ -25,12 +24,7 @@ DeviceSettings::DeviceSettings(SettingsModel* pSettingsModel, QWidget* parent)
pages.append(createForm(devId));
}

_pUi->deviceTabs->setTabs(pages, names);
}

DeviceSettings::~DeviceSettings()
{
delete _pUi;
_pDeviceTabs->setTabs(pages, names);
}

DeviceForm* DeviceSettings::createForm(deviceId_t devId)
Expand All @@ -47,7 +41,7 @@ void DeviceSettings::handleAddTab()
deviceId_t devId = _pSettingsModel->addNewDevice();
QString name = constructTabName(devId);

_pUi->deviceTabs->addNewTab(name, createForm(devId));
_pDeviceTabs->addNewTab(name, createForm(devId));
}

QString DeviceSettings::constructTabName(deviceId_t devId)
Expand All @@ -59,9 +53,9 @@ QString DeviceSettings::constructTabName(deviceId_t devId)
void DeviceSettings::updateTabName(deviceId_t devId)
{
int index = -1;
for (int i = 0; i < _pUi->deviceTabs->count(); ++i)
for (int i = 0; i < _pDeviceTabs->count(); ++i)
{
auto tabContent = dynamic_cast<DeviceForm*>(_pUi->deviceTabs->tabContent(i));
auto tabContent = qobject_cast<DeviceForm*>(_pDeviceTabs->tabContent(i));
if (tabContent && tabContent->deviceId() == devId)
{
index = i;
Expand All @@ -72,13 +66,13 @@ void DeviceSettings::updateTabName(deviceId_t devId)
if (index != -1)
{
QString name = constructTabName(devId);
_pUi->deviceTabs->setTabName(index, name);
_pDeviceTabs->setTabName(index, name);
}
}

void DeviceSettings::handleCloseTab(int index)
{
auto tabContent = dynamic_cast<DeviceForm*>(_pUi->deviceTabs->tabContent(index));
auto tabContent = qobject_cast<DeviceForm*>(_pDeviceTabs->tabContent(index));
if (tabContent)
{
deviceId_t devId = tabContent->deviceId();
Expand Down
10 changes: 3 additions & 7 deletions src/dialogs/devicesettings.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,16 @@
#include "models/settingsmodel.h"
#include <QWidget>

// Forward declaration
// Forward declarations
class AddableTabWidget;
class DeviceForm;

namespace Ui {
class DeviceSettings;
}

class DeviceSettings : public QWidget
{
Q_OBJECT

public:
explicit DeviceSettings(SettingsModel* pSettingsModel, QWidget* parent = nullptr);
~DeviceSettings();

private slots:
void handleAddTab();
Expand All @@ -28,7 +24,7 @@ private slots:
DeviceForm* createForm(deviceId_t devId);
QString constructTabName(deviceId_t devId);

Ui::DeviceSettings* _pUi;
AddableTabWidget* _pDeviceTabs;
SettingsModel* _pSettingsModel;
};

Expand Down
41 changes: 0 additions & 41 deletions src/dialogs/devicesettings.ui

This file was deleted.