From c1d8c582f9b683e14d43ff543398fe39d3fb825a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=CE=A7=CE=B3=CF=86=CF=84=20Kompanion?=
 <chigammaphitau@chelydra.at>
Date: Mon, 20 Oct 2025 00:17:33 +0200
Subject: [PATCH] Move HandlerMemory up again.

--
---
 AGENTS.md                     |   3 ++
 src/cli/.KompanionApp.cpp.swp | Bin 16384 -> 0 bytes
 src/mcp/HandlersMemory.hpp    |  95 +++++++++++++++++-----------------
 tests/CMakeLists.txt          |   4 ++
 4 files changed, 55 insertions(+), 47 deletions(-)
 delete mode 100644 src/cli/.KompanionApp.cpp.swp

diff --git a/AGENTS.md b/AGENTS.md
index 09b6f99..5b1303e 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,6 +1,9 @@
 # Repository Guidelines
 This guide supports new agents contributing to `metal-kompanion`, the MCP backend for Kompanion. Follow these practices to keep the service buildable, testable, and easy to review.
 
+
+source dev.env for envrionment variables.
+
 ## MCP Usage
 - This project uses agentic-control-framework Use this for task planning
 
diff --git a/src/cli/.KompanionApp.cpp.swp b/src/cli/.KompanionApp.cpp.swp
deleted file mode 100644
index 69bea14c218038bdd36489bb503ea0cc882d7b5c..0000000000000000000000000000000000000000
GIT binary patch
diff --git a/src/mcp/HandlersMemory.hpp b/src/mcp/HandlersMemory.hpp
index 8ff7bea..35a7fb1 100644
--- a/src/mcp/HandlersMemory.hpp
+++ b/src/mcp/HandlersMemory.hpp
@@ -594,6 +594,53 @@ inline std::string save_context(const std::string& reqJson) {
     return os.str();
 }
 
