From 42e8de9bf8cea4cc256ca4d34f93bbcd5408d13e Mon Sep 17 00:00:00 2001 From: 35C4n0r Date: Tue, 12 Aug 2025 23:14:44 +0530 Subject: [PATCH] feat: add support for cursor cli --- README.md | 2 +- cmd/server/server.go | 2 + cmd/server/server_test.go | 10 ++++ lib/msgfmt/msgfmt.go | 25 ++++++++-- .../cursor/confirmation_box/expected.txt | 19 ++++++++ .../format/cursor/confirmation_box/msg.txt | 26 ++++++++++ .../format/cursor/confirmation_box/user.txt | 1 + .../format/cursor/first_message/expected.txt | 11 +++++ .../format/cursor/first_message/msg.txt | 19 ++++++++ .../format/cursor/first_message/user.txt | 0 .../cursor/multi-line-input/expected.txt | 24 ++++++++++ .../format/cursor/multi-line-input/msg.txt | 48 +++++++++++++++++++ .../format/cursor/multi-line-input/user.txt | 7 +++ .../format/cursor/second_message/expected.txt | 9 ++++ .../format/cursor/second_message/msg.txt | 27 +++++++++++ .../format/cursor/second_message/user.txt | 1 + .../format/cursor/thinking/expected.txt | 1 + .../testdata/format/cursor/thinking/msg.txt | 17 +++++++ .../testdata/format/cursor/thinking/user.txt | 1 + 19 files changed, 245 insertions(+), 5 deletions(-) create mode 100644 lib/msgfmt/testdata/format/cursor/confirmation_box/expected.txt create mode 100644 lib/msgfmt/testdata/format/cursor/confirmation_box/msg.txt create mode 100644 lib/msgfmt/testdata/format/cursor/confirmation_box/user.txt create mode 100644 lib/msgfmt/testdata/format/cursor/first_message/expected.txt create mode 100644 lib/msgfmt/testdata/format/cursor/first_message/msg.txt create mode 100644 lib/msgfmt/testdata/format/cursor/first_message/user.txt create mode 100644 lib/msgfmt/testdata/format/cursor/multi-line-input/expected.txt create mode 100644 lib/msgfmt/testdata/format/cursor/multi-line-input/msg.txt create mode 100644 lib/msgfmt/testdata/format/cursor/multi-line-input/user.txt create mode 100644 lib/msgfmt/testdata/format/cursor/second_message/expected.txt create mode 100644 lib/msgfmt/testdata/format/cursor/second_message/msg.txt create mode 100644 lib/msgfmt/testdata/format/cursor/second_message/user.txt create mode 100644 lib/msgfmt/testdata/format/cursor/thinking/expected.txt create mode 100644 lib/msgfmt/testdata/format/cursor/thinking/msg.txt create mode 100644 lib/msgfmt/testdata/format/cursor/thinking/user.txt diff --git a/README.md b/README.md index e5a81a9..a5502f5 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), [Gemini](https://github.com/google-gemini/gemini-cli), [Sourcegraph Amp](https://github.com/sourcegraph/amp-cli) 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), [Sourcegraph Amp](https://github.com/sourcegraph/amp-cli), [Codex](https://github.com/openai/codex) and [Cursor CLI](https://cursor.com/en/cli) with an HTTP API. ![agentapi-chat](https://github.com/user-attachments/assets/57032c9f-4146-4b66-b219-09e38ab7690d) diff --git a/cmd/server/server.go b/cmd/server/server.go index 93c0541..621bc09 100644 --- a/cmd/server/server.go +++ b/cmd/server/server.go @@ -29,6 +29,7 @@ const ( AgentTypeCodex AgentType = msgfmt.AgentTypeCodex AgentTypeGemini AgentType = msgfmt.AgentTypeGemini AgentTypeAmp AgentType = msgfmt.AgentTypeAmp + AgentTypeCursor AgentType = msgfmt.AgentTypeCursor AgentTypeCustom AgentType = msgfmt.AgentTypeCustom ) @@ -40,6 +41,7 @@ var agentTypeMap = map[AgentType]bool{ AgentTypeCodex: true, AgentTypeGemini: true, AgentTypeAmp: true, + AgentTypeCursor: true, AgentTypeCustom: true, } diff --git a/cmd/server/server_test.go b/cmd/server/server_test.go index c09ffb8..121eade 100644 --- a/cmd/server/server_test.go +++ b/cmd/server/server_test.go @@ -47,6 +47,11 @@ func TestParseAgentType(t *testing.T) { agentTypeVar: "", want: AgentTypeGemini, }, + { + firstArg: "cursor", + agentTypeVar: "", + want: AgentTypeCursor, + }, { firstArg: "amp", agentTypeVar: "", @@ -82,6 +87,11 @@ func TestParseAgentType(t *testing.T) { agentTypeVar: "gemini", want: AgentTypeGemini, }, + { + firstArg: "claude", + agentTypeVar: "cursor", + want: AgentTypeCursor, + }, { firstArg: "aider", agentTypeVar: "claude", diff --git a/lib/msgfmt/msgfmt.go b/lib/msgfmt/msgfmt.go index ca57dec..d4baf50 100644 --- a/lib/msgfmt/msgfmt.go +++ b/lib/msgfmt/msgfmt.go @@ -140,6 +140,22 @@ func findUserInputEndIdx(userInputStartIdx int, msg []rune, userInput []rune) in return msgIdx } +// skipTrailingInputBoxLine skips the next line if it contains any of the markers. +// In case of Gemini and Cursor, the user input is echoed back in a box +// This function searches for the markers passed by the caller and skips the next line if it contains all of them. +func skipTrailingInputBoxLine(lines []string, lastUserInputLineIdx *int, markers ...string) { + if *lastUserInputLineIdx+1 >= len(lines) { + return + } + line := lines[*lastUserInputLineIdx+1] + for _, m := range markers { + if !strings.Contains(line, m) { + return + } + } + *lastUserInputLineIdx++ +} + // RemoveUserInput removes the user input from the message. // Goose, Aider, and Claude Code echo back the user's input to // make it visible in the terminal. This function makes a best effort @@ -169,10 +185,8 @@ func RemoveUserInput(msgRaw string, userInputRaw string) string { // 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 lastUserInputLineIdx+1 < len(msgLines) && strings.Contains(msgLines[lastUserInputLineIdx+1], "╯") && strings.Contains(msgLines[lastUserInputLineIdx+1], "╰") { - lastUserInputLineIdx += 1 - } + skipTrailingInputBoxLine(msgLines, &lastUserInputLineIdx, "╯", "╰") // Gemini + skipTrailingInputBoxLine(msgLines, &lastUserInputLineIdx, "┘", "└") // Cursor return strings.Join(msgLines[lastUserInputLineIdx+1:], "\n") } @@ -207,6 +221,7 @@ const ( AgentTypeCodex AgentType = "codex" AgentTypeGemini AgentType = "gemini" AgentTypeAmp AgentType = "amp" + AgentTypeCursor AgentType = "cursor" AgentTypeCustom AgentType = "custom" ) @@ -238,6 +253,8 @@ func FormatAgentMessage(agentType AgentType, message string, userInput string) s return formatGenericMessage(message, userInput) case AgentTypeAmp: return formatGenericMessage(message, userInput) + case AgentTypeCursor: + return formatGenericMessage(message, userInput) case AgentTypeCustom: return formatGenericMessage(message, userInput) default: diff --git a/lib/msgfmt/testdata/format/cursor/confirmation_box/expected.txt b/lib/msgfmt/testdata/format/cursor/confirmation_box/expected.txt new file mode 100644 index 0000000..2e4162f --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/confirmation_box/expected.txt @@ -0,0 +1,19 @@ + I'll check the repository's root, name, remotes, and current branch. + + + + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ $ git rev-parse --show-toplevel in . │ + │ $ basename "$(git rev-parse --show-toplevel)" in . │ + │ $ git remote -v in . │ + │ $ git rev-parse --abbrev-ref HEAD in . │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Run this command? │ + │ Not in allowlist: git │ + │ → Run (y) (enter) │ + │ Reject (esc or p) │ + │ Add Shell(git) to allowlist? (tab) │ + │ Auto-run all commands (shift+tab) │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/confirmation_box/msg.txt b/lib/msgfmt/testdata/format/cursor/confirmation_box/msg.txt new file mode 100644 index 0000000..1df8d24 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/confirmation_box/msg.txt @@ -0,0 +1,26 @@ + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Which repo is this ? │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + I'll check the repository's root, name, remotes, and current branch. + + + + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ $ git rev-parse --show-toplevel in . │ + │ $ basename "$(git rev-parse --show-toplevel)" in . │ + │ $ git remote -v in . │ + │ $ git rev-parse --abbrev-ref HEAD in . │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Run this command? │ + │ Not in allowlist: git │ + │ → Run (y) (enter) │ + │ Reject (esc or p) │ + │ Add Shell(git) to allowlist? (tab) │ + │ Auto-run all commands (shift+tab) │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/confirmation_box/user.txt b/lib/msgfmt/testdata/format/cursor/confirmation_box/user.txt new file mode 100644 index 0000000..c7d3776 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/confirmation_box/user.txt @@ -0,0 +1 @@ +Which repo is this ? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/first_message/expected.txt b/lib/msgfmt/testdata/format/cursor/first_message/expected.txt new file mode 100644 index 0000000..ed79526 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/first_message/expected.txt @@ -0,0 +1,11 @@ + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ⬢ Welcome to Cursor Agent Beta │ + │ │ + │ Cursor Agent CLI is in beta. Security safeguards are still evolving. It can read, modify, and delete files, and execute shell commands you approve. Use at your own risk and only in trusted environments. │ + │ │ + │ Please read about our security at https://cursor.com/security. │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/first_message/msg.txt b/lib/msgfmt/testdata/format/cursor/first_message/msg.txt new file mode 100644 index 0000000..c560cda --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/first_message/msg.txt @@ -0,0 +1,19 @@ + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ ⬢ Welcome to Cursor Agent Beta │ + │ │ + │ Cursor Agent CLI is in beta. Security safeguards are still evolving. It can read, modify, and delete files, and execute shell commands you approve. Use at your own risk and only in trusted environments. │ + │ │ + │ Please read about our security at https://cursor.com/security. │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ → Plan, search, build anything │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + OpenAI GPT-5 + / for commands · @ for files + diff --git a/lib/msgfmt/testdata/format/cursor/first_message/user.txt b/lib/msgfmt/testdata/format/cursor/first_message/user.txt new file mode 100644 index 0000000..e69de29 diff --git a/lib/msgfmt/testdata/format/cursor/multi-line-input/expected.txt b/lib/msgfmt/testdata/format/cursor/multi-line-input/expected.txt new file mode 100644 index 0000000..727c753 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/multi-line-input/expected.txt @@ -0,0 +1,24 @@ + I’m going to search the codebase for the function name and related identifiers to locate the file containing that snippet. + + ⬢ Searched 4 searches + … 1 earlier item hidden + Searched "removeCodexInputBox" in . + Searched "RemoveUserInput\(" in . + Searched "trimEmptyLines\(" in . + + Scanning relevant Go files to confirm the exact location and provide a precise citation. + + ⬢ Read lib/msgfmt/msgfmt.go + + It’s in lib/msgfmt/msgfmt.go. + + lib/msgfmt/msgfmt.go lines 235-240 + + 235 │ func formatCodexMessage(message string, userInput string) string { + 236 │ message = RemoveUserInput(message, userInput) + 237 │ message = removeCodexInputBox(message) + 238 │ message = trimEmptyLines(message) + 239 │ return message + 240 │ } + + • Found in lib/msgfmt/msgfmt.go (lines 235–240). \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/multi-line-input/msg.txt b/lib/msgfmt/testdata/format/cursor/multi-line-input/msg.txt new file mode 100644 index 0000000..5cc4417 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/multi-line-input/msg.txt @@ -0,0 +1,48 @@ + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Which file has this snippet │ + │ func formatCodexMessage(message string, userInput string) string { │ + │ message = RemoveUserInput(message, userInput) │ + │ message = removeCodexInputBox(message) │ + │ message = trimEmptyLines(message) │ + │ return message │ + │ } │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + I’m going to search the codebase for the function name and related identifiers to locate the file containing that snippet. + + ⬢ Searched 4 searches + … 1 earlier item hidden + Searched "removeCodexInputBox" in . + Searched "RemoveUserInput\(" in . + Searched "trimEmptyLines\(" in . + + Scanning relevant Go files to confirm the exact location and provide a precise citation. + + ⬢ Read lib/msgfmt/msgfmt.go + + It’s in lib/msgfmt/msgfmt.go. + + lib/msgfmt/msgfmt.go lines 235-240 + + 235 │ func formatCodexMessage(message string, userInput string) string { + 236 │ message = RemoveUserInput(message, userInput) + 237 │ message = removeCodexInputBox(message) + 238 │ message = trimEmptyLines(message) + 239 │ return message + 240 │ } + + • Found in lib/msgfmt/msgfmt.go (lines 235–240). + + + + + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ → Add a follow-up │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + OpenAI GPT-5 + / for commands · @ for files diff --git a/lib/msgfmt/testdata/format/cursor/multi-line-input/user.txt b/lib/msgfmt/testdata/format/cursor/multi-line-input/user.txt new file mode 100644 index 0000000..e85fa2a --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/multi-line-input/user.txt @@ -0,0 +1,7 @@ +Which file has this snippet +func formatCodexMessage(message string, userInput string) string { + message = RemoveUserInput(message, userInput) + message = removeCodexInputBox(message) + message = trimEmptyLines(message) + return message +} \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/second_message/expected.txt b/lib/msgfmt/testdata/format/cursor/second_message/expected.txt new file mode 100644 index 0000000..c166795 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/second_message/expected.txt @@ -0,0 +1,9 @@ + I’m going to check the current repository for untracked files and count them. + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ $ git ls-files --others --exclude-standard | wc -l 16ms in current dir │ + │ │ + │ 17 │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + 17 \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/second_message/msg.txt b/lib/msgfmt/testdata/format/cursor/second_message/msg.txt new file mode 100644 index 0000000..1e76f20 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/second_message/msg.txt @@ -0,0 +1,27 @@ + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ How many untracked files are there? │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + I’m going to check the current repository for untracked files and count them. + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ $ git ls-files --others --exclude-standard | wc -l 16ms in current dir │ + │ │ + │ 17 │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + 17 + + + + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ → Add a follow-up │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + ▶︎ Auto-run all commands (shift+tab to turn off) + + OpenAI GPT-5 + / for commands · @ for files diff --git a/lib/msgfmt/testdata/format/cursor/second_message/user.txt b/lib/msgfmt/testdata/format/cursor/second_message/user.txt new file mode 100644 index 0000000..f739c91 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/second_message/user.txt @@ -0,0 +1 @@ +How many untracked files are there? \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/thinking/expected.txt b/lib/msgfmt/testdata/format/cursor/thinking/expected.txt new file mode 100644 index 0000000..c0c75aa --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/thinking/expected.txt @@ -0,0 +1 @@ + ⬡ Thinking. 172 tokens \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/thinking/msg.txt b/lib/msgfmt/testdata/format/cursor/thinking/msg.txt new file mode 100644 index 0000000..0975dfb --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/thinking/msg.txt @@ -0,0 +1,17 @@ + Cursor Agent + ~/Documents/work/agentapi · feat-cursor-cli + + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ Which repo is this ? │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + + + + ⬡ Thinking. 172 tokens + ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ + │ → Add a follow-up ctrl+c to stop │ + └─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ + + OpenAI GPT-5 + / for commands · @ for files \ No newline at end of file diff --git a/lib/msgfmt/testdata/format/cursor/thinking/user.txt b/lib/msgfmt/testdata/format/cursor/thinking/user.txt new file mode 100644 index 0000000..c7d3776 --- /dev/null +++ b/lib/msgfmt/testdata/format/cursor/thinking/user.txt @@ -0,0 +1 @@ +Which repo is this ? \ No newline at end of file