#pragma once #include #include #include #include #include #include #include #include #include #include namespace Handlers { namespace detail { inline const std::filesystem::path& projectRoot() { static const std::filesystem::path root = #ifdef PROJECT_SOURCE_DIR std::filesystem::path(PROJECT_SOURCE_DIR); #else std::filesystem::current_path(); #endif return root; } inline std::string jsonEscape(const std::string& in) { std::ostringstream os; for (char c : in) { switch (c) { case '\"': os << "\\\""; break; case '\\': os << "\\\\"; break; case '\b': os << "\\b"; break; case '\f': os << "\\f"; break; case '\n': os << "\\n"; break; case '\r': os << "\\r"; break; case '\t': os << "\\t"; break; default: if (static_cast(c) < 0x20) { os << "\\u" << std::hex << std::uppercase << static_cast(c); } else { os << c; } break; } } return os.str(); } inline std::string readFileHead(const std::string& relativePath, int maxLines, std::size_t maxBytes) { const std::filesystem::path path = projectRoot() / relativePath; std::ifstream input(path); if (!input) { return std::string("missing: ") + relativePath; } std::ostringstream oss; std::string line; int lineCount = 0; while (std::getline(input, line)) { oss << line << '\n'; if (++lineCount >= maxLines || oss.tellp() >= static_cast(maxBytes)) { break; } } return oss.str(); } inline std::string runCommandCapture(const char* cmd, std::size_t maxBytes = 8192) { #ifdef _WIN32 (void)cmd; (void)maxBytes; return "git status capture not supported on this platform"; #else struct PipeCloser { void operator()(FILE* file) const noexcept { if (file != nullptr) { pclose(file); } } }; std::unique_ptr pipe(popen(cmd, "r"), PipeCloser{}); if (!pipe) { return "git status unavailable"; } std::ostringstream oss; char buffer[256]; std::size_t total = 0; while (fgets(buffer, sizeof(buffer), pipe.get())) { const std::size_t len = std::strlen(buffer); total += len; if (total > maxBytes) { oss.write(buffer, static_cast(maxBytes - (total - len))); break; } oss.write(buffer, static_cast(len)); } return oss.str(); #endif } inline std::optional currentDsnSource() { const char* env = std::getenv("PG_DSN"); if (env && *env) { return std::string(env); } return std::nullopt; } } // namespace detail // Produces a JSON response summarising project state: memory docs, task table, git status. inline std::string project_snapshot(const std::string& reqJson) { (void)reqJson; const std::string memorySummary = detail::readFileHead("docs/memory-architecture.md", 40, 4096); const std::string tasksSummary = detail::readFileHead("tasks-table.md", 40, 4096); const std::string gitStatus = detail::runCommandCapture("git status --short --branch 2>/dev/null"); std::ostringstream json; json << "{"; json << "\"sections\":["; json << "{\"title\":\"memory_architecture\",\"body\":\"" << detail::jsonEscape(memorySummary) << "\"},"; json << "{\"title\":\"tasks_table\",\"body\":\"" << detail::jsonEscape(tasksSummary) << "\"},"; json << "{\"title\":\"git_status\",\"body\":\"" << detail::jsonEscape(gitStatus) << "\"}"; json << "]"; if (auto dsn = detail::currentDsnSource()) { json << ",\"pg_dsn\":\"" << detail::jsonEscape(*dsn) << "\""; } json << ",\"notes\":\"Project snapshot generated by Kompanion to aid MCP agents.\""; json << "}"; return json.str(); } } // namespace Handlers