@bniladridas/cursor 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.clang-tidy +28 -0
- package/.dockerignore +56 -0
- package/.env.example +29 -0
- package/.github/CODEOWNERS +2 -0
- package/.github/ISSUE_TEMPLATE/blank.md +27 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- package/.github/SECURITY.md +24 -0
- package/.github/codeql/codeql-config.yml +8 -0
- package/.github/dependabot.yml +14 -0
- package/.github/labeler.yml +50 -0
- package/.github/packaging/brand-cursor.png +0 -0
- package/.github/packaging/database/init.sql +48 -0
- package/.github/packaging/docker/Dockerfile +111 -0
- package/.github/packaging/docker/docker-compose.yml +56 -0
- package/.github/packaging/scripts/preflight.sh +413 -0
- package/.github/packaging/scripts/prepare-release.sh +141 -0
- package/.github/packaging/scripts/release.sh +22 -0
- package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
- package/.github/pull_request_template.md +31 -0
- package/.github/signed.json +9 -0
- package/.github/workflows/README.md +23 -0
- package/.github/workflows/ci.yml +181 -0
- package/.github/workflows/cla.yml +33 -0
- package/.github/workflows/formula-sha.yml +63 -0
- package/.github/workflows/issue-response.yml +44 -0
- package/.github/workflows/labeler.yml +42 -0
- package/.github/workflows/pr-body.yml +49 -0
- package/.github/workflows/release.yml +176 -0
- package/.github/workflows/security.yml +94 -0
- package/.github/workflows/stale.yml +38 -0
- package/AGENTS.md +49 -0
- package/CHANGELOG.md +3 -0
- package/CMakeLists.txt +646 -0
- package/Formula/cursor.rb +46 -0
- package/LICENSE +201 -0
- package/Makefile +28 -0
- package/README.md +46 -0
- package/cli.js +16 -0
- package/include/agent.h +86 -0
- package/include/agent_mode.h +17 -0
- package/include/memory_manager.h +102 -0
- package/include/services/ai_service.h +31 -0
- package/include/services/auth_service.h +87 -0
- package/include/services/checkpoint_service.h +69 -0
- package/include/services/codebase_service.h +38 -0
- package/include/services/command_service.h +23 -0
- package/include/services/context_service.h +74 -0
- package/include/services/database_service.h +56 -0
- package/include/services/error_service.h +106 -0
- package/include/services/file_service.h +51 -0
- package/include/services/git_service.h +29 -0
- package/include/services/github_service.h +85 -0
- package/include/services/mcp_service.h +85 -0
- package/include/services/multi_file_service.h +93 -0
- package/include/services/sandbox_service.h +96 -0
- package/include/services/theme_service.h +67 -0
- package/include/services/web_service.h +52 -0
- package/include/utils/config.h +68 -0
- package/include/utils/memory_utils.h +79 -0
- package/include/utils/platform.h +56 -0
- package/include/utils/ui.h +43 -0
- package/include/utils/validation.h +63 -0
- package/include/utils/version.h.in +17 -0
- package/install.js +49 -0
- package/package.json +16 -0
- package/release/checksums.txt +3 -0
- package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
- package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
- package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
- package/src/agent.cpp +2026 -0
- package/src/main.cpp +97 -0
- package/src/memory_manager.cpp +814 -0
- package/src/services/ai_service.cpp +366 -0
- package/src/services/auth_service.cpp +779 -0
- package/src/services/checkpoint_service.cpp +465 -0
- package/src/services/codebase_service.cpp +233 -0
- package/src/services/command_service.cpp +82 -0
- package/src/services/context_service.cpp +348 -0
- package/src/services/database_service.cpp +148 -0
- package/src/services/error_service.cpp +438 -0
- package/src/services/file_service.cpp +349 -0
- package/src/services/git_service.cpp +148 -0
- package/src/services/github_service.cpp +435 -0
- package/src/services/mcp_service.cpp +481 -0
- package/src/services/multi_file_service.cpp +591 -0
- package/src/services/sandbox_service.cpp +678 -0
- package/src/services/theme_service.cpp +429 -0
- package/src/services/web_service.cpp +532 -0
- package/src/utils/config.cpp +77 -0
- package/src/utils/memory_utils.cpp +93 -0
- package/src/utils/ui.cpp +307 -0
- package/src/utils/validation.cpp +306 -0
- package/src/utils/version.cpp +175 -0
- package/tests/e2e/docker-compose.yml +195 -0
- package/tests/e2e/run_e2e_tests.sh +70 -0
- package/tests/e2e/run_tests_in_docker.sh +115 -0
- package/tests/main_test.cpp +16 -0
- package/tests/mocks/mock_ollama.py +98 -0
- package/tests/mocks/start_nginx.sh +64 -0
package/src/agent.cpp
ADDED
|
@@ -0,0 +1,2026 @@
|
|
|
1
|
+
#include "agent.h"
|
|
2
|
+
#include "memory_manager.h"
|
|
3
|
+
#include "services/ai_service.h"
|
|
4
|
+
#include "services/auth_service.h"
|
|
5
|
+
#include "services/checkpoint_service.h"
|
|
6
|
+
#include "services/codebase_service.h"
|
|
7
|
+
#include "services/command_service.h"
|
|
8
|
+
#include "services/context_service.h"
|
|
9
|
+
#include "services/error_service.h"
|
|
10
|
+
#include "services/file_service.h"
|
|
11
|
+
#include "services/git_service.h"
|
|
12
|
+
#include "services/github_service.h"
|
|
13
|
+
#include "services/mcp_service.h"
|
|
14
|
+
#include "services/multi_file_service.h"
|
|
15
|
+
#include "services/sandbox_service.h"
|
|
16
|
+
#include "services/theme_service.h"
|
|
17
|
+
#include "services/web_service.h"
|
|
18
|
+
#include "utils/config.h"
|
|
19
|
+
#include "utils/ui.h"
|
|
20
|
+
#include "utils/validation.h"
|
|
21
|
+
#include "version.h"
|
|
22
|
+
|
|
23
|
+
#include <algorithm>
|
|
24
|
+
#include <atomic>
|
|
25
|
+
#include <cctype>
|
|
26
|
+
#include <iostream>
|
|
27
|
+
#include <thread>
|
|
28
|
+
|
|
29
|
+
#include <filesystem>
|
|
30
|
+
#include <fstream>
|
|
31
|
+
#include <map>
|
|
32
|
+
#include <sstream>
|
|
33
|
+
#include <vector>
|
|
34
|
+
|
|
35
|
+
// Constants for response handling
|
|
36
|
+
const size_t MAX_RESPONSE_LENGTH = 8000;
|
|
37
|
+
|
|
38
|
+
// Using the Mode enum from agent.h instead of separate constants
|
|
39
|
+
|
|
40
|
+
namespace Core {
|
|
41
|
+
Agent::Agent()
|
|
42
|
+
: memory_(std::make_unique<Data::MemoryManager>()), ai_service_(nullptr) {}
|
|
43
|
+
|
|
44
|
+
Agent::~Agent() {
|
|
45
|
+
// Destructor defined here because unique_ptr types are forward declared
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Helper: trim whitespace
|
|
49
|
+
static inline std::string trim_copy(const std::string &s) {
|
|
50
|
+
size_t a = 0;
|
|
51
|
+
while (a < s.size() && std::isspace(static_cast<unsigned char>(s[a])))
|
|
52
|
+
++a;
|
|
53
|
+
size_t b = s.size();
|
|
54
|
+
while (b > a && std::isspace(static_cast<unsigned char>(s[b - 1])))
|
|
55
|
+
--b;
|
|
56
|
+
return s.substr(a, b - a);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
void Agent::run() {
|
|
60
|
+
initialize_mode();
|
|
61
|
+
|
|
62
|
+
// Data-driven model name mapping
|
|
63
|
+
// Data-driven model name mapping
|
|
64
|
+
std::map<Agent::Mode, std::string> model_names = {
|
|
65
|
+
{Agent::Mode::MODE_TOGETHER, "Together AI"},
|
|
66
|
+
{Agent::Mode::MODE_CEREBRAS, "Cerebras"},
|
|
67
|
+
{Agent::Mode::MODE_FIREWORKS, "Fireworks"},
|
|
68
|
+
{Agent::Mode::MODE_GROQ, "Groq"},
|
|
69
|
+
{Agent::Mode::MODE_DEEPSEEK, "DeepSeek"},
|
|
70
|
+
{Agent::Mode::MODE_OPENAI, "OpenAI"},
|
|
71
|
+
{Agent::Mode::MODE_LLAMA_3B, "llama3.2:3b"},
|
|
72
|
+
{Agent::Mode::MODE_LLAMA_LATEST, "llama3.2:latest"},
|
|
73
|
+
{Agent::Mode::MODE_LLAMA_31, "llama3.1:latest"}};
|
|
74
|
+
|
|
75
|
+
// Show enhanced ready interface with system info and quick help
|
|
76
|
+
std::string mode_name = is_online_mode() ? "Online" : "Offline";
|
|
77
|
+
std::string model_name =
|
|
78
|
+
model_names.count(mode_) ? model_names[mode_] : "Unknown";
|
|
79
|
+
Utils::UI::print_ready_interface(mode_name, model_name);
|
|
80
|
+
|
|
81
|
+
while (true) {
|
|
82
|
+
std::cout << "> ";
|
|
83
|
+
std::string user_input;
|
|
84
|
+
if (!std::getline(std::cin, user_input)) {
|
|
85
|
+
break; // EOF or error
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
user_input = trim_copy(user_input);
|
|
89
|
+
if (user_input.empty())
|
|
90
|
+
continue;
|
|
91
|
+
|
|
92
|
+
if (user_input == "exit" || user_input == "quit")
|
|
93
|
+
break;
|
|
94
|
+
if (user_input == "help") {
|
|
95
|
+
Utils::UI::print_help();
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (user_input == "version") {
|
|
99
|
+
Version::print_version_info();
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (user_input == "update") {
|
|
103
|
+
std::cout << "Checking for updates...\n";
|
|
104
|
+
std::string latest = Version::check_update();
|
|
105
|
+
if (latest.empty()) {
|
|
106
|
+
std::cout << "Already up to date (v" << Version::get_version()
|
|
107
|
+
<< ")\n";
|
|
108
|
+
} else if (Version::download_and_install(latest)) {
|
|
109
|
+
std::cout << "Restart cursor to use the new version.\n";
|
|
110
|
+
}
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
process_user_input(user_input);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
std::cout << "Goodbye\n";
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
void Agent::initialize_mode() {
|
|
121
|
+
// Data-driven provider configuration
|
|
122
|
+
struct ProviderInfo {
|
|
123
|
+
int choice;
|
|
124
|
+
Mode mode;
|
|
125
|
+
std::string env_var;
|
|
126
|
+
std::string display_name;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
std::vector<ProviderInfo> providers = {
|
|
130
|
+
{1, Agent::Mode::MODE_TOGETHER, "TOGETHER_API_KEY", "Together AI"},
|
|
131
|
+
{2, Agent::Mode::MODE_CEREBRAS, "CEREBRAS_API_KEY", "Cerebras"},
|
|
132
|
+
{3, Agent::Mode::MODE_FIREWORKS, "FIREWORKS_API_KEY", "Fireworks"},
|
|
133
|
+
{4, Agent::Mode::MODE_GROQ, "GROQ_API_KEY", "Groq"},
|
|
134
|
+
{5, Agent::Mode::MODE_DEEPSEEK, "DEEPSEEK_API_KEY", "DeepSeek"},
|
|
135
|
+
{6, Agent::Mode::MODE_OPENAI, "OPENAI_API_KEY", "OpenAI"}};
|
|
136
|
+
|
|
137
|
+
// Simplified mode selection with consistent numbering
|
|
138
|
+
int choice =
|
|
139
|
+
get_user_choice("Mode [1=Online / 2=Offline] (default 2): ", {1, 2}, 2);
|
|
140
|
+
|
|
141
|
+
if (choice == 1) {
|
|
142
|
+
// Online mode: pick provider
|
|
143
|
+
std::string prompt = "Provider [";
|
|
144
|
+
for (size_t i = 0; i < providers.size(); ++i) {
|
|
145
|
+
if (i > 0)
|
|
146
|
+
prompt += " / ";
|
|
147
|
+
prompt +=
|
|
148
|
+
std::to_string(providers[i].choice) + "=" + providers[i].display_name;
|
|
149
|
+
}
|
|
150
|
+
prompt += "] (default 1): ";
|
|
151
|
+
|
|
152
|
+
std::vector<int> valid_choices;
|
|
153
|
+
for (const auto &p : providers)
|
|
154
|
+
valid_choices.push_back(p.choice);
|
|
155
|
+
|
|
156
|
+
int provider_choice = get_user_choice(prompt, valid_choices, 1);
|
|
157
|
+
|
|
158
|
+
// Find and configure the selected provider
|
|
159
|
+
for (const auto &provider : providers) {
|
|
160
|
+
if (provider.choice == provider_choice) {
|
|
161
|
+
mode_ = provider.mode;
|
|
162
|
+
api_key_ = Utils::Config::get_env_var(provider.env_var);
|
|
163
|
+
if (api_key_.empty()) {
|
|
164
|
+
throw std::runtime_error(provider.env_var + " not set");
|
|
165
|
+
}
|
|
166
|
+
Utils::UI::print_success(provider.display_name);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
// Data-driven offline model configuration
|
|
172
|
+
struct ModelInfo {
|
|
173
|
+
int choice;
|
|
174
|
+
Mode mode;
|
|
175
|
+
std::string display_name;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
std::vector<ModelInfo> models = {
|
|
179
|
+
{1, Agent::Mode::MODE_LLAMA_3B, "llama3.2:3b"},
|
|
180
|
+
{2, Agent::Mode::MODE_LLAMA_LATEST, "llama3.2:latest"},
|
|
181
|
+
{3, Agent::Mode::MODE_LLAMA_31, "llama3.1:latest"}};
|
|
182
|
+
|
|
183
|
+
// Offline mode: pick model
|
|
184
|
+
std::string prompt = "Model [";
|
|
185
|
+
for (size_t i = 0; i < models.size(); ++i) {
|
|
186
|
+
if (i > 0)
|
|
187
|
+
prompt += " / ";
|
|
188
|
+
prompt += std::to_string(models[i].choice) + "=" + models[i].display_name;
|
|
189
|
+
}
|
|
190
|
+
prompt += "] (default 1): ";
|
|
191
|
+
|
|
192
|
+
std::vector<int> valid_choices;
|
|
193
|
+
for (const auto &m : models)
|
|
194
|
+
valid_choices.push_back(m.choice);
|
|
195
|
+
|
|
196
|
+
int model_choice = get_user_choice(prompt, valid_choices, 1);
|
|
197
|
+
|
|
198
|
+
// Find and configure the selected model
|
|
199
|
+
for (const auto &model : models) {
|
|
200
|
+
if (model.choice == model_choice) {
|
|
201
|
+
mode_ = model.mode;
|
|
202
|
+
Utils::UI::print_success(model.display_name);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bool Agent::is_online_mode() const {
|
|
210
|
+
return mode_ == Agent::Mode::MODE_TOGETHER ||
|
|
211
|
+
mode_ == Agent::Mode::MODE_CEREBRAS ||
|
|
212
|
+
mode_ == Agent::Mode::MODE_FIREWORKS ||
|
|
213
|
+
mode_ == Agent::Mode::MODE_GROQ ||
|
|
214
|
+
mode_ == Agent::Mode::MODE_DEEPSEEK ||
|
|
215
|
+
mode_ == Agent::Mode::MODE_OPENAI;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
int Agent::get_user_choice(const std::string &prompt,
|
|
219
|
+
const std::vector<int> &valid_choices,
|
|
220
|
+
int default_choice) {
|
|
221
|
+
while (true) {
|
|
222
|
+
std::cout << prompt;
|
|
223
|
+
std::string input;
|
|
224
|
+
if (!std::getline(std::cin, input)) {
|
|
225
|
+
// EOF -> return default
|
|
226
|
+
return default_choice;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
input = trim_copy(input);
|
|
230
|
+
|
|
231
|
+
// Accept empty line as confirmation of default choice
|
|
232
|
+
if (input.empty())
|
|
233
|
+
return default_choice;
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
int choice = std::stoi(input);
|
|
237
|
+
if (std::find(valid_choices.begin(), valid_choices.end(), choice) !=
|
|
238
|
+
valid_choices.end()) {
|
|
239
|
+
return choice;
|
|
240
|
+
}
|
|
241
|
+
} catch (const std::exception &) {
|
|
242
|
+
// fallthrough -> invalid input, prompt again
|
|
243
|
+
}
|
|
244
|
+
Utils::UI::print_warning("Invalid choice, please try again.");
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
void Agent::process_user_input(const std::string &input) {
|
|
249
|
+
command_count_++;
|
|
250
|
+
std::string trimmed_input = trim_copy(input);
|
|
251
|
+
|
|
252
|
+
// Handle @ file injection commands
|
|
253
|
+
if (trimmed_input.find('@') != std::string::npos) {
|
|
254
|
+
handle_file_injection_command(trimmed_input);
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Handle ! shell commands
|
|
259
|
+
if (trimmed_input.starts_with("!")) {
|
|
260
|
+
handle_shell_command(trimmed_input);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Handle / meta commands
|
|
265
|
+
if (trimmed_input.starts_with("/")) {
|
|
266
|
+
handle_meta_command(trimmed_input);
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Check for direct commands (detect colon)
|
|
271
|
+
if (trimmed_input.find(':') != std::string::npos) {
|
|
272
|
+
handle_direct_command(trimmed_input);
|
|
273
|
+
} else {
|
|
274
|
+
handle_ai_chat(trimmed_input);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
void Agent::handle_direct_command(const std::string &input) {
|
|
279
|
+
std::string result;
|
|
280
|
+
|
|
281
|
+
if (input.starts_with("search:")) {
|
|
282
|
+
std::string query = trim_copy(input.substr(7));
|
|
283
|
+
auto validation = Utils::Validator::validate_search_query(query);
|
|
284
|
+
if (!validation.is_valid) {
|
|
285
|
+
result = "Error: " + validation.error_message;
|
|
286
|
+
} else {
|
|
287
|
+
if (!validation.warnings.empty()) {
|
|
288
|
+
for (const auto &warning : validation.warnings) {
|
|
289
|
+
Utils::UI::print_warning(warning);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
result = Services::WebService::search(query);
|
|
293
|
+
memory_->save_interaction("search:" + query, result);
|
|
294
|
+
}
|
|
295
|
+
} else if (input.starts_with("cmd:")) {
|
|
296
|
+
std::string command = trim_copy(input.substr(4));
|
|
297
|
+
auto validation = Utils::Validator::validate_command_safe(command);
|
|
298
|
+
if (!validation.warnings.empty()) {
|
|
299
|
+
for (const auto &warning : validation.warnings) {
|
|
300
|
+
Utils::UI::print_warning(warning);
|
|
301
|
+
}
|
|
302
|
+
std::cout << "Continue? (y/N): ";
|
|
303
|
+
std::string confirm;
|
|
304
|
+
std::getline(std::cin, confirm);
|
|
305
|
+
if (confirm != "y" && confirm != "Y") {
|
|
306
|
+
result = "Command cancelled by user";
|
|
307
|
+
memory_->save_interaction("cmd:" + command, result);
|
|
308
|
+
std::cout << result << std::endl;
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
result = Services::CommandService::execute(command);
|
|
313
|
+
memory_->save_interaction("cmd:" + command, result);
|
|
314
|
+
} else if (input.starts_with("read:")) {
|
|
315
|
+
std::string params = trim_copy(input.substr(5));
|
|
316
|
+
// Check if it has range parameters: read:filename:start:count
|
|
317
|
+
size_t first_colon = params.find(':');
|
|
318
|
+
if (first_colon != std::string::npos) {
|
|
319
|
+
std::string filename = params.substr(0, first_colon);
|
|
320
|
+
auto validation = Utils::Validator::validate_file_exists(filename);
|
|
321
|
+
if (!validation.is_valid) {
|
|
322
|
+
result = "Error: " + validation.error_message;
|
|
323
|
+
} else {
|
|
324
|
+
std::string range_params = params.substr(first_colon + 1);
|
|
325
|
+
size_t second_colon = range_params.find(':');
|
|
326
|
+
|
|
327
|
+
if (second_colon != std::string::npos) {
|
|
328
|
+
int start_line = std::stoi(range_params.substr(0, second_colon));
|
|
329
|
+
int line_count = std::stoi(range_params.substr(second_colon + 1));
|
|
330
|
+
result = Services::FileService::read_file_range(filename, start_line,
|
|
331
|
+
line_count);
|
|
332
|
+
} else {
|
|
333
|
+
int start_line = std::stoi(range_params);
|
|
334
|
+
result = Services::FileService::read_file_range(filename, start_line);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
} else {
|
|
338
|
+
auto validation = Utils::Validator::validate_file_exists(params);
|
|
339
|
+
if (!validation.is_valid) {
|
|
340
|
+
result = "Error: " + validation.error_message;
|
|
341
|
+
} else {
|
|
342
|
+
result = Services::FileService::read_file(params);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
memory_->save_interaction("read:" + params, result);
|
|
346
|
+
} else if (input.starts_with("write:")) {
|
|
347
|
+
// Format: write:filename content...
|
|
348
|
+
size_t space_pos = input.find(' ', 6); // index 6 is safe for "write:"
|
|
349
|
+
if (space_pos != std::string::npos) {
|
|
350
|
+
std::string filename = trim_copy(input.substr(6, space_pos - 6));
|
|
351
|
+
std::string content = input.substr(space_pos + 1);
|
|
352
|
+
|
|
353
|
+
// Validate the file is writable
|
|
354
|
+
auto validation = Utils::Validator::validate_file_writable(filename);
|
|
355
|
+
if (!validation.is_valid) {
|
|
356
|
+
result = "Error: " + validation.error_message;
|
|
357
|
+
} else {
|
|
358
|
+
result = Services::FileService::write_file(filename, content);
|
|
359
|
+
memory_->save_interaction("write:" + filename, result);
|
|
360
|
+
}
|
|
361
|
+
} else {
|
|
362
|
+
result = "Usage: write:filename content";
|
|
363
|
+
}
|
|
364
|
+
} else if (input.starts_with("replace:")) {
|
|
365
|
+
// Format: replace:filename:old_text:new_text[:expected_count]
|
|
366
|
+
std::string params = input.substr(8);
|
|
367
|
+
std::vector<std::string> parts;
|
|
368
|
+
size_t pos = 0;
|
|
369
|
+
size_t colon_pos;
|
|
370
|
+
|
|
371
|
+
// Split by colons (up to 4 parts)
|
|
372
|
+
while ((colon_pos = params.find(':', pos)) != std::string::npos &&
|
|
373
|
+
parts.size() < 3) {
|
|
374
|
+
parts.push_back(params.substr(pos, colon_pos - pos));
|
|
375
|
+
pos = colon_pos + 1;
|
|
376
|
+
}
|
|
377
|
+
parts.push_back(params.substr(pos)); // Last part
|
|
378
|
+
|
|
379
|
+
if (parts.size() >= 3) {
|
|
380
|
+
const std::string &filename = parts[0];
|
|
381
|
+
const std::string &old_text = parts[1];
|
|
382
|
+
const std::string &new_text = parts[2];
|
|
383
|
+
int expected = (parts.size() > 3) ? std::stoi(parts[3]) : 1;
|
|
384
|
+
|
|
385
|
+
auto edit_result = Services::FileService::replace_text_in_file(
|
|
386
|
+
filename, old_text, new_text, expected);
|
|
387
|
+
result = edit_result.message;
|
|
388
|
+
memory_->save_interaction("replace:" + filename, result);
|
|
389
|
+
} else {
|
|
390
|
+
result = "Usage: replace:filename:old_text:new_text[:expected_count]";
|
|
391
|
+
}
|
|
392
|
+
} else if (input.starts_with("grep:")) {
|
|
393
|
+
// Format: grep:pattern[:directory[:file_filter]]
|
|
394
|
+
std::string params = input.substr(5);
|
|
395
|
+
std::vector<std::string> parts;
|
|
396
|
+
size_t pos = 0;
|
|
397
|
+
size_t colon_pos;
|
|
398
|
+
|
|
399
|
+
while ((colon_pos = params.find(':', pos)) != std::string::npos &&
|
|
400
|
+
parts.size() < 2) {
|
|
401
|
+
parts.push_back(params.substr(pos, colon_pos - pos));
|
|
402
|
+
pos = colon_pos + 1;
|
|
403
|
+
}
|
|
404
|
+
parts.push_back(params.substr(pos));
|
|
405
|
+
|
|
406
|
+
std::string pattern = parts[0];
|
|
407
|
+
std::string directory = (parts.size() > 1) ? parts[1] : ".";
|
|
408
|
+
std::string filter = (parts.size() > 2) ? parts[2] : "*";
|
|
409
|
+
|
|
410
|
+
auto search_results =
|
|
411
|
+
Services::FileService::search_in_directory(directory, pattern, filter);
|
|
412
|
+
|
|
413
|
+
if (search_results.empty()) {
|
|
414
|
+
result = "No matches found for pattern: " + pattern;
|
|
415
|
+
} else {
|
|
416
|
+
result = "Found " + std::to_string(search_results.size()) + " matches:\n";
|
|
417
|
+
for (const auto &match : search_results) {
|
|
418
|
+
result += match.file_path + ":" + std::to_string(match.line_number) +
|
|
419
|
+
": " + match.line_content + "\n";
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
memory_->save_interaction("grep:" + pattern, result);
|
|
423
|
+
} else if (input.starts_with("remember:")) {
|
|
424
|
+
std::string fact = trim_copy(input.substr(9));
|
|
425
|
+
memory_->save_global_fact(fact);
|
|
426
|
+
result = "Remembered: " + fact;
|
|
427
|
+
} else if (input.starts_with("forget")) {
|
|
428
|
+
memory_->clear_global_memory();
|
|
429
|
+
result = "Global memory cleared";
|
|
430
|
+
} else if (input.starts_with("memory")) {
|
|
431
|
+
std::string global_context = memory_->get_global_context();
|
|
432
|
+
result =
|
|
433
|
+
global_context.empty() ? "No global memories stored" : global_context;
|
|
434
|
+
} else if (input.starts_with("clear")) {
|
|
435
|
+
memory_->clear_memory();
|
|
436
|
+
result = "Session memory cleared";
|
|
437
|
+
} else if (input.starts_with("analyze:")) {
|
|
438
|
+
std::string path = trim_copy(input.substr(8));
|
|
439
|
+
if (path.empty()) {
|
|
440
|
+
path = "."; // Current directory
|
|
441
|
+
}
|
|
442
|
+
result = Services::CodebaseService::analyze_structure(path);
|
|
443
|
+
memory_->save_interaction("analyze:" + path, result);
|
|
444
|
+
} else if (input.starts_with("components:")) {
|
|
445
|
+
std::string path = trim_copy(input.substr(11));
|
|
446
|
+
if (path.empty()) {
|
|
447
|
+
path = "."; // Current directory
|
|
448
|
+
}
|
|
449
|
+
result = Services::CodebaseService::find_main_components(path);
|
|
450
|
+
memory_->save_interaction("components:" + path, result);
|
|
451
|
+
} else if (input.starts_with("todos:")) {
|
|
452
|
+
std::string path = trim_copy(input.substr(6));
|
|
453
|
+
if (path.empty()) {
|
|
454
|
+
path = "."; // Current directory
|
|
455
|
+
}
|
|
456
|
+
auto todos = Services::CodebaseService::find_todos(path);
|
|
457
|
+
if (todos.empty()) {
|
|
458
|
+
result = "No task comments found";
|
|
459
|
+
} else {
|
|
460
|
+
result = "Found " + std::to_string(todos.size()) + " task comments:\n";
|
|
461
|
+
for (const auto &todo : todos) {
|
|
462
|
+
result += todo + "\n";
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
memory_->save_interaction("todos:" + path, result);
|
|
466
|
+
} else if (input.starts_with("git:")) {
|
|
467
|
+
std::string params = trim_copy(input.substr(4));
|
|
468
|
+
std::string path = "."; // Default to current directory
|
|
469
|
+
|
|
470
|
+
if (params.find("log") == 0) {
|
|
471
|
+
result = Services::GitService::get_git_log(path, 7);
|
|
472
|
+
} else if (params.find("status") == 0) {
|
|
473
|
+
result = Services::GitService::get_git_status(path);
|
|
474
|
+
} else if (params.find("analyze") == 0) {
|
|
475
|
+
result = Services::GitService::analyze_repository(path);
|
|
476
|
+
} else {
|
|
477
|
+
result = "Usage: git:log, git:status, git:analyze";
|
|
478
|
+
}
|
|
479
|
+
memory_->save_interaction("git:" + params, result);
|
|
480
|
+
} else if (input.starts_with("tree:")) {
|
|
481
|
+
std::string path = trim_copy(input.substr(5));
|
|
482
|
+
if (path.empty()) {
|
|
483
|
+
path = "."; // Current directory
|
|
484
|
+
}
|
|
485
|
+
result = Services::CodebaseService::get_directory_tree(path, 3);
|
|
486
|
+
memory_->save_interaction("tree:" + path, result);
|
|
487
|
+
} else if (input.starts_with("github:")) {
|
|
488
|
+
std::string params = trim_copy(input.substr(7));
|
|
489
|
+
auto parse_repo_spec = [](const std::string &repo_spec, std::string &owner,
|
|
490
|
+
std::string &repo) -> bool {
|
|
491
|
+
size_t slash_pos = repo_spec.find('/');
|
|
492
|
+
if (slash_pos != std::string::npos) {
|
|
493
|
+
owner = repo_spec.substr(0, slash_pos);
|
|
494
|
+
repo = repo_spec.substr(slash_pos + 1);
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
return false;
|
|
498
|
+
};
|
|
499
|
+
if (params.starts_with("repo:")) {
|
|
500
|
+
std::string repo_spec = trim_copy(params.substr(5));
|
|
501
|
+
std::string owner, repo;
|
|
502
|
+
if (parse_repo_spec(repo_spec, owner, repo)) {
|
|
503
|
+
result = Services::GitHubService::get_repo_info(owner, repo);
|
|
504
|
+
} else {
|
|
505
|
+
result = "Usage: github:repo:owner/repo";
|
|
506
|
+
}
|
|
507
|
+
} else if (params.starts_with("issues:")) {
|
|
508
|
+
std::string repo_spec = trim_copy(params.substr(7));
|
|
509
|
+
std::string owner, repo;
|
|
510
|
+
if (parse_repo_spec(repo_spec, owner, repo)) {
|
|
511
|
+
auto issues = Services::GitHubService::get_issues(owner, repo);
|
|
512
|
+
if (issues.empty()) {
|
|
513
|
+
result = "No issues found";
|
|
514
|
+
} else {
|
|
515
|
+
result = "Found " + std::to_string(issues.size()) + " issues:\n";
|
|
516
|
+
for (const auto &issue : issues) {
|
|
517
|
+
result +=
|
|
518
|
+
"#" + std::to_string(issue.number) + ": " + issue.title + "\n";
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
} else {
|
|
522
|
+
result = "Usage: github:issues:owner/repo";
|
|
523
|
+
}
|
|
524
|
+
} else if (params.starts_with("health:")) {
|
|
525
|
+
std::string repo_spec = trim_copy(params.substr(7));
|
|
526
|
+
std::string owner, repo;
|
|
527
|
+
if (parse_repo_spec(repo_spec, owner, repo)) {
|
|
528
|
+
result = Services::GitHubService::run_health_check(owner, repo);
|
|
529
|
+
} else {
|
|
530
|
+
result = "Usage: github:health:owner/repo";
|
|
531
|
+
}
|
|
532
|
+
} else {
|
|
533
|
+
result = "Usage: github:repo:owner/repo, github:issues:owner/repo, "
|
|
534
|
+
"github:health:owner/repo";
|
|
535
|
+
}
|
|
536
|
+
memory_->save_interaction("github:" + params, result);
|
|
537
|
+
} else {
|
|
538
|
+
result = "Unknown command";
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (!result.empty()) {
|
|
542
|
+
std::cout << result << std::endl;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
void Agent::handle_ai_chat(const std::string &input) {
|
|
547
|
+
if (!ai_service_) {
|
|
548
|
+
ai_service_ = std::make_unique<Services::AIService>(mode_, api_key_);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (!ai_service_->is_available()) {
|
|
552
|
+
std::cout << "AI service unavailable\n";
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
std::atomic<bool> done(false);
|
|
557
|
+
std::thread spin([&done]() { Utils::UI::spinner(done); });
|
|
558
|
+
|
|
559
|
+
// Build enhanced context with hierarchical context
|
|
560
|
+
std::string memory_context = memory_->get_context_string();
|
|
561
|
+
std::string hierarchical_context =
|
|
562
|
+
Services::ContextService::load_hierarchical_context(".");
|
|
563
|
+
|
|
564
|
+
std::string full_context = memory_context;
|
|
565
|
+
if (!hierarchical_context.empty()) {
|
|
566
|
+
full_context = hierarchical_context + "\n\n" + memory_context;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
std::string response = ai_service_->chat(input, full_context);
|
|
570
|
+
|
|
571
|
+
done = true;
|
|
572
|
+
if (spin.joinable())
|
|
573
|
+
spin.join();
|
|
574
|
+
|
|
575
|
+
if (!response.empty()) {
|
|
576
|
+
std::cout << response << std::endl;
|
|
577
|
+
memory_->save_interaction(input, response);
|
|
578
|
+
} else {
|
|
579
|
+
std::cout << "No response\n";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
void Agent::handle_file_injection_command(const std::string &input) {
|
|
584
|
+
// Process @ file injections and then send to AI
|
|
585
|
+
std::string processed_input = process_file_injections(input);
|
|
586
|
+
handle_ai_chat(processed_input);
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
void Agent::handle_shell_command(const std::string &input) {
|
|
590
|
+
if (input == "!") {
|
|
591
|
+
// Toggle shell mode
|
|
592
|
+
toggle_shell_mode();
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// Execute shell command
|
|
597
|
+
std::string command = trim_copy(input.substr(1));
|
|
598
|
+
if (command.empty()) {
|
|
599
|
+
std::cout << "Usage: !<command> or ! to toggle shell mode" << std::endl;
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
auto validation = Utils::Validator::validate_command_safe(command);
|
|
604
|
+
if (!validation.warnings.empty()) {
|
|
605
|
+
for (const auto &warning : validation.warnings) {
|
|
606
|
+
Utils::UI::print_warning(warning);
|
|
607
|
+
}
|
|
608
|
+
std::cout << "Continue? (y/N): ";
|
|
609
|
+
std::string confirm;
|
|
610
|
+
std::getline(std::cin, confirm);
|
|
611
|
+
if (confirm != "y" && confirm != "Y") {
|
|
612
|
+
std::cout << "Command cancelled by user" << std::endl;
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
std::string result = Services::CommandService::execute(command);
|
|
618
|
+
std::cout << result << std::endl;
|
|
619
|
+
memory_->save_interaction("!" + command, result);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
void Agent::handle_meta_command(const std::string &input) {
|
|
623
|
+
std::string command = trim_copy(input.substr(1));
|
|
624
|
+
|
|
625
|
+
if (command == "help" || command == "?") {
|
|
626
|
+
show_meta_help();
|
|
627
|
+
} else if (command == "clear") {
|
|
628
|
+
clear_screen();
|
|
629
|
+
} else if (command.starts_with("chat ")) {
|
|
630
|
+
handle_chat_management(command.substr(5));
|
|
631
|
+
} else if (command == "tools") {
|
|
632
|
+
show_available_tools();
|
|
633
|
+
} else if (command == "memory show") {
|
|
634
|
+
show_memory_context();
|
|
635
|
+
} else if (command.starts_with("memory add ")) {
|
|
636
|
+
add_to_memory(command.substr(11));
|
|
637
|
+
} else if (command == "compress") {
|
|
638
|
+
compress_context();
|
|
639
|
+
} else if (command == "stats") {
|
|
640
|
+
show_session_stats();
|
|
641
|
+
} else if (command.starts_with("context ")) {
|
|
642
|
+
handle_context_management(command.substr(8));
|
|
643
|
+
} else if (command.starts_with("files ")) {
|
|
644
|
+
handle_multi_file_command(command.substr(6));
|
|
645
|
+
} else if (command.starts_with("fetch ")) {
|
|
646
|
+
handle_web_fetch_command(command.substr(6));
|
|
647
|
+
} else if (command.starts_with("checkpoint ")) {
|
|
648
|
+
handle_checkpoint_command(command.substr(11));
|
|
649
|
+
} else if (command == "restore") {
|
|
650
|
+
handle_checkpoint_command("list");
|
|
651
|
+
} else if (command.starts_with("restore ")) {
|
|
652
|
+
handle_checkpoint_command("restore " + command.substr(8));
|
|
653
|
+
} else if (command.starts_with("mcp ")) {
|
|
654
|
+
handle_mcp_command(command.substr(4));
|
|
655
|
+
} else if (command.starts_with("theme ")) {
|
|
656
|
+
handle_theme_command(command.substr(6));
|
|
657
|
+
} else if (command.starts_with("auth ")) {
|
|
658
|
+
handle_auth_command(command.substr(5));
|
|
659
|
+
} else if (command.starts_with("sandbox ")) {
|
|
660
|
+
handle_sandbox_command(command.substr(8));
|
|
661
|
+
} else if (command.starts_with("error ")) {
|
|
662
|
+
handle_error_command(command.substr(6));
|
|
663
|
+
} else if (command == "quit" || command == "exit") {
|
|
664
|
+
std::cout << "Goodbye!" << std::endl;
|
|
665
|
+
exit(0);
|
|
666
|
+
} else {
|
|
667
|
+
std::cout << "Unknown meta command: /" << command << std::endl;
|
|
668
|
+
std::cout << "Type /help for available commands." << std::endl;
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
std::string Agent::process_file_injections(const std::string &input) {
|
|
673
|
+
std::string result = input;
|
|
674
|
+
size_t pos = 0;
|
|
675
|
+
|
|
676
|
+
// Find all @ symbols and process file paths
|
|
677
|
+
while ((pos = result.find('@', pos)) != std::string::npos) {
|
|
678
|
+
// Skip if it's escaped or part of an email
|
|
679
|
+
if (pos > 0 && result[pos - 1] == '\\') {
|
|
680
|
+
pos++;
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// Find the end of the path (space, newline, or end of string)
|
|
685
|
+
size_t start = pos + 1;
|
|
686
|
+
size_t end = start;
|
|
687
|
+
|
|
688
|
+
// Handle quoted paths
|
|
689
|
+
bool quoted = false;
|
|
690
|
+
if (start < result.length() && result[start] == '"') {
|
|
691
|
+
quoted = true;
|
|
692
|
+
start++;
|
|
693
|
+
end = result.find('"', start);
|
|
694
|
+
if (end == std::string::npos)
|
|
695
|
+
end = result.length();
|
|
696
|
+
} else {
|
|
697
|
+
// Find end of unquoted path
|
|
698
|
+
while (end < result.length() && result[end] != ' ' &&
|
|
699
|
+
result[end] != '\n' && result[end] != '\t' &&
|
|
700
|
+
result[end] != '\r') {
|
|
701
|
+
end++;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (start < end) {
|
|
706
|
+
std::string path = result.substr(start, end - start);
|
|
707
|
+
std::string file_content = read_file_or_directory(path);
|
|
708
|
+
|
|
709
|
+
// Replace @path with file content
|
|
710
|
+
size_t replace_start = pos;
|
|
711
|
+
size_t replace_end = quoted ? end + 1 : end;
|
|
712
|
+
result.replace(replace_start, replace_end - replace_start, file_content);
|
|
713
|
+
|
|
714
|
+
pos = replace_start + file_content.length();
|
|
715
|
+
} else {
|
|
716
|
+
pos++;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
return result;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
std::string Agent::read_file_or_directory(const std::string &path) {
|
|
724
|
+
try {
|
|
725
|
+
if (!std::filesystem::exists(path)) {
|
|
726
|
+
return "[File not found: " + path + "]";
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (std::filesystem::is_directory(path)) {
|
|
730
|
+
// Use multi-file service for directory reading
|
|
731
|
+
Services::MultiFileOptions options;
|
|
732
|
+
options.max_files = 50; // Reasonable limit for @ injection
|
|
733
|
+
options.max_total_size = 5 * 1024 * 1024; // 5MB limit
|
|
734
|
+
|
|
735
|
+
auto files =
|
|
736
|
+
Services::MultiFileService::read_directory_files(path, options);
|
|
737
|
+
|
|
738
|
+
if (files.empty()) {
|
|
739
|
+
return "[No readable files found in directory: " + path + "]";
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
return Services::MultiFileService::format_multi_file_content(
|
|
743
|
+
files, "Contents of directory: " + path);
|
|
744
|
+
} else {
|
|
745
|
+
// Read single file
|
|
746
|
+
std::ifstream file(path);
|
|
747
|
+
if (!file.is_open()) {
|
|
748
|
+
return "[Could not read file: " + path + "]";
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
std::ostringstream content;
|
|
752
|
+
content << "[Contents of file: " << path << "]\n\n";
|
|
753
|
+
|
|
754
|
+
std::string line;
|
|
755
|
+
int line_count = 0;
|
|
756
|
+
while (std::getline(file, line) && line_count < 1000) {
|
|
757
|
+
content << line << "\n";
|
|
758
|
+
line_count++;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
if (line_count >= 1000) {
|
|
762
|
+
content << "\n[File truncated - too long]";
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return content.str();
|
|
766
|
+
}
|
|
767
|
+
} catch (const std::exception &e) {
|
|
768
|
+
return "[Error reading " + path + ": " + e.what() + "]";
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
void Agent::toggle_shell_mode() {
|
|
773
|
+
shell_mode_ = !shell_mode_;
|
|
774
|
+
if (shell_mode_) {
|
|
775
|
+
std::cout << "Entering shell mode. Type commands directly or '!' to exit."
|
|
776
|
+
<< std::endl;
|
|
777
|
+
} else {
|
|
778
|
+
std::cout << "Exiting shell mode." << std::endl;
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
bool Agent::should_skip_file(const std::string &file_path,
|
|
783
|
+
const std::string &ext) {
|
|
784
|
+
std::vector<std::string> skip_extensions = {
|
|
785
|
+
".exe", ".dll", ".so", ".dylib", ".a", ".lib", ".obj", ".o", ".png",
|
|
786
|
+
".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".svg", ".mp3", ".mp4", ".avi",
|
|
787
|
+
".mov", ".wav", ".pdf", ".zip", ".tar", ".gz", ".7z", ".rar"};
|
|
788
|
+
|
|
789
|
+
for (const auto &skip_ext : skip_extensions) {
|
|
790
|
+
if (ext == skip_ext)
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
if (file_path.find("node_modules") != std::string::npos ||
|
|
795
|
+
file_path.find(".git") != std::string::npos ||
|
|
796
|
+
file_path.find("build") != std::string::npos ||
|
|
797
|
+
file_path.find("dist") != std::string::npos ||
|
|
798
|
+
file_path.find(".cache") != std::string::npos) {
|
|
799
|
+
return true;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
void Agent::show_meta_help() {
|
|
806
|
+
std::cout << "Available meta commands:" << std::endl;
|
|
807
|
+
std::cout << " /help or /? - Show this help" << std::endl;
|
|
808
|
+
std::cout << " /clear - Clear screen" << std::endl;
|
|
809
|
+
std::cout << " /chat save <tag> - Save conversation state" << std::endl;
|
|
810
|
+
std::cout << " /chat resume <tag> - Resume conversation state"
|
|
811
|
+
<< std::endl;
|
|
812
|
+
std::cout << " /chat list - List saved conversations"
|
|
813
|
+
<< std::endl;
|
|
814
|
+
std::cout << " /tools - Show available tools" << std::endl;
|
|
815
|
+
std::cout << " /memory show - Show memory context" << std::endl;
|
|
816
|
+
std::cout << " /memory add <text> - Add to memory" << std::endl;
|
|
817
|
+
std::cout << " /compress - Compress conversation context"
|
|
818
|
+
<< std::endl;
|
|
819
|
+
std::cout << " /stats - Show session statistics" << std::endl;
|
|
820
|
+
std::cout << " /context show - Show hierarchical context"
|
|
821
|
+
<< std::endl;
|
|
822
|
+
std::cout << " /context refresh - Refresh context cache" << std::endl;
|
|
823
|
+
std::cout << " /context create - Create CURSOR.md file"
|
|
824
|
+
<< std::endl;
|
|
825
|
+
std::cout << " /files <patterns> - Read multiple files with patterns"
|
|
826
|
+
<< std::endl;
|
|
827
|
+
std::cout << " /fetch <url> [format] - Fetch web content (text/json/raw)"
|
|
828
|
+
<< std::endl;
|
|
829
|
+
std::cout
|
|
830
|
+
<< " /checkpoint <cmd> - Manage checkpoints (create/list/delete)"
|
|
831
|
+
<< std::endl;
|
|
832
|
+
std::cout << " /restore [id] - List or restore from checkpoint"
|
|
833
|
+
<< std::endl;
|
|
834
|
+
std::cout << " /mcp <cmd> - MCP server management "
|
|
835
|
+
"(servers/tools/resources)"
|
|
836
|
+
<< std::endl;
|
|
837
|
+
std::cout << " /theme <cmd> - Theme management (list/set/preview)"
|
|
838
|
+
<< std::endl;
|
|
839
|
+
std::cout
|
|
840
|
+
<< " /auth <cmd> - Authentication management (providers/keys)"
|
|
841
|
+
<< std::endl;
|
|
842
|
+
std::cout << " /sandbox <cmd> - Sandboxed command execution"
|
|
843
|
+
<< std::endl;
|
|
844
|
+
std::cout << " /error <cmd> - Error management and reporting"
|
|
845
|
+
<< std::endl;
|
|
846
|
+
std::cout << " /github repo:owner/repo - Get repository info" << std::endl;
|
|
847
|
+
std::cout << " /github issues:owner/repo - List repository issues"
|
|
848
|
+
<< std::endl;
|
|
849
|
+
std::cout << " /github health:owner/repo - Run health check" << std::endl;
|
|
850
|
+
std::cout << " /quit or /exit - Exit the program" << std::endl;
|
|
851
|
+
std::cout << std::endl;
|
|
852
|
+
std::cout << "File injection commands:" << std::endl;
|
|
853
|
+
std::cout << " @<path> - Include file/directory content"
|
|
854
|
+
<< std::endl;
|
|
855
|
+
std::cout << " Example: @src/main.cpp What does this code do?" << std::endl;
|
|
856
|
+
std::cout << std::endl;
|
|
857
|
+
std::cout << "Shell commands:" << std::endl;
|
|
858
|
+
std::cout << " !<command> - Execute shell command" << std::endl;
|
|
859
|
+
std::cout << " ! - Toggle shell mode" << std::endl;
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
void Agent::clear_screen() {
|
|
863
|
+
// Clear screen using ANSI escape codes
|
|
864
|
+
std::cout << "\033[2J\033[H" << std::flush;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
void Agent::handle_chat_management(const std::string &command) {
|
|
868
|
+
if (command.starts_with("save ")) {
|
|
869
|
+
std::string tag = trim_copy(command.substr(5));
|
|
870
|
+
if (tag.empty()) {
|
|
871
|
+
std::cout << "Usage: /chat save <tag>" << std::endl;
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
memory_->save_conversation_state(tag);
|
|
875
|
+
std::cout << "Conversation saved as: " << tag << std::endl;
|
|
876
|
+
} else if (command.starts_with("resume ")) {
|
|
877
|
+
std::string tag = trim_copy(command.substr(7));
|
|
878
|
+
if (tag.empty()) {
|
|
879
|
+
std::cout << "Usage: /chat resume <tag>" << std::endl;
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
if (memory_->resume_conversation_state(tag)) {
|
|
883
|
+
std::cout << "Conversation resumed: " << tag << std::endl;
|
|
884
|
+
} else {
|
|
885
|
+
std::cout << "Could not resume conversation: " << tag << std::endl;
|
|
886
|
+
}
|
|
887
|
+
} else if (command == "list") {
|
|
888
|
+
auto conversations = memory_->list_conversation_states();
|
|
889
|
+
if (conversations.empty()) {
|
|
890
|
+
std::cout << "No saved conversations." << std::endl;
|
|
891
|
+
} else {
|
|
892
|
+
std::cout << "Saved conversations:" << std::endl;
|
|
893
|
+
for (const auto &conv : conversations) {
|
|
894
|
+
std::cout << " " << conv << std::endl;
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
} else {
|
|
898
|
+
std::cout << "Usage: /chat [save|resume|list] <tag>" << std::endl;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
void Agent::show_available_tools() {
|
|
903
|
+
std::cout << "Available tools:" << std::endl;
|
|
904
|
+
std::cout << " File Operations:" << std::endl;
|
|
905
|
+
std::cout << " read:file[:start:count] - Read file content"
|
|
906
|
+
<< std::endl;
|
|
907
|
+
std::cout << " write:file content - Write to file" << std::endl;
|
|
908
|
+
std::cout << " replace:file:old:new - Replace text in file"
|
|
909
|
+
<< std::endl;
|
|
910
|
+
std::cout << " grep:pattern[:dir[:ext]] - Search in files" << std::endl;
|
|
911
|
+
std::cout << " Project Analysis:" << std::endl;
|
|
912
|
+
std::cout << " analyze:[path] - Analyze project structure"
|
|
913
|
+
<< std::endl;
|
|
914
|
+
std::cout << " components:[path] - Find main components"
|
|
915
|
+
<< std::endl;
|
|
916
|
+
std::cout << " todos:[path] - Find task comments"
|
|
917
|
+
<< std::endl;
|
|
918
|
+
std::cout << " tree:[path] - Show directory tree"
|
|
919
|
+
<< std::endl;
|
|
920
|
+
std::cout << " Git Operations:" << std::endl;
|
|
921
|
+
std::cout << " git:status - Show git status" << std::endl;
|
|
922
|
+
std::cout << " git:log - Show git log" << std::endl;
|
|
923
|
+
std::cout << " git:analyze - Analyze repository"
|
|
924
|
+
<< std::endl;
|
|
925
|
+
std::cout << " System:" << std::endl;
|
|
926
|
+
std::cout << " cmd:command - Execute shell command"
|
|
927
|
+
<< std::endl;
|
|
928
|
+
std::cout << " search:query - Web search" << std::endl;
|
|
929
|
+
std::cout << " remember:fact - Save to memory" << std::endl;
|
|
930
|
+
std::cout << " memory - Show memories" << std::endl;
|
|
931
|
+
std::cout << " GitHub Operations:" << std::endl;
|
|
932
|
+
std::cout << " github:repo:owner/repo - Get repository info"
|
|
933
|
+
<< std::endl;
|
|
934
|
+
std::cout << " github:issues:owner/repo - List repository issues"
|
|
935
|
+
<< std::endl;
|
|
936
|
+
std::cout << " github:health:owner/repo - Run health check"
|
|
937
|
+
<< std::endl;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
void Agent::show_memory_context() {
|
|
941
|
+
std::string context = memory_->get_context_string();
|
|
942
|
+
if (context.empty()) {
|
|
943
|
+
std::cout << "No memory context available." << std::endl;
|
|
944
|
+
} else {
|
|
945
|
+
std::cout << "Current memory context:" << std::endl;
|
|
946
|
+
std::cout << context << std::endl;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
void Agent::add_to_memory(const std::string &text) {
|
|
951
|
+
if (text.empty()) {
|
|
952
|
+
std::cout << "Usage: /memory add <text>" << std::endl;
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
memory_->save_global_fact(text);
|
|
956
|
+
std::cout << "Added to memory: " << text << std::endl;
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
void Agent::compress_context() {
|
|
960
|
+
try {
|
|
961
|
+
std::cout << "Analyzing conversation context for compression..."
|
|
962
|
+
<< std::endl;
|
|
963
|
+
|
|
964
|
+
// Get compressible content from memory
|
|
965
|
+
std::string compressible_content = memory_->get_compressible_context();
|
|
966
|
+
|
|
967
|
+
if (compressible_content.empty()) {
|
|
968
|
+
std::cout << "Not enough conversation history to compress (need at least "
|
|
969
|
+
"5 interactions)."
|
|
970
|
+
<< std::endl;
|
|
971
|
+
return;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
// Create compression prompt
|
|
975
|
+
std::string compression_prompt =
|
|
976
|
+
"Please create a concise summary of the following conversation "
|
|
977
|
+
"history. "
|
|
978
|
+
"Preserve key information, decisions made, important facts discovered, "
|
|
979
|
+
"and context that would be needed for future interactions. "
|
|
980
|
+
"Focus on actionable information and maintain continuity. Keep the "
|
|
981
|
+
"summary under 500 words.\n\n"
|
|
982
|
+
"Conversation History:\n" +
|
|
983
|
+
compressible_content;
|
|
984
|
+
|
|
985
|
+
std::cout << "Generating AI-powered summary..." << std::endl;
|
|
986
|
+
|
|
987
|
+
// Use AI service to generate summary
|
|
988
|
+
std::string summary = ai_service_->chat(compression_prompt, "");
|
|
989
|
+
|
|
990
|
+
if (summary.empty()) {
|
|
991
|
+
std::cout << "Failed to generate compression summary." << std::endl;
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// Apply compression to memory
|
|
996
|
+
memory_->compress_memory(summary);
|
|
997
|
+
|
|
998
|
+
std::cout << " Context successfully compressed!" << std::endl;
|
|
999
|
+
std::cout << "Original interactions have been summarized and recent "
|
|
1000
|
+
"context preserved."
|
|
1001
|
+
<< std::endl;
|
|
1002
|
+
std::cout << "Backup created automatically for safety." << std::endl;
|
|
1003
|
+
|
|
1004
|
+
} catch (const std::exception &e) {
|
|
1005
|
+
std::cout << "Error during context compression: " << e.what() << std::endl;
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
void Agent::show_session_stats() {
|
|
1010
|
+
std::string mode_str =
|
|
1011
|
+
(mode_ == Agent::Mode::MODE_TOGETHER ? "Together AI"
|
|
1012
|
+
: mode_ == Agent::Mode::MODE_CEREBRAS ? "Cerebras"
|
|
1013
|
+
: mode_ == Agent::Mode::MODE_FIREWORKS ? "Fireworks"
|
|
1014
|
+
: mode_ == Agent::Mode::MODE_GROQ ? "Groq"
|
|
1015
|
+
: mode_ == Agent::Mode::MODE_DEEPSEEK ? "DeepSeek"
|
|
1016
|
+
: mode_ == Agent::Mode::MODE_OPENAI ? "OpenAI"
|
|
1017
|
+
: mode_ == Agent::Mode::MODE_LLAMA_3B ? "Llama 3B"
|
|
1018
|
+
: mode_ == Agent::Mode::MODE_LLAMA_LATEST ? "Llama Latest"
|
|
1019
|
+
: mode_ == Agent::Mode::MODE_LLAMA_31 ? "Llama 3.1"
|
|
1020
|
+
: "Local Ollama");
|
|
1021
|
+
std::cout << "Session Statistics:" << std::endl;
|
|
1022
|
+
std::cout << " Mode: " << mode_str << std::endl;
|
|
1023
|
+
std::cout << " Shell Mode: " << (shell_mode_ ? "Active" : "Inactive")
|
|
1024
|
+
<< std::endl;
|
|
1025
|
+
std::cout << " Commands Processed: " << command_count_ << std::endl;
|
|
1026
|
+
std::cout << " Token Usage: " << token_usage_ << std::endl;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
void Agent::handle_context_management(const std::string &command) {
|
|
1030
|
+
if (command == "show") {
|
|
1031
|
+
std::string context =
|
|
1032
|
+
Services::ContextService::load_hierarchical_context(".");
|
|
1033
|
+
if (context.empty()) {
|
|
1034
|
+
std::cout << "No hierarchical context found." << std::endl;
|
|
1035
|
+
std::cout << "Create a CURSOR.md file with /context create"
|
|
1036
|
+
<< std::endl;
|
|
1037
|
+
} else {
|
|
1038
|
+
std::cout << "Hierarchical Context:" << std::endl;
|
|
1039
|
+
std::cout << context << std::endl;
|
|
1040
|
+
}
|
|
1041
|
+
} else if (command == "refresh") {
|
|
1042
|
+
Services::ContextService::refresh_context_cache();
|
|
1043
|
+
std::cout << "Context cache refreshed." << std::endl;
|
|
1044
|
+
} else if (command == "create") {
|
|
1045
|
+
if (Services::ContextService::create_context_file(".")) {
|
|
1046
|
+
std::cout << "Created CURSOR.md in current directory." << std::endl;
|
|
1047
|
+
std::cout << "Edit it to provide context for this project." << std::endl;
|
|
1048
|
+
} else {
|
|
1049
|
+
std::cout << "Could not create CURSOR.md (file may already exist)."
|
|
1050
|
+
<< std::endl;
|
|
1051
|
+
}
|
|
1052
|
+
} else {
|
|
1053
|
+
std::cout << "Usage: /context [show|refresh|create]" << std::endl;
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
void Agent::handle_multi_file_command(const std::string &command) {
|
|
1058
|
+
if (command.empty()) {
|
|
1059
|
+
std::cout << "Usage: /files <path1> [path2] [--include pattern] [--exclude "
|
|
1060
|
+
"pattern]"
|
|
1061
|
+
<< std::endl;
|
|
1062
|
+
std::cout << "Examples:" << std::endl;
|
|
1063
|
+
std::cout << " /files src/" << std::endl;
|
|
1064
|
+
std::cout << " /files . --include *.cpp --include *.h" << std::endl;
|
|
1065
|
+
std::cout << " /files src/ --exclude *.log --exclude build/*" << std::endl;
|
|
1066
|
+
return;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
// Parse command arguments
|
|
1070
|
+
std::vector<std::string> paths;
|
|
1071
|
+
Services::MultiFileOptions options;
|
|
1072
|
+
|
|
1073
|
+
std::istringstream iss(command);
|
|
1074
|
+
std::string token;
|
|
1075
|
+
std::string current_flag;
|
|
1076
|
+
|
|
1077
|
+
while (iss >> token) {
|
|
1078
|
+
if (token == "--include") {
|
|
1079
|
+
current_flag = "include";
|
|
1080
|
+
} else if (token == "--exclude") {
|
|
1081
|
+
current_flag = "exclude";
|
|
1082
|
+
} else if (token == "--no-gitignore") {
|
|
1083
|
+
options.respect_gitignore = false;
|
|
1084
|
+
current_flag.clear();
|
|
1085
|
+
} else if (token == "--no-recursive") {
|
|
1086
|
+
options.recursive = false;
|
|
1087
|
+
current_flag.clear();
|
|
1088
|
+
} else {
|
|
1089
|
+
if (current_flag == "include") {
|
|
1090
|
+
options.include_patterns.push_back(token);
|
|
1091
|
+
} else if (current_flag == "exclude") {
|
|
1092
|
+
options.exclude_patterns.push_back(token);
|
|
1093
|
+
} else {
|
|
1094
|
+
paths.push_back(token);
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
if (paths.empty()) {
|
|
1100
|
+
paths.push_back("."); // Default to current directory
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// Read files
|
|
1104
|
+
auto files = Services::MultiFileService::read_many_files(paths, options);
|
|
1105
|
+
|
|
1106
|
+
if (files.empty()) {
|
|
1107
|
+
std::cout << "No files found matching the criteria." << std::endl;
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
// Format and display
|
|
1112
|
+
std::string formatted = Services::MultiFileService::format_multi_file_content(
|
|
1113
|
+
files, "Multi-file read result");
|
|
1114
|
+
|
|
1115
|
+
std::cout << formatted << std::endl;
|
|
1116
|
+
|
|
1117
|
+
// Save to memory for AI context
|
|
1118
|
+
memory_->save_interaction("/files " + command, formatted);
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
void Agent::handle_web_fetch_command(const std::string &command) {
|
|
1122
|
+
if (command.empty()) {
|
|
1123
|
+
std::cout << "Usage: /fetch <url> [options]" << std::endl;
|
|
1124
|
+
std::cout << "Options:" << std::endl;
|
|
1125
|
+
std::cout << " text - Extract text content from HTML" << std::endl;
|
|
1126
|
+
std::cout << " json - Parse and format JSON response" << std::endl;
|
|
1127
|
+
std::cout << " raw - Return raw response content" << std::endl;
|
|
1128
|
+
std::cout << "Examples:" << std::endl;
|
|
1129
|
+
std::cout << " /fetch https://api.github.com/users/octocat json"
|
|
1130
|
+
<< std::endl;
|
|
1131
|
+
std::cout << " /fetch https://example.com text" << std::endl;
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
std::istringstream iss(command);
|
|
1136
|
+
std::string url, format = "text";
|
|
1137
|
+
iss >> url >> format;
|
|
1138
|
+
|
|
1139
|
+
if (url.empty()) {
|
|
1140
|
+
std::cout << "Error: URL is required" << std::endl;
|
|
1141
|
+
return;
|
|
1142
|
+
}
|
|
1143
|
+
|
|
1144
|
+
if (!Services::WebService::is_valid_url(url)) {
|
|
1145
|
+
std::cout << "Error: Invalid URL format. Use http:// or https://"
|
|
1146
|
+
<< std::endl;
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
std::cout << "Fetching: " << url << std::endl;
|
|
1151
|
+
|
|
1152
|
+
std::string result;
|
|
1153
|
+
if (format == "json") {
|
|
1154
|
+
result = Services::WebService::fetch_json(url);
|
|
1155
|
+
} else if (format == "raw") {
|
|
1156
|
+
auto response = Services::WebService::fetch_url(url);
|
|
1157
|
+
if (response.success) {
|
|
1158
|
+
result = "Status: " + std::to_string(response.status_code) + "\n";
|
|
1159
|
+
result += "Content-Type: " + response.content_type + "\n\n";
|
|
1160
|
+
result += response.content;
|
|
1161
|
+
if (result.length() > MAX_RESPONSE_LENGTH) {
|
|
1162
|
+
result = result.substr(0, MAX_RESPONSE_LENGTH) +
|
|
1163
|
+
"\n\n[Content truncated - showing first " +
|
|
1164
|
+
std::to_string(MAX_RESPONSE_LENGTH) + " characters]";
|
|
1165
|
+
}
|
|
1166
|
+
} else {
|
|
1167
|
+
result = "Error: " + response.error_message;
|
|
1168
|
+
}
|
|
1169
|
+
} else { // default to text
|
|
1170
|
+
result = Services::WebService::fetch_text(url);
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
// Process the fetched content with AI if needed
|
|
1174
|
+
if (result.length() > 1000) { // Only process large content with AI
|
|
1175
|
+
std::atomic<bool> done(false);
|
|
1176
|
+
std::thread spin([&done]() { Utils::UI::spinner(done); });
|
|
1177
|
+
|
|
1178
|
+
std::string context = memory_->get_context_string();
|
|
1179
|
+
std::string ai_prompt = "Summarize the following content:\n\n" + result;
|
|
1180
|
+
std::string response = ai_service_->chat(ai_prompt, context);
|
|
1181
|
+
|
|
1182
|
+
done = true;
|
|
1183
|
+
if (spin.joinable())
|
|
1184
|
+
spin.join();
|
|
1185
|
+
|
|
1186
|
+
if (!response.empty()) {
|
|
1187
|
+
std::cout << "\nAI Summary:\n" << response << std::endl;
|
|
1188
|
+
memory_->save_interaction("web_fetch_summary", response);
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
std::cout << result << std::endl;
|
|
1193
|
+
|
|
1194
|
+
// Save to memory for AI context
|
|
1195
|
+
memory_->save_interaction("/fetch " + command, result);
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
void Agent::handle_checkpoint_command(const std::string &command) {
|
|
1199
|
+
if (command.empty() || command == "help") {
|
|
1200
|
+
std::cout << "Checkpoint commands:" << std::endl;
|
|
1201
|
+
std::cout
|
|
1202
|
+
<< " /checkpoint create <name> [description] - Create new checkpoint"
|
|
1203
|
+
<< std::endl;
|
|
1204
|
+
std::cout
|
|
1205
|
+
<< " /checkpoint list - List all checkpoints"
|
|
1206
|
+
<< std::endl;
|
|
1207
|
+
std::cout
|
|
1208
|
+
<< " /checkpoint info <id> - Show checkpoint details"
|
|
1209
|
+
<< std::endl;
|
|
1210
|
+
std::cout << " /checkpoint delete <id> - Delete checkpoint"
|
|
1211
|
+
<< std::endl;
|
|
1212
|
+
std::cout << " /checkpoint cleanup [count] - Keep only N "
|
|
1213
|
+
"recent checkpoints"
|
|
1214
|
+
<< std::endl;
|
|
1215
|
+
std::cout << " /restore - List checkpoints "
|
|
1216
|
+
"for restore"
|
|
1217
|
+
<< std::endl;
|
|
1218
|
+
std::cout
|
|
1219
|
+
<< " /restore <id> - Restore from checkpoint"
|
|
1220
|
+
<< std::endl;
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
std::istringstream iss(command);
|
|
1225
|
+
std::string action;
|
|
1226
|
+
iss >> action;
|
|
1227
|
+
|
|
1228
|
+
try {
|
|
1229
|
+
if (action == "create") {
|
|
1230
|
+
std::string name, description;
|
|
1231
|
+
iss >> name;
|
|
1232
|
+
std::getline(iss, description);
|
|
1233
|
+
if (!description.empty() && description[0] == ' ') {
|
|
1234
|
+
description = description.substr(1); // Remove leading space
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
if (name.empty()) {
|
|
1238
|
+
std::cout << "Error: Checkpoint name is required" << std::endl;
|
|
1239
|
+
std::cout << "Usage: /checkpoint create <name> [description]"
|
|
1240
|
+
<< std::endl;
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
std::cout << "Creating checkpoint '" << name << "'..." << std::endl;
|
|
1245
|
+
std::string checkpoint_id =
|
|
1246
|
+
Services::CheckpointService::create_checkpoint(name, description);
|
|
1247
|
+
std::cout << " Checkpoint created with ID: " << checkpoint_id
|
|
1248
|
+
<< std::endl;
|
|
1249
|
+
|
|
1250
|
+
} else if (action == "list") {
|
|
1251
|
+
auto checkpoints = Services::CheckpointService::list_checkpoints();
|
|
1252
|
+
if (checkpoints.empty()) {
|
|
1253
|
+
std::cout << "No checkpoints found." << std::endl;
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
|
|
1257
|
+
std::cout << "Available checkpoints:" << std::endl;
|
|
1258
|
+
for (const auto &cp : checkpoints) {
|
|
1259
|
+
std::cout << " " << cp.id << " - " << cp.name << std::endl;
|
|
1260
|
+
std::cout << " Created: " << cp.timestamp << std::endl;
|
|
1261
|
+
if (!cp.description.empty()) {
|
|
1262
|
+
std::cout << " Description: " << cp.description << std::endl;
|
|
1263
|
+
}
|
|
1264
|
+
std::cout << " Files: " << cp.backed_up_files.size() << " ("
|
|
1265
|
+
<< (cp.total_size / 1024) << " KB)" << std::endl;
|
|
1266
|
+
std::cout << std::endl;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
} else if (action == "info") {
|
|
1270
|
+
std::string checkpoint_id;
|
|
1271
|
+
iss >> checkpoint_id;
|
|
1272
|
+
if (checkpoint_id.empty()) {
|
|
1273
|
+
std::cout << "Error: Checkpoint ID is required" << std::endl;
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
auto info =
|
|
1278
|
+
Services::CheckpointService::get_checkpoint_info(checkpoint_id);
|
|
1279
|
+
std::cout << "Checkpoint Details:" << std::endl;
|
|
1280
|
+
std::cout << " ID: " << info.id << std::endl;
|
|
1281
|
+
std::cout << " Name: " << info.name << std::endl;
|
|
1282
|
+
std::cout << " Created: " << info.timestamp << std::endl;
|
|
1283
|
+
if (!info.description.empty()) {
|
|
1284
|
+
std::cout << " Description: " << info.description << std::endl;
|
|
1285
|
+
}
|
|
1286
|
+
std::cout << " Total Size: " << (info.total_size / 1024) << " KB"
|
|
1287
|
+
<< std::endl;
|
|
1288
|
+
std::cout << " Files (" << info.backed_up_files.size()
|
|
1289
|
+
<< "):" << std::endl;
|
|
1290
|
+
for (const auto &file : info.backed_up_files) {
|
|
1291
|
+
std::cout << " " << file << std::endl;
|
|
1292
|
+
}
|
|
1293
|
+
|
|
1294
|
+
} else if (action == "delete") {
|
|
1295
|
+
std::string checkpoint_id;
|
|
1296
|
+
iss >> checkpoint_id;
|
|
1297
|
+
if (checkpoint_id.empty()) {
|
|
1298
|
+
std::cout << "Error: Checkpoint ID is required" << std::endl;
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
if (Services::CheckpointService::delete_checkpoint(checkpoint_id)) {
|
|
1303
|
+
std::cout << " Checkpoint " << checkpoint_id << " deleted" << std::endl;
|
|
1304
|
+
} else {
|
|
1305
|
+
std::cout << "Error: Failed to delete checkpoint " << checkpoint_id
|
|
1306
|
+
<< std::endl;
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
} else if (action == "cleanup") {
|
|
1310
|
+
int keep_count = 10;
|
|
1311
|
+
iss >> keep_count;
|
|
1312
|
+
if (keep_count < 1)
|
|
1313
|
+
keep_count = 10;
|
|
1314
|
+
|
|
1315
|
+
Services::CheckpointService::cleanup_old_checkpoints(keep_count);
|
|
1316
|
+
std::cout << " Cleaned up old checkpoints, keeping " << keep_count
|
|
1317
|
+
<< " most recent" << std::endl;
|
|
1318
|
+
|
|
1319
|
+
} else if (action == "restore") {
|
|
1320
|
+
std::string checkpoint_id;
|
|
1321
|
+
iss >> checkpoint_id;
|
|
1322
|
+
|
|
1323
|
+
if (checkpoint_id.empty()) {
|
|
1324
|
+
// List checkpoints for selection
|
|
1325
|
+
auto checkpoints = Services::CheckpointService::list_checkpoints();
|
|
1326
|
+
if (checkpoints.empty()) {
|
|
1327
|
+
std::cout << "No checkpoints available for restore." << std::endl;
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
|
|
1331
|
+
std::cout << "Available checkpoints for restore:" << std::endl;
|
|
1332
|
+
for (const auto &cp : checkpoints) {
|
|
1333
|
+
std::cout << " " << cp.id << " - " << cp.name << " (" << cp.timestamp
|
|
1334
|
+
<< ")" << std::endl;
|
|
1335
|
+
}
|
|
1336
|
+
std::cout << std::endl << "Use: /restore <checkpoint_id>" << std::endl;
|
|
1337
|
+
return;
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
std::cout << "Restoring from checkpoint " << checkpoint_id << "..."
|
|
1341
|
+
<< std::endl;
|
|
1342
|
+
std::cout
|
|
1343
|
+
<< "Note: A backup will be created automatically before restore."
|
|
1344
|
+
<< std::endl;
|
|
1345
|
+
|
|
1346
|
+
Services::RestoreOptions options;
|
|
1347
|
+
options.create_backup_before_restore = true;
|
|
1348
|
+
options.restore_memory = true;
|
|
1349
|
+
options.restore_files = true;
|
|
1350
|
+
|
|
1351
|
+
if (Services::CheckpointService::restore_checkpoint(checkpoint_id,
|
|
1352
|
+
options)) {
|
|
1353
|
+
std::cout << " Successfully restored from checkpoint " << checkpoint_id
|
|
1354
|
+
<< std::endl;
|
|
1355
|
+
std::cout << "Note: You may need to restart the application to see all "
|
|
1356
|
+
"changes."
|
|
1357
|
+
<< std::endl;
|
|
1358
|
+
} else {
|
|
1359
|
+
std::cout << "Error: Failed to restore from checkpoint "
|
|
1360
|
+
<< checkpoint_id << std::endl;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
} else {
|
|
1364
|
+
std::cout << "Unknown checkpoint command: " << action << std::endl;
|
|
1365
|
+
std::cout << "Use '/checkpoint help' for available commands."
|
|
1366
|
+
<< std::endl;
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
} catch (const std::exception &e) {
|
|
1370
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
// Save to memory for AI context
|
|
1374
|
+
memory_->save_interaction("/checkpoint " + command,
|
|
1375
|
+
"Checkpoint command executed");
|
|
1376
|
+
}
|
|
1377
|
+
|
|
1378
|
+
void Agent::handle_mcp_command(const std::string &command) {
|
|
1379
|
+
if (command.empty() || command == "help") {
|
|
1380
|
+
std::cout << "MCP (Model Context Protocol) commands:" << std::endl;
|
|
1381
|
+
std::cout << " /mcp servers - List registered "
|
|
1382
|
+
"MCP servers"
|
|
1383
|
+
<< std::endl;
|
|
1384
|
+
std::cout << " /mcp status <server> - Show server status"
|
|
1385
|
+
<< std::endl;
|
|
1386
|
+
std::cout << " /mcp start <server> - Start MCP server"
|
|
1387
|
+
<< std::endl;
|
|
1388
|
+
std::cout << " /mcp stop <server> - Stop MCP server"
|
|
1389
|
+
<< std::endl;
|
|
1390
|
+
std::cout
|
|
1391
|
+
<< " /mcp resources <server> - List server resources"
|
|
1392
|
+
<< std::endl;
|
|
1393
|
+
std::cout << " /mcp read <server> <uri> - Read resource "
|
|
1394
|
+
"from server"
|
|
1395
|
+
<< std::endl;
|
|
1396
|
+
std::cout << " /mcp tools <server> - List server tools"
|
|
1397
|
+
<< std::endl;
|
|
1398
|
+
std::cout << " /mcp call <server> <tool> [args] - Call server tool"
|
|
1399
|
+
<< std::endl;
|
|
1400
|
+
std::cout
|
|
1401
|
+
<< " /mcp prompts <server> - List server prompts"
|
|
1402
|
+
<< std::endl;
|
|
1403
|
+
std::cout << " /mcp prompt <server> <name> [args] - Get server prompt"
|
|
1404
|
+
<< std::endl;
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
std::istringstream iss(command);
|
|
1409
|
+
std::string action;
|
|
1410
|
+
iss >> action;
|
|
1411
|
+
|
|
1412
|
+
try {
|
|
1413
|
+
// Initialize MCP service configuration
|
|
1414
|
+
Services::MCPService::load_server_config();
|
|
1415
|
+
|
|
1416
|
+
if (action == "servers") {
|
|
1417
|
+
auto servers = Services::MCPService::list_mcp_servers();
|
|
1418
|
+
if (servers.empty()) {
|
|
1419
|
+
std::cout << "No MCP servers registered." << std::endl;
|
|
1420
|
+
std::cout
|
|
1421
|
+
<< "Default servers (filesystem, git) will be added automatically."
|
|
1422
|
+
<< std::endl;
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
std::cout << "Registered MCP servers:" << std::endl;
|
|
1427
|
+
for (const auto &server_name : servers) {
|
|
1428
|
+
auto server = Services::MCPService::get_mcp_server(server_name);
|
|
1429
|
+
std::string status =
|
|
1430
|
+
Services::MCPService::get_server_status(server_name);
|
|
1431
|
+
std::cout << " " << server_name << " - " << status << std::endl;
|
|
1432
|
+
std::cout << " Executable: " << server.executable;
|
|
1433
|
+
for (const auto &arg : server.args) {
|
|
1434
|
+
std::cout << " " << arg;
|
|
1435
|
+
}
|
|
1436
|
+
std::cout << std::endl;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
} else if (action == "status") {
|
|
1440
|
+
std::string server_name;
|
|
1441
|
+
iss >> server_name;
|
|
1442
|
+
if (server_name.empty()) {
|
|
1443
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
std::string status = Services::MCPService::get_server_status(server_name);
|
|
1448
|
+
std::cout << "Server '" << server_name << "' status: " << status
|
|
1449
|
+
<< std::endl;
|
|
1450
|
+
|
|
1451
|
+
} else if (action == "start") {
|
|
1452
|
+
std::string server_name;
|
|
1453
|
+
iss >> server_name;
|
|
1454
|
+
if (server_name.empty()) {
|
|
1455
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1459
|
+
std::cout << "Starting MCP server '" << server_name << "'..."
|
|
1460
|
+
<< std::endl;
|
|
1461
|
+
if (Services::MCPService::is_server_running(server_name)) {
|
|
1462
|
+
std::cout << "Server is already running." << std::endl;
|
|
1463
|
+
} else {
|
|
1464
|
+
// This would start the actual server in a full implementation
|
|
1465
|
+
std::cout << " MCP server '" << server_name << "' started (simulated)"
|
|
1466
|
+
<< std::endl;
|
|
1467
|
+
std::cout << "Note: Full MCP server integration requires process "
|
|
1468
|
+
"management implementation."
|
|
1469
|
+
<< std::endl;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
} else if (action == "stop") {
|
|
1473
|
+
std::string server_name;
|
|
1474
|
+
iss >> server_name;
|
|
1475
|
+
if (server_name.empty()) {
|
|
1476
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1477
|
+
return;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
std::cout << "Stopping MCP server '" << server_name << "'..."
|
|
1481
|
+
<< std::endl;
|
|
1482
|
+
std::cout << " MCP server '" << server_name << "' stopped (simulated)"
|
|
1483
|
+
<< std::endl;
|
|
1484
|
+
|
|
1485
|
+
} else if (action == "resources") {
|
|
1486
|
+
std::string server_name;
|
|
1487
|
+
iss >> server_name;
|
|
1488
|
+
if (server_name.empty()) {
|
|
1489
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1490
|
+
return;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
auto resources = Services::MCPService::list_resources(server_name);
|
|
1494
|
+
if (resources.empty()) {
|
|
1495
|
+
std::cout << "No resources available from server '" << server_name
|
|
1496
|
+
<< "'" << std::endl;
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
|
|
1500
|
+
std::cout << "Resources from server '" << server_name
|
|
1501
|
+
<< "':" << std::endl;
|
|
1502
|
+
for (const auto &resource : resources) {
|
|
1503
|
+
std::cout << " " << resource.uri << " - " << resource.name
|
|
1504
|
+
<< std::endl;
|
|
1505
|
+
if (!resource.description.empty()) {
|
|
1506
|
+
std::cout << " Description: " << resource.description << std::endl;
|
|
1507
|
+
}
|
|
1508
|
+
if (!resource.mime_type.empty()) {
|
|
1509
|
+
std::cout << " Type: " << resource.mime_type << std::endl;
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
|
|
1513
|
+
} else if (action == "read") {
|
|
1514
|
+
std::string server_name, uri;
|
|
1515
|
+
iss >> server_name >> uri;
|
|
1516
|
+
if (server_name.empty() || uri.empty()) {
|
|
1517
|
+
std::cout << "Error: Server name and URI are required" << std::endl;
|
|
1518
|
+
return;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
std::cout << "Reading resource '" << uri << "' from server '"
|
|
1522
|
+
<< server_name << "'..." << std::endl;
|
|
1523
|
+
std::string content =
|
|
1524
|
+
Services::MCPService::read_resource(server_name, uri);
|
|
1525
|
+
std::cout << content << std::endl;
|
|
1526
|
+
|
|
1527
|
+
} else if (action == "tools") {
|
|
1528
|
+
std::string server_name;
|
|
1529
|
+
iss >> server_name;
|
|
1530
|
+
if (server_name.empty()) {
|
|
1531
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1532
|
+
return;
|
|
1533
|
+
}
|
|
1534
|
+
|
|
1535
|
+
auto tools = Services::MCPService::list_tools(server_name);
|
|
1536
|
+
if (tools.empty()) {
|
|
1537
|
+
std::cout << "No tools available from server '" << server_name << "'"
|
|
1538
|
+
<< std::endl;
|
|
1539
|
+
return;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
std::cout << "Tools from server '" << server_name << "':" << std::endl;
|
|
1543
|
+
for (const auto &tool : tools) {
|
|
1544
|
+
std::cout << " " << tool.name << " - " << tool.description
|
|
1545
|
+
<< std::endl;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
} else if (action == "call") {
|
|
1549
|
+
std::string server_name, tool_name, args_str;
|
|
1550
|
+
iss >> server_name >> tool_name;
|
|
1551
|
+
std::getline(iss, args_str);
|
|
1552
|
+
if (!args_str.empty() && args_str[0] == ' ') {
|
|
1553
|
+
args_str = args_str.substr(1); // Remove leading space
|
|
1554
|
+
}
|
|
1555
|
+
|
|
1556
|
+
if (server_name.empty() || tool_name.empty()) {
|
|
1557
|
+
std::cout << "Error: Server name and tool name are required"
|
|
1558
|
+
<< std::endl;
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1561
|
+
|
|
1562
|
+
nlohmann::json args = nlohmann::json::object();
|
|
1563
|
+
if (!args_str.empty()) {
|
|
1564
|
+
try {
|
|
1565
|
+
args = nlohmann::json::parse(args_str);
|
|
1566
|
+
} catch (const std::exception &) {
|
|
1567
|
+
// If not valid JSON, treat as simple string argument
|
|
1568
|
+
args["input"] = args_str;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
std::cout << "Calling tool '" << tool_name << "' on server '"
|
|
1573
|
+
<< server_name << "'..." << std::endl;
|
|
1574
|
+
auto result =
|
|
1575
|
+
Services::MCPService::call_tool(server_name, tool_name, args);
|
|
1576
|
+
std::cout << result.dump(2) << std::endl;
|
|
1577
|
+
|
|
1578
|
+
} else if (action == "prompts") {
|
|
1579
|
+
std::string server_name;
|
|
1580
|
+
iss >> server_name;
|
|
1581
|
+
if (server_name.empty()) {
|
|
1582
|
+
std::cout << "Error: Server name is required" << std::endl;
|
|
1583
|
+
return;
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
auto prompts = Services::MCPService::list_prompts(server_name);
|
|
1587
|
+
if (prompts.empty()) {
|
|
1588
|
+
std::cout << "No prompts available from server '" << server_name << "'"
|
|
1589
|
+
<< std::endl;
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
|
|
1593
|
+
std::cout << "Prompts from server '" << server_name << "':" << std::endl;
|
|
1594
|
+
for (const auto &prompt : prompts) {
|
|
1595
|
+
std::cout << " " << prompt.name << " - " << prompt.description
|
|
1596
|
+
<< std::endl;
|
|
1597
|
+
if (!prompt.arguments.empty()) {
|
|
1598
|
+
std::cout << " Arguments: ";
|
|
1599
|
+
for (size_t i = 0; i < prompt.arguments.size(); ++i) {
|
|
1600
|
+
if (i > 0)
|
|
1601
|
+
std::cout << ", ";
|
|
1602
|
+
std::cout << prompt.arguments[i];
|
|
1603
|
+
}
|
|
1604
|
+
std::cout << std::endl;
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
} else if (action == "prompt") {
|
|
1609
|
+
std::string server_name, prompt_name, args_str;
|
|
1610
|
+
iss >> server_name >> prompt_name;
|
|
1611
|
+
std::getline(iss, args_str);
|
|
1612
|
+
if (!args_str.empty() && args_str[0] == ' ') {
|
|
1613
|
+
args_str = args_str.substr(1); // Remove leading space
|
|
1614
|
+
}
|
|
1615
|
+
|
|
1616
|
+
if (server_name.empty() || prompt_name.empty()) {
|
|
1617
|
+
std::cout << "Error: Server name and prompt name are required"
|
|
1618
|
+
<< std::endl;
|
|
1619
|
+
return;
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
nlohmann::json args = nlohmann::json::object();
|
|
1623
|
+
if (!args_str.empty()) {
|
|
1624
|
+
try {
|
|
1625
|
+
args = nlohmann::json::parse(args_str);
|
|
1626
|
+
} catch (const std::exception &) {
|
|
1627
|
+
// If not valid JSON, treat as simple string argument
|
|
1628
|
+
args["input"] = args_str;
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
std::cout << "Getting prompt '" << prompt_name << "' from server '"
|
|
1633
|
+
<< server_name << "'..." << std::endl;
|
|
1634
|
+
std::string prompt_text =
|
|
1635
|
+
Services::MCPService::get_prompt(server_name, prompt_name, args);
|
|
1636
|
+
std::cout << prompt_text << std::endl;
|
|
1637
|
+
|
|
1638
|
+
} else {
|
|
1639
|
+
std::cout << "Unknown MCP command: " << action << std::endl;
|
|
1640
|
+
std::cout << "Use '/mcp help' for available commands." << std::endl;
|
|
1641
|
+
}
|
|
1642
|
+
|
|
1643
|
+
} catch (const std::exception &e) {
|
|
1644
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
// Save to memory for AI context
|
|
1648
|
+
memory_->save_interaction("/mcp " + command, "MCP command executed");
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
void Agent::handle_theme_command(const std::string &command) {
|
|
1652
|
+
if (command.empty() || command == "help") {
|
|
1653
|
+
std::cout << "Theme commands:" << std::endl;
|
|
1654
|
+
std::cout
|
|
1655
|
+
<< " /theme list - List available themes"
|
|
1656
|
+
<< std::endl;
|
|
1657
|
+
std::cout << " /theme set <name> - Set active theme"
|
|
1658
|
+
<< std::endl;
|
|
1659
|
+
std::cout
|
|
1660
|
+
<< " /theme preview <name> - Preview theme colors"
|
|
1661
|
+
<< std::endl;
|
|
1662
|
+
std::cout << " /theme current - Show current theme"
|
|
1663
|
+
<< std::endl;
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
std::istringstream iss(command);
|
|
1668
|
+
std::string action;
|
|
1669
|
+
iss >> action;
|
|
1670
|
+
|
|
1671
|
+
try {
|
|
1672
|
+
Services::ThemeService::initialize();
|
|
1673
|
+
|
|
1674
|
+
if (action == "list") {
|
|
1675
|
+
auto themes = Services::ThemeService::list_available_themes();
|
|
1676
|
+
std::cout << "Available themes:" << std::endl;
|
|
1677
|
+
for (const auto &theme_name : themes) {
|
|
1678
|
+
auto theme_info = Services::ThemeService::get_theme_info(theme_name);
|
|
1679
|
+
std::string current =
|
|
1680
|
+
(theme_name == Services::ThemeService::get_current_theme())
|
|
1681
|
+
? " (current)"
|
|
1682
|
+
: "";
|
|
1683
|
+
std::cout << " " << Services::ThemeService::colorize_accent(theme_name)
|
|
1684
|
+
<< current << std::endl;
|
|
1685
|
+
std::cout << " " << theme_info.description << std::endl;
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
} else if (action == "set") {
|
|
1689
|
+
std::string theme_name;
|
|
1690
|
+
iss >> theme_name;
|
|
1691
|
+
if (theme_name.empty()) {
|
|
1692
|
+
std::cout << "Error: Theme name is required" << std::endl;
|
|
1693
|
+
return;
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
if (Services::ThemeService::set_theme(theme_name)) {
|
|
1697
|
+
std::cout << Services::ThemeService::colorize_success(
|
|
1698
|
+
" Theme set to: " + theme_name)
|
|
1699
|
+
<< std::endl;
|
|
1700
|
+
} else {
|
|
1701
|
+
std::cout << Services::ThemeService::colorize_error(
|
|
1702
|
+
"Error: Theme not found: " + theme_name)
|
|
1703
|
+
<< std::endl;
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1706
|
+
} else if (action == "preview") {
|
|
1707
|
+
std::string theme_name;
|
|
1708
|
+
iss >> theme_name;
|
|
1709
|
+
if (theme_name.empty()) {
|
|
1710
|
+
std::cout << "Error: Theme name is required" << std::endl;
|
|
1711
|
+
return;
|
|
1712
|
+
}
|
|
1713
|
+
|
|
1714
|
+
Services::ThemeService::print_theme_preview(theme_name);
|
|
1715
|
+
|
|
1716
|
+
} else if (action == "current") {
|
|
1717
|
+
std::string current_theme = Services::ThemeService::get_current_theme();
|
|
1718
|
+
auto theme_info = Services::ThemeService::get_theme_info(current_theme);
|
|
1719
|
+
std::cout << "Current theme: "
|
|
1720
|
+
<< Services::ThemeService::colorize_accent(current_theme)
|
|
1721
|
+
<< std::endl;
|
|
1722
|
+
std::cout << "Description: " << theme_info.description << std::endl;
|
|
1723
|
+
|
|
1724
|
+
} else {
|
|
1725
|
+
std::cout << "Unknown theme command: " << action << std::endl;
|
|
1726
|
+
std::cout << "Use '/theme help' for available commands." << std::endl;
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1729
|
+
} catch (const std::exception &e) {
|
|
1730
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
memory_->save_interaction("/theme " + command, "Theme command executed");
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
void Agent::handle_auth_command(const std::string &command) {
|
|
1737
|
+
if (command.empty() || command == "help") {
|
|
1738
|
+
std::cout << "Authentication commands:" << std::endl;
|
|
1739
|
+
std::cout
|
|
1740
|
+
<< " /auth providers - List available providers"
|
|
1741
|
+
<< std::endl;
|
|
1742
|
+
std::cout
|
|
1743
|
+
<< " /auth set <provider> - Set active provider"
|
|
1744
|
+
<< std::endl;
|
|
1745
|
+
std::cout
|
|
1746
|
+
<< " /auth key <provider> <key> - Set API key for provider"
|
|
1747
|
+
<< std::endl;
|
|
1748
|
+
std::cout
|
|
1749
|
+
<< " /auth status [provider] - Show provider status"
|
|
1750
|
+
<< std::endl;
|
|
1751
|
+
std::cout << " /auth test <provider> - Test provider "
|
|
1752
|
+
"connection"
|
|
1753
|
+
<< std::endl;
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
std::istringstream iss(command);
|
|
1758
|
+
std::string action;
|
|
1759
|
+
iss >> action;
|
|
1760
|
+
|
|
1761
|
+
try {
|
|
1762
|
+
Services::AuthService::initialize();
|
|
1763
|
+
|
|
1764
|
+
if (action == "providers") {
|
|
1765
|
+
auto providers = Services::AuthService::list_providers();
|
|
1766
|
+
std::cout << "Available authentication providers:" << std::endl;
|
|
1767
|
+
for (const auto &provider_name : providers) {
|
|
1768
|
+
auto provider_info =
|
|
1769
|
+
Services::AuthService::get_provider_info(provider_name);
|
|
1770
|
+
std::string status =
|
|
1771
|
+
Services::AuthService::get_provider_status(provider_name);
|
|
1772
|
+
std::string active = provider_info.is_active ? " (active)" : "";
|
|
1773
|
+
std::cout << " " << provider_info.display_name << active << " - "
|
|
1774
|
+
<< status << std::endl;
|
|
1775
|
+
std::cout << " Model: " << provider_info.model << std::endl;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1778
|
+
} else if (action == "set") {
|
|
1779
|
+
std::string provider_name;
|
|
1780
|
+
iss >> provider_name;
|
|
1781
|
+
if (provider_name.empty()) {
|
|
1782
|
+
std::cout << "Error: Provider name is required" << std::endl;
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
|
|
1786
|
+
if (Services::AuthService::set_active_provider(provider_name)) {
|
|
1787
|
+
std::cout << " Active provider set to: " << provider_name << std::endl;
|
|
1788
|
+
} else {
|
|
1789
|
+
std::cout << "Error: Provider not found: " << provider_name
|
|
1790
|
+
<< std::endl;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
} else if (action == "key") {
|
|
1794
|
+
std::string provider_name, api_key;
|
|
1795
|
+
iss >> provider_name >> api_key;
|
|
1796
|
+
if (provider_name.empty() || api_key.empty()) {
|
|
1797
|
+
std::cout << "Error: Provider name and API key are required"
|
|
1798
|
+
<< std::endl;
|
|
1799
|
+
return;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
if (Services::AuthService::set_api_key(provider_name, api_key)) {
|
|
1803
|
+
std::cout << " API key set for provider: " << provider_name
|
|
1804
|
+
<< std::endl;
|
|
1805
|
+
} else {
|
|
1806
|
+
std::cout << "Error: Failed to set API key for provider: "
|
|
1807
|
+
<< provider_name << std::endl;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
} else if (action == "status") {
|
|
1811
|
+
std::string provider_name;
|
|
1812
|
+
iss >> provider_name;
|
|
1813
|
+
if (provider_name.empty()) {
|
|
1814
|
+
provider_name = Services::AuthService::get_active_provider();
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
std::string status =
|
|
1818
|
+
Services::AuthService::get_provider_status(provider_name);
|
|
1819
|
+
auto provider_info =
|
|
1820
|
+
Services::AuthService::get_provider_info(provider_name);
|
|
1821
|
+
std::cout << "Provider: " << provider_info.display_name << std::endl;
|
|
1822
|
+
std::cout << "Status: " << status << std::endl;
|
|
1823
|
+
std::cout << "Model: " << provider_info.model << std::endl;
|
|
1824
|
+
std::cout << "Base URL: " << provider_info.base_url << std::endl;
|
|
1825
|
+
|
|
1826
|
+
} else if (action == "test") {
|
|
1827
|
+
std::string provider_name;
|
|
1828
|
+
iss >> provider_name;
|
|
1829
|
+
if (provider_name.empty()) {
|
|
1830
|
+
std::cout << "Error: Provider name is required" << std::endl;
|
|
1831
|
+
return;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1834
|
+
if (Services::AuthService::test_provider_connection(provider_name)) {
|
|
1835
|
+
std::cout << " Connection test successful for: " << provider_name
|
|
1836
|
+
<< std::endl;
|
|
1837
|
+
} else {
|
|
1838
|
+
std::cout << " Connection test failed for: " << provider_name
|
|
1839
|
+
<< std::endl;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
} else {
|
|
1843
|
+
std::cout << "Unknown auth command: " << action << std::endl;
|
|
1844
|
+
std::cout << "Use '/auth help' for available commands." << std::endl;
|
|
1845
|
+
}
|
|
1846
|
+
|
|
1847
|
+
} catch (const std::exception &e) {
|
|
1848
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
1849
|
+
}
|
|
1850
|
+
|
|
1851
|
+
memory_->save_interaction("/auth " + command, "Auth command executed");
|
|
1852
|
+
}
|
|
1853
|
+
|
|
1854
|
+
void Agent::handle_sandbox_command(const std::string &command) {
|
|
1855
|
+
if (command.empty() || command == "help") {
|
|
1856
|
+
std::cout << "Sandbox commands:" << std::endl;
|
|
1857
|
+
std::cout << " /sandbox run <command> - Execute command "
|
|
1858
|
+
"in sandbox"
|
|
1859
|
+
<< std::endl;
|
|
1860
|
+
std::cout << " /sandbox list - List available "
|
|
1861
|
+
"sandbox configs"
|
|
1862
|
+
<< std::endl;
|
|
1863
|
+
std::cout << " /sandbox status - Show Docker status"
|
|
1864
|
+
<< std::endl;
|
|
1865
|
+
std::cout
|
|
1866
|
+
<< " /sandbox containers - List active containers"
|
|
1867
|
+
<< std::endl;
|
|
1868
|
+
std::cout
|
|
1869
|
+
<< " /sandbox cleanup - Clean up old containers"
|
|
1870
|
+
<< std::endl;
|
|
1871
|
+
return;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
std::istringstream iss(command);
|
|
1875
|
+
std::string action;
|
|
1876
|
+
iss >> action;
|
|
1877
|
+
|
|
1878
|
+
try {
|
|
1879
|
+
Services::SandboxService::initialize();
|
|
1880
|
+
|
|
1881
|
+
if (action == "run") {
|
|
1882
|
+
std::string cmd;
|
|
1883
|
+
std::getline(iss, cmd);
|
|
1884
|
+
if (!cmd.empty() && cmd[0] == ' ') {
|
|
1885
|
+
cmd = cmd.substr(1);
|
|
1886
|
+
}
|
|
1887
|
+
if (cmd.empty()) {
|
|
1888
|
+
std::cout << "Error: Command is required" << std::endl;
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
std::cout << "Executing in sandbox: " << cmd << std::endl;
|
|
1893
|
+
auto result = Services::SandboxService::execute_command(cmd);
|
|
1894
|
+
|
|
1895
|
+
std::cout << "Exit code: " << result.exit_code << std::endl;
|
|
1896
|
+
std::cout << "Execution time: " << result.execution_time_seconds << "s"
|
|
1897
|
+
<< std::endl;
|
|
1898
|
+
if (!result.stdout_output.empty()) {
|
|
1899
|
+
std::cout << "Output:\n" << result.stdout_output << std::endl;
|
|
1900
|
+
}
|
|
1901
|
+
if (!result.error_message.empty()) {
|
|
1902
|
+
std::cout << "Error: " << result.error_message << std::endl;
|
|
1903
|
+
}
|
|
1904
|
+
|
|
1905
|
+
} else if (action == "list") {
|
|
1906
|
+
auto configs = Services::SandboxService::list_sandbox_configs();
|
|
1907
|
+
std::cout << "Available sandbox configurations:" << std::endl;
|
|
1908
|
+
for (const auto &config_name : configs) {
|
|
1909
|
+
auto config = Services::SandboxService::get_sandbox_config(config_name);
|
|
1910
|
+
std::cout << " " << config_name << " - " << config.image << std::endl;
|
|
1911
|
+
std::cout << " Memory: " << config.memory_limit_mb
|
|
1912
|
+
<< "MB, CPU: " << config.cpu_limit_percent << "%"
|
|
1913
|
+
<< std::endl;
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
} else if (action == "status") {
|
|
1917
|
+
if (Services::SandboxService::check_docker_installation()) {
|
|
1918
|
+
std::cout << "Docker Status: Available" << std::endl;
|
|
1919
|
+
std::cout << "Version: "
|
|
1920
|
+
<< Services::SandboxService::get_docker_version()
|
|
1921
|
+
<< std::endl;
|
|
1922
|
+
} else {
|
|
1923
|
+
std::cout << "Docker Status: Not Available" << std::endl;
|
|
1924
|
+
std::cout << "Please install Docker to use sandbox features."
|
|
1925
|
+
<< std::endl;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
} else if (action == "containers") {
|
|
1929
|
+
auto containers = Services::SandboxService::list_active_containers();
|
|
1930
|
+
if (containers.empty()) {
|
|
1931
|
+
std::cout << "No active sandbox containers." << std::endl;
|
|
1932
|
+
} else {
|
|
1933
|
+
std::cout << "Active sandbox containers:" << std::endl;
|
|
1934
|
+
for (const auto &container : containers) {
|
|
1935
|
+
std::cout << " " << container << std::endl;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1939
|
+
} else if (action == "cleanup") {
|
|
1940
|
+
Services::SandboxService::cleanup_old_containers();
|
|
1941
|
+
std::cout << " Cleaned up old sandbox containers" << std::endl;
|
|
1942
|
+
|
|
1943
|
+
} else {
|
|
1944
|
+
std::cout << "Unknown sandbox command: " << action << std::endl;
|
|
1945
|
+
std::cout << "Use '/sandbox help' for available commands." << std::endl;
|
|
1946
|
+
}
|
|
1947
|
+
|
|
1948
|
+
} catch (const std::exception &e) {
|
|
1949
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
1950
|
+
}
|
|
1951
|
+
|
|
1952
|
+
memory_->save_interaction("/sandbox " + command, "Sandbox command executed");
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
void Agent::handle_error_command(const std::string &command) {
|
|
1956
|
+
if (command.empty() || command == "help") {
|
|
1957
|
+
std::cout << "Error management commands:" << std::endl;
|
|
1958
|
+
std::cout << " /error report - Show error summary"
|
|
1959
|
+
<< std::endl;
|
|
1960
|
+
std::cout << " /error recent [count] - Show recent errors"
|
|
1961
|
+
<< std::endl;
|
|
1962
|
+
std::cout << " /error clear - Clear error log"
|
|
1963
|
+
<< std::endl;
|
|
1964
|
+
std::cout << " /error export <path> - Export error log"
|
|
1965
|
+
<< std::endl;
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
std::istringstream iss(command);
|
|
1970
|
+
std::string action;
|
|
1971
|
+
iss >> action;
|
|
1972
|
+
|
|
1973
|
+
try {
|
|
1974
|
+
Services::ErrorService::initialize();
|
|
1975
|
+
|
|
1976
|
+
if (action == "report") {
|
|
1977
|
+
Services::ErrorService::print_error_report();
|
|
1978
|
+
|
|
1979
|
+
} else if (action == "recent") {
|
|
1980
|
+
int count = 5;
|
|
1981
|
+
iss >> count;
|
|
1982
|
+
if (count < 1)
|
|
1983
|
+
count = 5;
|
|
1984
|
+
|
|
1985
|
+
auto recent_errors = Services::ErrorService::get_recent_errors(count);
|
|
1986
|
+
if (recent_errors.empty()) {
|
|
1987
|
+
std::cout << "No recent errors." << std::endl;
|
|
1988
|
+
} else {
|
|
1989
|
+
std::cout << "Recent errors (" << recent_errors.size()
|
|
1990
|
+
<< "):" << std::endl;
|
|
1991
|
+
for (const auto &error : recent_errors) {
|
|
1992
|
+
std::cout << Services::ErrorService::format_error(error, true)
|
|
1993
|
+
<< std::endl
|
|
1994
|
+
<< std::endl;
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
} else if (action == "clear") {
|
|
1999
|
+
Services::ErrorService::clear_error_log();
|
|
2000
|
+
std::cout << " Error log cleared" << std::endl;
|
|
2001
|
+
|
|
2002
|
+
} else if (action == "export") {
|
|
2003
|
+
std::string export_path;
|
|
2004
|
+
iss >> export_path;
|
|
2005
|
+
if (export_path.empty()) {
|
|
2006
|
+
export_path = "error_log_export.json";
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
if (Services::ErrorService::export_error_log(export_path)) {
|
|
2010
|
+
std::cout << " Error log exported to: " << export_path << std::endl;
|
|
2011
|
+
} else {
|
|
2012
|
+
std::cout << "Error: Failed to export error log" << std::endl;
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
} else {
|
|
2016
|
+
std::cout << "Unknown error command: " << action << std::endl;
|
|
2017
|
+
std::cout << "Use '/error help' for available commands." << std::endl;
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
} catch (const std::exception &e) {
|
|
2021
|
+
std::cout << "Error: " << e.what() << std::endl;
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
memory_->save_interaction("/error " + command, "Error command executed");
|
|
2025
|
+
}
|
|
2026
|
+
} // namespace Core
|