| name | create-universal-module |
| description | Activate when creating a new Logos module with the universal C++ interface. Covers scaffolding, metadata.json, flake.nix with code generator, CMakeLists.txt, and the pure C++ impl header. |
Create a Universal C++ Module
When to Use
Use this skill when:
- Creating a new Logos module from scratch
- The module provides backend services (no UI)
- You need a
"type": "core" module with "interface": "universal"
Step 1: Create Project Directory
mkdir logos-<name> && cd logos-<name>
git init
Use snake_case for the module name: crypto_utils, data_processor, auth_service.
Step 2: Create metadata.json
{
"name": "<name>",
"version": "1.0.0",
"description": "<description>",
"author": "<author>",
"type": "core",
"interface": "universal",
"category": "<category>",
"main": "<name>_plugin",
"dependencies": [],
"include": [],
"capabilities": [],
"nix": {
"packages": {
"build": [],
"runtime": []
},
"external_libraries": [],
"cmake": {
"find_packages": [],
"extra_sources": [],
"extra_include_dirs": [],
"extra_link_libraries": []
}
}
}
Replace <name> with the module name (e.g., crypto_utils). The main field is <name>_plugin.
Step 3: Create the Impl Header
Create src/<name>_impl.h. This is your module's public API — every public method becomes callable via LogosAPI.
#pragma once
#include <string>
#include <vector>
#include <cstdint>
class <ImplClassName> {
public:
<ImplClassName>();
~<ImplClassName>();
std::string exampleMethod(const std::string& input);
bool validate(const std::string& data);
int64_t count();
std::vector<std::string> listItems();
private:
};
Rules:
- Class name is PascalCase +
Impl: CryptoUtilsImpl for module crypto_utils
- Use ONLY:
std::string, bool, int64_t, uint64_t, double, void, std::vector<T>
- NO Qt types: no
QString, QObject, Q_INVOKABLE, QVariant
- Only
public methods become module API methods
Step 4: Create the Implementation
Create src/<name>_impl.cpp:
#include "<name>_impl.h"
<ImplClassName>::<ImplClassName>() {}
<ImplClassName>::~<ImplClassName>() {}
std::string <ImplClassName>::exampleMethod(const std::string& input) {
return "processed: " + input;
}
Step 5: Create CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(Logos<PascalName>Plugin LANGUAGES CXX)
if(DEFINED ENV{LOGOS_MODULE_BUILDER_ROOT})
include($ENV{LOGOS_MODULE_BUILDER_ROOT}/cmake/LogosModule.cmake)
else()
message(FATAL_ERROR "LogosModule.cmake not found. Set LOGOS_MODULE_BUILDER_ROOT.")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/metadata.json ${CMAKE_CURRENT_BINARY_DIR}/metadata.json COPYONLY)
logos_module(
NAME <name>
SOURCES
src/<name>_impl.h
src/<name>_impl.cpp
generated_code/<name>_qt_glue.h
generated_code/<name>_dispatch.cpp
INCLUDE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/generated_code
)
Step 6: Create flake.nix
{
description = "Logos <PascalName> Module";
inputs = {
logos-module-builder.url = "github:logos-co/logos-module-builder";
nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx";
};
outputs = inputs@{ logos-module-builder, ... }:
logos-module-builder.lib.mkLogosModule {
src = ./.;
configFile = ./metadata.json;
flakeInputs = inputs;
preConfigure = ''
logos-cpp-generator --from-header src/<name>_impl.h \
--backend qt \
--impl-class <ImplClassName> \
--impl-header <name>_impl.h \
--metadata metadata.json \
--output-dir ./generated_code
'';
};
}
Step 7: Build
git add -A
nix build
Files must be tracked by git before Nix can see them.
Step 8: Test
logoscore -m ./result/lib -l <name> -c "<name>.exampleMethod(test)"
nix flake check -L
Step 9: Inspect
lm ./result/lib/<name>_plugin.so
lm methods ./result/lib/<name>_plugin.so --json
Checklist