# AI Assistant instructions for Mudlet

## Project overview

Mudlet is a cross-platform MUD client built with Qt6 and C++20, providing scripting capabilities in Lua 5.1. The project emphasizes "powerful simplicity" - clean interface with deep customization options.

## Core technologies

- **C++20** with Qt6 (minimum 6.8.2)
- **CMake** build system (minimum 3.25.1)
- **Lua 5.1** scripting engine
- **Cross-platform**: Windows, macOS, Linux

## Project structure

- `src/` - main application source code
- `test/` - Unit tests for C++ core, written in Qt Test
- `src/mudlet-lua/tests/` - Unit tests covering the Lua API, written in Busted
- `3rdparty/` - External dependencies and libraries
- `translations/` - Internationalization files
- `.github/workflows/` - Github Actions workflows

## Coding standards
All files should end with a newline character at the end of the file.

### C++ Conventions
In general: write modern C++20 code, but avoid C++ exceptions, templates, and concepts as those have performance/complexity considerations, avoiding which has made Mudlet the success it is today.

Use range-based for loops instead of iterator-based or index-based loops where appropriate.

See `docs/CONTRIBUTING.md` for the coding standards as well as the information below:

```cpp
// Class names: PascalCase with 'T' prefix for main classes
class TConsole : public QWidget

// Member variables: camelCase with 'm' prefix
// avoid starting with the `is` prefix
QString mProfileName;

// avoid starting function names with the `is` prefix as well
bool ready();

// Qt signals/slots: camelCase
signals:
    void profileChanged(const QString& name);
```

### QFlags and enums

When working with Qt `Q_DECLARE_FLAGS` types, always use the proper enum/flags type - never pass them as raw `int`:

```cpp
// Good - uses the typed enum
void setServerOrigin(Host*, const Host::DiscordOptionFlag);
QMap<Host*, Host::DiscordOptionFlags> mServerOriginFlags;

// Bad - loses type safety
void setServerOrigin(Host*, int flag);
QMap<Host*, int> mServerOriginFlags;
```

### String handling

```cpp
// Use qsl() macro for string literals (defined as QStringLiteral)
QString objectName = qsl("timer(Host:%1)(TTimerId:%2)").arg(hostName, timerName);

// Prefer QString for UI, tr() for user-visible strings
QString displayText = tr("Connection failed: %1").arg(errorMessage);

// Always add contextual comments for translators using //:
//: Toast notification shown when user dismisses an editor tip banner
QString toastMessage = tr("Banner hidden. <a href='undo'>Undo</a>");
```

```xml
// In .ui files, set "notr" attribute to true for string literals which require no translation
<widget>
  <property name="text">
    <string notr="true">-</string>
  </property>
</widget>
```

### Memory management

- Use Qt's parent-child system for automatic cleanup for Qt classes
- Otherwise, use C++ smart pointers for non-Qt classes

### Include management

Minimize `#include` directives to reduce build times:

**In header files (.h):**
- Only include what's needed for declarations in that header
- Use forward declarations (`class Foo;`) when only pointers or references are used
- Never include headers "just in case" - each include in a header propagates to all files that include it

**In source files (.cpp):**
- Only include headers actually used in that file
- When adding new code, verify you need each include you add
- Don't copy includes from similar files without checking if they're needed

**Forward declaration examples:**
```cpp
// In header: use forward declaration when only pointer/reference is needed
class Host;  // Forward declare instead of #include "Host.h"
class QCloseEvent;

class MyClass {
    Host* mpHost;  // Pointer - forward declaration sufficient
    void closeEvent(QCloseEvent* event);  // Pointer param - forward declaration sufficient
};

// In cpp: include the full header where the type is actually used
#include "Host.h"
#include <QCloseEvent>
```

## Key architecture points
Mudlet is single-threaded - all profiles, triggers, and the Lua engine run on the main thread. The only exception is networking, which is automatically handled in the background by Qt.

### Core classes (src/ directory)

- `mudlet.h/cpp` - main application
- `Host.h/cpp` - game connection management using profiles
- `ctelnet.h/cpp` - telnet protocol handling
- `TConsole.h/cpp` - text display and input
- `TLuaInterpreter.h/cpp` - Lua scripting engine
- `TMap.h/cpp` - mapping system
- `src/lua-function-list.json` - autogenerated functions file, do not update

