75 std::regex(R
"(([^:]+):(\d+):(\d+):\s*(error|warning):\s*(.+))"),
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];
88 std::regex(R
"(([^(]+)\((\d+)\):\s*(error|warning)\s*([A-Z0-9]+):\s*(.+))"),
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];
101 std::regex(R
"(undefined reference to\s*[`']([^']+)[`'])"),
105 result.description = absl::StrCat(
"Undefined reference to: ", match[1].str());
106 result.raw_error = match[0];
112 std::regex(R
"(Undefined symbols for architecture .+:\s*\"([^\"]+)\")"),
116 result.description = absl::StrCat(
"Undefined symbol: ", match[1].str());
117 result.raw_error = match[0];
123 std::regex(R
"(fatal error:\s*([^:]+):\s*No such file or directory)"),
127 result.description = absl::StrCat(
"Missing header file: ", match[1].str());
128 result.raw_error = match[0];
134 std::regex(R
"(CMake Error at ([^:]+):(\d+)\s*\(([^)]+)\):\s*(.+))"),
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];
147 std::regex(R
"(Segmentation fault|SIGSEGV|segfault)"),
151 result.description =
"Segmentation fault detected";
152 result.raw_error = match[0];
158 std::regex(R
"(Assertion\s*[`']([^']+)[`']\s*failed)"),
162 result.description = absl::StrCat(
"Assertion failed: ", match[1].str());
163 result.raw_error = match[0];
169 std::regex(R
"(Stack overflow|stack smashing detected)"),
173 result.description =
"Stack overflow detected";
174 result.raw_error = match[0];
180 std::regex(R
"(\[\s*FAILED\s*\]\s*([^.]+)\.([^\s]+))"),
184 result.description = absl::StrCat(
"Test failed: ", match[1].str(),
".", match[2].str());
185 result.raw_error = match[0];
284 const std::vector<std::string>& changed_files) {
285 std::vector<TestSuggestion> suggestions;
287 for (
const auto& file : changed_files) {
289 bool is_source =
false;
290 for (
const auto& ext : kSourceExtensions) {
291 if (absl::EndsWith(file, ext)) {
297 for (
const auto& ext : kHeaderExtensions) {
298 if (absl::EndsWith(file, ext)) {
304 if (!is_source)
continue;
310 if (std::filesystem::exists(test_file)) {
313 suggestion.
test_name = GetFileName(test_file);
314 suggestion.
reason = absl::StrCat(
"Tests for modified file: ", file);
316 suggestions.push_back(suggestion);
321 suggestion.
test_name = GetFileName(test_file);
322 suggestion.
reason = absl::StrCat(
"No tests found for: ", file,
". Consider adding tests.");
324 suggestions.push_back(suggestion);
329 for (
const auto& related : related_tests) {
330 if (related != test_file) {
333 suggestion.
test_name = GetFileName(related);
334 suggestion.
reason = absl::StrCat(
"May test functionality from: ", file);
336 suggestions.push_back(suggestion);
424 std::regex symbol_regex(R
"([`"']([^`"']+)[`"'])");
426 if (std::regex_search(result.
description, match, symbol_regex)) {
427 std::string symbol = match[1];
430 absl::StrCat(
"Symbol '", symbol,
"' is not defined or not linked"));
432 "Check if the source file containing this symbol is compiled");
434 "Verify library link order in CMakeLists.txt");
436 absl::StrCat(
"Search for definition: grep -r '", symbol,
"' src/"));
439 if (symbol.find(
"::") != std::string::npos) {
441 "C++ namespace issue - ensure implementation matches declaration");
443 if (symbol.find(
"vtable") != std::string::npos) {
445 "Virtual function not implemented - check pure virtual functions");
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");
459 return absl::UnavailableError(
"AI service not available");
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"
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");
476 if (!result.raw_error.empty()) {
477 prompt += absl::StrCat(
"Raw Error: ", result.raw_error,
"\n");
480 prompt +=
"\nProvide a concise, actionable fix suggestion.";
483 auto response =
ai_service_->GenerateResponse(prompt);
484 if (!response.ok()) {
485 return response.status();
488 return response->text_response;
492 std::vector<std::string> test_files;
501 std::string base_name = source_file;
503 size_t pos = base_name.rfind(ext);
504 if (pos != std::string::npos) {
505 base_name = base_name.substr(0, pos);
510 size_t pos = base_name.rfind(ext);
511 if (pos != std::string::npos) {
512 base_name = base_name.substr(0, pos);
520 std::string test_file = base_name + suffix + ext;
521 if (std::filesystem::exists(test_file)) {
522 test_files.push_back(test_file);
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);
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);
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);
552 if (file_path.find(suffix) != std::string::npos) {
556 return file_path.find(
"/test/") != std::string::npos ||
557 file_path.find(
"\\test\\") != std::string::npos;
562 std::string base = source_file;
564 size_t pos = base.rfind(ext);
565 if (pos != std::string::npos) {
566 base = base.substr(0, pos);
571 size_t pos = base.rfind(ext);
572 if (pos != std::string::npos) {
573 base = base.substr(0, pos);
579 if (base.find(
"src/") == 0) {
580 base =
"test/unit/" + base.substr(4);
584 return base +
"_test.cc";
590 return absl::FailedPreconditionError(
"Tool dispatcher not initialized");
596 tool_call.
args[
"command"] = command;
601absl::StatusOr<std::vector<DevAssistAgent::AnalysisResult>>
606 std::string error_output = std::string(output.status().message().data(), output.status().message().size());
614absl::StatusOr<std::vector<DevAssistAgent::AnalysisResult>>
616 std::string command =
"ctest --output-on-failure";
617 if (!test_pattern.empty()) {
618 command +=
" -R " + test_pattern;
624 std::string error_output = std::string(output.status().message().data(), output.status().message().size());
633 std::stringstream status;
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";
647 const BuildConfig& config,
648 std::function<
void(
const AnalysisResult&)> on_error) {
650 return absl::FailedPreconditionError(
"Agent not initialized");
654 std::string command =
"cmake --build " + config.build_dir;
655 if (!config.preset.empty()) {
656 command =
"cmake --preset " + config.preset +
" && " + command;
658 if (config.verbose) {
659 command +=
" --verbose";
661 if (config.parallel && config.parallel_jobs > 0) {
662 command +=
" -j" + std::to_string(config.parallel_jobs);
668 return results.status();
672 for (
const auto& result : *results) {
677 if (config.stop_on_error &&
679 return absl::AbortedError(
"Build stopped on first error");
683 return absl::OkStatus();
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));
694 std::ifstream file(source_file);
695 std::stringstream buffer;
696 buffer << file.rdbuf();
697 std::string source_code = buffer.str();
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"
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",
719 test_code += absl::StrCat(
720 "TEST(",
GetFileName(source_file),
"Test, BasicTest) {\n",
721 " // TODO: Implement tests\n",
722 " EXPECT_TRUE(true);\n",
727 test_code +=
"} // namespace test\n} // namespace yaze\n";
759 const std::string& file_path) {
760 std::vector<AnalysisResult> results;
762 if (!std::filesystem::exists(file_path)) {
768 results.push_back(result);
773 std::ifstream file(file_path);
777 while (std::getline(file, line)) {
783 if (line.find(
"TODO") != std::string::npos ||
784 line.find(
"FIXME") != std::string::npos ||
785 line.find(
"XXX") != std::string::npos) {
793 results.push_back(result);
797 if (line.length() > 100) {
801 result.
description = absl::StrCat(
"Line too long (", line.length(),
" characters)");
804 result.
suggested_fixes.push_back(
"Break long line for better readability");
805 results.push_back(result);
809 if (line.find(
"->") != std::string::npos &&
810 line.find(
"if") == std::string::npos &&
811 line.find(
"?") == std::string::npos) {
816 result.
description =
"Pointer dereference without visible null check";
820 result.
suggested_fixes.push_back(
"Ensure pointer is checked for null before dereferencing");
821 results.push_back(result);