#pragma once // --- BufferOpsLifecycle.h --- // Extracted from BufferOps.h (Sprint 8 cleanup). // Session metadata and lifecycle initialization helpers. // Included from BufferOps.h after the EditorState struct definition. inline std::string EditorState::osLabel() { #ifdef _WIN32 return "windows"; #elif __APPLE__ return "macos"; #else return "linux"; #endif } inline std::string EditorState::detectProjectType() const { std::filesystem::path root(workspaceRoot); std::error_code ec; auto exists = [&](const std::string& name) { return std::filesystem::exists(root / name, ec); }; if (exists("CMakeLists.txt")) return "cmake"; if (exists("package.json")) return "node"; if (exists("pyproject.toml") || exists("requirements.txt")) return "python"; if (exists("Cargo.toml")) return "rust"; if (exists("go.mod")) return "go"; if (exists("pom.xml") || exists("build.gradle")) return "java"; return "unknown"; } inline json EditorState::buildSessionMetadata(bool autoRecording) const { json meta; meta["editorVersion"] = welcome.getVersion(); meta["os"] = osLabel(); meta["language"] = activeBuffer ? activeBuffer->language : std::string("unknown"); meta["projectType"] = detectProjectType(); meta["autoRecording"] = autoRecording; return meta; } inline void EditorState::startSessionRecording(const std::string& name, bool autoRecording) { WorkflowRecorder::RecordingConfig cfg; cfg.recordAllSessions = true; cfg.recordControlMethods = true; cfg.recordEditorEvents = true; cfg.autoRecording = autoRecording; agent.workflowRecorder.startRecording(name, "", cfg, buildSessionMetadata(autoRecording)); agent.workflowRecorder.recordEvent("session_start", {{"name", name}, {"autoRecording", autoRecording}}); notify(NotificationLevel::Info, "Session recording started."); } inline void EditorState::stopSessionRecording() { if (!agent.workflowRecorder.isRecording()) return; agent.workflowRecorder.recordEvent("session_stop", json::object()); agent.workflowRecorder.stopRecording(); notify(NotificationLevel::Info, "Session recording stopped."); } inline void EditorState::toggleSessionRecording() { if (agent.workflowRecorder.isRecording()) stopSessionRecording(); else startSessionRecording("session", false); } inline void EditorState::recordUIEvent(const UIEvent& ev) { json payload = { {"path", ev.path}, {"detail", ev.detail}, {"uiTimestamp", ev.timestamp} }; agent.workflowRecorder.recordEvent(uiEventLabel(ev.type), payload); } inline void EditorState::recordEditorEvent(const std::string& type, const json& payload) { agent.workflowRecorder.recordEvent(type, payload); } inline void EditorState::init() { notify(NotificationLevel::Info, "Whetstone Editor ready."); workspaceRoot = std::filesystem::current_path().string(); search.projectSearch.setRoot(workspaceRoot); fileTreeDirty = true; loadRecentFiles(); loadFeatureHints(featureHints, workspaceRoot); loadSettingsFromDisk(); if (settings.getAutoRecordSessions() && !agent.workflowRecorder.isRecording()) { startSessionRecording("auto-session", true); } registerCommands(); library.vulnDb.startBackgroundRefresh(); library.semanticTags.load(); events.subscribe(UIEventType::BufferSwitched, [this](const UIEvent& ev) { if (!active()) return; library.primitives.setRoot(activeAST()); library.primitives.setLanguage(active()->language); symbolsPending = true; symbolsLastChange = ImGui::GetTime(); if (lsp) lsp->clearDocumentSymbols(); if (active()->language == "elisp") { emacsState.emacsFunctionIndexDirty = true; } recordUIEvent(ev); }); events.subscribe(UIEventType::ASTChanged, [this](const UIEvent&) { if (!active()) return; library.primitives.setRoot(activeAST()); library.primitives.setLanguage(active()->language); symbolsPending = true; symbolsLastChange = ImGui::GetTime(); if (lsp) lsp->clearDocumentSymbols(); }); events.subscribe(UIEventType::SettingsChanged, [this](const UIEvent& ev) { applySettingsToState(); recordUIEvent(ev); }); events.subscribe(UIEventType::ThemeChanged, [this](const UIEvent& ev) { recordUIEvent(ev); }); events.subscribe(UIEventType::FileModified, [this](const UIEvent& ev) { recordUIEvent(ev); }); events.subscribe(UIEventType::NotificationPosted, [this](const UIEvent& ev) { recordUIEvent(ev); }); startEmacsDaemonFromSettings(); initAgentServer(); refreshBuildSystem(); telemetry.initialize(workspaceRoot, settings.getTelemetryOptIn()); telemetry.installCrashHandler(); }