From 38e179c09f7381bc78d5bee91499e2e3c5f15b44 Mon Sep 17 00:00:00 2001 From: jimmy-claw <261145897+jimmy-claw@users.noreply.github.com> Date: Sat, 18 Apr 2026 22:04:04 +0000 Subject: [PATCH 1/2] chore: update logos-core-module flake.lock to latest logos-cpp-sdk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updates logos-cpp-sdk from 95f763b (2026-02-25) to 1468180 (2026-04-13), picking up SDK PRs #48 (async methods), #49 (async crash fix), #31 (QVariantList/Map), and #33 (IDL parser wip). All Rust workspace tests pass. No source changes needed — IComponent API is unchanged. Fixes #146 Co-Authored-By: Claude Opus 4.6 (1M context) --- logos-core-module/flake.lock | 2453 +++++++++++++++++++++++++++++++--- 1 file changed, 2302 insertions(+), 151 deletions(-) diff --git a/logos-core-module/flake.lock b/logos-core-module/flake.lock index f46d117..7dd8458 100644 --- a/logos-core-module/flake.lock +++ b/logos-core-module/flake.lock @@ -2,22 +2,54 @@ "nodes": { "logos-capability-module": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_2", - "logos-liblogos": "logos-liblogos_2", + "logos-cpp-sdk": [ + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-module": "logos-module_4", + "logos-nix": "logos-nix_9", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", + "owner": "logos-co", + "repo": "logos-capability-module", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-capability-module", + "type": "github" + } + }, + "logos-capability-module_2": { + "inputs": { + "logos-cpp-sdk": "logos-cpp-sdk_3", + "logos-module": "logos-module_5", + "logos-nix": "logos-nix_14", "nixpkgs": [ "logos-module-builder", + "logos-standalone-app", "logos-liblogos", "logos-capability-module", - "logos-liblogos", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1767809111, - "narHash": "sha256-jehjsB+BpDJlVu3I7x+vFVOdXmy9MDmFTJtRqzFUONo=", + "lastModified": 1774455138, + "narHash": "sha256-szx2dnnY9MP1NpdBnR8E2DRSz9CtQlo/6698zgJcAEM=", "owner": "logos-co", "repo": "logos-capability-module", - "rev": "7b35383e0aa4e28a4633ed18a87efb57636939b1", + "rev": "0655be68e0078bede0682bb6a5b53330dac37a72", "type": "github" }, "original": { @@ -28,14 +60,20 @@ }, "logos-cpp-sdk": { "inputs": { - "nixpkgs": "nixpkgs" + "logos-nix": "logos-nix", + "nixpkgs": [ + "logos-module-builder", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1772028960, - "narHash": "sha256-BDWFjaKeoJW8oWDlPphNINt5U3P1xt1z1Y4f9jyC7uU=", + "lastModified": 1776101366, + "narHash": "sha256-HxkzOs2xv0grkNAJMBLXKDjVl8Z+z3YFn+sC4eFKy/8=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "95f763b48d74bcdc63093b05159f43500cab139e", + "rev": "1468180b2567f4c59346bb94f74951e76341f5c5", "type": "github" }, "original": { @@ -46,14 +84,21 @@ }, "logos-cpp-sdk_2": { "inputs": { - "nixpkgs": "nixpkgs_2" + "logos-nix": "logos-nix_10", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1761230734, - "narHash": "sha256-CMRUwXH7pJZ1OI6bd/TDDDXKqQ1tQZHQEOOwK8TgYHI=", + "lastModified": 1776101366, + "narHash": "sha256-HxkzOs2xv0grkNAJMBLXKDjVl8Z+z3YFn+sC4eFKy/8=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "4b143922c190df00bb3835441c9f0075cb28283b", + "rev": "1468180b2567f4c59346bb94f74951e76341f5c5", "type": "github" }, "original": { @@ -64,14 +109,23 @@ }, "logos-cpp-sdk_3": { "inputs": { - "nixpkgs": "nixpkgs_3" + "logos-nix": "logos-nix_12", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-capability-module", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1761230734, - "narHash": "sha256-CMRUwXH7pJZ1OI6bd/TDDDXKqQ1tQZHQEOOwK8TgYHI=", + "lastModified": 1773956385, + "narHash": "sha256-CV0Lo1FrosBt/MSP+GWQGWXnYobxRGXGOREylNuwZ58=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "4b143922c190df00bb3835441c9f0075cb28283b", + "rev": "4b66dac015e4b977d33cfae80a4c8e1d518679f3", "type": "github" }, "original": { @@ -82,14 +136,22 @@ }, "logos-cpp-sdk_4": { "inputs": { - "nixpkgs": "nixpkgs_4" + "logos-nix": "logos-nix_15", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-cpp-sdk", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1772028960, - "narHash": "sha256-BDWFjaKeoJW8oWDlPphNINt5U3P1xt1z1Y4f9jyC7uU=", + "lastModified": 1775745471, + "narHash": "sha256-Flz0Ipok57ivbqg7Fw4qRcfCL3ainrRTXMIlNDh3ajY=", "owner": "logos-co", "repo": "logos-cpp-sdk", - "rev": "95f763b48d74bcdc63093b05159f43500cab139e", + "rev": "8b1cfadf090f0df9d75e61ac7475d83f9c58b0a9", "type": "github" }, "original": { @@ -98,44 +160,52 @@ "type": "github" } }, - "logos-cpp-sdk_5": { + "logos-design-system": { "inputs": { - "nixpkgs": "nixpkgs_5" + "logos-nix": "logos-nix_11", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-design-system", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1767724329, - "narHash": "sha256-UPkqxqxbKwU5Dmu00TnjiJVXUmfVylF3p1qziEuYwIE=", + "lastModified": 1774455271, + "narHash": "sha256-fXPDvB4VoS9k0oiW3CjN1w2cw9noqcloftXKMc8E0ng=", "owner": "logos-co", - "repo": "logos-cpp-sdk", - "rev": "32f1d7080d784ff044d91d076ef2f0c7305d4784", + "repo": "logos-design-system", + "rev": "75201e56002327864544b729ad0077bca7e5b03d", "type": "github" }, "original": { "owner": "logos-co", - "repo": "logos-cpp-sdk", + "repo": "logos-design-system", "type": "github" } }, "logos-liblogos": { "inputs": { - "logos-capability-module": "logos-capability-module", + "logos-capability-module": "logos-capability-module_2", "logos-cpp-sdk": "logos-cpp-sdk_4", - "logos-module": "logos-module", - "nix-bundle-appimage": "nix-bundle-appimage", - "nix-bundle-dir": "nix-bundle-dir_2", + "logos-module": "logos-module_6", + "logos-nix": "logos-nix_17", + "logos-package-manager": "logos-package-manager", "nixpkgs": [ "logos-module-builder", - "logos-liblogos", - "logos-cpp-sdk", + "logos-standalone-app", + "logos-nix", "nixpkgs" - ] + ], + "process-stats": "process-stats" }, "locked": { - "lastModified": 1772115748, - "narHash": "sha256-sPdAuYiLOjsulrk+uKMT7EG05ZlGT7OYEpgUh+f0nME=", + "lastModified": 1776084938, + "narHash": "sha256-0UL6tG6mK00HN99fm9CLJu3JA9ay2ry6dgeHfyApiWo=", "owner": "logos-co", "repo": "logos-liblogos", - "rev": "07780444deb99f10e600247e3696ba495f2f071a", + "rev": "b293e9d70a04983778ef2ef3ef42596f76f41161", "type": "github" }, "original": { @@ -144,49 +214,78 @@ "type": "github" } }, - "logos-liblogos_2": { + "logos-module": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_3", + "logos-nix": "logos-nix_2", "nixpkgs": [ "logos-module-builder", - "logos-liblogos", - "logos-capability-module", - "logos-liblogos", - "logos-cpp-sdk", + "logos-module", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1761845775, - "narHash": "sha256-ulK8xq05ejK6qIgZ7WtWb/MJt2rk5BKfDA2z7mM3wq8=", + "lastModified": 1775763932, + "narHash": "sha256-PrVdkHNN2PPXoUEJoJUKv61t6IeQ3iQSRarIpFr9GHE=", "owner": "logos-co", - "repo": "logos-liblogos", - "rev": "a92c2c1268bc70764c8f73c7bce07d21024f5af9", + "repo": "logos-module", + "rev": "73cd9c4b2646dedb1b624a3178b32a7af1670047", "type": "github" }, "original": { "owner": "logos-co", - "repo": "logos-liblogos", + "repo": "logos-module", "type": "github" } }, - "logos-module": { + "logos-module-builder": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk_5", + "logos-cpp-sdk": "logos-cpp-sdk", + "logos-module": "logos-module", + "logos-nix": "logos-nix_3", + "logos-plugin-core": "logos-plugin-core", + "logos-plugin-qt": "logos-plugin-qt", + "logos-standalone-app": "logos-standalone-app", + "logos-test-framework": "logos-test-framework", + "nix-bundle-lgx": "nix-bundle-lgx_2", + "nix-bundle-logos-module-install": "nix-bundle-logos-module-install", "nixpkgs": [ "logos-module-builder", - "logos-liblogos", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776431480, + "narHash": "sha256-QMdoBJfwQzXemrGKiWPGY797DQ6aH/NuzK0T2SNP+ho=", + "owner": "logos-co", + "repo": "logos-module-builder", + "rev": "1247e5c0cc8823a75412e82b2c1ff2409bb1eacd", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-module-builder", + "type": "github" + } + }, + "logos-module_2": { + "inputs": { + "logos-nix": "logos-nix_4", + "nixpkgs": [ + "logos-module-builder", + "logos-plugin-core", "logos-module", - "logos-cpp-sdk", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1770999556, - "narHash": "sha256-anpsEniGTTwUAwknRxjaT9GP4avHzIsolEHdHDTV9rM=", + "lastModified": 1774988698, + "narHash": "sha256-Ugngv17u5CA3lOSNHN6nJ+/WpIyNn8yui0M2VDdkENk=", "owner": "logos-co", "repo": "logos-module", - "rev": "d1b35f335f938bb5de21a2a6010f1104075bdb1c", + "rev": "337223f2a72710d8052ca750510cd25d33e05047", "type": "github" }, "original": { @@ -195,199 +294,2251 @@ "type": "github" } }, - "logos-module-builder": { + "logos-module_3": { "inputs": { - "logos-cpp-sdk": "logos-cpp-sdk", - "logos-liblogos": "logos-liblogos", + "logos-nix": "logos-nix_6", "nixpkgs": [ "logos-module-builder", - "logos-cpp-sdk", + "logos-plugin-qt", + "logos-module", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1770934129, - "narHash": "sha256-RjeB17MQh4HcS5vKtSrDM/J7fz3K8ZDNpswCGr5JF/k=", + "lastModified": 1774988698, + "narHash": "sha256-Ugngv17u5CA3lOSNHN6nJ+/WpIyNn8yui0M2VDdkENk=", "owner": "logos-co", - "repo": "logos-module-builder", - "rev": "a86eea50450511f4474b35032e5fd218a0f17c3c", + "repo": "logos-module", + "rev": "337223f2a72710d8052ca750510cd25d33e05047", "type": "github" }, "original": { "owner": "logos-co", - "repo": "logos-module-builder", + "repo": "logos-module", "type": "github" } }, - "nix-bundle-appimage": { + "logos-module_4": { "inputs": { - "nix-bundle-dir": "nix-bundle-dir", - "nixpkgs": "nixpkgs_6" + "logos-nix": "logos-nix_8", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-capability-module", + "logos-module", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1772047346, - "narHash": "sha256-RUsTUxKCxuQ3+D2LfBbK0EX1vF7HNMkpWgOGFfZbrEg=", + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", "owner": "logos-co", - "repo": "nix-bundle-appimage", - "rev": "4d68437c97ac59c3c70c1b2b116235c434d571a8", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", "type": "github" }, "original": { "owner": "logos-co", - "repo": "nix-bundle-appimage", + "repo": "logos-module", "type": "github" } }, - "nix-bundle-dir": { + "logos-module_5": { "inputs": { + "logos-nix": "logos-nix_13", "nixpkgs": [ "logos-module-builder", + "logos-standalone-app", "logos-liblogos", - "nix-bundle-appimage", + "logos-capability-module", + "logos-module", + "logos-nix", "nixpkgs" ] }, "locked": { - "lastModified": 1771971384, - "narHash": "sha256-fq0H+sxQhkGN054jdN+ZfHZibbOjHA+KD5SpRH78T1g=", + "lastModified": 1773963329, + "narHash": "sha256-zdvDHoYWQDse0eJ/UCKIJcfuYJ8NMgl6QfxRcyDEovI=", "owner": "logos-co", - "repo": "nix-bundle-dir", - "rev": "1ecb9662145a1ad84007a970b4bef50a4af159c9", + "repo": "logos-module", + "rev": "ac5a4f06ea94b01dd9c5fbb9ed4f20620beab88d", "type": "github" }, "original": { "owner": "logos-co", - "repo": "nix-bundle-dir", + "repo": "logos-module", "type": "github" } }, - "nix-bundle-dir_2": { + "logos-module_6": { "inputs": { - "nixpkgs": "nixpkgs_7" + "logos-nix": "logos-nix_16", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-module", + "logos-nix", + "nixpkgs" + ] }, "locked": { - "lastModified": 1771971384, - "narHash": "sha256-fq0H+sxQhkGN054jdN+ZfHZibbOjHA+KD5SpRH78T1g=", + "lastModified": 1775763932, + "narHash": "sha256-PrVdkHNN2PPXoUEJoJUKv61t6IeQ3iQSRarIpFr9GHE=", "owner": "logos-co", - "repo": "nix-bundle-dir", - "rev": "1ecb9662145a1ad84007a970b4bef50a4af159c9", + "repo": "logos-module", + "rev": "73cd9c4b2646dedb1b624a3178b32a7af1670047", "type": "github" }, "original": { "owner": "logos-co", - "repo": "nix-bundle-dir", + "repo": "logos-module", "type": "github" } }, - "nixpkgs": { + "logos-nix": { + "inputs": { + "nixpkgs": "nixpkgs" + }, "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_2": { + "logos-nix_10": { + "inputs": { + "nixpkgs": "nixpkgs_10" + }, "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_3": { + "logos-nix_11": { + "inputs": { + "nixpkgs": "nixpkgs_11" + }, "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_4": { + "logos-nix_12": { + "inputs": { + "nixpkgs": "nixpkgs_12" + }, "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_5": { + "logos-nix_13": { + "inputs": { + "nixpkgs": "nixpkgs_13" + }, "locked": { - "lastModified": 1759036355, - "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_6": { + "logos-nix_14": { + "inputs": { + "nixpkgs": "nixpkgs_14" + }, "locked": { - "lastModified": 1771848320, - "narHash": "sha256-0MAd+0mun3K/Ns8JATeHT1sX28faLII5hVLq0L3BdZU=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "2fc6539b481e1d2569f25f8799236694180c0993", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", "type": "github" } }, - "nixpkgs_7": { + "logos-nix_15": { + "inputs": { + "nixpkgs": "nixpkgs_15" + }, "locked": { - "lastModified": 1770562336, - "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", "type": "github" }, "original": { - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_16": { + "inputs": { + "nixpkgs": "nixpkgs_16" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_17": { + "inputs": { + "nixpkgs": "nixpkgs_17" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_18": { + "inputs": { + "nixpkgs": "nixpkgs_18" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_19": { + "inputs": { + "nixpkgs": "nixpkgs_19" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_2": { + "inputs": { + "nixpkgs": "nixpkgs_2" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_20": { + "inputs": { + "nixpkgs": "nixpkgs_20" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_21": { + "inputs": { + "nixpkgs": "nixpkgs_21" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_22": { + "inputs": { + "nixpkgs": "nixpkgs_22" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_23": { + "inputs": { + "nixpkgs": "nixpkgs_23" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_24": { + "inputs": { + "nixpkgs": "nixpkgs_24" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_25": { + "inputs": { + "nixpkgs": "nixpkgs_25" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_26": { + "inputs": { + "nixpkgs": "nixpkgs_26" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_27": { + "inputs": { + "nixpkgs": "nixpkgs_27" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_28": { + "inputs": { + "nixpkgs": "nixpkgs_28" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_29": { + "inputs": { + "nixpkgs": "nixpkgs_29" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_3": { + "inputs": { + "nixpkgs": "nixpkgs_3" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_30": { + "inputs": { + "nixpkgs": "nixpkgs_30" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_31": { + "inputs": { + "nixpkgs": "nixpkgs_31" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_32": { + "inputs": { + "nixpkgs": "nixpkgs_32" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_33": { + "inputs": { + "nixpkgs": "nixpkgs_33" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_34": { + "inputs": { + "nixpkgs": "nixpkgs_34" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_35": { + "inputs": { + "nixpkgs": "nixpkgs_35" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_36": { + "inputs": { + "nixpkgs": "nixpkgs_36" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_37": { + "inputs": { + "nixpkgs": "nixpkgs_37" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_38": { + "inputs": { + "nixpkgs": "nixpkgs_38" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_39": { + "inputs": { + "nixpkgs": "nixpkgs_39" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_4": { + "inputs": { + "nixpkgs": "nixpkgs_4" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_40": { + "inputs": { + "nixpkgs": "nixpkgs_40" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_41": { + "inputs": { + "nixpkgs": "nixpkgs_41" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_42": { + "inputs": { + "nixpkgs": "nixpkgs_42" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_5": { + "inputs": { + "nixpkgs": "nixpkgs_5" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_6": { + "inputs": { + "nixpkgs": "nixpkgs_6" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_7": { + "inputs": { + "nixpkgs": "nixpkgs_7" + }, + "locked": { + "lastModified": 1774455309, + "narHash": "sha256-3AN7aFnArdysrbQQ2UskWzjNSFADb4hDCsnx69Fa0ng=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "e637a1f5e871244d1c2df1e3c52a067f2eb406f2", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_8": { + "inputs": { + "nixpkgs": "nixpkgs_8" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-nix_9": { + "inputs": { + "nixpkgs": "nixpkgs_9" + }, + "locked": { + "lastModified": 1773955630, + "narHash": "sha256-KqzMoWYIVp2xMgphs7v02T/BE54RKMFxpdC2duhJKG0=", + "owner": "logos-co", + "repo": "logos-nix", + "rev": "0e9e6d66ab8eb34f59e45ed448f7dc29130feb88", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-nix", + "type": "github" + } + }, + "logos-package": { + "inputs": { + "logos-nix": "logos-nix_19", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775677349, + "narHash": "sha256-G+0E1mkmG3QDeTR4Pgy+xkiole/TDq+FYrvHwNp9Yrc=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "64edea0e64309e1c9f91259d16f8f81e5e39e40e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package-manager": { + "inputs": { + "logos-nix": "logos-nix_18", + "logos-package": "logos-package", + "nix-bundle-appimage": "nix-bundle-appimage", + "nix-bundle-dir": "nix-bundle-dir_2", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775680583, + "narHash": "sha256-0Bh48zTfi4lPL78ZLgmiX+QMW+nvjWKXHp5iJPEhvLg=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "8110734252edf9ca4266f475ace1c7c9bee68018", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package-manager_2": { + "inputs": { + "logos-nix": "logos-nix_35", + "logos-package": "logos-package_4", + "nix-bundle-appimage": "nix-bundle-appimage_2", + "nix-bundle-dir": "nix-bundle-dir_6", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836847, + "narHash": "sha256-pU7GShEizE8HkDGvR9NWZPqksiGyvfcWodtFyc318TM=", + "owner": "logos-co", + "repo": "logos-package-manager", + "rev": "39118d6c52226e88a77c6ff7d1196229f56b757e", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package-manager", + "type": "github" + } + }, + "logos-package_2": { + "inputs": { + "logos-nix": "logos-nix_28", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_3": { + "inputs": { + "logos-nix": "logos-nix_32", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_4": { + "inputs": { + "logos-nix": "logos-nix_36", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-package_5": { + "inputs": { + "logos-nix": "logos-nix_41", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-package", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835037, + "narHash": "sha256-Cti0DhkzyLQs98BSzcHWMLtGXpa3n+R+5upfSw6vKdQ=", + "owner": "logos-co", + "repo": "logos-package", + "rev": "ff93a0df15ceab255f27687d22d962ea2737efbe", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-package", + "type": "github" + } + }, + "logos-plugin-core": { + "inputs": { + "logos-module": "logos-module_2", + "logos-nix": "logos-nix_5", + "nixpkgs": [ + "logos-module-builder", + "logos-plugin-core", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835070, + "narHash": "sha256-RaJzt4sx6WrLtwhRhkGki/I+Bvt8fuC+p+oCcuLTm3g=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "78a04d603d910d864a26b158eca0882321258d44", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-plugin-qt": { + "inputs": { + "logos-module": "logos-module_3", + "logos-nix": "logos-nix_7", + "nixpkgs": [ + "logos-module-builder", + "logos-plugin-qt", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775835070, + "narHash": "sha256-RaJzt4sx6WrLtwhRhkGki/I+Bvt8fuC+p+oCcuLTm3g=", + "owner": "logos-co", + "repo": "logos-plugin-qt", + "rev": "78a04d603d910d864a26b158eca0882321258d44", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-plugin-qt", + "type": "github" + } + }, + "logos-qt-mcp": { + "inputs": { + "logos-nix": "logos-nix_25", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455349, + "narHash": "sha256-rebrtH1UxC1hDuwQBwyYbGzNCrnuuqiVL7OvzUhk65k=", + "owner": "logos-co", + "repo": "logos-qt-mcp", + "rev": "c5223b4b640add09e461983b8fddbd12c8b31f4f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-qt-mcp", + "type": "github" + } + }, + "logos-standalone-app": { + "inputs": { + "logos-capability-module": "logos-capability-module", + "logos-cpp-sdk": "logos-cpp-sdk_2", + "logos-design-system": "logos-design-system", + "logos-liblogos": "logos-liblogos", + "logos-nix": "logos-nix_24", + "logos-qt-mcp": "logos-qt-mcp", + "logos-view-module-runtime": "logos-view-module-runtime", + "nix-bundle-lgx": "nix-bundle-lgx", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776431432, + "narHash": "sha256-eO1nnUS33OUkdU36I27OJ0wVcFxRFkJDVuRYT6WKzy8=", + "owner": "logos-co", + "repo": "logos-standalone-app", + "rev": "09461edcaa1c617f813120124655c37f803e3a17", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-standalone-app", + "type": "github" + } + }, + "logos-test-framework": { + "inputs": { + "logos-cpp-sdk": [ + "logos-module-builder", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_30", + "nixpkgs": [ + "logos-module-builder", + "logos-test-framework", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775684803, + "narHash": "sha256-BnnrAjYJHW994WYAhd6e6/T7igLqJm4utjhqx1a6kLw=", + "owner": "logos-co", + "repo": "logos-test-framework", + "rev": "55b15075b5990b3c043030a3e404c7f11d57c32b", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-test-framework", + "type": "github" + } + }, + "logos-view-module-runtime": { + "inputs": { + "logos-cpp-sdk": [ + "logos-module-builder", + "logos-standalone-app", + "logos-cpp-sdk" + ], + "logos-nix": "logos-nix_26", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1776431382, + "narHash": "sha256-21SqqdOuHBLUGcYxGvjtC4iKp+wLGEQOKn64qLVl/+0=", + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "rev": "d611d697bf4ff48405d71f6ea29b8103d7969b22", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "logos-view-module-runtime", + "type": "github" + } + }, + "nix-bundle-appimage": { + "inputs": { + "logos-nix": "logos-nix_20", + "nix-bundle-dir": "nix-bundle-dir", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-appimage_2": { + "inputs": { + "logos-nix": "logos-nix_37", + "nix-bundle-dir": "nix-bundle-dir_5", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455478, + "narHash": "sha256-S8IMfdDc+2Wwri0krLDsIUwSqmwanmvHAJWHOFo8ykk=", + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "rev": "2428125a4a1b34ad9119efa97edb98676283e3da", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-appimage", + "type": "github" + } + }, + "nix-bundle-dir": { + "inputs": { + "logos-nix": "logos-nix_21", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_2": { + "inputs": { + "logos-nix": "logos-nix_22", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_3": { + "inputs": { + "logos-nix": "logos-nix_29", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_4": { + "inputs": { + "logos-nix": "logos-nix_33", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_5": { + "inputs": { + "logos-nix": "logos-nix_38", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-appimage", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1773961179, + "narHash": "sha256-bpaTvz//R8WFP5xnnDLv3a9l7unDmBwJjCewx3wCjzM=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "cd214dbf15487d80967389847ae2210468be6ebf", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_6": { + "inputs": { + "logos-nix": "logos-nix_39", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-package-manager", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-dir_7": { + "inputs": { + "logos-nix": "logos-nix_42", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "nix-bundle-dir", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1774455641, + "narHash": "sha256-HrVJguPxhIoZMCH+x8Wooa0tE6slUhgNOU6P89t2uQc=", + "owner": "logos-co", + "repo": "nix-bundle-dir", + "rev": "3d155cab09051703a0b02ff2de166a53c30cbca8", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-dir", + "type": "github" + } + }, + "nix-bundle-lgx": { + "inputs": { + "logos-nix": "logos-nix_27", + "logos-package": "logos-package_2", + "nix-bundle-dir": "nix-bundle-dir_3", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_2": { + "inputs": { + "logos-nix": "logos-nix_31", + "logos-package": "logos-package_3", + "nix-bundle-dir": "nix-bundle-dir_4", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-lgx_3": { + "inputs": { + "logos-nix": "logos-nix_40", + "logos-package": "logos-package_5", + "nix-bundle-dir": "nix-bundle-dir_7", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "nix-bundle-lgx", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775836380, + "narHash": "sha256-XbBPcMuDFA/SxYVw9TIRQbhie5Vj5MqwdU+Gh1iR1LA=", + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "rev": "9d8f8602b1574ec9ac4c9b31ae0c92570221c268", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-lgx", + "type": "github" + } + }, + "nix-bundle-logos-module-install": { + "inputs": { + "logos-nix": "logos-nix_34", + "logos-package-manager": "logos-package-manager_2", + "nix-bundle-lgx": "nix-bundle-lgx_3", + "nixpkgs": [ + "logos-module-builder", + "nix-bundle-logos-module-install", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775839388, + "narHash": "sha256-0QH146bzL2kKBYJq2yA35iPwug55j2xjEyKCS7tjhvg=", + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "rev": "89cc9ea91275396d589c767d76926459ac77ef20", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "nix-bundle-logos-module-install", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_10": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_11": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_12": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_13": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_14": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_15": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_16": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_17": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_18": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_19": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_20": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_21": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_22": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_23": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_24": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_25": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_26": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_27": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_28": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_29": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_30": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_31": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_32": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_33": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_34": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_35": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_36": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_37": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_38": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_39": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_4": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_40": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_41": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_42": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_5": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_6": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_7": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_8": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_9": { + "locked": { + "lastModified": 1759036355, + "narHash": "sha256-0m27AKv6ka+q270dw48KflE0LwQYrO7Fm4/2//KCVWg=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "e9f00bd893984bc8ce46c895c3bf7cac95331127", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "process-stats": { + "inputs": { + "logos-nix": "logos-nix_23", + "nixpkgs": [ + "logos-module-builder", + "logos-standalone-app", + "logos-liblogos", + "process-stats", + "logos-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1775744159, + "narHash": "sha256-hTVAnDREBQOVHML6KU3K7Ge0CRBqnFIg7uYL7qDnD8o=", + "owner": "logos-co", + "repo": "process-stats", + "rev": "33ace1270f90c89b3565e803139c0970fcd1ce8f", + "type": "github" + }, + "original": { + "owner": "logos-co", + "repo": "process-stats", "type": "github" } }, From 4bb08af8fe3f35d8b5b403c86c828cc20357e0cf Mon Sep 17 00:00:00 2001 From: jimmy-claw <261145897+jimmy-claw@users.noreply.github.com> Date: Sun, 19 Apr 2026 07:02:45 +0000 Subject: [PATCH 2/2] feat: integrate logos-dev-boost for AI agent tooling (#145) Install logos-dev-boost to provide AI coding agents with live Logos SDK knowledge. Adds CLAUDE.md, AGENTS.md, .mcp.json, and 8 Claude Code skills for Logos module development. CI job verifies files are present. Co-Authored-By: Claude Opus 4.6 (1M context) --- .claude/skills/add-to-workspace/SKILL.md | 83 ++ .claude/skills/create-ui-app/SKILL.md | 484 +++++++ .../skills/create-universal-module/SKILL.md | 246 ++++ .claude/skills/inter-module-comm/SKILL.md | 108 ++ .claude/skills/nix-flake-setup/SKILL.md | 119 ++ .claude/skills/package-lgx/SKILL.md | 109 ++ .claude/skills/testing-modules/SKILL.md | 258 ++++ .claude/skills/wrap-external-lib/SKILL.md | 155 +++ .github/workflows/ci.yml | 17 + .mcp.json | 10 + AGENTS.md | 1171 ++++++++++++++++ CLAUDE.md | 1189 +++++++++++++++++ 12 files changed, 3949 insertions(+) create mode 100644 .claude/skills/add-to-workspace/SKILL.md create mode 100644 .claude/skills/create-ui-app/SKILL.md create mode 100644 .claude/skills/create-universal-module/SKILL.md create mode 100644 .claude/skills/inter-module-comm/SKILL.md create mode 100644 .claude/skills/nix-flake-setup/SKILL.md create mode 100644 .claude/skills/package-lgx/SKILL.md create mode 100644 .claude/skills/testing-modules/SKILL.md create mode 100644 .claude/skills/wrap-external-lib/SKILL.md create mode 100644 .mcp.json create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/.claude/skills/add-to-workspace/SKILL.md b/.claude/skills/add-to-workspace/SKILL.md new file mode 100644 index 0000000..0f41ad9 --- /dev/null +++ b/.claude/skills/add-to-workspace/SKILL.md @@ -0,0 +1,83 @@ +--- +name: add-to-workspace +description: Activate when registering a new module in the logos-workspace. Covers adding git submodules, flake.nix inputs with follows declarations, scripts/ws REPOS array, repo groups, and dep-graph.nix regeneration. +--- + +# Add a Module to logos-workspace + +## When to Use + +Use this skill when: +- Adding a new module repo as a submodule to the workspace +- Registering a module in the workspace flake.nix +- Updating the ws CLI configuration + +## Step 1: Add Git Submodule + +```bash +git submodule add git@github.com:logos-co/.git repos/ +``` + +## Step 2: Read the Module's flake.nix + +```bash +cat repos//flake.nix +``` + +Identify which workspace repos it depends on (logos-module-builder, logos-cpp-sdk, etc.) and what input names it uses. + +## Step 3: Add to Workspace flake.nix + +Add an input block with `follows` for each workspace dependency: + +```nix + = { + url = "github:logos-co/"; + inputs.logos-module-builder.follows = "logos-module-builder"; + inputs.nix-bundle-lgx.follows = "nix-bundle-lgx"; + # Add follows for each workspace dep the repo uses +}; +``` + +**follows Rules:** +- Left side = input name as declared in the module's own flake.nix +- Right side = workspace flake input name +- These usually match but not always +- External deps (not in workspace) do NOT get follows + +Add the repo name to the `repoInputNames` list in the appropriate category. + +## Step 4: Add to scripts/ws REPOS Array + +```bash +"||https://github.com/logos-co/.git|yes" +``` + +Format: `"input_name|directory_name|git_url|has_flake"` + +If the repo belongs in a group, also update the `REPO_GROUPS` array. + +## Step 5: Regenerate dep-graph.nix + +```bash +ws sync-graph +``` + +This reads every repo's flake.nix, extracts dependencies, and regenerates `nix/dep-graph.nix`. + +## Step 6: Verify + +```bash +ws build +ws test +ws graph +``` + +## Checklist + +- [ ] `.gitmodules` — submodule registered +- [ ] `flake.nix` inputs — input block with correct `follows` +- [ ] `flake.nix` `repoInputNames` — listed in correct category +- [ ] `scripts/ws` REPOS — entry with `has_flake=yes` +- [ ] `scripts/ws` REPO_GROUPS — added to relevant group (if applicable) +- [ ] `nix/dep-graph.nix` — regenerated via `ws sync-graph` diff --git a/.claude/skills/create-ui-app/SKILL.md b/.claude/skills/create-ui-app/SKILL.md new file mode 100644 index 0000000..510567f --- /dev/null +++ b/.claude/skills/create-ui-app/SKILL.md @@ -0,0 +1,484 @@ +--- + +## name: create-ui-app + +description: Activate when creating a Logos Basecamp UI app. Covers two subtypes — pure QML (no C++) and QML + process-isolated C++ backend with Qt Remote Objects. + +# Create a UI App for Logos Basecamp + +## When to Use + +Use this skill when: + +- Creating an application with a graphical interface for Basecamp +- Building a pure QML app that calls backend modules via `logos.callModule()` +- Building a QML app with a process-isolated C++ backend via Qt Remote Objects + +## Choose Your Subtype + +| Need | Type | Scaffold command | +|------|------|-----------------| +| QML-only UI, calls existing modules | Pure QML | `--type ui-qml` | +| Custom business logic + QML UI | QML + Backend | `--type ui-qml-backend` | + +--- + +## Path A: Pure QML App + +### Fastest path: scaffold + +```bash +nix run github:logos-co/logos-dev-boost -- init my_app --type ui-qml +cd logos-my-app +git init && git add -A +nix build +nix run . +``` + +### Project Structure + +``` +logos-my-app/ +├── Main.qml ← QML entry point +├── metadata.json ← "type": "ui_qml", "view": "Main.qml" +├── flake.nix ← uses mkLogosQmlModule +└── .gitignore +``` + +No C++ files, no CMakeLists.txt, no compilation. + +### `metadata.json` + +```json +{ + "name": "my_app", + "version": "1.0.0", + "description": "My QML UI application", + "type": "ui_qml", + "view": "Main.qml", + "category": "tools", + "dependencies": ["some_backend_module"] +} +``` + +Note: No `"main"` field — this signals a pure QML app. + +### `Main.qml` + +```qml +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.15 + +Item { + id: root + + ColumnLayout { + anchors.fill: parent + anchors.margins: 24 + spacing: 16 + + Text { + text: "My App" + font.pixelSize: 24 + color: "#ffffff" + } + + Button { + text: "Call Backend" + onClicked: { + // logos bridge is injected by the host + var result = logos.callModule("some_backend_module", "myMethod", ["arg"]) + console.log("Result:", result) + } + } + } +} +``` + +### `flake.nix` + +If the app depends on backend modules (listed in `metadata.json` `"dependencies"`), add them as flake inputs. The input attribute name must match the dependency name in `metadata.json`. + +```nix +{ + description = "My QML UI App"; + + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + + # Add each dependency from metadata.json as a flake input. + # The attribute name must match the dependency name. + # Use path: for local development, github: for CI/published modules: + some_backend_module.url = "path:../logos-some-backend-module"; + # some_backend_module.url = "github:logos-co/logos-some-backend-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +### Resolving Module Dependencies + +Each backend module dependency must be **built** with its shared library (`.so` on Linux, `.dylib` on macOS) present in its `lib/` directory. If the library is missing, the nix build fails with linker errors. + +Three ways to point at a dependency: + +| Approach | `flake.nix` input URL | Build command | +|----------|----------------------|---------------| +| Local path in flake.nix | `path:../logos-my-module` | `nix run .` | +| Remote URL + local override | `github:org/repo` | `nix run . --override-input some_backend_module path:../logos-my-module` | +| Fully remote | `github:org/repo` | `nix run .` | + +`--override-input` is useful for quick iteration — it overrides the flake input at build time without editing `flake.nix`. Use `path:` in `flake.nix` when you persistently develop against a local checkout. + +### Build and Test + +```bash +git init && git add -A +nix build +nix run . # standalone app with QML Inspector on localhost:3768 + +# Or with a local module override: +nix run . --override-input some_backend_module path:../logos-some-backend-module +``` + +### Integration Testing (Optional) + +Create `tests/smoke.mjs`: + +```javascript +const { resolve } = await import("node:path"); +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: verify UI loads", async (app) => { + await app.expectTexts(["Call Backend"]); + await app.click("Call Backend"); +}); + +run(); +``` + +Run: `nix build .#integration-test` (headless) or `node tests/smoke.mjs` (interactive, app must be running). + +--- + +## Path B: QML + C++ Backend App + +### Fastest path: scaffold + +```bash +nix run github:logos-co/logos-dev-boost -- init my_app --type ui-qml-backend +cd logos-my-app +git init && git add -A +nix build +nix run . +``` + +### Project Structure + +``` +logos-my-app/ +├── src/ +│ ├── my_app_interface.h ← extends PluginInterface +│ ├── my_app.rep ← Qt Remote Objects interface definition +│ ├── my_app_plugin.h ← inherits SimpleSource + Interface + ViewPluginBase +│ ├── my_app_plugin.cpp ← implementation +│ └── qml/ +│ └── Main.qml ← QML frontend (uses logos.module() replica) +├── metadata.json ← "type": "ui_qml", "main": "my_app_plugin" +├── CMakeLists.txt ← logos_module() with REP_FILE +├── flake.nix ← uses mkLogosQmlModule +└── .gitignore +``` + +### Step 1: `metadata.json` + +```json +{ + "name": "my_app", + "version": "1.0.0", + "description": "My UI app with C++ backend", + "type": "ui_qml", + "main": "my_app_plugin", + "view": "qml/Main.qml", + "category": "tools", + "dependencies": [] +} +``` + +The `"main"` field signals a C++ backend is present. + +### Step 2: `.rep` File (Qt Remote Objects Interface) + +`src/my_app.rep`: + +``` +class MyApp +{ + PROP(QString status READWRITE) + SLOT(int add(int a, int b)) +} +``` + +Defines properties (auto-synced to QML) and callable slots. + +### Step 3: Interface Header + +`src/my_app_interface.h`: + +```cpp +#ifndef MY_APP_INTERFACE_H +#define MY_APP_INTERFACE_H + +#include +#include +#include "interface.h" + +class MyAppInterface : public PluginInterface +{ +public: + virtual ~MyAppInterface() = default; +}; + +#define MyAppInterface_iid "org.logos.MyAppInterface" +Q_DECLARE_INTERFACE(MyAppInterface, MyAppInterface_iid) + +#endif +``` + +### Step 4: Plugin Class + +`src/my_app_plugin.h`: + +```cpp +#ifndef MY_APP_PLUGIN_H +#define MY_APP_PLUGIN_H + +#include +#include +#include "my_app_interface.h" +#include "LogosViewPluginBase.h" +#include "rep_my_app_source.h" + +class LogosAPI; + +class MyAppPlugin : public MyAppSimpleSource, + public MyAppInterface, + public MyAppViewPluginBase +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID MyAppInterface_iid FILE "metadata.json") + Q_INTERFACES(MyAppInterface) + +public: + explicit MyAppPlugin(QObject* parent = nullptr); + ~MyAppPlugin() override; + + QString name() const override { return "my_app"; } + QString version() const override { return "1.0.0"; } + + Q_INVOKABLE void initLogos(LogosAPI* api); + + // Slots from my_app.rep + int add(int a, int b) override; + +signals: + void eventResponse(const QString& eventName, const QVariantList& args); + +private: + LogosAPI* m_logosAPI = nullptr; +}; + +#endif +``` + +Three parent classes: +- `MyAppSimpleSource` — generated from `.rep`, property storage + slot declarations +- `MyAppInterface` — extends `PluginInterface` for Qt plugin loading +- `MyAppViewPluginBase` — provides `setBackend()` for Qt Remote Objects wiring + +`src/my_app_plugin.cpp`: + +```cpp +#include "my_app_plugin.h" +#include "logos_api.h" +#include + +MyAppPlugin::MyAppPlugin(QObject* parent) + : MyAppSimpleSource(parent) +{ + setStatus("Ready"); +} + +MyAppPlugin::~MyAppPlugin() = default; + +void MyAppPlugin::initLogos(LogosAPI* api) +{ + m_logosAPI = api; + setBackend(this); // wires up Qt Remote Objects source + qDebug() << "MyAppPlugin: initialized"; +} + +int MyAppPlugin::add(int a, int b) +{ + int result = a + b; + setStatus(QStringLiteral("%1 + %2 = %3").arg(a).arg(b).arg(result)); + return result; +} +``` + +### Step 5: QML Frontend + +`src/qml/Main.qml`: + +```qml +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: root + + readonly property var backend: logos.module("my_app") + readonly property bool ready: backend !== null && logos.isViewModuleReady("my_app") + readonly property string status: backend ? backend.status : "" + + ColumnLayout { + anchors.fill: parent + anchors.margins: 24 + spacing: 16 + + Text { + text: root.ready ? "Connected" : "Connecting to backend..." + color: root.ready ? "#56d364" : "#f0883e" + font.pixelSize: 12 + } + + Button { + text: "Add" + enabled: root.ready + onClicked: { + logos.watch(backend.add(1, 2), + function(value) { resultText.text = "Result: " + value }, + function(error) { resultText.text = "Error: " + error } + ) + } + } + + Text { + id: resultText + text: "Press Add to call the backend" + color: "#56d364" + } + + Text { + text: "Backend status: " + root.status + color: "#8b949e" + } + } +} +``` + +Key QML APIs: +- `logos.module("name")` — typed Qt Remote Objects replica +- `logos.isViewModuleReady("name")` — backend connection status +- `logos.watch(pendingReply, onSuccess, onError)` — async slot call handling + +### Step 6: `CMakeLists.txt` + +```cmake +cmake_minimum_required(VERSION 3.14) +project(MyAppPlugin 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() + +logos_module( + NAME my_app + REP_FILE src/my_app.rep + SOURCES + src/my_app_interface.h + src/my_app_plugin.h + src/my_app_plugin.cpp +) +``` + +`REP_FILE` tells the build system to generate Qt Remote Objects source/replica headers. + +### Step 7: `flake.nix` + +If the app depends on other modules (listed in `metadata.json` `"dependencies"`), add them as flake inputs. The input attribute name must match the dependency name. + +```nix +{ + description = "My UI App with C++ Backend"; + + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + + # Add dependencies here if metadata.json lists any. + # Use path: for local, github: for remote: + # some_module.url = "path:../logos-some-module"; + # some_module.url = "github:logos-co/logos-some-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +Each module dependency must be built with its `.so`/`.dylib` present. Use `--override-input` at build time to point at a local checkout without editing `flake.nix` (see the Pure QML section above for the full table of approaches). + +### Step 8: Build and Test + +```bash +git init && git add -A +nix build +nix run . # standalone app with QML Inspector on localhost:3768 + +# With a local module override: +# nix run . --override-input some_module path:../logos-some-module +``` + +### Step 9: Integration Testing (Optional) + +Create `tests/smoke.mjs`: + +```javascript +const { resolve } = await import("node:path"); +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: add numbers", async (app) => { + await app.expectTexts(["Connecting to backend...", "Press Add"]); + // Wait for backend connection + await app.waitFor( + async () => { await app.expectTexts(["Connected"]); }, + { timeout: 10000, description: "backend to connect" } + ); + await app.click("Add"); + await app.expectTexts(["Result:"]); +}); + +run(); +``` + +Run: `nix build .#integration-test` (headless) or `node tests/smoke.mjs` (interactive, app must be running). + +The QML Inspector MCP tools (`qml_screenshot`, `qml_find_and_click`, `qml_get_tree`, etc.) are also available to AI agents via the `.mcp.json` that auto-registers when the app is running. diff --git a/.claude/skills/create-universal-module/SKILL.md b/.claude/skills/create-universal-module/SKILL.md new file mode 100644 index 0000000..bda9118 --- /dev/null +++ b/.claude/skills/create-universal-module/SKILL.md @@ -0,0 +1,246 @@ +--- +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 + +```bash +mkdir logos- && cd logos- +git init +``` + +Use `snake_case` for the module name: `crypto_utils`, `data_processor`, `auth_service`. + +## Step 2: Create metadata.json + +```json +{ + "name": "", + "version": "1.0.0", + "description": "", + "author": "", + "type": "core", + "interface": "universal", + "category": "", + "main": "_plugin", + "dependencies": [], + "include": [], + "capabilities": [], + + "nix": { + "packages": { + "build": [], + "runtime": [] + }, + "external_libraries": [], + "cmake": { + "find_packages": [], + "extra_sources": [], + "extra_include_dirs": [], + "extra_link_libraries": [] + } + } +} +``` + +Replace `` with the module name (e.g., `crypto_utils`). The `main` field is `_plugin`. + +## Step 3: Create the Impl Header + +Create `src/_impl.h`. This is your module's public API — every public method becomes callable via LogosAPI. + +```cpp +#pragma once +#include +#include +#include + +class { +public: + (); + ~(); + + // Your module's public API (pure C++ types only): + std::string exampleMethod(const std::string& input); + bool validate(const std::string& data); + int64_t count(); + std::vector listItems(); + +private: + // Private members and helpers (not exposed as API) +}; +``` + +**Rules:** +- Class name is PascalCase + `Impl`: `CryptoUtilsImpl` for module `crypto_utils` +- Use ONLY: `std::string`, `bool`, `int64_t`, `uint64_t`, `double`, `void`, `std::vector` +- NO Qt types: no `QString`, `QObject`, `Q_INVOKABLE`, `QVariant` +- Only `public` methods become module API methods + +## Step 4: Create the Implementation + +Create `src/_impl.cpp`: + +```cpp +#include "_impl.h" + +::() {} +::~() {} + +std::string ::exampleMethod(const std::string& input) { + return "processed: " + input; +} + +// ... implement all public methods +``` + +## Step 5: Create CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.14) +project(LogosPlugin 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 + SOURCES + src/_impl.h + src/_impl.cpp + generated_code/_qt_glue.h + generated_code/_dispatch.cpp + INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/generated_code +) +``` + +## Step 6: Create flake.nix + +```nix +{ + description = "Logos 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/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code + ''; + }; +} +``` + +## Step 7: Build + +```bash +git add -A +nix build +``` + +Files must be tracked by git before Nix can see them. + +## Step 8: Add Unit Tests + +The scaffold creates test files using logos-test-framework. If adding tests manually: + +``` +tests/ +├── main.cpp # Test runner entry point +├── test_.cpp # Test cases +└── CMakeLists.txt # logos_test() integration +``` + +```cpp +// tests/main.cpp +#include +LOGOS_TEST_MAIN() +``` + +```cpp +// tests/test_.cpp +#include +#include "../src/_impl.h" + +LOGOS_TEST(example_method_works) { + impl; + LOGOS_ASSERT_FALSE(impl.exampleMethod("test").empty()); +} +``` + +```cmake +# tests/CMakeLists.txt +cmake_minimum_required(VERSION 3.14) +project(Tests LANGUAGES CXX) + +include(LogosTest) + +logos_test( + NAME _tests + MODULE_SOURCES ../src/_impl.cpp + TEST_SOURCES + main.cpp + test_.cpp +) +``` + +`logos-module-builder` auto-detects `tests/CMakeLists.txt` and creates the `unit-tests` target. + +## Step 9: Run Tests + +```bash +# Unit tests (direct impl class testing, no logoscore needed) +nix build .#unit-tests -L + +# Integration test via logoscore +logoscore -m ./result/lib -l -c ".exampleMethod(test)" +``` + +For mocking other modules or C libraries in tests, see the `testing-modules` skill. + +## Step 10: Inspect + +```bash +lm ./result/lib/_plugin.so +lm methods ./result/lib/_plugin.so --json +``` + +## Checklist + +- [ ] `metadata.json` has `"interface": "universal"` and `"type": "core"` +- [ ] Impl header uses only pure C++ types (no Qt) +- [ ] Impl class name matches `--impl-class` in flake.nix preConfigure +- [ ] `CMakeLists.txt` lists generated_code files in SOURCES +- [ ] `flake.nix` has preConfigure with logos-cpp-generator +- [ ] `tests/CMakeLists.txt` uses `include(LogosTest)` + `logos_test()` +- [ ] All files tracked by git (`git add -A`) +- [ ] `nix build` succeeds +- [ ] `nix build .#unit-tests -L` succeeds +- [ ] `logoscore` can load and call the module diff --git a/.claude/skills/inter-module-comm/SKILL.md b/.claude/skills/inter-module-comm/SKILL.md new file mode 100644 index 0000000..578b023 --- /dev/null +++ b/.claude/skills/inter-module-comm/SKILL.md @@ -0,0 +1,108 @@ +--- +name: inter-module-comm +description: Activate when implementing inter-module communication in Logos. Covers LogosAPI::callModule(), LogosResult handling, dependency declaration in metadata.json, generated client stubs, and the QML bridge. +--- + +# Inter-Module Communication + +## When to Use + +Use this skill when: +- Calling methods on another Logos module +- Declaring module dependencies +- Handling LogosResult return values +- Using generated type-safe client stubs + +## Calling Another Module (C++) + +From a universal module's impl class or a UI app's backend: + +```cpp +#include "logos_api.h" + +LogosResult result = logosAPI->callModule("other_module", "methodName", {arg1, arg2}); +if (result.success()) { + QVariant data = result.data(); + QString stringVal = data.toString(); +} else { + QString error = result.errorMessage(); +} +``` + +## Calling from QML (UI Apps Only) + +```qml +var result = logos.callModule("other_module", "methodName", [arg1, arg2]) +``` + +The QML bridge serializes results to JSON. This is a thin wrapper over the C++ LogosAPI. + +## LogosResult + +All cross-module calls return `LogosResult`: + +| Method | Returns | Description | +|--------|---------|-------------| +| `success()` | `bool` | Whether the call succeeded | +| `data()` | `QVariant` | Return value (may be string, int, map, list) | +| `errorMessage()` | `QString` | Error description (empty on success) | + +Returning a LogosResult from your module (in universal module C++ code, handled by the generated dispatch): + +The generator wraps your C++ return values in LogosResult automatically. If your method returns `std::string`, the caller receives `result.data().toString()`. + +## Declaring Dependencies + +In `metadata.json`: + +```json +{ + "dependencies": ["storage_module", "crypto_module"] +} +``` + +Dependency names must match the `name` field in the dependency module's own `metadata.json`. The runtime loads dependencies before your module. + +In `flake.nix`, add the dependency module as an input so Nix can build it: + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + logos-storage-module.url = "github:logos-co/logos-storage-module"; +}; +``` + +## Generated Client Stubs + +The code generator can produce type-safe wrappers for calling other modules: + +```bash +logos-cpp-generator [--output-dir ] +``` + +This generates `Client.h` with typed methods: + +```cpp +#include "logos_sdk.h" + +LogosModules* logos = new LogosModules(logosAPI); +QString result = logos->storage_module.save("key", "value"); +std::vector keys = logos->storage_module.listKeys(); +``` + +## Always Handle Missing Modules + +A target module may not be loaded. Always check the result: + +```cpp +LogosResult result = api->callModule("optional_module", "method", {}); +if (!result.success()) { + // Module not loaded or method failed — handle gracefully +} +``` + +## Accessing LogosAPI + +- **Universal modules:** The API is available via the generated `onInit(LogosAPI* api)` hook. Store the pointer in your impl class if needed. +- **UI apps (with C++ backend):** Received via `initLogos(LogosAPI* api)`. Store it in your plugin class. Pure QML apps use `logos.callModule()` from QML instead. +- **In logoscore tests:** LogosAPI is available when modules are loaded by logoscore. Not available in standalone unit tests. diff --git a/.claude/skills/nix-flake-setup/SKILL.md b/.claude/skills/nix-flake-setup/SKILL.md new file mode 100644 index 0000000..3487a8f --- /dev/null +++ b/.claude/skills/nix-flake-setup/SKILL.md @@ -0,0 +1,119 @@ +--- +name: nix-flake-setup +description: Activate when configuring Nix flake.nix for a Logos module. Covers flake inputs, follows declarations, preConfigure for code generation, mkLogosModule, overrides for local development, and workspace integration. +--- + +# Nix Flake Setup for Logos Modules + +## When to Use + +Use this skill when: +- Creating a flake.nix for a new Logos module +- Adding or modifying flake inputs +- Configuring the code generator in preConfigure +- Setting up local development overrides + +## Minimal Universal Module flake.nix + +```nix +{ + description = "My Logos 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/my_module_impl.h \ + --backend qt \ + --impl-class MyModuleImpl \ + --impl-header my_module_impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code + ''; + }; +} +``` + +## With Module Dependencies + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; + logos-storage-module.url = "github:logos-co/logos-storage-module"; + logos-crypto-module.url = "github:logos-co/logos-crypto-module"; +}; +``` + +No `follows` declarations needed at the individual module level — the workspace handles dependency unification via its own `follows` in the workspace `flake.nix`. + +## With External Libraries + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; + my-ext-lib = { + url = "github:org/my-lib/"; + flake = false; + }; +}; + +outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + externalLibInputs = { + mylib = inputs.my-ext-lib; + }; + preConfigure = '' + logos-cpp-generator --from-header src/my_module_impl.h \ + --backend qt --impl-class MyModuleImpl \ + --impl-header my_module_impl.h \ + --metadata metadata.json --output-dir ./generated_code + ''; + }; +``` + +The `externalLibInputs` key must match the `name` in `metadata.json` `nix.external_libraries`. + +## mkLogosModule Parameters + +| Parameter | Required | Description | +|-----------|----------|-------------| +| `src` | Yes | Source directory (usually `./.`) | +| `configFile` | Yes | Path to metadata.json | +| `flakeInputs` | Yes | Pass `inputs` to make all flake inputs available | +| `externalLibInputs` | No | Map of external library names to flake inputs | +| `preConfigure` | No | Shell script to run before CMake (code generator) | + +## Local Development with Overrides + +Test with local changes to a dependency: + +```bash +# Manual override +nix build --override-input logos-storage-module path:../logos-storage-module + +# Via workspace CLI (auto-detects dirty repos) +ws build my-module --auto-local + +# Explicit local overrides +ws build my-module --local logos-storage-module logos-cpp-sdk +``` + +## Key Rules + +- All flake inputs must be tracked by git (`git add flake.nix flake.lock`) +- nixpkgs is inherited through logos-module-builder (never pin separately) +- Qt version comes from logos-cpp-sdk (never override) +- `preConfigure` runs BEFORE cmake — this is where the code generator goes +- The `nix-bundle-lgx` input enables LGX packaging outputs diff --git a/.claude/skills/package-lgx/SKILL.md b/.claude/skills/package-lgx/SKILL.md new file mode 100644 index 0000000..018c63e --- /dev/null +++ b/.claude/skills/package-lgx/SKILL.md @@ -0,0 +1,109 @@ +--- +name: package-lgx +description: Activate when packaging a Logos module or UI app for distribution as an LGX package. Covers lgx create, adding platform variants, verification, portable builds, and installation via lgpm. +--- + +# Package a Module as LGX + +## When to Use + +Use this skill when: +- Distributing a module or UI app as an LGX package +- Creating platform-specific variants (linux-x86_64, darwin-arm64) +- Installing packages locally via lgpm + +## Step 1: Build the Module + +```bash +nix build +``` + +The built plugin is at `./result/lib/_plugin.so` (Linux) or `./result/lib/_plugin.dylib` (macOS). + +## Step 2: Create the LGX Package + +```bash +lgx create +``` + +This creates an empty `.lgx` archive. + +## Step 3: Add Platform Variants + +```bash +lgx add .lgx -v linux-x86_64 -f ./result/lib/_plugin.so +lgx add .lgx -v darwin-arm64 -f ./result/lib/_plugin.dylib +``` + +For directory variants (e.g., UI apps with multiple files): + +```bash +lgx add .lgx -v linux-x86_64 -f ./result/lib/ --main _plugin.so -y +``` + +## Step 4: Verify + +```bash +lgx verify .lgx +``` + +Validates the package against the LGX specification. + +## Step 5: Install Locally + +```bash +# Install a single package +lgpm --modules-dir ./test-modules install --file .lgx + +# Install all packages in a directory +lgpm --modules-dir ./test-modules install --dir ./packages/ + +# List installed packages +lgpm --modules-dir ./test-modules list + +# Get package info +lgpm --modules-dir ./test-modules info +``` + +## Step 6: Test the Installed Package + +```bash +logoscore -m ./test-modules -l -c ".someMethod(test)" +``` + +## Portable Builds + +For distribution, ensure the shared library has no Nix store references: + +```bash +# Build portable (self-contained) +nix build .#portable + +# Or use nix-bundle-lgx for automated bundling +nix build .#bin-bundle-lgx +``` + +Verify no store references leaked: + +```bash +rg /nix/store result/ && echo "FAIL: store refs found" || echo "OK: portable" +``` + +## Automated Packaging with nix-bundle-lgx + +If the module's `flake.nix` includes `nix-bundle-lgx` as an input, it may provide automated packaging outputs: + +```bash +nix build .#lgx-package +``` + +This produces a ready-to-distribute `.lgx` file with the correct platform variant. + +## Downloading from the Online Catalog + +```bash +lgpd search +lgpd list --category +lgpd download -o ./packages/ +lgpm --modules-dir ./modules install --dir ./packages/ +``` diff --git a/.claude/skills/testing-modules/SKILL.md b/.claude/skills/testing-modules/SKILL.md new file mode 100644 index 0000000..74e4927 --- /dev/null +++ b/.claude/skills/testing-modules/SKILL.md @@ -0,0 +1,258 @@ +--- +name: testing-modules +description: Activate when writing tests for Logos modules. Covers the logos-test-framework (LOGOS_TEST macros, LogosTestContext, module mocking, C library mocking, event testing), logos_test() CMake integration, logoscore integration tests, and Nix check configuration. +--- + +# Testing Logos Modules + +## When to Use + +Use this skill when: +- Writing unit tests for a universal module +- Setting up test infrastructure (tests/CMakeLists.txt, test files) +- Mocking calls to other modules or external C libraries in tests +- Writing integration tests with logoscore +- Debugging test failures + +## logos-test-framework (Unit Tests) + +The test framework is provided by `logos-module-builder` automatically. No extra flake inputs needed. + +### File Structure + +``` +tests/ +├── main.cpp # LOGOS_TEST_MAIN() entry point +├── test_my_module.cpp # Test cases using LOGOS_TEST() +└── CMakeLists.txt # logos_test() macro +``` + +### Test Entry Point + +```cpp +// tests/main.cpp +#include +LOGOS_TEST_MAIN() +``` + +### Writing Tests + +```cpp +// tests/test_my_module.cpp +#include +#include "../src/my_module_impl.h" + +LOGOS_TEST(echo_returns_expected_value) { + MyModuleImpl impl; + LOGOS_ASSERT_EQ(impl.echo("hello"), std::string("echo: hello")); +} + +LOGOS_TEST(validate_rejects_empty_input) { + MyModuleImpl impl; + LOGOS_ASSERT_FALSE(impl.validate("")); +} +``` + +### CMakeLists.txt + +```cmake +# tests/CMakeLists.txt +cmake_minimum_required(VERSION 3.14) +project(MyModuleTests LANGUAGES CXX) + +include(LogosTest) + +logos_test( + NAME my_module_tests + MODULE_SOURCES ../src/my_module_impl.cpp + TEST_SOURCES + main.cpp + test_my_module.cpp +) +``` + +### Running Tests + +```bash +nix build .#unit-tests -L # Build and run unit tests +nix flake check -L # Run all Nix checks including tests +``` + +`logos-module-builder` auto-detects `tests/CMakeLists.txt` and creates `checks..unit-tests` and `packages..unit-tests`. + +### Test CLI Options + +```bash +./my_module_tests --filter # Run only matching tests +./my_module_tests --json # JSON output for CI/agents +./my_module_tests --no-color # Disable colored output +./my_module_tests --help # Show help +``` + +## Assertions + +| Macro | Description | +|-------|-------------| +| `LOGOS_ASSERT(expr)` | Expression is truthy | +| `LOGOS_ASSERT_TRUE(expr)` | Alias for LOGOS_ASSERT | +| `LOGOS_ASSERT_FALSE(expr)` | Expression is falsy | +| `LOGOS_ASSERT_EQ(a, b)` | `a == b` with diff on failure | +| `LOGOS_ASSERT_NE(a, b)` | `a != b` | +| `LOGOS_ASSERT_GT(a, b)` | `a > b` | +| `LOGOS_ASSERT_GE(a, b)` | `a >= b` | +| `LOGOS_ASSERT_LT(a, b)` | `a < b` | +| `LOGOS_ASSERT_LE(a, b)` | `a <= b` | +| `LOGOS_ASSERT_CONTAINS(haystack, needle)` | String contains substring | +| `LOGOS_ASSERT_THROWS(expr)` | Expression throws | + +## Mocking Other Modules + +Use `LogosTestContext` to mock calls to other Logos modules: + +```cpp +LOGOS_TEST(calls_other_module) { + auto t = LogosTestContext("my_module"); + t.mockModule("other_module", "getData").returns(42); + + MyModuleImpl impl; + t.init(&impl); + + auto result = impl.fetchData(); + LOGOS_ASSERT_EQ(result, 42); + LOGOS_ASSERT(t.moduleCalled("other_module", "getData")); + LOGOS_ASSERT_EQ(t.moduleCallCount("other_module", "getData"), 1); +} +``` + +## Mocking C Libraries + +For modules wrapping external C/C++ libraries: + +### 1. Write mock stubs + +```cpp +// tests/mocks/mock_libcalc.cpp +#include +extern "C" { #include "libcalc.h" } + +extern "C" int calc_add(int a, int b) { + LOGOS_CMOCK_RECORD("calc_add"); + return LOGOS_CMOCK_RETURN(int, "calc_add"); +} +``` + +### 2. Reference in CMake + +```cmake +logos_test( + NAME calc_module_tests + MODULE_SOURCES ../src/calc_module_impl.cpp + TEST_SOURCES main.cpp test_calc.cpp + MOCK_C_SOURCES mocks/mock_libcalc.cpp +) +``` + +### 3. Use in tests + +```cpp +LOGOS_TEST(add_returns_mocked_value) { + auto t = LogosTestContext("calc_module"); + t.mockCFunction("calc_add").returns(99); + + CalcModuleImpl impl; + t.init(&impl); + + auto result = impl.add(10, 20); + LOGOS_ASSERT_EQ(result, 99); + LOGOS_ASSERT(t.cFunctionCalled("calc_add")); +} +``` + +### 4. Configure Nix for C library mocking + +```nix +logos-module-builder.lib.mkLogosModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + tests = { + dir = ./tests; + mockCLibs = ["mylib"]; + }; +}; +``` + +## Event Testing + +```cpp +LOGOS_TEST(method_emits_event) { + auto t = LogosTestContext("my_module"); + t.captureEvents(); + + MyModuleImpl impl; + t.init(&impl); + + impl.doSomething("data"); + + LOGOS_ASSERT(t.eventEmitted("myEvent")); + LOGOS_ASSERT_EQ(t.eventCount("myEvent"), 1); + LOGOS_ASSERT_EQ(t.lastEventData("myEvent").at(0).toString(), "data"); +} +``` + +## Integration Tests with logoscore + +Test the module as a loaded plugin via the headless runtime: + +```bash +logoscore -m ./result/lib -l my_module \ + -c "my_module.doSomething(test_input)" + +logoscore -m ./result/lib -l my_module \ + -c "my_module.init(config)" \ + -c "my_module.process(data)" + +logoscore -m ./result/lib -l my_module,other_module \ + -c "my_module.callOther(hello)" +``` + +### logoscore Argument Types + +Arguments in `-c` calls are auto-detected: +- `true` / `false` -> bool +- `42` -> int +- `3.14` -> double +- Everything else -> string +- `@filename` -> file content as string argument + +### TEST_GROUPS + +For repos with many tests, group them: + +```bash +TEST_GROUPS=basic ws test my-module --auto-local +TEST_GROUPS=ipc ws test my-module --auto-local +``` + +## Running Tests via Nix / Workspace + +```bash +nix build .#unit-tests -L # Run unit tests +nix flake check -L # All checks in the flake + +ws test my-module # In the workspace +ws test my-module --auto-local # With local dep overrides +ws test --all --type cpp # All C++ repos +``` + +After adding `checks` outputs to a repo's `flake.nix`, run `ws sync-graph` so the workspace discovers them. + +## Key Rules + +- Unit tests use `LOGOS_TEST()` macro, not raw `assert()` or GoogleTest +- Instantiate the impl class directly in unit tests -- no logoscore, no Qt needed +- Use `LogosTestContext` for mocking module calls and C library functions +- `tests/CMakeLists.txt` must use `include(LogosTest)` + `logos_test()` +- Integration tests verify the full plugin lifecycle (load, call, response) +- Always use `--quit-on-finish` in CI for logoscore integration tests +- 30-second timeout per `-c` call; exit code 1 on failure diff --git a/.claude/skills/wrap-external-lib/SKILL.md b/.claude/skills/wrap-external-lib/SKILL.md new file mode 100644 index 0000000..55b47c2 --- /dev/null +++ b/.claude/skills/wrap-external-lib/SKILL.md @@ -0,0 +1,155 @@ +--- +name: wrap-external-lib +description: Activate when wrapping an external C or C++ library as a Logos universal module. Covers external_libraries in metadata.json, flake input configuration, extern C includes, and the Go library special case. +--- + +# Wrap an External C/C++ Library as a Module + +## When to Use + +Use this skill when: +- Creating a module that wraps an existing C or C++ library (e.g., libsodium, SQLite, a Go CGo library) +- The external library is not in nixpkgs and needs to be built from source +- Following the pattern established by `logos-accounts-module` (wrapping go-wallet-sdk) + +## Step 1: Set Up the Module + +Follow the `create-universal-module` skill for the basic structure, then add external library configuration. + +## Step 2: Configure External Library in metadata.json + +```json +{ + "name": "my_module", + "type": "core", + "interface": "universal", + "nix": { + "external_libraries": [ + { + "name": "mylib", + "build_command": "make static-library", + "output_pattern": "build/libmylib.*" + } + ], + "cmake": { + "find_packages": ["Threads"], + "extra_include_dirs": ["lib"] + } + } +} +``` + +For Go libraries with CGo, add `"go_build": true`: + +```json +{ + "name": "mylib", + "build_command": "make static-library", + "go_build": true, + "output_pattern": "build/libmylib.*" +} +``` + +## Step 3: Add External Library as Flake Input + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; + my-lib = { + url = "github:org/my-lib/"; + flake = false; + }; +}; + +outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + externalLibInputs = { + mylib = inputs.my-lib; + }; + preConfigure = '' + logos-cpp-generator --from-header src/my_module_impl.h \ + --backend qt --impl-class MyModuleImpl \ + --impl-header my_module_impl.h \ + --metadata metadata.json --output-dir ./generated_code + ''; + }; +``` + +The `externalLibInputs` key must match the `name` in `external_libraries`. The module builder copies built library binaries to `lib/`. + +## Step 4: Write the Impl Header + +```cpp +#pragma once +#include +#include +#include + +extern "C" { + #include "lib/mylib.h" +} + +class MyModuleImpl { +public: + MyModuleImpl(); + ~MyModuleImpl(); + + std::string encrypt(const std::string& plaintext, const std::string& key); + std::string decrypt(const std::string& ciphertext, const std::string& key); + std::string generateKey(); + +private: + void* m_handle; +}; +``` + +The `extern "C"` block includes the library's C header. The module builder places headers in `lib/` alongside the static library. + +## Step 5: Link the External Library in CMakeLists.txt + +```cmake +logos_module( + NAME my_module + SOURCES + src/my_module_impl.h + src/my_module_impl.cpp + generated_code/my_module_qt_glue.h + generated_code/my_module_dispatch.cpp + FIND_PACKAGES + Threads + LINK_LIBRARIES + Threads::Threads + INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/generated_code +) + +find_library(LIBMYLIB + NAMES libmylib.a libmylib.lib + PATHS ${CMAKE_CURRENT_SOURCE_DIR}/lib + NO_DEFAULT_PATH +) + +if(LIBMYLIB) + target_link_libraries(my_module_module_plugin PRIVATE ${LIBMYLIB}) +endif() +``` + +For Go static libraries, use whole-archive linking: + +```cmake +if(APPLE) + target_link_options(my_module_module_plugin PRIVATE -Wl,-force_load ${LIBMYLIB}) + target_link_libraries(my_module_module_plugin PUBLIC "-framework CoreFoundation" "-framework Security") +else() + target_link_options(my_module_module_plugin PRIVATE + -Wl,--whole-archive ${LIBMYLIB} -Wl,--no-whole-archive) +endif() +``` + +## Reference + +See `logos-accounts-module` for a complete working example of wrapping a Go CGo library. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f51f13..062d93a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -73,6 +73,23 @@ jobs: env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + logos-dev-boost: + name: Logos Dev Boost + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Verify logos-dev-boost files + run: | + test -f CLAUDE.md + test -f AGENTS.md + test -f .mcp.json + test -d .claude/skills + echo "All logos-dev-boost files present" + - name: Validate MCP config + run: | + node -e "const c = require('./.mcp.json'); if (!c.mcpServers['logos-dev-boost']) { process.exit(1); }" + echo "MCP config valid" + # Optional: end-to-end tests with nwaku Docker (manual trigger only) test-e2e: name: E2E Tests (nwaku) diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 0000000..f417805 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,10 @@ +{ + "mcpServers": { + "logos-dev-boost": { + "command": "node", + "args": [ + "/nix/store/d3m0q58j18xgj1ildsd1s6fyakc5npim-logos-dev-boost-0.1.0/lib/logos-dev-boost/dist/mcp-server/index.js" + ] + } + } +} diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..01e9672 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,1171 @@ +# Logos Development Context + +> This file is generated by logos-dev-boost. It provides AI coding agents with +> accurate context about the Logos modular application platform. +> Do not edit the section between BEGIN and END markers. + + + +## Logos Development Guidelines + +# Code Generator (logos-cpp-generator) + +## Overview + +`logos-cpp-generator` bridges pure C++ module implementations to the Logos runtime's Qt plugin system. Module authors write standard C++ and the generator produces all Qt boilerplate automatically. + +## The --from-header Pipeline + +This is the primary mode for universal modules: + +``` +C++ impl header (your code) + │ + ▼ +parseImplHeader() — extracts public methods, maps C++ types to LIDL types + │ + ▼ +ModuleDecl (internal AST) + │ + ├──► _qt_glue.h — Plugin class + ProviderObject with typed wrappers + │ + └──► _dispatch.cpp — callMethod() dispatch + getMethods() metadata +``` + +### Command + +```bash +logos-cpp-generator --from-header src/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code +``` + +| Flag | Description | +|------|-------------| +| `--from-header ` | Path to the pure C++ impl header | +| `--backend qt` | Generate Qt plugin glue (currently the only backend) | +| `--impl-class ` | Name of the C++ implementation class (PascalCase + Impl) | +| `--impl-header ` | Header filename (for include directives in generated code) | +| `--metadata ` | Path to metadata.json (provides name, version, description) | +| `--output-dir ` | Directory for generated files | + +### Generated Files + +**`_qt_glue.h`** — Contains two classes: +1. **ProviderObject** — Inherits `LogosProviderBase`. Holds `m_impl` (your impl class). Each public method gets a typed wrapper that converts Qt params to C++ std params, calls `m_impl.method(...)`, and converts the return value back. +2. **Plugin** — `QObject` subclass with `Q_PLUGIN_METADATA` and `Q_INTERFACES`. Factory method `createProviderObject()` returns a new ProviderObject. + +**`_dispatch.cpp`** — Implements two methods on the ProviderObject: +1. `callMethod(methodName, args)` — String-based dispatch table mapping method names to typed calls +2. `getMethods()` — Returns `QJsonArray` of method metadata (name, signature, returnType, parameters) + +### Type Mapping Table + +| C++ type | LIDL type | Qt mapping | +|----------|-----------|------------| +| `std::string` / `const std::string&` | `tstr` | `QString` | +| `bool` | `bool` | `bool` | +| `int64_t` | `int` | `int` | +| `uint64_t` | `uint` | `int` | +| `double` | `float64` | `double` | +| `void` | `void` | `void` | +| `std::vector` | `[tstr]` | `QStringList` | +| `std::vector` | `bstr` | `QByteArray` | +| `std::vector` | `[int]` | `QVariantList` | +| Anything else | `any` | `QVariant` | + +## LIDL (Alternative Input Format) + +LIDL is a lightweight Interface Definition Language. Instead of parsing a C++ header, you write a `.lidl` file: + +``` +module crypto_utils { + version "1.0.0" + description "Cryptographic utilities" + + method hash(input: tstr) -> tstr + method verify(input: tstr, hash: tstr) -> bool + method generateKey(bits: int) -> tstr + method listAlgorithms() -> [tstr] +} +``` + +Both paths (C++ header and LIDL) produce identical generated output. Use `--from-header` for most modules; use LIDL when you want to define the interface before writing the implementation. + +## Common Issues + +- **Unknown type warning**: If the generator encounters a C++ type not in the mapping table, it maps to `any` (`QVariant`). Prefer explicit types from the table. +- **Class not found**: `--impl-class` must exactly match the class name in the header (case-sensitive). +- **metadata.json mismatch**: The `name` in metadata.json must match the expected plugin binary name. +- **Generated files not found by CMake**: Ensure `generated_code/` files are listed in `CMakeLists.txt` SOURCES and the directory is in INCLUDE_DIRS. + +## In CMakeLists.txt + +```cmake +logos_module( + NAME my_module + SOURCES + src/my_module_impl.h + src/my_module_impl.cpp + generated_code/my_module_qt_glue.h + generated_code/my_module_dispatch.cpp + INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/generated_code +) +``` + +## In flake.nix + +The generator runs in `preConfigure`, before CMake: + +```nix +preConfigure = '' + logos-cpp-generator --from-header src/my_module_impl.h \ + --backend qt --impl-class MyModuleImpl \ + --impl-header my_module_impl.h \ + --metadata metadata.json --output-dir ./generated_code +''; +``` + +--- + +# Logos Core Conventions + +## Two Component Types + +Logos has two fundamentally different types of components. Always identify which you are building before writing code. + +**Logos Modules** (`"type": "core"`) — Process-isolated backend services. Pure C++ implementation using standard types. No Qt types in user code. All Qt glue is generated at build time. Loaded by `logoscore` or `liblogos_core`. Each runs in its own `logos_host` subprocess. + +**UI Apps** (`"type": "ui_qml"`) — QML-based UI apps displayed as tabs in Basecamp's MDI workspace. Two subtypes: pure QML (no C++, calls modules via `logos.callModule()`) or QML + C++ backend (process-isolated via Qt Remote Objects, QML gets a typed replica via `logos.module()`). + +**Rule:** Never mix these. A module is either core (headless, universal interface) or UI (visual, ui_qml). If something needs both backend logic and a UI, create a core module for the logic and a separate UI app that calls it, or use a QML + backend app. + +## Module Naming + +- Module names use `snake_case`: `crypto_utils`, `accounts_module`, `storage_module` +- Impl class is `PascalCase` + `Impl`: `CryptoUtilsImpl`, `AccountsModuleImpl` +- Impl header is `_impl.h`: `crypto_utils_impl.h` +- Plugin binary is `_plugin.so/.dylib`: `crypto_utils_plugin.so` +- The `name` in `metadata.json` must match the binary name prefix exactly + +## File Structure + +Universal module: +``` +my_module/ +├── src/ +│ ├── my_module_impl.h # Public API (pure C++ types) +│ └── my_module_impl.cpp # Implementation +├── metadata.json # "interface": "universal", "type": "core" +├── CMakeLists.txt # logos_module() macro +├── flake.nix # preConfigure runs logos-cpp-generator +└── tests/ +``` + +Pure QML app: +``` +my_app/ +├── Main.qml # QML entry point +├── metadata.json # "type": "ui_qml", "view": "Main.qml" +└── flake.nix # mkLogosQmlModule +``` + +QML + C++ backend app: +``` +my_app/ +├── src/ +│ ├── my_app.rep # Qt Remote Objects interface +│ ├── my_app_interface.h # extends PluginInterface +│ ├── my_app_plugin.h/cpp # SimpleSource + ViewPluginBase +│ └── qml/Main.qml # QML frontend (logos.module() replica) +├── metadata.json # "type": "ui_qml", "main": "my_app_plugin" +├── CMakeLists.txt # logos_module() with REP_FILE +└── flake.nix # mkLogosQmlModule +``` + +## metadata.json Is the Source of Truth + +Every module and UI app must have a `metadata.json`. It declares identity, type, interface, dependencies, and build configuration. The `name` field must match the binary name prefix. The `dependencies` array must list exact `name` values from dependent modules' own `metadata.json` files. + +## Inter-Module Communication + +All cross-module calls go through `LogosAPI`: +```cpp +LogosResult result = api->callModule("module_name", "method_name", {arg1, arg2}); +if (result.success()) { + QVariant data = result.data(); +} +``` + +Always handle the case where a target module is not loaded. Always declare dependencies in `metadata.json`. + +--- + +# metadata.json Schema + +## Full Schema + +```json +{ + "name": "my_module", + "version": "1.0.0", + "description": "What this module does", + "author": "Author Name", + "type": "core", + "interface": "universal", + "category": "general", + "main": "my_module_plugin", + "dependencies": [], + "include": [], + "capabilities": [], + + "nix": { + "packages": { + "build": [], + "runtime": [] + }, + "external_libraries": [], + "cmake": { + "find_packages": [], + "extra_sources": [], + "extra_include_dirs": [], + "extra_link_libraries": [] + } + } +} +``` + +## Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Module identifier. Must match binary prefix: `my_module` -> `my_module_plugin.so` | +| `version` | string | Semantic version (`"1.0.0"`) | +| `type` | string | `"core"` for modules, `"ui_qml"` for UI apps | +| `main` | string | Plugin binary name without extension: `"my_module_plugin"` (optional for pure QML UI apps) | + +## Universal Module Fields + +| Field | Value | Description | +|-------|-------|-------------| +| `interface` | `"universal"` | Signals that this module uses pure C++ impl + code generation | + +When `"interface": "universal"` is set, the build system expects `logos-cpp-generator --from-header` to run in `preConfigure` and produce Qt glue files. + +## Dependencies + +```json +"dependencies": ["storage_module", "crypto_module"] +``` + +Values must match the `name` field in the dependency module's own `metadata.json`. The runtime loads dependencies before the module. + +Flake input attribute names should also match the dependency module names when possible. Example: if you depend on `storage_module` from repo `logos-storage-module`, the flake input should be named `logos-storage-module`. + +## External Libraries + +```json +"nix": { + "external_libraries": [ + { + "name": "mylib", + "build_command": "make static-library", + "output_pattern": "build/libmylib.*" + } + ] +} +``` + +For Go libraries, add `"go_build": true`. The external library source is provided as a non-flake input in `flake.nix` and mapped via `externalLibInputs`. + +## Nix Packages + +```json +"nix": { + "packages": { + "build": ["pkg-config"], + "runtime": ["nlohmann_json", "openssl"] + } +} +``` + +`build` packages are available during compilation only. `runtime` packages are linked and available at runtime. + +## CMake Configuration + +```json +"nix": { + "cmake": { + "find_packages": ["Threads", "OpenSSL"], + "extra_sources": ["src/helper.cpp"], + "extra_include_dirs": ["include"], + "extra_link_libraries": ["Threads::Threads"] + } +} +``` + +These values are passed to CMake by the `logos_module()` macro. They supplement, not replace, the automatic SDK and Qt dependencies. + +## UI App Specific Fields + +```json +{ + "type": "ui_qml", + "view": "Main.qml", + "icon": "icon.png", + "category": "tools" +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `view` | string | **Required** for `ui_qml`. Path to QML entry point (e.g., `"Main.qml"` or `"qml/Main.qml"`) | +| `main` | string | **Optional**. If present, indicates a C++ backend plugin (e.g., `"my_app_plugin"`). If absent, the app is pure QML | + +Pure QML apps have no `"main"` field — no C++ compilation occurs. QML + backend apps have both `"main"` (C++ plugin) and `"view"` (QML entry point). + +UI apps do not use `"interface": "universal"` — they use `mkLogosQmlModule` in their flake.nix. + +--- + +# Nix Build Patterns + +## All Builds Go Through Nix + +Never run raw `cmake` without `nix develop` or `ws develop`. The Nix build system provides Qt, the SDK, the code generator, and all dependencies. Running `cmake --build` outside Nix will fail. + +## Flake Structure for Universal Modules + +```nix +{ + description = "My Logos 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/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code + ''; + }; +} +``` + +The `preConfigure` hook runs the code generator before CMake. This is required for universal modules. + +## Flake Structure for Modules with External Libraries + +Add the external library as a non-flake input and pass it via `externalLibInputs`: + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; + my-lib = { + url = "github:org/my-lib/commit-hash"; + flake = false; + }; +}; + +outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + externalLibInputs = { + mylib = inputs.my-lib; + }; + preConfigure = '' + logos-cpp-generator --from-header src/_impl.h \ + --backend qt --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json --output-dir ./generated_code + ''; + }; +``` + +## Build Commands + +```bash +nix build # Build the module +nix build .#lib # Build just the shared library +nix flake check -L # Run tests +nix develop # Enter dev shell with build tools +``` + +Inside the workspace (multi-repo): +```bash +ws build my-module # Build +ws build my-module --auto-local # Build with local dirty dep overrides +ws test my-module # Run tests +ws test my-module --auto-local # Test with local overrides +ws develop my-module # Enter dev shell +``` + +## Key Rules + +- Flake inputs must be tracked by git. Run `git add ` before Nix can see new files. +- `nixpkgs` follows `logos-cpp-sdk` via `logos-module-builder`. Never pin a separate nixpkgs. +- Qt version is fixed by `logos-cpp-sdk`. All repos must use the same Qt to avoid version conflicts. +- Use `-L` flag to stream build logs: `nix build -L` +- Use `--override-input` to test with local dependency changes (the `ws` CLI does this for you with `--auto-local`). + +## Flake Structure for UI QML Apps + +Pure QML (no C++ backend): + +```nix +{ + inputs.logos-module-builder.url = "github:logos-co/logos-module-builder"; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +QML + C++ backend: + +```nix +{ + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + # Add module dependencies as inputs: + # some_module.url = "github:logos-co/logos-some-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +Both use `mkLogosQmlModule` (not `mkLogosModule`). No `preConfigure` needed — the `.rep` file (if present) is handled automatically via `REP_FILE` in CMakeLists.txt. + +## Dev Shell for CMake Iteration + +```bash +nix develop +cmake -B build -GNinja && cmake --build build +``` + +The dev shell provides all build dependencies. Use this for rapid C++ iteration without full Nix rebuilds. + +--- + +# Testing Logos Modules + +## Unit Tests with logos-test-framework + +Universal modules have a plain C++ impl class. Test it using the logos-test-framework, which is provided automatically by `logos-module-builder`. + +### Test File Structure + +``` +tests/ +├── main.cpp # LOGOS_TEST_MAIN() entry point +├── test_my_module.cpp # Test cases using LOGOS_TEST() +└── CMakeLists.txt # logos_test() macro +``` + +### Writing Tests + +```cpp +// tests/main.cpp +#include +LOGOS_TEST_MAIN() +``` + +```cpp +// tests/test_my_module.cpp +#include +#include "../src/my_module_impl.h" + +LOGOS_TEST(hash_returns_nonempty_string) { + MyModuleImpl impl; + LOGOS_ASSERT_FALSE(impl.hash("hello").empty()); +} + +LOGOS_TEST(verify_matches_hash) { + MyModuleImpl impl; + auto hash = impl.hash("hello"); + LOGOS_ASSERT_TRUE(impl.verify("hello", hash)); +} +``` + +### CMakeLists.txt for Tests + +```cmake +# tests/CMakeLists.txt +cmake_minimum_required(VERSION 3.14) +project(MyModuleTests LANGUAGES CXX) + +include(LogosTest) + +logos_test( + NAME my_module_tests + MODULE_SOURCES ../src/my_module_impl.cpp + TEST_SOURCES + main.cpp + test_my_module.cpp +) +``` + +The `logos_test()` CMake macro handles all framework wiring: Qt dependencies, SDK mock headers, include paths, and CTest registration. + +### Mocking Other Modules + +Use `LogosTestContext` when your module calls other modules: + +```cpp +LOGOS_TEST(calls_waku_publish) { + auto t = LogosTestContext("chat_module"); + t.mockModule("waku_module", "relayPublish").returns(true); + + ChatImpl impl; + t.init(&impl); + + impl.sendMessage("hello"); + LOGOS_ASSERT(t.moduleCalled("waku_module", "relayPublish")); +} +``` + +### Mocking C Libraries + +For modules wrapping external C/C++ libraries, write mock stubs: + +```cpp +// tests/mocks/mock_libcalc.cpp +#include +extern "C" { #include "libcalc.h" } + +extern "C" int calc_add(int a, int b) { + LOGOS_CMOCK_RECORD("calc_add"); + return LOGOS_CMOCK_RETURN(int, "calc_add"); +} +``` + +Reference them in CMake: + +```cmake +logos_test( + NAME calc_module_tests + MODULE_SOURCES ../src/calc_module_impl.cpp + TEST_SOURCES main.cpp test_calc.cpp + MOCK_C_SOURCES mocks/mock_libcalc.cpp +) +``` + +### Running Unit Tests + +```bash +nix build .#unit-tests -L # Build and run unit tests +nix flake check -L # All Nix checks including tests +``` + +`logos-module-builder` auto-detects `tests/CMakeLists.txt` and adds `checks..unit-tests` and `packages..unit-tests` automatically. + +## Integration Tests with logoscore + +Test the module as a loaded plugin via the headless runtime: + +```bash +logoscore -m ./result/lib -l my_module \ + -c "my_module.doSomething(test_input)" + +logoscore -m ./result/lib -l my_module \ + -c "my_module.init(config)" \ + -c "my_module.process(data)" + +logoscore -m ./result/lib -l my_module,other_module \ + -c "my_module.callOther(hello)" +``` + +logoscore arguments: +- `-m ` -- Directory to scan for module plugins (repeatable) +- `-l ` -- Comma-separated modules to load +- `-c ".(args)"` -- Call a method (repeatable, sequential) +- `--quit-on-finish` -- Exit after calls complete (for CI) + +Type auto-detection in `-c` args: `true`/`false` -> bool, `42` -> int, `3.14` -> double, else -> string. Use `@filename` to load file content as an argument. + +### TEST_GROUPS + +The test runner supports groups for selective testing: + +```bash +TEST_GROUPS=basic ws test logos-test-modules --auto-local +TEST_GROUPS=ipc ws test logos-test-modules --auto-local +TEST_GROUPS=basic,ipc,errors ws test logos-test-modules --auto-local +``` + +### Running Tests via Nix + +```bash +nix build .#unit-tests -L # Run unit tests +nix flake check -L # Run all checks defined in the flake + +ws test my-module # In the workspace +ws test my-module --auto-local # With local dep overrides +ws test --all --type cpp # All C++ repos +``` + +## 3. UI Integration Tests (QML Inspector) + +UI apps (`type: "ui_qml"`) can be tested via the QML Inspector MCP server built into `logos-standalone-app`. Tests interact with the live UI — clicking buttons, reading text, taking screenshots. + +### Test file pattern + +```javascript +// tests/smoke.mjs +const { resolve } = await import("node:path"); +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: basic interaction", async (app) => { + await app.expectTexts(["My App"]); + await app.click("Add"); + await app.expectTexts(["Result:"]); +}); + +run(); +``` + +### Test API + +| Method | Description | +|--------|-------------| +| `app.click(text, opts?)` | Find element by text and click it | +| `app.expectTexts(texts)` | Assert all texts are visible | +| `app.waitFor(fn, opts)` | Poll until fn succeeds (timeout, interval, description) | +| `app.screenshot()` | Capture current state | +| `app.findByType(type)` | Find elements by QML type | +| `app.findByProperty(prop, value)` | Find elements by property | +| `app.getTree()` | Get full QML element tree | + +### Running UI tests + +```bash +# Interactive (app already running on localhost:3768) +node tests/smoke.mjs + +# CI mode (launches app headless, tests, exits) +node tests/smoke.mjs --ci ./result/bin/logos-standalone-app --verbose + +# Hermetic via Nix (offscreen, no display needed) +nix build .#integration-test +``` + +Modules with `.mjs` test files in `tests/` automatically get `nix build .#integration-test` via `mkPluginTest`. + +### MCP tools for AI agents + +When the app is running, the `.mcp.json` auto-registers these tools with Claude Code / Cursor: + +`qml_screenshot`, `qml_find_and_click`, `qml_find_by_type`, `qml_find_by_property`, `qml_list_interactive`, `qml_get_tree` + +This lets AI agents visually verify UI changes, click through workflows, and debug layout issues in real time. + +## Key Testing Rules + +- Unit tests use `LOGOS_TEST()` and `LOGOS_ASSERT_*` macros from `` +- Unit tests should NOT require logoscore -- instantiate the impl class directly +- `tests/CMakeLists.txt` must use `include(LogosTest)` + `logos_test()` +- Use `LogosTestContext` for mocking module calls and C library functions +- Integration tests verify the full plugin lifecycle (load, call, response) +- Always test with `--quit-on-finish` in CI to ensure the process exits +- 30-second timeout per `-c` call; exit code 1 on failure +- After adding `checks` to a repo's `flake.nix`, run `ws sync-graph` so the workspace discovers them + +--- + +# UI App Development + +UI apps use `"type": "ui_qml"` in metadata.json. There are two subtypes: + +1. **Pure QML** — no C++ compilation, QML files only, calls backend modules via `logos.callModule()` +2. **QML + C++ Backend** — process-isolated C++ backend (Qt Remote Objects), QML frontend runs in-process + +## Pure QML Apps + +Simplest UI app type. No compilation, no C++ code. The host loads QML directly. + +### metadata.json + +```json +{ + "name": "my_app", + "type": "ui_qml", + "version": "1.0.0", + "description": "My QML UI application", + "view": "Main.qml", + "icon": null, + "category": "tools", + "dependencies": ["some_backend_module"] +} +``` + +Key fields: `"type": "ui_qml"` and `"view"` pointing to the QML entry point. No `"main"` field. + +### QML Pattern + +```qml +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Item { + id: root + + function callBackend(method, args) { + if (typeof logos === "undefined" || !logos.callModule) { + console.log("Logos bridge not available") + return + } + return logos.callModule("some_backend_module", method, args) + } + + Button { + text: "Do Something" + onClicked: { + var result = callBackend("myMethod", ["arg1", "arg2"]) + console.log("Result:", result) + } + } +} +``` + +The `logos` bridge is injected by the host (Basecamp or standalone runner). Use `logos.callModule(moduleName, method, args)` to call backend modules. + +### flake.nix + +Add backend module dependencies (from `metadata.json` `"dependencies"`) as flake inputs. The input attribute name must match the dependency name. + +```nix +{ + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + + # Each metadata.json dependency needs a matching flake input. + # Use path: for local development, github: for CI/published modules: + some_backend_module.url = "path:../logos-some-backend-module"; + # some_backend_module.url = "github:logos-co/logos-some-backend-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +Uses `mkLogosQmlModule` (not `mkLogosModule`). No `preConfigure` needed. + +### Resolving Module Dependencies + +Each backend module must be built with its shared library (`.so`/`.dylib`) present in `lib/`. Three ways to point at a dependency: + +| Approach | `flake.nix` input URL | Build command | +|----------|----------------------|---------------| +| Local path in flake.nix | `path:../logos-my-module` | `nix run .` | +| Remote URL + local override | `github:org/repo` | `nix run . --override-input dep_name path:../logos-my-module` | +| Fully remote | `github:org/repo` | `nix run .` | + +`--override-input` overrides a flake input at build time without editing `flake.nix` — useful for quick iteration. + +## QML + C++ Backend Apps + +For apps that need business logic, state management, or access to system APIs. The C++ backend runs in a **separate isolated process** (`logos_host`), communicating with the QML frontend via Qt Remote Objects IPC. + +### metadata.json + +```json +{ + "name": "my_app", + "type": "ui_qml", + "version": "1.0.0", + "description": "My UI app with C++ backend", + "main": "my_app_plugin", + "view": "qml/Main.qml", + "icon": null, + "category": "tools", + "dependencies": [] +} +``` + +Key difference from pure QML: has `"main"` field pointing to the C++ plugin binary, and `"view"` points to `qml/Main.qml` (inside `src/`). + +### .rep File (Qt Remote Objects Interface) + +``` +class MyApp +{ + PROP(QString status READWRITE) + SLOT(int doSomething(int a, int b)) +} +``` + +Defines the IPC interface. Properties auto-sync to QML replicas. Slots are callable from QML. + +### C++ Plugin Class + +The plugin inherits three bases: +- `MyAppSimpleSource` — generated from .rep, provides property storage + slot declarations +- `MyAppInterface` — extends `PluginInterface`, used for Qt plugin loading +- `MyAppViewPluginBase` — provides `setBackend()` to wire up Qt Remote Objects + +```cpp +class MyAppPlugin : public MyAppSimpleSource, + public MyAppInterface, + public MyAppViewPluginBase +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID MyAppInterface_iid FILE "metadata.json") + Q_INTERFACES(MyAppInterface) + +public: + explicit MyAppPlugin(QObject* parent = nullptr); + + QString name() const override { return "my_app"; } + QString version() const override { return "1.0.0"; } + + Q_INVOKABLE void initLogos(LogosAPI* api); + + // Implement slots from .rep + int doSomething(int a, int b) override; + +private: + LogosAPI* m_logosAPI = nullptr; +}; +``` + +In `initLogos()`, call `setBackend(this)` to wire up the Qt Remote Objects source: + +```cpp +void MyAppPlugin::initLogos(LogosAPI* api) { + m_logosAPI = api; + setBackend(this); +} +``` + +### CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.14) +project(MyAppPlugin 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() + +logos_module( + NAME my_app + REP_FILE src/my_app.rep + SOURCES + src/my_app_interface.h + src/my_app_plugin.h + src/my_app_plugin.cpp +) +``` + +Key: `REP_FILE` tells the build system to generate Qt Remote Objects source/replica headers from the `.rep` file. + +### QML Frontend + +```qml +import QtQuick +import QtQuick.Controls + +Item { + id: root + + // Typed replica — auto-synced properties and callable slots + readonly property var backend: logos.module("my_app") + readonly property bool ready: backend !== null && logos.isViewModuleReady("my_app") + + // Auto-synced property from .rep + readonly property string status: backend ? backend.status : "" + + Button { + text: "Do Something" + enabled: root.ready + onClicked: { + logos.watch(backend.doSomething(1, 2), + function(value) { console.log("Result:", value) }, + function(error) { console.log("Error:", error) } + ) + } + } + + Text { + text: "Status: " + root.status + } +} +``` + +Key QML APIs: +- `logos.module("name")` — returns a typed Qt Remote Objects replica +- `logos.isViewModuleReady("name")` — checks if the backend process is connected +- `logos.watch(pendingReply, onSuccess, onError)` — handles async slot calls + +## C++/QML Boundary Rules + +| Concern | C++ (backend plugin) | QML | +| -------------------- | ---------------------------------------- | -------------------------------------- | +| Data models, state | `PROP()` in `.rep` file | Bind to `backend.property` | +| Business logic | Implement as `SLOT()` in `.rep` | Never — no JS business logic | +| Module calls | Via `LogosAPI*` in `initLogos()` | Via `logos.callModule()` (pure QML) | +| File I/O, networking | Always C++ | Never | +| UI layout, styling | Never | QML; use `Logos.Theme` inside Basecamp | +| User interactions | `SLOT()` methods | `onClicked: logos.watch(backend.doX())`| +| Plugin lifecycle | `initLogos()` + `setBackend(this)` | N/A | + +## Build and Test + +```bash +git init && git add -A # nix needs files tracked +nix build # compiles (backend) or packages (pure QML) +nix run . # standalone app + +# Override a module dependency at build time (no flake.nix edits needed): +nix run . --override-input some_module path:../logos-some-module +``` + +## Calling Logos Modules + +From C++ backend (in `initLogos` or slot implementations): + +```cpp +auto* client = m_logosAPI->getClient("storage_module"); +QVariant result = client->invokeRemoteMethod("storage_module", "save", key, value); +``` + +From pure QML: + +```qml +var result = logos.callModule("storage_module", "save", [key, value]) +``` + +Always declare module dependencies in `metadata.json` `"dependencies"` so they are loaded before the UI app. + +## UI Integration Testing (QML Inspector + MCP) + +`logos-standalone-app` includes a QML Inspector MCP server that lets AI agents and test scripts interact with the running UI — take screenshots, click elements, inspect the QML tree. + +### Available MCP tools + +| Tool | Description | +|------|-------------| +| `qml_screenshot` | Capture a screenshot of the current app state | +| `qml_find_and_click` | Find a UI element by text and click it | +| `qml_find_by_type` | Locate elements by QML type name | +| `qml_find_by_property` | Locate elements by property value | +| `qml_list_interactive` | List all clickable/interactive elements | +| `qml_get_tree` | Get the full QML element tree | + +### Interactive testing (AI agent workflow) + +```bash +nix build && nix run . # launches app with inspector on localhost:3768 +``` + +The `.mcp.json` in the project directory auto-registers the MCP server with Claude Code and other MCP clients. The AI agent can then screenshot, click buttons, and verify UI state in real time. + +### Writing integration test files + +```javascript +// tests/smoke.mjs +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: click add and verify result", async (app) => { + await app.click("Add"); + await app.expectTexts(["Result:"]); +}); + +run(); +``` + +### Running tests + +```bash +# Interactive (app already running) +node tests/smoke.mjs + +# CI mode (launches app headless, tests, exits) +node tests/smoke.mjs --ci ./result/bin/logos-standalone-app --verbose + +# Hermetic via Nix (headless, offscreen) +nix build .#integration-test +``` + +### Nix `mkPluginTest` builder + +UI modules with `.mjs` test files in `tests/` automatically get `nix build .#integration-test`. For manual setup: + +```nix +integration-test = logos-standalone-app.lib.${system}.mkPluginTest { + inherit pkgs; + pluginPkg = myModulePackage; + testFiles = [ ./tests/smoke.mjs ]; + name = "my-module-integration-test"; +}; +``` + +The builder runs headless (`QT_QPA_PLATFORM=offscreen`), connects to the QML inspector, and executes each test file sequentially. + +--- + +# Universal Module Development + +## The Universal Interface Pattern + +Universal modules use **pure C++** for their implementation. You write a single implementation class using standard C++ types. The build system generates all Qt/plugin infrastructure automatically via `logos-cpp-generator --from-header`. + +**You write:** A C++ class with `std::string`, `int64_t`, `bool`, `std::vector`. +**The generator produces:** Qt plugin class, method dispatch, introspection metadata. + +## Rules + +- **NO Qt types** in your impl header or implementation: no `QString`, `QObject`, `Q_INVOKABLE`, `QVariant` +- **NO Qt includes** in your impl header (Qt headers in `.cpp` are OK if needed for internal use, but the public API must be pure C++) +- Set `"interface": "universal"` in `metadata.json` +- Name the impl class `Impl` (e.g., `CryptoUtilsImpl`) +- Name the impl header `_impl.h` (e.g., `crypto_utils_impl.h`) +- Only `public` methods become module API methods. Private/protected are ignored by the generator. +- Constructors, destructors, typedefs, and using declarations are skipped by the generator. + +## Type Mapping + +| Use this in your C++ | Generator maps to | Qt type produced | +|----------------------|-------------------|-----------------| +| `std::string` / `const std::string&` | `tstr` | `QString` | +| `bool` | `bool` | `bool` | +| `int64_t` | `int` | `int` | +| `uint64_t` | `uint` | `int` | +| `double` | `float64` | `double` | +| `void` | `void` | `void` | +| `std::vector` | `[tstr]` | `QStringList` | +| `std::vector` | `bstr` | `QByteArray` | +| `std::vector` | `[int]` | `QVariantList` | +| `std::vector` | `[float64]` | `QVariantList` | +| `std::vector` | `[bool]` | `QVariantList` | + +If you use a type not in this table, the generator maps it to `any` (`QVariant`). Prefer explicit types from the table for type safety. + +## Impl Header Template + +```cpp +#pragma once +#include +#include +#include + +class MyModuleImpl { +public: + MyModuleImpl(); + ~MyModuleImpl(); + + std::string doSomething(const std::string& input); + bool validate(const std::string& data); + int64_t count(); + std::vector listItems(); + +private: + // Private members are not exposed as module API +}; +``` + +## Build Pipeline + +The `flake.nix` `preConfigure` hook runs the generator before CMake: + +```bash +logos-cpp-generator --from-header src/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code +``` + +This produces `generated_code/_qt_glue.h` and `generated_code/_dispatch.cpp`. These files are listed in `CMakeLists.txt` under the `SOURCES` of `logos_module()`. + +## Testing + +Unit tests instantiate the impl class directly — it is a plain C++ class: + +```cpp +#include "my_module_impl.h" +// No Qt test framework needed for basic tests +MyModuleImpl impl; +assert(impl.doSomething("test") == "expected"); +``` + +Integration tests use `logoscore`: +```bash +logoscore -m ./result/lib -l my_module -c "my_module.doSomething(test)" +``` + +## Quick Reference + +### Build Commands + +```bash +nix build # Build the module/app +nix build -L # Build with streaming logs +nix flake check -L # Run tests +nix develop # Enter dev shell +``` + +### Testing + +```bash +# Integration test with logoscore +logoscore -m ./result/lib -l -c ".methodName(args)" + +# Inspect module metadata and methods +lm ./result/lib/_plugin.so +lm methods ./result/lib/_plugin.so --json +``` + +### Packaging + +```bash +lgx create +lgx add .lgx -v linux-x86_64 -f ./result/lib/_plugin.so +lgx verify .lgx +``` + +### For detailed documentation, see the logos-dev-boost docs/ directory +### or activate a skill: create-universal-module, wrap-external-lib, +### create-ui-app, package-lgx, inter-module-comm, testing-modules, +### nix-flake-setup, add-to-workspace + + \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..b75c0c0 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,1189 @@ +# Logos Development Context (Claude Code) + + + +## Logos Development Guidelines + +# Code Generator (logos-cpp-generator) + +## Overview + +`logos-cpp-generator` bridges pure C++ module implementations to the Logos runtime's Qt plugin system. Module authors write standard C++ and the generator produces all Qt boilerplate automatically. + +## The --from-header Pipeline + +This is the primary mode for universal modules: + +``` +C++ impl header (your code) + │ + ▼ +parseImplHeader() — extracts public methods, maps C++ types to LIDL types + │ + ▼ +ModuleDecl (internal AST) + │ + ├──► _qt_glue.h — Plugin class + ProviderObject with typed wrappers + │ + └──► _dispatch.cpp — callMethod() dispatch + getMethods() metadata +``` + +### Command + +```bash +logos-cpp-generator --from-header src/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code +``` + +| Flag | Description | +|------|-------------| +| `--from-header ` | Path to the pure C++ impl header | +| `--backend qt` | Generate Qt plugin glue (currently the only backend) | +| `--impl-class ` | Name of the C++ implementation class (PascalCase + Impl) | +| `--impl-header ` | Header filename (for include directives in generated code) | +| `--metadata ` | Path to metadata.json (provides name, version, description) | +| `--output-dir ` | Directory for generated files | + +### Generated Files + +**`_qt_glue.h`** — Contains two classes: +1. **ProviderObject** — Inherits `LogosProviderBase`. Holds `m_impl` (your impl class). Each public method gets a typed wrapper that converts Qt params to C++ std params, calls `m_impl.method(...)`, and converts the return value back. +2. **Plugin** — `QObject` subclass with `Q_PLUGIN_METADATA` and `Q_INTERFACES`. Factory method `createProviderObject()` returns a new ProviderObject. + +**`_dispatch.cpp`** — Implements two methods on the ProviderObject: +1. `callMethod(methodName, args)` — String-based dispatch table mapping method names to typed calls +2. `getMethods()` — Returns `QJsonArray` of method metadata (name, signature, returnType, parameters) + +### Type Mapping Table + +| C++ type | LIDL type | Qt mapping | +|----------|-----------|------------| +| `std::string` / `const std::string&` | `tstr` | `QString` | +| `bool` | `bool` | `bool` | +| `int64_t` | `int` | `int` | +| `uint64_t` | `uint` | `int` | +| `double` | `float64` | `double` | +| `void` | `void` | `void` | +| `std::vector` | `[tstr]` | `QStringList` | +| `std::vector` | `bstr` | `QByteArray` | +| `std::vector` | `[int]` | `QVariantList` | +| Anything else | `any` | `QVariant` | + +## LIDL (Alternative Input Format) + +LIDL is a lightweight Interface Definition Language. Instead of parsing a C++ header, you write a `.lidl` file: + +``` +module crypto_utils { + version "1.0.0" + description "Cryptographic utilities" + + method hash(input: tstr) -> tstr + method verify(input: tstr, hash: tstr) -> bool + method generateKey(bits: int) -> tstr + method listAlgorithms() -> [tstr] +} +``` + +Both paths (C++ header and LIDL) produce identical generated output. Use `--from-header` for most modules; use LIDL when you want to define the interface before writing the implementation. + +## Common Issues + +- **Unknown type warning**: If the generator encounters a C++ type not in the mapping table, it maps to `any` (`QVariant`). Prefer explicit types from the table. +- **Class not found**: `--impl-class` must exactly match the class name in the header (case-sensitive). +- **metadata.json mismatch**: The `name` in metadata.json must match the expected plugin binary name. +- **Generated files not found by CMake**: Ensure `generated_code/` files are listed in `CMakeLists.txt` SOURCES and the directory is in INCLUDE_DIRS. + +## In CMakeLists.txt + +```cmake +logos_module( + NAME my_module + SOURCES + src/my_module_impl.h + src/my_module_impl.cpp + generated_code/my_module_qt_glue.h + generated_code/my_module_dispatch.cpp + INCLUDE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/generated_code +) +``` + +## In flake.nix + +The generator runs in `preConfigure`, before CMake: + +```nix +preConfigure = '' + logos-cpp-generator --from-header src/my_module_impl.h \ + --backend qt --impl-class MyModuleImpl \ + --impl-header my_module_impl.h \ + --metadata metadata.json --output-dir ./generated_code +''; +``` + +--- + +# Logos Core Conventions + +## Two Component Types + +Logos has two fundamentally different types of components. Always identify which you are building before writing code. + +**Logos Modules** (`"type": "core"`) — Process-isolated backend services. Pure C++ implementation using standard types. No Qt types in user code. All Qt glue is generated at build time. Loaded by `logoscore` or `liblogos_core`. Each runs in its own `logos_host` subprocess. + +**UI Apps** (`"type": "ui_qml"`) — QML-based UI apps displayed as tabs in Basecamp's MDI workspace. Two subtypes: pure QML (no C++, calls modules via `logos.callModule()`) or QML + C++ backend (process-isolated via Qt Remote Objects, QML gets a typed replica via `logos.module()`). + +**Rule:** Never mix these. A module is either core (headless, universal interface) or UI (visual, ui_qml). If something needs both backend logic and a UI, create a core module for the logic and a separate UI app that calls it, or use a QML + backend app. + +## Module Naming + +- Module names use `snake_case`: `crypto_utils`, `accounts_module`, `storage_module` +- Impl class is `PascalCase` + `Impl`: `CryptoUtilsImpl`, `AccountsModuleImpl` +- Impl header is `_impl.h`: `crypto_utils_impl.h` +- Plugin binary is `_plugin.so/.dylib`: `crypto_utils_plugin.so` +- The `name` in `metadata.json` must match the binary name prefix exactly + +## File Structure + +Universal module: +``` +my_module/ +├── src/ +│ ├── my_module_impl.h # Public API (pure C++ types) +│ └── my_module_impl.cpp # Implementation +├── metadata.json # "interface": "universal", "type": "core" +├── CMakeLists.txt # logos_module() macro +├── flake.nix # preConfigure runs logos-cpp-generator +└── tests/ +``` + +Pure QML app: +``` +my_app/ +├── Main.qml # QML entry point +├── metadata.json # "type": "ui_qml", "view": "Main.qml" +└── flake.nix # mkLogosQmlModule +``` + +QML + C++ backend app: +``` +my_app/ +├── src/ +│ ├── my_app.rep # Qt Remote Objects interface +│ ├── my_app_interface.h # extends PluginInterface +│ ├── my_app_plugin.h/cpp # SimpleSource + ViewPluginBase +│ └── qml/Main.qml # QML frontend (logos.module() replica) +├── metadata.json # "type": "ui_qml", "main": "my_app_plugin" +├── CMakeLists.txt # logos_module() with REP_FILE +└── flake.nix # mkLogosQmlModule +``` + +## metadata.json Is the Source of Truth + +Every module and UI app must have a `metadata.json`. It declares identity, type, interface, dependencies, and build configuration. The `name` field must match the binary name prefix. The `dependencies` array must list exact `name` values from dependent modules' own `metadata.json` files. + +## Inter-Module Communication + +All cross-module calls go through `LogosAPI`: +```cpp +LogosResult result = api->callModule("module_name", "method_name", {arg1, arg2}); +if (result.success()) { + QVariant data = result.data(); +} +``` + +Always handle the case where a target module is not loaded. Always declare dependencies in `metadata.json`. + +--- + +# metadata.json Schema + +## Full Schema + +```json +{ + "name": "my_module", + "version": "1.0.0", + "description": "What this module does", + "author": "Author Name", + "type": "core", + "interface": "universal", + "category": "general", + "main": "my_module_plugin", + "dependencies": [], + "include": [], + "capabilities": [], + + "nix": { + "packages": { + "build": [], + "runtime": [] + }, + "external_libraries": [], + "cmake": { + "find_packages": [], + "extra_sources": [], + "extra_include_dirs": [], + "extra_link_libraries": [] + } + } +} +``` + +## Required Fields + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Module identifier. Must match binary prefix: `my_module` -> `my_module_plugin.so` | +| `version` | string | Semantic version (`"1.0.0"`) | +| `type` | string | `"core"` for modules, `"ui_qml"` for UI apps | +| `main` | string | Plugin binary name without extension: `"my_module_plugin"` (optional for pure QML UI apps) | + +## Universal Module Fields + +| Field | Value | Description | +|-------|-------|-------------| +| `interface` | `"universal"` | Signals that this module uses pure C++ impl + code generation | + +When `"interface": "universal"` is set, the build system expects `logos-cpp-generator --from-header` to run in `preConfigure` and produce Qt glue files. + +## Dependencies + +```json +"dependencies": ["storage_module", "crypto_module"] +``` + +Values must match the `name` field in the dependency module's own `metadata.json`. The runtime loads dependencies before the module. + +Flake input attribute names should also match the dependency module names when possible. Example: if you depend on `storage_module` from repo `logos-storage-module`, the flake input should be named `logos-storage-module`. + +## External Libraries + +```json +"nix": { + "external_libraries": [ + { + "name": "mylib", + "build_command": "make static-library", + "output_pattern": "build/libmylib.*" + } + ] +} +``` + +For Go libraries, add `"go_build": true`. The external library source is provided as a non-flake input in `flake.nix` and mapped via `externalLibInputs`. + +## Nix Packages + +```json +"nix": { + "packages": { + "build": ["pkg-config"], + "runtime": ["nlohmann_json", "openssl"] + } +} +``` + +`build` packages are available during compilation only. `runtime` packages are linked and available at runtime. + +## CMake Configuration + +```json +"nix": { + "cmake": { + "find_packages": ["Threads", "OpenSSL"], + "extra_sources": ["src/helper.cpp"], + "extra_include_dirs": ["include"], + "extra_link_libraries": ["Threads::Threads"] + } +} +``` + +These values are passed to CMake by the `logos_module()` macro. They supplement, not replace, the automatic SDK and Qt dependencies. + +## UI App Specific Fields + +```json +{ + "type": "ui_qml", + "view": "Main.qml", + "icon": "icon.png", + "category": "tools" +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `view` | string | **Required** for `ui_qml`. Path to QML entry point (e.g., `"Main.qml"` or `"qml/Main.qml"`) | +| `main` | string | **Optional**. If present, indicates a C++ backend plugin (e.g., `"my_app_plugin"`). If absent, the app is pure QML | + +Pure QML apps have no `"main"` field — no C++ compilation occurs. QML + backend apps have both `"main"` (C++ plugin) and `"view"` (QML entry point). + +UI apps do not use `"interface": "universal"` — they use `mkLogosQmlModule` in their flake.nix. + +--- + +# Nix Build Patterns + +## All Builds Go Through Nix + +Never run raw `cmake` without `nix develop` or `ws develop`. The Nix build system provides Qt, the SDK, the code generator, and all dependencies. Running `cmake --build` outside Nix will fail. + +## Flake Structure for Universal Modules + +```nix +{ + description = "My Logos 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/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code + ''; + }; +} +``` + +The `preConfigure` hook runs the code generator before CMake. This is required for universal modules. + +## Flake Structure for Modules with External Libraries + +Add the external library as a non-flake input and pass it via `externalLibInputs`: + +```nix +inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + nix-bundle-lgx.url = "github:logos-co/nix-bundle-lgx"; + my-lib = { + url = "github:org/my-lib/commit-hash"; + flake = false; + }; +}; + +outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + externalLibInputs = { + mylib = inputs.my-lib; + }; + preConfigure = '' + logos-cpp-generator --from-header src/_impl.h \ + --backend qt --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json --output-dir ./generated_code + ''; + }; +``` + +## Build Commands + +```bash +nix build # Build the module +nix build .#lib # Build just the shared library +nix flake check -L # Run tests +nix develop # Enter dev shell with build tools +``` + +Inside the workspace (multi-repo): +```bash +ws build my-module # Build +ws build my-module --auto-local # Build with local dirty dep overrides +ws test my-module # Run tests +ws test my-module --auto-local # Test with local overrides +ws develop my-module # Enter dev shell +``` + +## Key Rules + +- Flake inputs must be tracked by git. Run `git add ` before Nix can see new files. +- `nixpkgs` follows `logos-cpp-sdk` via `logos-module-builder`. Never pin a separate nixpkgs. +- Qt version is fixed by `logos-cpp-sdk`. All repos must use the same Qt to avoid version conflicts. +- Use `-L` flag to stream build logs: `nix build -L` +- Use `--override-input` to test with local dependency changes (the `ws` CLI does this for you with `--auto-local`). + +## Flake Structure for UI QML Apps + +Pure QML (no C++ backend): + +```nix +{ + inputs.logos-module-builder.url = "github:logos-co/logos-module-builder"; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +QML + C++ backend: + +```nix +{ + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + # Add module dependencies as inputs: + # some_module.url = "github:logos-co/logos-some-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +Both use `mkLogosQmlModule` (not `mkLogosModule`). No `preConfigure` needed — the `.rep` file (if present) is handled automatically via `REP_FILE` in CMakeLists.txt. + +## Dev Shell for CMake Iteration + +```bash +nix develop +cmake -B build -GNinja && cmake --build build +``` + +The dev shell provides all build dependencies. Use this for rapid C++ iteration without full Nix rebuilds. + +--- + +# Testing Logos Modules + +## Unit Tests with logos-test-framework + +Universal modules have a plain C++ impl class. Test it using the logos-test-framework, which is provided automatically by `logos-module-builder`. + +### Test File Structure + +``` +tests/ +├── main.cpp # LOGOS_TEST_MAIN() entry point +├── test_my_module.cpp # Test cases using LOGOS_TEST() +└── CMakeLists.txt # logos_test() macro +``` + +### Writing Tests + +```cpp +// tests/main.cpp +#include +LOGOS_TEST_MAIN() +``` + +```cpp +// tests/test_my_module.cpp +#include +#include "../src/my_module_impl.h" + +LOGOS_TEST(hash_returns_nonempty_string) { + MyModuleImpl impl; + LOGOS_ASSERT_FALSE(impl.hash("hello").empty()); +} + +LOGOS_TEST(verify_matches_hash) { + MyModuleImpl impl; + auto hash = impl.hash("hello"); + LOGOS_ASSERT_TRUE(impl.verify("hello", hash)); +} +``` + +### CMakeLists.txt for Tests + +```cmake +# tests/CMakeLists.txt +cmake_minimum_required(VERSION 3.14) +project(MyModuleTests LANGUAGES CXX) + +include(LogosTest) + +logos_test( + NAME my_module_tests + MODULE_SOURCES ../src/my_module_impl.cpp + TEST_SOURCES + main.cpp + test_my_module.cpp +) +``` + +The `logos_test()` CMake macro handles all framework wiring: Qt dependencies, SDK mock headers, include paths, and CTest registration. + +### Mocking Other Modules + +Use `LogosTestContext` when your module calls other modules: + +```cpp +LOGOS_TEST(calls_waku_publish) { + auto t = LogosTestContext("chat_module"); + t.mockModule("waku_module", "relayPublish").returns(true); + + ChatImpl impl; + t.init(&impl); + + impl.sendMessage("hello"); + LOGOS_ASSERT(t.moduleCalled("waku_module", "relayPublish")); +} +``` + +### Mocking C Libraries + +For modules wrapping external C/C++ libraries, write mock stubs: + +```cpp +// tests/mocks/mock_libcalc.cpp +#include +extern "C" { #include "libcalc.h" } + +extern "C" int calc_add(int a, int b) { + LOGOS_CMOCK_RECORD("calc_add"); + return LOGOS_CMOCK_RETURN(int, "calc_add"); +} +``` + +Reference them in CMake: + +```cmake +logos_test( + NAME calc_module_tests + MODULE_SOURCES ../src/calc_module_impl.cpp + TEST_SOURCES main.cpp test_calc.cpp + MOCK_C_SOURCES mocks/mock_libcalc.cpp +) +``` + +### Running Unit Tests + +```bash +nix build .#unit-tests -L # Build and run unit tests +nix flake check -L # All Nix checks including tests +``` + +`logos-module-builder` auto-detects `tests/CMakeLists.txt` and adds `checks..unit-tests` and `packages..unit-tests` automatically. + +## Integration Tests with logoscore + +Test the module as a loaded plugin via the headless runtime: + +```bash +logoscore -m ./result/lib -l my_module \ + -c "my_module.doSomething(test_input)" + +logoscore -m ./result/lib -l my_module \ + -c "my_module.init(config)" \ + -c "my_module.process(data)" + +logoscore -m ./result/lib -l my_module,other_module \ + -c "my_module.callOther(hello)" +``` + +logoscore arguments: +- `-m ` -- Directory to scan for module plugins (repeatable) +- `-l ` -- Comma-separated modules to load +- `-c ".(args)"` -- Call a method (repeatable, sequential) +- `--quit-on-finish` -- Exit after calls complete (for CI) + +Type auto-detection in `-c` args: `true`/`false` -> bool, `42` -> int, `3.14` -> double, else -> string. Use `@filename` to load file content as an argument. + +### TEST_GROUPS + +The test runner supports groups for selective testing: + +```bash +TEST_GROUPS=basic ws test logos-test-modules --auto-local +TEST_GROUPS=ipc ws test logos-test-modules --auto-local +TEST_GROUPS=basic,ipc,errors ws test logos-test-modules --auto-local +``` + +### Running Tests via Nix + +```bash +nix build .#unit-tests -L # Run unit tests +nix flake check -L # Run all checks defined in the flake + +ws test my-module # In the workspace +ws test my-module --auto-local # With local dep overrides +ws test --all --type cpp # All C++ repos +``` + +## 3. UI Integration Tests (QML Inspector) + +UI apps (`type: "ui_qml"`) can be tested via the QML Inspector MCP server built into `logos-standalone-app`. Tests interact with the live UI — clicking buttons, reading text, taking screenshots. + +### Test file pattern + +```javascript +// tests/smoke.mjs +const { resolve } = await import("node:path"); +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: basic interaction", async (app) => { + await app.expectTexts(["My App"]); + await app.click("Add"); + await app.expectTexts(["Result:"]); +}); + +run(); +``` + +### Test API + +| Method | Description | +|--------|-------------| +| `app.click(text, opts?)` | Find element by text and click it | +| `app.expectTexts(texts)` | Assert all texts are visible | +| `app.waitFor(fn, opts)` | Poll until fn succeeds (timeout, interval, description) | +| `app.screenshot()` | Capture current state | +| `app.findByType(type)` | Find elements by QML type | +| `app.findByProperty(prop, value)` | Find elements by property | +| `app.getTree()` | Get full QML element tree | + +### Running UI tests + +```bash +# Interactive (app already running on localhost:3768) +node tests/smoke.mjs + +# CI mode (launches app headless, tests, exits) +node tests/smoke.mjs --ci ./result/bin/logos-standalone-app --verbose + +# Hermetic via Nix (offscreen, no display needed) +nix build .#integration-test +``` + +Modules with `.mjs` test files in `tests/` automatically get `nix build .#integration-test` via `mkPluginTest`. + +### MCP tools for AI agents + +When the app is running, the `.mcp.json` auto-registers these tools with Claude Code / Cursor: + +`qml_screenshot`, `qml_find_and_click`, `qml_find_by_type`, `qml_find_by_property`, `qml_list_interactive`, `qml_get_tree` + +This lets AI agents visually verify UI changes, click through workflows, and debug layout issues in real time. + +## Key Testing Rules + +- Unit tests use `LOGOS_TEST()` and `LOGOS_ASSERT_*` macros from `` +- Unit tests should NOT require logoscore -- instantiate the impl class directly +- `tests/CMakeLists.txt` must use `include(LogosTest)` + `logos_test()` +- Use `LogosTestContext` for mocking module calls and C library functions +- Integration tests verify the full plugin lifecycle (load, call, response) +- Always test with `--quit-on-finish` in CI to ensure the process exits +- 30-second timeout per `-c` call; exit code 1 on failure +- After adding `checks` to a repo's `flake.nix`, run `ws sync-graph` so the workspace discovers them + +--- + +# UI App Development + +UI apps use `"type": "ui_qml"` in metadata.json. There are two subtypes: + +1. **Pure QML** — no C++ compilation, QML files only, calls backend modules via `logos.callModule()` +2. **QML + C++ Backend** — process-isolated C++ backend (Qt Remote Objects), QML frontend runs in-process + +## Pure QML Apps + +Simplest UI app type. No compilation, no C++ code. The host loads QML directly. + +### metadata.json + +```json +{ + "name": "my_app", + "type": "ui_qml", + "version": "1.0.0", + "description": "My QML UI application", + "view": "Main.qml", + "icon": null, + "category": "tools", + "dependencies": ["some_backend_module"] +} +``` + +Key fields: `"type": "ui_qml"` and `"view"` pointing to the QML entry point. No `"main"` field. + +### QML Pattern + +```qml +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +Item { + id: root + + function callBackend(method, args) { + if (typeof logos === "undefined" || !logos.callModule) { + console.log("Logos bridge not available") + return + } + return logos.callModule("some_backend_module", method, args) + } + + Button { + text: "Do Something" + onClicked: { + var result = callBackend("myMethod", ["arg1", "arg2"]) + console.log("Result:", result) + } + } +} +``` + +The `logos` bridge is injected by the host (Basecamp or standalone runner). Use `logos.callModule(moduleName, method, args)` to call backend modules. + +### flake.nix + +Add backend module dependencies (from `metadata.json` `"dependencies"`) as flake inputs. The input attribute name must match the dependency name. + +```nix +{ + inputs = { + logos-module-builder.url = "github:logos-co/logos-module-builder"; + + # Each metadata.json dependency needs a matching flake input. + # Use path: for local development, github: for CI/published modules: + some_backend_module.url = "path:../logos-some-backend-module"; + # some_backend_module.url = "github:logos-co/logos-some-backend-module"; + }; + + outputs = inputs@{ logos-module-builder, ... }: + logos-module-builder.lib.mkLogosQmlModule { + src = ./.; + configFile = ./metadata.json; + flakeInputs = inputs; + }; +} +``` + +Uses `mkLogosQmlModule` (not `mkLogosModule`). No `preConfigure` needed. + +### Resolving Module Dependencies + +Each backend module must be built with its shared library (`.so`/`.dylib`) present in `lib/`. Three ways to point at a dependency: + +| Approach | `flake.nix` input URL | Build command | +|----------|----------------------|---------------| +| Local path in flake.nix | `path:../logos-my-module` | `nix run .` | +| Remote URL + local override | `github:org/repo` | `nix run . --override-input dep_name path:../logos-my-module` | +| Fully remote | `github:org/repo` | `nix run .` | + +`--override-input` overrides a flake input at build time without editing `flake.nix` — useful for quick iteration. + +## QML + C++ Backend Apps + +For apps that need business logic, state management, or access to system APIs. The C++ backend runs in a **separate isolated process** (`logos_host`), communicating with the QML frontend via Qt Remote Objects IPC. + +### metadata.json + +```json +{ + "name": "my_app", + "type": "ui_qml", + "version": "1.0.0", + "description": "My UI app with C++ backend", + "main": "my_app_plugin", + "view": "qml/Main.qml", + "icon": null, + "category": "tools", + "dependencies": [] +} +``` + +Key difference from pure QML: has `"main"` field pointing to the C++ plugin binary, and `"view"` points to `qml/Main.qml` (inside `src/`). + +### .rep File (Qt Remote Objects Interface) + +``` +class MyApp +{ + PROP(QString status READWRITE) + SLOT(int doSomething(int a, int b)) +} +``` + +Defines the IPC interface. Properties auto-sync to QML replicas. Slots are callable from QML. + +### C++ Plugin Class + +The plugin inherits three bases: +- `MyAppSimpleSource` — generated from .rep, provides property storage + slot declarations +- `MyAppInterface` — extends `PluginInterface`, used for Qt plugin loading +- `MyAppViewPluginBase` — provides `setBackend()` to wire up Qt Remote Objects + +```cpp +class MyAppPlugin : public MyAppSimpleSource, + public MyAppInterface, + public MyAppViewPluginBase +{ + Q_OBJECT + Q_PLUGIN_METADATA(IID MyAppInterface_iid FILE "metadata.json") + Q_INTERFACES(MyAppInterface) + +public: + explicit MyAppPlugin(QObject* parent = nullptr); + + QString name() const override { return "my_app"; } + QString version() const override { return "1.0.0"; } + + Q_INVOKABLE void initLogos(LogosAPI* api); + + // Implement slots from .rep + int doSomething(int a, int b) override; + +private: + LogosAPI* m_logosAPI = nullptr; +}; +``` + +In `initLogos()`, call `setBackend(this)` to wire up the Qt Remote Objects source: + +```cpp +void MyAppPlugin::initLogos(LogosAPI* api) { + m_logosAPI = api; + setBackend(this); +} +``` + +### CMakeLists.txt + +```cmake +cmake_minimum_required(VERSION 3.14) +project(MyAppPlugin 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() + +logos_module( + NAME my_app + REP_FILE src/my_app.rep + SOURCES + src/my_app_interface.h + src/my_app_plugin.h + src/my_app_plugin.cpp +) +``` + +Key: `REP_FILE` tells the build system to generate Qt Remote Objects source/replica headers from the `.rep` file. + +### QML Frontend + +```qml +import QtQuick +import QtQuick.Controls + +Item { + id: root + + // Typed replica — auto-synced properties and callable slots + readonly property var backend: logos.module("my_app") + readonly property bool ready: backend !== null && logos.isViewModuleReady("my_app") + + // Auto-synced property from .rep + readonly property string status: backend ? backend.status : "" + + Button { + text: "Do Something" + enabled: root.ready + onClicked: { + logos.watch(backend.doSomething(1, 2), + function(value) { console.log("Result:", value) }, + function(error) { console.log("Error:", error) } + ) + } + } + + Text { + text: "Status: " + root.status + } +} +``` + +Key QML APIs: +- `logos.module("name")` — returns a typed Qt Remote Objects replica +- `logos.isViewModuleReady("name")` — checks if the backend process is connected +- `logos.watch(pendingReply, onSuccess, onError)` — handles async slot calls + +## C++/QML Boundary Rules + +| Concern | C++ (backend plugin) | QML | +| -------------------- | ---------------------------------------- | -------------------------------------- | +| Data models, state | `PROP()` in `.rep` file | Bind to `backend.property` | +| Business logic | Implement as `SLOT()` in `.rep` | Never — no JS business logic | +| Module calls | Via `LogosAPI*` in `initLogos()` | Via `logos.callModule()` (pure QML) | +| File I/O, networking | Always C++ | Never | +| UI layout, styling | Never | QML; use `Logos.Theme` inside Basecamp | +| User interactions | `SLOT()` methods | `onClicked: logos.watch(backend.doX())`| +| Plugin lifecycle | `initLogos()` + `setBackend(this)` | N/A | + +## Build and Test + +```bash +git init && git add -A # nix needs files tracked +nix build # compiles (backend) or packages (pure QML) +nix run . # standalone app + +# Override a module dependency at build time (no flake.nix edits needed): +nix run . --override-input some_module path:../logos-some-module +``` + +## Calling Logos Modules + +From C++ backend (in `initLogos` or slot implementations): + +```cpp +auto* client = m_logosAPI->getClient("storage_module"); +QVariant result = client->invokeRemoteMethod("storage_module", "save", key, value); +``` + +From pure QML: + +```qml +var result = logos.callModule("storage_module", "save", [key, value]) +``` + +Always declare module dependencies in `metadata.json` `"dependencies"` so they are loaded before the UI app. + +## UI Integration Testing (QML Inspector + MCP) + +`logos-standalone-app` includes a QML Inspector MCP server that lets AI agents and test scripts interact with the running UI — take screenshots, click elements, inspect the QML tree. + +### Available MCP tools + +| Tool | Description | +|------|-------------| +| `qml_screenshot` | Capture a screenshot of the current app state | +| `qml_find_and_click` | Find a UI element by text and click it | +| `qml_find_by_type` | Locate elements by QML type name | +| `qml_find_by_property` | Locate elements by property value | +| `qml_list_interactive` | List all clickable/interactive elements | +| `qml_get_tree` | Get the full QML element tree | + +### Interactive testing (AI agent workflow) + +```bash +nix build && nix run . # launches app with inspector on localhost:3768 +``` + +The `.mcp.json` in the project directory auto-registers the MCP server with Claude Code and other MCP clients. The AI agent can then screenshot, click buttons, and verify UI state in real time. + +### Writing integration test files + +```javascript +// tests/smoke.mjs +const { test, run } = await import( + resolve(process.env.LOGOS_QT_MCP || "./result-mcp", "test-framework/framework.mjs") +); + +test("my_app: click add and verify result", async (app) => { + await app.click("Add"); + await app.expectTexts(["Result:"]); +}); + +run(); +``` + +### Running tests + +```bash +# Interactive (app already running) +node tests/smoke.mjs + +# CI mode (launches app headless, tests, exits) +node tests/smoke.mjs --ci ./result/bin/logos-standalone-app --verbose + +# Hermetic via Nix (headless, offscreen) +nix build .#integration-test +``` + +### Nix `mkPluginTest` builder + +UI modules with `.mjs` test files in `tests/` automatically get `nix build .#integration-test`. For manual setup: + +```nix +integration-test = logos-standalone-app.lib.${system}.mkPluginTest { + inherit pkgs; + pluginPkg = myModulePackage; + testFiles = [ ./tests/smoke.mjs ]; + name = "my-module-integration-test"; +}; +``` + +The builder runs headless (`QT_QPA_PLATFORM=offscreen`), connects to the QML inspector, and executes each test file sequentially. + +--- + +# Universal Module Development + +## The Universal Interface Pattern + +Universal modules use **pure C++** for their implementation. You write a single implementation class using standard C++ types. The build system generates all Qt/plugin infrastructure automatically via `logos-cpp-generator --from-header`. + +**You write:** A C++ class with `std::string`, `int64_t`, `bool`, `std::vector`. +**The generator produces:** Qt plugin class, method dispatch, introspection metadata. + +## Rules + +- **NO Qt types** in your impl header or implementation: no `QString`, `QObject`, `Q_INVOKABLE`, `QVariant` +- **NO Qt includes** in your impl header (Qt headers in `.cpp` are OK if needed for internal use, but the public API must be pure C++) +- Set `"interface": "universal"` in `metadata.json` +- Name the impl class `Impl` (e.g., `CryptoUtilsImpl`) +- Name the impl header `_impl.h` (e.g., `crypto_utils_impl.h`) +- Only `public` methods become module API methods. Private/protected are ignored by the generator. +- Constructors, destructors, typedefs, and using declarations are skipped by the generator. + +## Type Mapping + +| Use this in your C++ | Generator maps to | Qt type produced | +|----------------------|-------------------|-----------------| +| `std::string` / `const std::string&` | `tstr` | `QString` | +| `bool` | `bool` | `bool` | +| `int64_t` | `int` | `int` | +| `uint64_t` | `uint` | `int` | +| `double` | `float64` | `double` | +| `void` | `void` | `void` | +| `std::vector` | `[tstr]` | `QStringList` | +| `std::vector` | `bstr` | `QByteArray` | +| `std::vector` | `[int]` | `QVariantList` | +| `std::vector` | `[float64]` | `QVariantList` | +| `std::vector` | `[bool]` | `QVariantList` | + +If you use a type not in this table, the generator maps it to `any` (`QVariant`). Prefer explicit types from the table for type safety. + +## Impl Header Template + +```cpp +#pragma once +#include +#include +#include + +class MyModuleImpl { +public: + MyModuleImpl(); + ~MyModuleImpl(); + + std::string doSomething(const std::string& input); + bool validate(const std::string& data); + int64_t count(); + std::vector listItems(); + +private: + // Private members are not exposed as module API +}; +``` + +## Build Pipeline + +The `flake.nix` `preConfigure` hook runs the generator before CMake: + +```bash +logos-cpp-generator --from-header src/_impl.h \ + --backend qt \ + --impl-class \ + --impl-header _impl.h \ + --metadata metadata.json \ + --output-dir ./generated_code +``` + +This produces `generated_code/_qt_glue.h` and `generated_code/_dispatch.cpp`. These files are listed in `CMakeLists.txt` under the `SOURCES` of `logos_module()`. + +## Testing + +Unit tests instantiate the impl class directly — it is a plain C++ class: + +```cpp +#include "my_module_impl.h" +// No Qt test framework needed for basic tests +MyModuleImpl impl; +assert(impl.doSomething("test") == "expected"); +``` + +Integration tests use `logoscore`: +```bash +logoscore -m ./result/lib -l my_module -c "my_module.doSomething(test)" +``` + +## Quick Reference + +### Build Commands + +```bash +nix build # Build the module/app +nix build -L # Build with streaming logs +nix flake check -L # Run tests +nix develop # Enter dev shell +``` + +### Testing + +```bash +# Integration test with logoscore +logoscore -m ./result/lib -l -c ".methodName(args)" + +# Inspect module metadata and methods +lm ./result/lib/_plugin.so +lm methods ./result/lib/_plugin.so --json +``` + +### Packaging + +```bash +lgx create +lgx add .lgx -v linux-x86_64 -f ./result/lib/_plugin.so +lgx verify .lgx +``` + +### For detailed documentation, see the logos-dev-boost docs/ directory +### or activate a skill: create-universal-module, wrap-external-lib, +### create-ui-app, package-lgx, inter-module-comm, testing-modules, +### nix-flake-setup, add-to-workspace + + + +## Claude Code Skills + +The following skills are available in `.claude/skills/`: + +- **add-to-workspace** — Activate when registering a new module in the logos-workspace. Covers adding git submodules, flake.nix inputs with follows declarations, scripts/ws REPOS array, repo groups, and dep-graph.nix regeneration. +- **create-ui-app** — Activate when creating a Logos Basecamp UI app. Covers two subtypes — pure QML (no C++) and QML + process-isolated C++ backend with Qt Remote Objects. +- **create-universal-module** — 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. +- **inter-module-comm** — Activate when implementing inter-module communication in Logos. Covers LogosAPI::callModule(), LogosResult handling, dependency declaration in metadata.json, generated client stubs, and the QML bridge. +- **nix-flake-setup** — Activate when configuring Nix flake.nix for a Logos module. Covers flake inputs, follows declarations, preConfigure for code generation, mkLogosModule, overrides for local development, and workspace integration. +- **package-lgx** — Activate when packaging a Logos module or UI app for distribution as an LGX package. Covers lgx create, adding platform variants, verification, portable builds, and installation via lgpm. +- **testing-modules** — Activate when writing tests for Logos modules. Covers the logos-test-framework (LOGOS_TEST macros, LogosTestContext, module mocking, C library mocking, event testing), logos_test() CMake integration, logoscore integration tests, and Nix check configuration. +- **wrap-external-lib** — Activate when wrapping an external C or C++ library as a Logos universal module. Covers external_libraries in metadata.json, flake input configuration, extern C includes, and the Go library special case. + +## MCP Server + +If configured in `.mcp.json`, the `logos-dev-boost` MCP server provides: +- `logos_project_info` — Project metadata and build targets +- `logos_search_docs` — Search Logos documentation +- `logos_api_reference` — API reference for LogosAPI, LogosResult, type system +- `logos_build_help` — Context-aware build commands and troubleshooting +- `logos_scaffold` — Generate new module/app from template \ No newline at end of file