From 5236ce9ca6e1d99a383ad3c1965ccf85daeb6914 Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sat, 19 Jul 2025 12:34:32 +0530 Subject: [PATCH 1/6] fix: update .gitignore to include IDE settings and improve code formatting in msgfmt.go --- .gitignore | 4 ++++ lib/msgfmt/msgfmt.go | 11 ++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 5b47947..dee2827 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ snapshot2-*.log schema.yaml **/.claude/settings.local.json out + + +#IDEs +.idea \ No newline at end of file diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index ca1e83b..4fa6f68 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -1,6 +1,8 @@ package msgfmt -import "strings" +import ( + "strings" +) const WhiteSpaceChars = " \t\n\r\f\v" @@ -166,6 +168,12 @@ func RemoveUserInput(msgRaw string, userInputRaw string) string { // Return the original message starting with the first line // that doesn't contain the echoed user input. lastUserInputLineIdx := msgRuneLineLocations[userInputEndIdx] + + // In case of Gemini, the user input echoed back is wrapped in a rounded box, so we remove it. + if strings.Contains(msgLines[lastUserInputLineIdx+1], "╯") && strings.Contains(msgLines[lastUserInputLineIdx+1], "╰") { + lastUserInputLineIdx += 1 + } + return strings.Join(msgLines[lastUserInputLineIdx+1:], "\n") } @@ -201,6 +209,7 @@ const ( ) func formatGenericMessage(message string, userInput string) string { + //fmt.Println("Message:", message, "UserInput:", userInput) message = RemoveUserInput(message, userInput) message = removeMessageBox(message) message = trimEmptyLines(message) From fbfebdc02789bb8f60f3593eebd8b4d8dcdcd64e Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sat, 19 Jul 2025 12:53:29 +0530 Subject: [PATCH 2/6] feat: add explicit support for Gemini agent in API and update documentation --- README.md | 2 +- cmd/root.go | 2 +- cmd/server/server.go | 7 ++++++- lib/msgfmt/msgfmt.go | 3 +++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 7150d0b..1956061 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # AgentAPI -Control [Claude Code](https://github.com/anthropics/claude-code), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), and [Codex](https://github.com/openai/codex) with an HTTP API. +Control [Claude Code](https://github.com/anthropics/claude-code), [Goose](https://github.com/block/goose), [Aider](https://github.com/Aider-AI/aider), [Gemini](https://github.com/google-gemini/gemini-cli) and [Codex](https://github.com/openai/codex) with an HTTP API. ![agentapi-chat](https://github.com/user-attachments/assets/57032c9f-4146-4b66-b219-09e38ab7690d) diff --git a/cmd/root.go b/cmd/root.go index 49b99bb..3dabbc9 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -12,7 +12,7 @@ import ( var rootCmd = &cobra.Command{ Use: "agentapi", Short: "AgentAPI CLI", - Long: `AgentAPI - HTTP API for Claude Code, Goose, Aider, and Codex`, + Long: `AgentAPI - HTTP API for Claude Code, Goose, Aider, Gemini and Codex`, Version: "0.2.3", } diff --git a/cmd/server/server.go b/cmd/server/server.go index 58d0964..5cb479a 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -34,6 +34,7 @@ const ( AgentTypeGoose AgentType = msgfmt.AgentTypeGoose AgentTypeAider AgentType = msgfmt.AgentTypeAider AgentTypeCodex AgentType = msgfmt.AgentTypeCodex + AgentTypeGemini AgentType = msgfmt.AgentTypeGemini AgentTypeCustom AgentType = msgfmt.AgentTypeCustom ) @@ -46,6 +47,8 @@ func parseAgentType(firstArg string, agentTypeVar string) (AgentType, error) { agentType = AgentTypeGoose case string(AgentTypeAider): agentType = AgentTypeAider + case string(AgentTypeGemini): + agentType = AgentTypeGemini case string(AgentTypeCustom): agentType = AgentTypeCustom case string(AgentTypeCodex): @@ -68,6 +71,8 @@ func parseAgentType(firstArg string, agentTypeVar string) (AgentType, error) { agentType = AgentTypeAider case string(AgentTypeCodex): agentType = AgentTypeCodex + case string(AgentTypeGemini): + agentType = AgentTypeGemini default: agentType = AgentTypeCustom } @@ -137,7 +142,7 @@ func runServer(ctx context.Context, logger *slog.Logger, argsToPass []string) er var ServerCmd = &cobra.Command{ Use: "server [agent]", Short: "Run the server", - Long: `Run the server with the specified agent (claude, goose, aider, codex)`, + Long: `Run the server with the specified agent (claude, goose, aider, gemini, codex)`, Args: cobra.MinimumNArgs(1), Run: func(cmd *cobra.Command, args []string) { logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index 4fa6f68..ed04b71 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -205,6 +205,7 @@ const ( AgentTypeGoose AgentType = "goose" AgentTypeAider AgentType = "aider" AgentTypeCodex AgentType = "codex" + AgentTypeGemini AgentType = "gemini" AgentTypeCustom AgentType = "custom" ) @@ -226,6 +227,8 @@ func FormatAgentMessage(agentType AgentType, message string, userInput string) s return formatGenericMessage(message, userInput) case AgentTypeCodex: return formatGenericMessage(message, userInput) + case AgentTypeGemini: + return formatGenericMessage(message, userInput) case AgentTypeCustom: return formatGenericMessage(message, userInput) default: From cc1449f0c4613353c27460a38ca6f6db64201caa Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sat, 19 Jul 2025 12:58:17 +0530 Subject: [PATCH 3/6] fix: remove debug print statement from formatGenericMessage function --- lib/msgfmt/msgfmt.go | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index ed04b71..a9ff118 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -210,7 +210,6 @@ const ( ) func formatGenericMessage(message string, userInput string) string { - //fmt.Println("Message:", message, "UserInput:", userInput) message = RemoveUserInput(message, userInput) message = removeMessageBox(message) message = trimEmptyLines(message) From daa0681ca0c8de20ad439224934a1a23e663184d Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sat, 19 Jul 2025 12:58:54 +0530 Subject: [PATCH 4/6] test: add test cases for Gemini agent type in server_test.go --- cmd/server/server_test.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/cmd/server/server_test.go b/cmd/server/server_test.go index fd4a032..ff0611e 100644 --- a/cmd/server/server_test.go +++ b/cmd/server/server_test.go @@ -23,6 +23,11 @@ func TestParseAgentType(t *testing.T) { agentTypeVar: "", want: AgentTypeClaude, }, + { + firstArg: "gemini", + agentTypeVar: "", + want: AgentTypeGemini, + }, { firstArg: "goose", agentTypeVar: "", @@ -48,6 +53,11 @@ func TestParseAgentType(t *testing.T) { agentTypeVar: "claude", want: AgentTypeClaude, }, + { + firstArg: "claude", + agentTypeVar: "gemini", + want: AgentTypeGemini, + }, { firstArg: "aider", agentTypeVar: "claude", From 0dece42eeb49a3fe96a27ea78eba73e342db7767 Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Sat, 19 Jul 2025 13:15:07 +0530 Subject: [PATCH 5/6] fix: check for overflow --- lib/msgfmt/msgfmt.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index a9ff118..cca75d1 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -170,7 +170,7 @@ func RemoveUserInput(msgRaw string, userInputRaw string) string { lastUserInputLineIdx := msgRuneLineLocations[userInputEndIdx] // In case of Gemini, the user input echoed back is wrapped in a rounded box, so we remove it. - if strings.Contains(msgLines[lastUserInputLineIdx+1], "╯") && strings.Contains(msgLines[lastUserInputLineIdx+1], "╰") { + if lastUserInputLineIdx+1 < len(msgLines) && strings.Contains(msgLines[lastUserInputLineIdx+1], "╯") && strings.Contains(msgLines[lastUserInputLineIdx+1], "╰") { lastUserInputLineIdx += 1 } From 805545f91e411828b8c99975a5645c1368a9eee4 Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Tue, 22 Jul 2025 00:48:50 +0530 Subject: [PATCH 6/6] feat: add tests --- lib/msgfmt/msgfmt_test.go | 2 +- .../format/gemini/first_message/expected.txt | 5 +++ .../format/gemini/first_message/msg.txt | 13 ++++++ .../format/gemini/first_message/user.txt | 0 .../gemini/multi-line-input/expected.txt | 14 ++++++ .../format/gemini/multi-line-input/msg.txt | 43 +++++++++++++++++++ .../format/gemini/multi-line-input/user.txt | 19 ++++++++ .../format/gemini/second_message/expected.txt | 1 + .../format/gemini/second_message/msg.txt | 12 ++++++ .../format/gemini/second_message/user.txt | 1 + 10 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 lib/msgfmt/testdata/format/gemini/first_message/expected.txt create mode 100644 lib/msgfmt/testdata/format/gemini/first_message/msg.txt create mode 100644 lib/msgfmt/testdata/format/gemini/first_message/user.txt create mode 100644 lib/msgfmt/testdata/format/gemini/multi-line-input/expected.txt create mode 100644 lib/msgfmt/testdata/format/gemini/multi-line-input/msg.txt create mode 100644 lib/msgfmt/testdata/format/gemini/multi-line-input/user.txt create mode 100644 lib/msgfmt/testdata/format/gemini/second_message/expected.txt create mode 100644 lib/msgfmt/testdata/format/gemini/second_message/msg.txt create mode 100644 lib/msgfmt/testdata/format/gemini/second_message/user.txt diff --git a/lib/msgfmt/msgfmt_test.go b/lib/msgfmt/msgfmt_test.go index 9758741..b02cbbc 100644 --- a/lib/msgfmt/msgfmt_test.go +++ b/lib/msgfmt/msgfmt_test.go @@ -218,7 +218,7 @@ func TestTrimEmptyLines(t *testing.T) { func TestFormatAgentMessage(t *testing.T) { dir := "testdata/format" - agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeCodex, AgentTypeCustom} + agentTypes := []AgentType{AgentTypeClaude, AgentTypeGoose, AgentTypeAider, AgentTypeGemini, AgentTypeCodex, AgentTypeCustom} for _, agentType := range agentTypes { t.Run(string(agentType), func(t *testing.T) { cases, err := testdataDir.ReadDir(path.Join(dir, string(agentType))) diff --git a/lib/msgfmt/testdata/format/gemini/first_message/expected.txt b/lib/msgfmt/testdata/format/gemini/first_message/expected.txt new file mode 100644 index 0000000..baf10ec --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/first_message/expected.txt @@ -0,0 +1,5 @@ +Tips for getting started: +1. Ask questions, edit files, or run commands. +2. Be specific for the best results. +3. Create GEMINI.md files to customize your interactions with Gemini. +4. /help for more information. \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/gemini/first_message/msg.txt b/lib/msgfmt/testdata/format/gemini/first_message/msg.txt new file mode 100644 index 0000000..2cad627 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/first_message/msg.txt @@ -0,0 +1,13 @@ +Tips for getting started: +1. Ask questions, edit files, or run commands. +2. Be specific for the best results. +3. Create GEMINI.md files to customize your interactions with Gemini. +4. /help for more information. + + + +╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ > Type your message or @path/to/file │ +╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +~/Documents/work/agentapi (feat-claude-cli*) no sandbox (see /docs) gemini-2.5-pro (100% context left) diff --git a/lib/msgfmt/testdata/format/gemini/first_message/user.txt b/lib/msgfmt/testdata/format/gemini/first_message/user.txt new file mode 100644 index 0000000..e69de29 diff --git a/lib/msgfmt/testdata/format/gemini/multi-line-input/expected.txt b/lib/msgfmt/testdata/format/gemini/multi-line-input/expected.txt new file mode 100644 index 0000000..00bee4c --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/multi-line-input/expected.txt @@ -0,0 +1,14 @@ + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ✔ SearchText 'setInterval\(fetchMessages, 1000\)' in **/*.tsx │ + │ │ + │ No matches found │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ✔ SearchText 'checkServerStatus, 250' in **/*.tsx │ + │ │ + │ No matches found │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +✦ I couldn't find that exact code snippet in the project. It's possible it has been modified or is from a file that wasn't included in the initial project listing. + + To help me locate it, could you tell me more about what the code does or what component it might be a part of? For example, is it part of the main chat window, a status indicator, + or something else? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/gemini/multi-line-input/msg.txt b/lib/msgfmt/testdata/format/gemini/multi-line-input/msg.txt new file mode 100644 index 0000000..2618aa1 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/multi-line-input/msg.txt @@ -0,0 +1,43 @@ +╭───────────────────────────────────────────────────────────────────╮ +│ > Which file is this code from? │ +│ │ +│ ```ts │ +│ // Set up polling for messages and server status │ +│ useEffect(() => { │ +│ // Check server status initially │ +│ checkServerStatus(); │ +│ │ +│ // Set up polling intervals │ +│ const messageInterval = setInterval(fetchMessages, 1000); │ +│ const statusInterval = setInterval(checkServerStatus, 250); │ +│ │ +│ // Clean up intervals on component unmount │ +│ return () => { │ +│ clearInterval(messageInterval); │ +│ clearInterval(statusInterval); │ +│ }; │ +│ }, []); │ +│ ``` │ +╰───────────────────────────────────────────────────────────────────╯ + + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ✔ SearchText 'setInterval\(fetchMessages, 1000\)' in **/*.tsx │ + │ │ + │ No matches found │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ + │ ✔ SearchText 'checkServerStatus, 250' in **/*.tsx │ + │ │ + │ No matches found │ + ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ +✦ I couldn't find that exact code snippet in the project. It's possible it has been modified or is from a file that wasn't included in the initial project listing. + + To help me locate it, could you tell me more about what the code does or what component it might be a part of? For example, is it part of the main chat window, a status indicator, + or something else? + + +╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ > Type your message or @path/to/file │ +╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +~/Documents/work/agentapi (feat-claude-cli*) no sandbox (see /docs) gemini-2.5-pro (99% context left) diff --git a/lib/msgfmt/testdata/format/gemini/multi-line-input/user.txt b/lib/msgfmt/testdata/format/gemini/multi-line-input/user.txt new file mode 100644 index 0000000..c3b14b0 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/multi-line-input/user.txt @@ -0,0 +1,19 @@ +Which file is this code from? + +```ts +// Set up polling for messages and server status +useEffect(() => { + // Check server status initially + checkServerStatus(); + + // Set up polling intervals + const messageInterval = setInterval(fetchMessages, 1000); + const statusInterval = setInterval(checkServerStatus, 250); + + // Clean up intervals on component unmount + return () => { + clearInterval(messageInterval); + clearInterval(statusInterval); + }; +}, []); +``` diff --git a/lib/msgfmt/testdata/format/gemini/second_message/expected.txt b/lib/msgfmt/testdata/format/gemini/second_message/expected.txt new file mode 100644 index 0000000..c407519 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/second_message/expected.txt @@ -0,0 +1 @@ +✦ I am ready to assist you. What can I help you with? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/gemini/second_message/msg.txt b/lib/msgfmt/testdata/format/gemini/second_message/msg.txt new file mode 100644 index 0000000..21e86b3 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/second_message/msg.txt @@ -0,0 +1,12 @@ +╭──────────────────╮ +│ > How are you? │ +╰──────────────────╯ + +✦ I am ready to assist you. What can I help you with? + + +╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ +│ > Type your message or @path/to/file │ +╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ + +~/Documents/work/agentapi (feat-claude-cli*) no sandbox (see /docs) gemini-2.5-pro (99% context left) \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/gemini/second_message/user.txt b/lib/msgfmt/testdata/format/gemini/second_message/user.txt new file mode 100644 index 0000000..3099b43 --- /dev/null +++ b/lib/msgfmt/testdata/format/gemini/second_message/user.txt @@ -0,0 +1 @@ +How are you? \ No newline at end of file