yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
dev_assist_agent.cc
Go to the documentation of this file.
2
3#include <algorithm>
4#include <cctype>
5#include <filesystem>
6#include <fstream>
7#include <sstream>
8
9#include "absl/strings/match.h"
10#include "absl/strings/str_cat.h"
11#include "absl/strings/str_split.h"
12#include "absl/strings/strip.h"
16
17namespace yaze {
18namespace cli {
19namespace agent {
20
21namespace {
22
23// Common file extensions for C++ source and test files
24const std::vector<std::string> kSourceExtensions = {".cc", ".cpp", ".cxx", ".c"};
25const std::vector<std::string> kHeaderExtensions = {".h", ".hpp", ".hxx"};
26const std::vector<std::string> kTestSuffixes = {"_test", "_unittest", "_tests"};
27
28// Extract filename from path
29std::string GetFileName(const std::string& path) {
30 size_t pos = path.find_last_of("/\\");
31 return (pos == std::string::npos) ? path : path.substr(pos + 1);
32}
33
34// Extract directory from path
35std::string GetDirectory(const std::string& path) {
36 size_t pos = path.find_last_of("/\\");
37 return (pos == std::string::npos) ? "." : path.substr(0, pos);
38}
39
40// Check if string contains any of the patterns
41bool ContainsAny(const std::string& text, const std::vector<std::string>& patterns) {
42 for (const auto& pattern : patterns) {
43 if (text.find(pattern) != std::string::npos) {
44 return true;
45 }
46 }
47 return false;
48}
49
50} // namespace
51
55
57
59 std::shared_ptr<ToolDispatcher> tool_dispatcher,
60 std::shared_ptr<yaze::cli::AIService> ai_service) {
61 if (!tool_dispatcher) {
62 return absl::InvalidArgumentError("Tool dispatcher is required");
63 }
64
65 tool_dispatcher_ = tool_dispatcher;
66 ai_service_ = ai_service;
67 initialized_ = true;
68
69 return absl::OkStatus();
70}
71
73 // GCC/Clang compilation errors
74 error_patterns_.push_back({
75 std::regex(R"(([^:]+):(\d+):(\d+):\s*(error|warning):\s*(.+))"),
77 "Compilation",
78 [](const std::smatch& match, AnalysisResult& result) {
79 result.file_path = match[1];
80 result.line_number = std::stoi(match[2]);
81 result.description = match[5];
82 result.raw_error = match[0];
83 }
84 });
85
86 // MSVC compilation errors
87 error_patterns_.push_back({
88 std::regex(R"(([^(]+)\‍((\d+)\):\s*(error|warning)\s*([A-Z0-9]+):\s*(.+))"),
90 "Compilation",
91 [](const std::smatch& match, AnalysisResult& result) {
92 result.file_path = match[1];
93 result.line_number = std::stoi(match[2]);
94 result.description = match[5];
95 result.raw_error = match[0];
96 }
97 });
98
99 // Undefined reference (link error)
100 error_patterns_.push_back({
101 std::regex(R"(undefined reference to\s*[`']([^']+)[`'])"),
103 "Linking",
104 [](const std::smatch& match, AnalysisResult& result) {
105 result.description = absl::StrCat("Undefined reference to: ", match[1].str());
106 result.raw_error = match[0];
107 }
108 });
109
110 // Undefined symbols (macOS linker)
111 error_patterns_.push_back({
112 std::regex(R"(Undefined symbols for architecture .+:\s*\"([^\"]+)\")"),
114 "Linking",
115 [](const std::smatch& match, AnalysisResult& result) {
116 result.description = absl::StrCat("Undefined symbol: ", match[1].str());
117 result.raw_error = match[0];
118 }
119 });
120
121 // Missing header file
122 error_patterns_.push_back({
123 std::regex(R"(fatal error:\s*([^:]+):\s*No such file or directory)"),
125 "Compilation",
126 [](const std::smatch& match, AnalysisResult& result) {
127 result.description = absl::StrCat("Missing header file: ", match[1].str());
128 result.raw_error = match[0];
129 }
130 });
131
132 // CMake errors
133 error_patterns_.push_back({
134 std::regex(R"(CMake Error at ([^:]+):(\d+)\s*\‍(([^)]+)\):\s*(.+))"),
136 "CMake",
137 [](const std::smatch& match, AnalysisResult& result) {
138 result.file_path = match[1];
139 result.line_number = std::stoi(match[2]);
140 result.description = match[4];
141 result.raw_error = match[0];
142 }
143 });
144
145 // Segmentation fault
146 error_patterns_.push_back({
147 std::regex(R"(Segmentation fault|SIGSEGV|segfault)"),
149 "Runtime",
150 [](const std::smatch& match, AnalysisResult& result) {
151 result.description = "Segmentation fault detected";
152 result.raw_error = match[0];
153 }
154 });
155
156 // Assertion failure
157 error_patterns_.push_back({
158 std::regex(R"(Assertion\s*[`']([^']+)[`']\s*failed)"),
160 "Runtime",
161 [](const std::smatch& match, AnalysisResult& result) {
162 result.description = absl::StrCat("Assertion failed: ", match[1].str());
163 result.raw_error = match[0];
164 }
165 });
166
167 // Stack overflow
168 error_patterns_.push_back({
169 std::regex(R"(Stack overflow|stack smashing detected)"),
171 "Runtime",
172 [](const std::smatch& match, AnalysisResult& result) {
173 result.description = "Stack overflow detected";
174 result.raw_error = match[0];
175 }
176 });
177
178 // Test failure (Google Test)
179 error_patterns_.push_back({
180 std::regex(R"(\[\s*FAILED\s*\]\s*([^.]+)\.([^\s]+))"),
182 "Test",
183 [](const std::smatch& match, AnalysisResult& result) {
184 result.description = absl::StrCat("Test failed: ", match[1].str(), ".", match[2].str());
185 result.raw_error = match[0];
186 }
187 });
188}
189
190std::vector<DevAssistAgent::AnalysisResult> DevAssistAgent::AnalyzeBuildOutput(
191 const std::string& output) {
192 std::vector<AnalysisResult> results;
193 std::istringstream stream(output);
194 std::string line;
195
196 while (std::getline(stream, line)) {
197 for (const auto& pattern : error_patterns_) {
198 std::smatch match;
199 if (std::regex_search(line, match, pattern.pattern)) {
200 AnalysisResult result;
201 result.error_type = pattern.type;
202 result.error_category = pattern.category;
203 pattern.extractor(match, result);
204
205 // Generate fix suggestions
207
208 // Try to get AI suggestions if available
209 if (use_ai_ && ai_service_) {
210 auto ai_suggestion = GetAISuggestion(result);
211 if (ai_suggestion.ok()) {
212 result.suggested_fixes.push_back(*ai_suggestion);
213 result.ai_assisted = true;
214 }
215 }
216
217 results.push_back(result);
218 break; // Only match first pattern per line
219 }
220 }
221 }
222
223 return results;
224}
225
227 const std::string& stack_trace) {
228 AnalysisResult result;
229 result.error_category = "Runtime";
230
231 // Check for common crash types
232 if (stack_trace.find("SIGSEGV") != std::string::npos ||
233 stack_trace.find("Segmentation fault") != std::string::npos) {
235 result.description = "Segmentation fault (memory access violation)";
236 result.root_cause = "Likely causes: null pointer dereference, buffer overflow, use-after-free";
237 } else if (stack_trace.find("Stack overflow") != std::string::npos) {
239 result.description = "Stack overflow detected";
240 result.root_cause = "Likely causes: infinite recursion, large stack allocations";
241 } else if (stack_trace.find("Assertion") != std::string::npos) {
243 result.description = "Assertion failure";
244 result.root_cause = "A debug assertion or CHECK failed";
245 }
246
247 // Extract file and line from stack trace (if present)
248 std::regex frame_regex(R"(#\d+\s+.+\s+at\s+([^:]+):(\d+))");
249 std::smatch match;
250 if (std::regex_search(stack_trace, match, frame_regex)) {
251 result.file_path = match[1];
252 result.line_number = std::stoi(match[2]);
253 }
254
255 // Extract function names from stack
256 std::regex func_regex(R"(#\d+\s+.+\s+in\s+([^\s(]+))");
257 std::string functions;
258 auto begin = std::sregex_iterator(stack_trace.begin(), stack_trace.end(), func_regex);
259 auto end = std::sregex_iterator();
260 for (auto it = begin; it != end; ++it) {
261 if (!functions.empty()) functions += " -> ";
262 functions += (*it)[1].str();
263 }
264 if (!functions.empty()) {
265 result.description += "\nCall stack: " + functions;
266 }
267
269
270 // Get AI analysis if available
271 if (use_ai_ && ai_service_) {
272 auto ai_suggestion = GetAISuggestion(result);
273 if (ai_suggestion.ok()) {
274 result.suggested_fixes.push_back(*ai_suggestion);
275 result.ai_assisted = true;
276 }
277 }
278
279 result.confidence = 0.8; // High confidence for crash analysis
280 return result;
281}
282
283std::vector<DevAssistAgent::TestSuggestion> DevAssistAgent::GetAffectedTests(
284 const std::vector<std::string>& changed_files) {
285 std::vector<TestSuggestion> suggestions;
286
287 for (const auto& file : changed_files) {
288 // Skip non-source files
289 bool is_source = false;
290 for (const auto& ext : kSourceExtensions) {
291 if (absl::EndsWith(file, ext)) {
292 is_source = true;
293 break;
294 }
295 }
296 if (!is_source) {
297 for (const auto& ext : kHeaderExtensions) {
298 if (absl::EndsWith(file, ext)) {
299 is_source = true;
300 break;
301 }
302 }
303 }
304 if (!is_source) continue;
305
306 // Find corresponding test file
307 std::string test_file = GetTestFileForSource(file);
308
309 // Check if test file exists
310 if (std::filesystem::exists(test_file)) {
311 TestSuggestion suggestion;
312 suggestion.test_file = test_file;
313 suggestion.test_name = GetFileName(test_file);
314 suggestion.reason = absl::StrCat("Tests for modified file: ", file);
315 suggestion.is_existing = true;
316 suggestions.push_back(suggestion);
317 } else {
318 // Suggest creating a new test file
319 TestSuggestion suggestion;
320 suggestion.test_file = test_file;
321 suggestion.test_name = GetFileName(test_file);
322 suggestion.reason = absl::StrCat("No tests found for: ", file, ". Consider adding tests.");
323 suggestion.is_existing = false;
324 suggestions.push_back(suggestion);
325 }
326
327 // Also find any other tests that might reference this file
328 auto related_tests = FindTestsForFile(file);
329 for (const auto& related : related_tests) {
330 if (related != test_file) { // Avoid duplicates
331 TestSuggestion suggestion;
332 suggestion.test_file = related;
333 suggestion.test_name = GetFileName(related);
334 suggestion.reason = absl::StrCat("May test functionality from: ", file);
335 suggestion.is_existing = true;
336 suggestions.push_back(suggestion);
337 }
338 }
339 }
340
341 return suggestions;
342}
343
345 switch (result.error_type) {
348 break;
349
352 SuggestLinkOrderFix(result);
353 break;
354
357 break;
358
360 result.suggested_fixes.push_back("Check for null pointer dereferences");
361 result.suggested_fixes.push_back("Verify array bounds and buffer sizes");
362 result.suggested_fixes.push_back("Look for use-after-free or dangling pointers");
363 result.suggested_fixes.push_back("Run with AddressSanitizer: -fsanitize=address");
364 break;
365
367 result.suggested_fixes.push_back("Check for infinite recursion");
368 result.suggested_fixes.push_back("Reduce large stack allocations (use heap instead)");
369 result.suggested_fixes.push_back("Increase stack size if necessary");
370 break;
371
373 result.suggested_fixes.push_back("Review include dependencies");
374 result.suggested_fixes.push_back("Use forward declarations where possible");
375 result.suggested_fixes.push_back("Consider extracting common interfaces");
376 break;
377
379 result.suggested_fixes.push_back("Check CMakeLists.txt syntax");
380 result.suggested_fixes.push_back("Verify target and dependency names");
381 result.suggested_fixes.push_back("Run 'cmake --debug-output' for more details");
382 break;
383
385 result.suggested_fixes.push_back("Review test expectations vs actual behavior");
386 result.suggested_fixes.push_back("Check for recent changes to tested code");
387 result.suggested_fixes.push_back("Run test in isolation to rule out interference");
388 break;
389
390 default:
391 result.suggested_fixes.push_back("Review the error message and surrounding code");
392 result.suggested_fixes.push_back("Check recent changes that might have introduced the issue");
393 break;
394 }
395}
396
398 // Extract header name from error
399 std::regex header_regex(R"(["<]([^">]+)[">])");
400 std::smatch match;
401 if (std::regex_search(result.description, match, header_regex)) {
402 std::string header = match[1];
403
404 result.suggested_fixes.push_back(
405 absl::StrCat("Add '#include \"", header, "\"' or '#include <", header, ">'"));
406 result.suggested_fixes.push_back(
407 absl::StrCat("Check if '", header, "' exists in include paths"));
408 result.suggested_fixes.push_back(
409 "Verify CMakeLists.txt includes the correct directories");
410 result.suggested_fixes.push_back(
411 absl::StrCat("Search for the header: find . -name '", header, "'"));
412
413 // Common header mappings
414 if (header.find("absl/") == 0) {
415 result.suggested_fixes.push_back("Ensure Abseil is properly linked in CMakeLists.txt");
416 } else if (header.find("gtest") != std::string::npos) {
417 result.suggested_fixes.push_back("Ensure Google Test is included in the build");
418 }
419 }
420}
421
423 // Extract symbol name if present
424 std::regex symbol_regex(R"([`"']([^`"']+)[`"'])");
425 std::smatch match;
426 if (std::regex_search(result.description, match, symbol_regex)) {
427 std::string symbol = match[1];
428
429 result.suggested_fixes.push_back(
430 absl::StrCat("Symbol '", symbol, "' is not defined or not linked"));
431 result.suggested_fixes.push_back(
432 "Check if the source file containing this symbol is compiled");
433 result.suggested_fixes.push_back(
434 "Verify library link order in CMakeLists.txt");
435 result.suggested_fixes.push_back(
436 absl::StrCat("Search for definition: grep -r '", symbol, "' src/"));
437
438 // Check for common patterns
439 if (symbol.find("::") != std::string::npos) {
440 result.suggested_fixes.push_back(
441 "C++ namespace issue - ensure implementation matches declaration");
442 }
443 if (symbol.find("vtable") != std::string::npos) {
444 result.suggested_fixes.push_back(
445 "Virtual function not implemented - check pure virtual functions");
446 }
447 }
448}
449
450void DevAssistAgent::SuggestTypeMismatchFix(AnalysisResult& result) {
451 result.suggested_fixes.push_back("Check function signatures match between declaration and definition");
452 result.suggested_fixes.push_back("Verify template instantiations are correct");
453 result.suggested_fixes.push_back("Look for const-correctness issues");
454 result.suggested_fixes.push_back("Check for implicit conversions that might fail");
455}
456
457absl::StatusOr<std::string> DevAssistAgent::GetAISuggestion(const AnalysisResult& result) {
458 if (!ai_service_) {
459 return absl::UnavailableError("AI service not available");
460 }
461
462 // Build prompt for AI
463 std::string prompt = absl::StrCat(
464 "Analyze this build error and suggest a fix:\n",
465 "Error Type: ", result.error_category, "\n",
466 "Description: ", result.description, "\n"
467 );
468
469 if (!result.file_path.empty()) {
470 prompt += absl::StrCat("File: ", result.file_path, "\n");
471 if (result.line_number > 0) {
472 prompt += absl::StrCat("Line: ", result.line_number, "\n");
473 }
474 }
475
476 if (!result.raw_error.empty()) {
477 prompt += absl::StrCat("Raw Error: ", result.raw_error, "\n");
478 }
479
480 prompt += "\nProvide a concise, actionable fix suggestion.";
481
482 // Call AI service
483 auto response = ai_service_->GenerateResponse(prompt);
484 if (!response.ok()) {
485 return response.status();
486 }
487
488 return response->text_response;
489}
490
491std::vector<std::string> DevAssistAgent::FindTestsForFile(const std::string& source_file) {
492 std::vector<std::string> test_files;
493
494 // Check cache first
495 auto it = test_file_cache_.find(source_file);
496 if (it != test_file_cache_.end()) {
497 return it->second;
498 }
499
500 // Extract base name without extension
501 std::string base_name = source_file;
502 for (const auto& ext : kSourceExtensions) {
503 size_t pos = base_name.rfind(ext);
504 if (pos != std::string::npos) {
505 base_name = base_name.substr(0, pos);
506 break;
507 }
508 }
509 for (const auto& ext : kHeaderExtensions) {
510 size_t pos = base_name.rfind(ext);
511 if (pos != std::string::npos) {
512 base_name = base_name.substr(0, pos);
513 break;
514 }
515 }
516
517 // Look for test files with common patterns
518 for (const auto& suffix : kTestSuffixes) {
519 for (const auto& ext : kSourceExtensions) {
520 std::string test_file = base_name + suffix + ext;
521 if (std::filesystem::exists(test_file)) {
522 test_files.push_back(test_file);
523 }
524
525 // Also check in test/ directory
526 std::string test_dir_file = "test/" + GetFileName(base_name) + suffix + ext;
527 if (std::filesystem::exists(test_dir_file)) {
528 test_files.push_back(test_dir_file);
529 }
530
531 // Check in test/unit/ and test/integration/
532 test_dir_file = "test/unit/" + GetFileName(base_name) + suffix + ext;
533 if (std::filesystem::exists(test_dir_file)) {
534 test_files.push_back(test_dir_file);
535 }
536
537 test_dir_file = "test/integration/" + GetFileName(base_name) + suffix + ext;
538 if (std::filesystem::exists(test_dir_file)) {
539 test_files.push_back(test_dir_file);
540 }
541 }
542 }
543
544 // Cache the result
545 test_file_cache_[source_file] = test_files;
546
547 return test_files;
548}
549
550bool DevAssistAgent::IsTestFile(const std::string& file_path) const {
551 for (const auto& suffix : kTestSuffixes) {
552 if (file_path.find(suffix) != std::string::npos) {
553 return true;
554 }
555 }
556 return file_path.find("/test/") != std::string::npos ||
557 file_path.find("\\test\\") != std::string::npos;
558}
559
560std::string DevAssistAgent::GetTestFileForSource(const std::string& source_file) const {
561 // Remove extension
562 std::string base = source_file;
563 for (const auto& ext : kSourceExtensions) {
564 size_t pos = base.rfind(ext);
565 if (pos != std::string::npos) {
566 base = base.substr(0, pos);
567 break;
568 }
569 }
570 for (const auto& ext : kHeaderExtensions) {
571 size_t pos = base.rfind(ext);
572 if (pos != std::string::npos) {
573 base = base.substr(0, pos);
574 break;
575 }
576 }
577
578 // Convert src/ to test/
579 if (base.find("src/") == 0) {
580 base = "test/unit/" + base.substr(4);
581 }
582
583 // Add test suffix
584 return base + "_test.cc";
585}
586
587absl::StatusOr<std::string> DevAssistAgent::ExecuteCommand(const std::string& command) {
588 // Use tool dispatcher to execute build commands
589 if (!tool_dispatcher_) {
590 return absl::FailedPreconditionError("Tool dispatcher not initialized");
591 }
592
593 // Create a build tool call
594 ::yaze::cli::ToolCall tool_call;
595 tool_call.tool_name = "build-compile";
596 tool_call.args["command"] = command;
597
598 return tool_dispatcher_->Dispatch(tool_call);
599}
600
601absl::StatusOr<std::vector<DevAssistAgent::AnalysisResult>>
602DevAssistAgent::RunBuildWithAnalysis(const std::string& command) {
603 auto output = ExecuteCommand(command);
604 if (!output.ok()) {
605 // Even if build fails, we want to analyze the output
606 std::string error_output = std::string(output.status().message().data(), output.status().message().size());
607 return AnalyzeBuildOutput(error_output);
608 }
609
610 // Analyze successful build output for warnings
611 return AnalyzeBuildOutput(*output);
612}
613
614absl::StatusOr<std::vector<DevAssistAgent::AnalysisResult>>
615DevAssistAgent::RunTestsWithAnalysis(const std::string& test_pattern) {
616 std::string command = "ctest --output-on-failure";
617 if (!test_pattern.empty()) {
618 command += " -R " + test_pattern;
619 }
620
621 auto output = ExecuteCommand(command);
622 if (!output.ok()) {
623 // Analyze test failures
624 std::string error_output = std::string(output.status().message().data(), output.status().message().size());
625 return AnalyzeBuildOutput(error_output);
626 }
627
628 // Check for test failures in successful run
629 return AnalyzeBuildOutput(*output);
630}
631
632std::string DevAssistAgent::GetBuildStatus() const {
633 std::stringstream status;
634
635 status << "DevAssistAgent Build Status\n";
636 status << "===========================\n";
637 status << "Initialized: " << (initialized_ ? "Yes" : "No") << "\n";
638 status << "AI Service: " << (ai_service_ ? "Available" : "Not available") << "\n";
639 status << "AI Enabled: " << (use_ai_ ? "Yes" : "No") << "\n";
640 status << "Error Patterns Loaded: " << error_patterns_.size() << "\n";
641 status << "Test File Cache Size: " << test_file_cache_.size() << "\n";
642
643 return status.str();
644}
645
647 const BuildConfig& config,
648 std::function<void(const AnalysisResult&)> on_error) {
649 if (!initialized_) {
650 return absl::FailedPreconditionError("Agent not initialized");
651 }
652
653 // Build command based on config
654 std::string command = "cmake --build " + config.build_dir;
655 if (!config.preset.empty()) {
656 command = "cmake --preset " + config.preset + " && " + command;
657 }
658 if (config.verbose) {
659 command += " --verbose";
660 }
661 if (config.parallel && config.parallel_jobs > 0) {
662 command += " -j" + std::to_string(config.parallel_jobs);
663 }
664
665 // Run build and analyze output
666 auto results = RunBuildWithAnalysis(command);
667 if (!results.ok()) {
668 return results.status();
669 }
670
671 // Report errors via callback
672 for (const auto& result : *results) {
673 if (on_error) {
674 on_error(result);
675 }
676
677 if (config.stop_on_error &&
678 result.error_type != AnalysisResult::ErrorType::kUnknown) {
679 return absl::AbortedError("Build stopped on first error");
680 }
681 }
682
683 return absl::OkStatus();
684}
685
686absl::StatusOr<std::string> DevAssistAgent::GenerateTestCode(
687 const std::string& source_file,
688 const std::string& function_name) {
689 if (!std::filesystem::exists(source_file)) {
690 return absl::NotFoundError(absl::StrCat("Source file not found: ", source_file));
691 }
692
693 // Read source file
694 std::ifstream file(source_file);
695 std::stringstream buffer;
696 buffer << file.rdbuf();
697 std::string source_code = buffer.str();
698
699 if (use_ai_ && ai_service_) {
700 return GenerateTestWithAI(source_code, function_name);
701 }
702
703 // Basic test template without AI
704 std::string test_code = absl::StrCat(
705 "#include \"gtest/gtest.h\"\n",
706 "#include \"", source_file, "\"\n\n",
707 "namespace yaze {\n",
708 "namespace test {\n\n"
709 );
710
711 if (!function_name.empty()) {
712 test_code += absl::StrCat(
713 "TEST(", GetFileName(source_file), "Test, ", function_name, ") {\n",
714 " // TODO: Implement test for ", function_name, "\n",
715 " EXPECT_TRUE(true);\n",
716 "}\n\n"
717 );
718 } else {
719 test_code += absl::StrCat(
720 "TEST(", GetFileName(source_file), "Test, BasicTest) {\n",
721 " // TODO: Implement tests\n",
722 " EXPECT_TRUE(true);\n",
723 "}\n\n"
724 );
725 }
726
727 test_code += "} // namespace test\n} // namespace yaze\n";
728
729 return test_code;
730}
731
732absl::StatusOr<std::string> DevAssistAgent::GenerateTestWithAI(
733 const std::string& source_code,
734 const std::string& function_name) {
735 if (!ai_service_) {
736 return absl::UnavailableError("AI service not available");
737 }
738
739 std::string prompt = "Generate comprehensive Google Test unit tests for the following C++ code:\n\n";
740 prompt += source_code;
741 prompt += "\n\n";
742
743 if (!function_name.empty()) {
744 prompt += absl::StrCat("Focus on testing the function: ", function_name, "\n");
745 }
746
747 prompt += "Include edge cases, error conditions, and normal operation tests.";
748 prompt += "Follow the yaze project testing conventions.";
749
750 auto response = ai_service_->GenerateResponse(prompt);
751 if (!response.ok()) {
752 return response.status();
753 }
754
755 return response->text_response;
756}
757
758std::vector<DevAssistAgent::AnalysisResult> DevAssistAgent::AnalyzeCodeFile(
759 const std::string& file_path) {
760 std::vector<AnalysisResult> results;
761
762 if (!std::filesystem::exists(file_path)) {
763 AnalysisResult result;
765 result.error_category = "File";
766 result.description = "File not found";
767 result.file_path = file_path;
768 results.push_back(result);
769 return results;
770 }
771
772 // Read file
773 std::ifstream file(file_path);
774 std::string line;
775 int line_number = 0;
776
777 while (std::getline(file, line)) {
778 line_number++;
779
780 // Check for common code issues
781
782 // TODO comments
783 if (line.find("TODO") != std::string::npos ||
784 line.find("FIXME") != std::string::npos ||
785 line.find("XXX") != std::string::npos) {
786 AnalysisResult result;
788 result.error_category = "Code Quality";
789 result.description = "TODO/FIXME comment found";
790 result.file_path = file_path;
791 result.line_number = line_number;
792 result.suggested_fixes.push_back("Address the TODO/FIXME comment");
793 results.push_back(result);
794 }
795
796 // Very long lines
797 if (line.length() > 100) {
798 AnalysisResult result;
800 result.error_category = "Style";
801 result.description = absl::StrCat("Line too long (", line.length(), " characters)");
802 result.file_path = file_path;
803 result.line_number = line_number;
804 result.suggested_fixes.push_back("Break long line for better readability");
805 results.push_back(result);
806 }
807
808 // Potential null pointer issues
809 if (line.find("->") != std::string::npos &&
810 line.find("if") == std::string::npos &&
811 line.find("?") == std::string::npos) {
812 // Simple heuristic: pointer dereference without obvious null check
813 AnalysisResult result;
815 result.error_category = "Potential Issue";
816 result.description = "Pointer dereference without visible null check";
817 result.file_path = file_path;
818 result.line_number = line_number;
819 result.confidence = 0.3; // Low confidence heuristic
820 result.suggested_fixes.push_back("Ensure pointer is checked for null before dereferencing");
821 results.push_back(result);
822 }
823 }
824
825 return results;
826}
827
828} // namespace agent
829} // namespace cli
830} // namespace yaze
absl::StatusOr< std::vector< AnalysisResult > > RunBuildWithAnalysis(const std::string &command)
Run a build command and analyze output.
std::shared_ptr< yaze::cli::AIService > ai_service_
std::string GetBuildStatus() const
Get build system status and health.
absl::StatusOr< std::string > GenerateTestCode(const std::string &source_file, const std::string &function_name="")
Generate test code for a function or class.
void SuggestTypeMismatchFix(AnalysisResult &result)
AnalysisResult AnalyzeCrash(const std::string &stack_trace)
Analyze a crash or stack trace.
absl::StatusOr< std::string > GenerateTestWithAI(const std::string &source_code, const std::string &function_name)
std::map< std::string, std::vector< std::string > > test_file_cache_
void SuggestMissingHeaderFix(AnalysisResult &result)
std::vector< std::string > FindTestsForFile(const std::string &source_file)
absl::StatusOr< std::vector< AnalysisResult > > RunTestsWithAnalysis(const std::string &test_pattern="")
Run tests and analyze failures.
void SuggestLinkOrderFix(AnalysisResult &result)
absl::Status Initialize(std::shared_ptr< ToolDispatcher > tool_dispatcher, std::shared_ptr< yaze::cli::AIService > ai_service=nullptr)
Initialize the agent with optional AI service.
bool IsTestFile(const std::string &file_path) const
std::string GetTestFileForSource(const std::string &source_file) const
void GenerateFixSuggestions(AnalysisResult &result)
std::vector< AnalysisResult > AnalyzeBuildOutput(const std::string &output)
Analyze build output for errors and warnings.
std::vector< TestSuggestion > GetAffectedTests(const std::vector< std::string > &changed_files)
Get suggested tests for changed files.
std::vector< AnalysisResult > AnalyzeCodeFile(const std::string &file_path)
Check code for common issues.
absl::StatusOr< std::string > ExecuteCommand(const std::string &command)
std::shared_ptr< ToolDispatcher > tool_dispatcher_
absl::StatusOr< std::string > GetAISuggestion(const AnalysisResult &result)
std::vector< ErrorPattern > error_patterns_
absl::Status MonitorBuild(const BuildConfig &config, std::function< void(const AnalysisResult &)> on_error)
Monitor build process interactively.
bool ContainsAny(const std::string &text, const std::vector< std::string > &patterns)
std::map< std::string, std::string > args
Definition common.h:14
std::string tool_name
Definition common.h:13
Analysis result for build errors or crashes.