+/**
+ * upsert_and_embed
+ * Request: { namespace, model?, items: [{id?, text, tags?, metadata?}] }
+ * Response: { upserted, embedded }
+ */
+inline std::string upsert_and_embed(const std::string& reqJson) {
+    const std::string nsName = detail::extract_string_field(reqJson, "namespace");
+    if (nsName.empty()) return detail::error_response("bad_request","namespace is required");
+    auto nsRow = detail::database().ensureNamespace(nsName);
+    if (!nsRow) return detail::error_response("internal_error","failed to ensure namespace");
+
+    auto items = detail::parse_items(reqJson);
+    if (items.empty()) return detail::error_response("bad_request","items array must contain at least one entry");
+    std::string model = detail::extract_string_field(reqJson, "model");
+
+    // Upsert items first and collect texts/ids
+    std::vector<std::string> itemIds; itemIds.reserve(items.size());
+    std::vector<std::string> texts; texts.reserve(items.size());
+    for (auto &it : items) {
+        ki::ItemRow row; row.id = it.id; row.namespace_id = nsRow->id; row.text = it.text;
+        row.tags = it.tags; row.revision = 1; row.metadata_json = it.metadataJson.empty()?"{}":it.metadataJson; row.content_json = it.rawJson;
+        const std::string id = detail::database().upsertItem(row);
+        itemIds.push_back(id); texts.push_back(it.text);
+    }
+
+    // Embed via libKI
+    KI::KIClient client; KI::OllamaProvider provider; client.setProvider(&provider);
+    KI::KIEmbedOptions opts; if (!model.empty()) opts.model = QString::fromStdString(model);
+    QStringList qtexts; for (auto &t : texts) qtexts.push_back(QString::fromStdString(t));
+    QEventLoop loop; QFuture<KI::KIEmbeddingResult> fut = client.embed(qtexts, opts);
+    QFutureWatcher<KI::KIEmbeddingResult> watcher; QObject::connect(&watcher, &QFutureWatcher<KI::KIEmbeddingResult>::finished, &loop, &QEventLoop::quit); watcher.setFuture(fut); loop.exec();
+    const KI::KIEmbeddingResult result = watcher.result();
+
+    // Upsert chunks + embeddings (ord=0)
+    int embedded = 0;
+    const int n = std::min(itemIds.size(), (std::size_t)result.vectors.size());
+    for (int i = 0; i < n; ++i) {
+        ki::ChunkRow chunk; chunk.item_id = itemIds[(size_t)i]; chunk.ord = 0; chunk.text = texts[(size_t)i];
+        auto chunkIds = detail::database().upsertChunks(std::vector<ki::ChunkRow>{chunk}); if (chunkIds.empty()) continue;
+        ki::EmbeddingRow emb; emb.chunk_id = chunkIds.front(); emb.model = result.model.toStdString(); emb.dim = result.vectors[i].size();
+        emb.vector.assign(result.vectors[i].begin(), result.vectors[i].end());
+        detail::database().upsertEmbeddings(std::vector<ki::EmbeddingRow>{emb}); embedded++;
+    }
+
+    std::ostringstream os; os << "{\"upserted\":" << itemIds.size() << ",\"embedded\":" << embedded << "}"; return os.str();
+}
+
 /**
  * recall_context
  * Request: { "namespace": string, "key?": string, "tags?": string[], "limit?": int, "since?": iso8601 }
@@ -771,7 +818,7 @@ inline std::string warm_cache(const std::string& reqJson) {
     watcher.setFuture(fut); loop.exec(); const KI::KIEmbeddingResult result = watcher.result();
 
     // Persist
-    int persisted = 0; const int n = std::min(result.vectors.size(), (int)toEmbed.size());
+    int persisted = 0; const int n = std::min((size_t)result.vectors.size(), toEmbed.size());
     for (int i = 0; i < n; ++i) {
         const auto &pair = toEmbed[(size_t)i];
         ki::ChunkRow chunk; chunk.item_id = pair.first; chunk.ord = 0; chunk.text = pair.second;
@@ -798,49 +845,3 @@ inline std::string delete_context(const std::string& reqJson) {
 }
 
 } // namespace Handlers
-/**
- * upsert_and_embed
- * Request: { namespace, model?, items: [{id?, text, tags?, metadata?}] }
- * Response: { upserted, embedded }
- */
-inline std::string upsert_and_embed(const std::string& reqJson) {
-    const std::string nsName = detail::extract_string_field(reqJson, "namespace");
-    if (nsName.empty()) return detail::error_response("bad_request","namespace is required");
-    auto nsRow = detail::database().ensureNamespace(nsName);
-    if (!nsRow) return detail::error_response("internal_error","failed to ensure namespace");
-
-    auto items = detail::parse_items(reqJson);
-    if (items.empty()) return detail::error_response("bad_request","items array must contain at least one entry");
-    std::string model = detail::extract_string_field(reqJson, "model");
-
-    // Upsert items first and collect texts/ids
-    std::vector<std::string> itemIds; itemIds.reserve(items.size());
-    std::vector<std::string> texts; texts.reserve(items.size());
-    for (auto &it : items) {
-        ki::ItemRow row; row.id = it.id; row.namespace_id = nsRow->id; row.text = it.text;
-        row.tags = it.tags; row.revision = 1; row.metadata_json = it.metadataJson.empty()?"{}":it.metadataJson; row.content_json = it.rawJson;
-        const std::string id = detail::database().upsertItem(row);
-        itemIds.push_back(id); texts.push_back(it.text);
-    }
-
-    // Embed via libKI
-    KI::KIClient client; KI::OllamaProvider provider; client.setProvider(&provider);
-    KI::KIEmbedOptions opts; if (!model.empty()) opts.model = QString::fromStdString(model);
-    QStringList qtexts; for (auto &t : texts) qtexts.push_back(QString::fromStdString(t));
-    QEventLoop loop; QFuture<KI::KIEmbeddingResult> fut = client.embed(qtexts, opts);
-    QFutureWatcher<KI::KIEmbeddingResult> watcher; QObject::connect(&watcher, &QFutureWatcher<KI::KIEmbeddingResult>::finished, &loop, &QEventLoop::quit); watcher.setFuture(fut); loop.exec();
-    const KI::KIEmbeddingResult result = watcher.result();
-
-    // Upsert chunks + embeddings (ord=0)
-    int embedded = 0;
-    const int n = std::min((int)itemIds.size(), result.vectors.size());
-    for (int i = 0; i < n; ++i) {
-        ki::ChunkRow chunk; chunk.item_id = itemIds[(size_t)i]; chunk.ord = 0; chunk.text = texts[(size_t)i];
-        auto chunkIds = detail::database().upsertChunks(std::vector<ki::ChunkRow>{chunk}); if (chunkIds.empty()) continue;
-        ki::EmbeddingRow emb; emb.chunk_id = chunkIds.front(); emb.model = result.model.toStdString(); emb.dim = result.vectors[i].size();
-        emb.vector.assign(result.vectors[i].begin(), result.vectors[i].end());
-        detail::database().upsertEmbeddings(std::vector<ki::EmbeddingRow>{emb}); embedded++;
-    }
-
-    std::ostringstream os; os << "{\"upserted\":" << itemIds.size() << ",\"embedded\":" << embedded << "}"; return os.str();
-}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index d0b1884..a644bec 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -17,3 +17,7 @@ qt_add_executable(test_snapshot
 )
 target_link_libraries(test_snapshot PRIVATE Qt6::Core Qt6::Network Qt6::Test kompanion_mw)
 add_test(NAME test_snapshot COMMAND test_snapshot)
+
+add_test(NAME cli_smoke
+  COMMAND sh ${CMAKE_SOURCE_DIR}/tests/cli_smoke.sh $<TARGET_FILE:kompanion>)
+set_tests_properties(cli_smoke PROPERTIES ENVIRONMENT "KOMPANION_SKIP_CLI_SMOKE=1")
