@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.
Files changed (100) hide show
  1. package/.clang-tidy +28 -0
  2. package/.dockerignore +56 -0
  3. package/.env.example +29 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/ISSUE_TEMPLATE/blank.md +27 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. package/.github/SECURITY.md +24 -0
  9. package/.github/codeql/codeql-config.yml +8 -0
  10. package/.github/dependabot.yml +14 -0
  11. package/.github/labeler.yml +50 -0
  12. package/.github/packaging/brand-cursor.png +0 -0
  13. package/.github/packaging/database/init.sql +48 -0
  14. package/.github/packaging/docker/Dockerfile +111 -0
  15. package/.github/packaging/docker/docker-compose.yml +56 -0
  16. package/.github/packaging/scripts/preflight.sh +413 -0
  17. package/.github/packaging/scripts/prepare-release.sh +141 -0
  18. package/.github/packaging/scripts/release.sh +22 -0
  19. package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
  20. package/.github/pull_request_template.md +31 -0
  21. package/.github/signed.json +9 -0
  22. package/.github/workflows/README.md +23 -0
  23. package/.github/workflows/ci.yml +181 -0
  24. package/.github/workflows/cla.yml +33 -0
  25. package/.github/workflows/formula-sha.yml +63 -0
  26. package/.github/workflows/issue-response.yml +44 -0
  27. package/.github/workflows/labeler.yml +42 -0
  28. package/.github/workflows/pr-body.yml +49 -0
  29. package/.github/workflows/release.yml +176 -0
  30. package/.github/workflows/security.yml +94 -0
  31. package/.github/workflows/stale.yml +38 -0
  32. package/AGENTS.md +49 -0
  33. package/CHANGELOG.md +3 -0
  34. package/CMakeLists.txt +646 -0
  35. package/Formula/cursor.rb +46 -0
  36. package/LICENSE +201 -0
  37. package/Makefile +28 -0
  38. package/README.md +46 -0
  39. package/cli.js +16 -0
  40. package/include/agent.h +86 -0
  41. package/include/agent_mode.h +17 -0
  42. package/include/memory_manager.h +102 -0
  43. package/include/services/ai_service.h +31 -0
  44. package/include/services/auth_service.h +87 -0
  45. package/include/services/checkpoint_service.h +69 -0
  46. package/include/services/codebase_service.h +38 -0
  47. package/include/services/command_service.h +23 -0
  48. package/include/services/context_service.h +74 -0
  49. package/include/services/database_service.h +56 -0
  50. package/include/services/error_service.h +106 -0
  51. package/include/services/file_service.h +51 -0
  52. package/include/services/git_service.h +29 -0
  53. package/include/services/github_service.h +85 -0
  54. package/include/services/mcp_service.h +85 -0
  55. package/include/services/multi_file_service.h +93 -0
  56. package/include/services/sandbox_service.h +96 -0
  57. package/include/services/theme_service.h +67 -0
  58. package/include/services/web_service.h +52 -0
  59. package/include/utils/config.h +68 -0
  60. package/include/utils/memory_utils.h +79 -0
  61. package/include/utils/platform.h +56 -0
  62. package/include/utils/ui.h +43 -0
  63. package/include/utils/validation.h +63 -0
  64. package/include/utils/version.h.in +17 -0
  65. package/install.js +49 -0
  66. package/package.json +16 -0
  67. package/release/checksums.txt +3 -0
  68. package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
  69. package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
  70. package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
  71. package/src/agent.cpp +2026 -0
  72. package/src/main.cpp +97 -0
  73. package/src/memory_manager.cpp +814 -0
  74. package/src/services/ai_service.cpp +366 -0
  75. package/src/services/auth_service.cpp +779 -0
  76. package/src/services/checkpoint_service.cpp +465 -0
  77. package/src/services/codebase_service.cpp +233 -0
  78. package/src/services/command_service.cpp +82 -0
  79. package/src/services/context_service.cpp +348 -0
  80. package/src/services/database_service.cpp +148 -0
  81. package/src/services/error_service.cpp +438 -0
  82. package/src/services/file_service.cpp +349 -0
  83. package/src/services/git_service.cpp +148 -0
  84. package/src/services/github_service.cpp +435 -0
  85. package/src/services/mcp_service.cpp +481 -0
  86. package/src/services/multi_file_service.cpp +591 -0
  87. package/src/services/sandbox_service.cpp +678 -0
  88. package/src/services/theme_service.cpp +429 -0
  89. package/src/services/web_service.cpp +532 -0
  90. package/src/utils/config.cpp +77 -0
  91. package/src/utils/memory_utils.cpp +93 -0
  92. package/src/utils/ui.cpp +307 -0
  93. package/src/utils/validation.cpp +306 -0
  94. package/src/utils/version.cpp +175 -0
  95. package/tests/e2e/docker-compose.yml +195 -0
  96. package/tests/e2e/run_e2e_tests.sh +70 -0
  97. package/tests/e2e/run_tests_in_docker.sh +115 -0
  98. package/tests/main_test.cpp +16 -0
  99. package/tests/mocks/mock_ollama.py +98 -0
  100. 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