45 std::string cmd =
"gemini " +
EscapeShellArg(prompt) +
" --output-format json --model " +
model_;
52 FILE* pipe = _popen(cmd.c_str(),
"r");
54 FILE* pipe = popen(cmd.c_str(),
"r");
58 return absl::InternalError(
"popen() failed! Is gemini-cli installed and in PATH?");
61 std::array<char, 128> buffer;
63 while (fgets(buffer.data(), buffer.size(), pipe) !=
nullptr) {
64 result += buffer.data();
68 int status = _pclose(pipe);
70 int status = pclose(pipe);
74 return absl::InternalError(absl::StrCat(
"gemini-cli exited with status ", status,
". Output: ", result));
78 return absl::InternalError(
"Empty response from gemini-cli");
83 size_t json_start = result.find(
'{');
84 if (json_start == std::string::npos) {
85 return absl::InternalError(
"Invalid JSON response from gemini-cli: " + result);
87 std::string json_str = result.substr(json_start);
89 auto json = nlohmann::json::parse(json_str);
92 if (json.contains(
"response")) {
95 return absl::InternalError(
"JSON response missing 'response' field");
106 if (absl::StartsWith(text_content,
"```json")) {
107 text_content = text_content.substr(7);
108 }
else if (absl::StartsWith(text_content,
"```")) {
109 text_content = text_content.substr(3);
111 if (absl::EndsWith(text_content,
"```")) {
112 text_content = text_content.substr(0, text_content.length() - 3);
116 auto parsed_inner = nlohmann::json::parse(text_content,
nullptr,
false);
117 if (!parsed_inner.is_discarded()) {
118 if (parsed_inner.contains(
"text_response") && parsed_inner[
"text_response"].is_string()) {
119 response.
text_response = parsed_inner[
"text_response"].get<std::string>();
121 if (parsed_inner.contains(
"commands") && parsed_inner[
"commands"].is_array()) {
122 for (
const auto& cmd : parsed_inner[
"commands"]) {
123 if (cmd.is_string()) {
124 std::string command = cmd.get<std::string>();
125 if (absl::StartsWith(command,
"z3ed ")) {
126 command = command.substr(5);
128 response.
commands.push_back(command);
134 std::vector<std::string> lines = absl::StrSplit(text_content,
'\n');
135 for (
const auto& line : lines) {
136 std::string trimmed = std::string(absl::StripAsciiWhitespace(line));
137 if (!trimmed.empty() && (absl::StartsWith(trimmed,
"z3ed ") ||
138 absl::StartsWith(trimmed,
"palette ") ||
139 absl::StartsWith(trimmed,
"overworld ") ||
140 absl::StartsWith(trimmed,
"sprite ") ||
141 absl::StartsWith(trimmed,
"dungeon "))) {
142 if (absl::StartsWith(trimmed,
"z3ed ")) {
143 trimmed = trimmed.substr(5);
145 response.
commands.push_back(trimmed);
152 }
catch (
const std::exception& e) {
153 return absl::InternalError(absl::StrCat(
"JSON parse error: ", e.what()));