### Lua API development

```cpp
// Standard Lua function template
int TLuaInterpreter::functionName(lua_State* L)
{
    const QString param = getVerifiedString(L, __func__, 1, "parameter name");
    // ... implementation

    lua_pushboolean(L, true);
    return 1; // number of return values
}
```

## Common patterns

### Comments

Don't add comments for obvious code as that increases cognitive load on the reader. Only add comments in unintuitive situations to explain why something was done.

### Error handling

```cpp
// Qt-style error handling
if (!file.open(QIODevice::ReadOnly)) {
    qWarning() << "Failed to open file:" << file.errorString();
    return false;
}
```

### UI components

- Dialog classes use `dlg*.h/cpp` naming
- Follow Qt's Model-View pattern
- Use Qt's signal/slot mechanism for communication

## Build system notes

- **Build system**: CMake (handles platform-specific configurations). See https://wiki.mudlet.org/w/Compiling_Mudlet for instructions.
- Check code quality with clang-tidy using `.clang-tidy` configuration file
- Allow up to 10mins for a build - it can take a while

### Code formatting

After editing any C++ files (`.cpp`, `.h`), run clang-format before committing:

```bash
clang-format -i path/to/edited/file.cpp path/to/edited/file.h
```

On macOS, use the Homebrew-installed LLVM version to ensure compatibility:
```bash
$(brew --prefix llvm)/bin/clang-format -i path/to/edited/file.cpp path/to/edited/file.h
```

The project uses the `.clang-format` configuration in the repo root. This ensures consistent code style across the codebase.

### Static analysis

For complete setup instructions on how to run static analysis during a build see, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Static_Analysis

### Debugging options

`src/CMakeLists.txt` contains commented debugging defines for development (search "Debugging code inclusions"):

- `DEBUG_TELNET` - Telnet protocol debugging
- `DEBUG_UTF8_PROCESSING` - UTF-8 decoding messages
- `DEBUG_SGR_PROCESSING` - ANSI color sequence debugging
- `DEBUG_WINDOW_HANDLING` - UI window operations
- And others for encoding, MXP, map autosave, etc.

**Usage**: Uncomment relevant `target_compile_definitions(mudlet PRIVATE DEBUG_XXX)` lines when debugging specific areas. **Important**: Do not commit uncommented debug lines to git.

### Git

Do not force-push to remote branches.

#### Commit trailers for AI-assisted work

When you (an AI assistant) help produce a commit, the commit message MUST include:

- `Assisted-by: AGENT_NAME:MODEL_VERSION` - identifies the AI tool and model. Use the actual model ID you are running as (e.g. `Assisted-by: Claude:claude-4.6-opus`).
- `Signed-off-by: Full Name <email>` - the human submitter's DCO sign-off. Do NOT fabricate or auto-add this on the human's behalf, and do NOT add a `Signed-off-by` for the AI itself.

Before the human signs off, they must have built and manually tested the change to confirm it works. Mudlet developers should not be the first to test AI-generated code. Ask the human to confirm they've tested the PR, and to provide the name and email to use for sign-off; only then add the `Signed-off-by` trailer.

Both trailers go at the end of the commit message. Apply this to every AI-assisted commit, not just the first. See the "AI Coding Assistants" section in `docs/CONTRIBUTING.md` for the full policy.

### Building on macOS

For complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Compiling_on_macOS

**Essential build commands:**

```bash
# Build
cd /path/to/Mudlet/build
# wait up to 10mins for a build
cmake ../../Mudlet -DCMAKE_PREFIX_PATH=`brew --prefix qt6`
make -j `sysctl -n hw.ncpu`

# Run Mudlet - use absolute path to avoid directory confusion
/path/to/Mudlet/build/src/mudlet.app/Contents/MacOS/mudlet
```

### Building on Linux

For complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet

```bash
# cd to the right build directory
cd /path/to/Mudlet/build

# configure (only needed the first time)
cmake ../ -G Ninja

# Compile using this command and wait up to 10mins for a build. Cmake runs the build in parallel by default, no need to specify number of jobs:
cmake --build .

# Run Mudlet - it's a visual, desktop application
cd /path/to/Mudlet/build
./src/mudlet
```

### Building on Windows

For complete setup instructions, see: https://wiki.mudlet.org/w/Compiling_Mudlet#Compiling_on_Windows

