mirror of
https://wget.la/https://github.com/leookun/cursor-byok
synced 2026-08-18 03:57:06 +08:00
fix(prompt): replay selected Cursor commands
Include client-resolved command content in model-visible user history so slash commands such as /init retain their actual instructions.
This commit is contained in:
@@ -26,10 +26,13 @@ func BuildUserMessageReplayMessage(userMessage *agentv1.UserMessage) (Message, b
|
|||||||
|
|
||||||
func buildUserReplayMessage(text string, selectedContext *agentv1.SelectedContext) (Message, bool) {
|
func buildUserReplayMessage(text string, selectedContext *agentv1.SelectedContext) (Message, bool) {
|
||||||
images := buildSelectedImageContentParts(selectedContext)
|
images := buildSelectedImageContentParts(selectedContext)
|
||||||
sections := make([]string, 0, 4)
|
sections := make([]string, 0, 5)
|
||||||
if text != "" {
|
if text != "" {
|
||||||
sections = append(sections, formatMessageText(fmt.Sprintf("<user_query>\n%s\n</user_query>", text)))
|
sections = append(sections, formatMessageText(fmt.Sprintf("<user_query>\n%s\n</user_query>", text)))
|
||||||
}
|
}
|
||||||
|
if cursorCommands := buildSelectedCursorCommandsPromptSection(selectedContext); cursorCommands != "" {
|
||||||
|
sections = append(sections, cursorCommands)
|
||||||
|
}
|
||||||
if ideState := buildSelectedIDEStatePromptSection(selectedContext); ideState != "" {
|
if ideState := buildSelectedIDEStatePromptSection(selectedContext); ideState != "" {
|
||||||
sections = append(sections, ideState)
|
sections = append(sections, ideState)
|
||||||
}
|
}
|
||||||
@@ -62,6 +65,32 @@ func buildUserReplayMessage(text string, selectedContext *agentv1.SelectedContex
|
|||||||
}, true
|
}, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildSelectedCursorCommandsPromptSection(selectedContext *agentv1.SelectedContext) string {
|
||||||
|
if selectedContext == nil || len(selectedContext.GetCursorCommands()) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
entries := make([]string, 0, len(selectedContext.GetCursorCommands()))
|
||||||
|
for _, command := range selectedContext.GetCursorCommands() {
|
||||||
|
if command == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
content := strings.TrimSpace(command.GetContent())
|
||||||
|
if content == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name := strings.TrimSpace(command.GetName())
|
||||||
|
if name == "" {
|
||||||
|
entries = append(entries, "<cursor_command>\n"+content+"\n</cursor_command>")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
entries = append(entries, fmt.Sprintf("<cursor_command name=\"%s\">\n%s\n</cursor_command>", escapePromptXML(name), content))
|
||||||
|
}
|
||||||
|
if len(entries) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "<cursor_commands>\n" + strings.Join(entries, "\n\n") + "\n</cursor_commands>"
|
||||||
|
}
|
||||||
|
|
||||||
func buildSelectedIDEStatePromptSection(selectedContext *agentv1.SelectedContext) string {
|
func buildSelectedIDEStatePromptSection(selectedContext *agentv1.SelectedContext) string {
|
||||||
if selectedContext == nil || selectedContext.GetInvocationContext() == nil {
|
if selectedContext == nil || selectedContext.GetInvocationContext() == nil {
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -2,9 +2,67 @@ package promptengine
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"cursor/gen/agentv1"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBuildUserMessageReplayMessageIncludesSelectedCursorCommands(t *testing.T) {
|
||||||
|
message, ok := BuildUserMessageReplayMessage(&agentv1.UserMessage{
|
||||||
|
Text: "/init",
|
||||||
|
SelectedContext: &agentv1.SelectedContext{
|
||||||
|
CursorCommands: []*agentv1.SelectedCursorCommand{
|
||||||
|
{Name: "init", Content: "Analyze the repository and create AGENTS.md."},
|
||||||
|
{Name: `review"<&`, Content: "Review the implementation."},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("BuildUserMessageReplayMessage() returned ok=false")
|
||||||
|
}
|
||||||
|
|
||||||
|
want := strings.Join([]string{
|
||||||
|
"<user_query>\n/init\n</user_query>",
|
||||||
|
"<cursor_commands>\n" +
|
||||||
|
"<cursor_command name=\"init\">\nAnalyze the repository and create AGENTS.md.\n</cursor_command>\n\n" +
|
||||||
|
"<cursor_command name=\"review"<&\">\nReview the implementation.\n</cursor_command>\n" +
|
||||||
|
"</cursor_commands>",
|
||||||
|
}, "\n\n")
|
||||||
|
if message.Role != "user" || message.Content != want {
|
||||||
|
t.Fatalf("message = %#v, want content %q", message, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBuildUserMessageReplayMessageSkipsEmptyCursorCommandsAndKeepsOrder(t *testing.T) {
|
||||||
|
message, ok := BuildUserMessageReplayMessage(&agentv1.UserMessage{
|
||||||
|
Text: "run commands",
|
||||||
|
SelectedContext: &agentv1.SelectedContext{
|
||||||
|
CursorCommands: []*agentv1.SelectedCursorCommand{
|
||||||
|
nil,
|
||||||
|
{Name: "empty", Content: " "},
|
||||||
|
{Content: "First command."},
|
||||||
|
{Name: "second", Content: "Second command."},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("BuildUserMessageReplayMessage() returned ok=false")
|
||||||
|
}
|
||||||
|
|
||||||
|
first := strings.Index(message.Content, "First command.")
|
||||||
|
second := strings.Index(message.Content, "Second command.")
|
||||||
|
if first < 0 || second < 0 || first >= second {
|
||||||
|
t.Fatalf("cursor command order was not preserved: %q", message.Content)
|
||||||
|
}
|
||||||
|
if strings.Contains(message.Content, "empty") {
|
||||||
|
t.Fatalf("empty cursor command was not skipped: %q", message.Content)
|
||||||
|
}
|
||||||
|
if !strings.Contains(message.Content, "<cursor_command>\nFirst command.\n</cursor_command>") {
|
||||||
|
t.Fatalf("unnamed cursor command was not rendered safely: %q", message.Content)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBuildReplayMessagesFromPendingAssistantOutputsKeepsTextAndToolCallInOneAssistantTurn(t *testing.T) {
|
func TestBuildReplayMessagesFromPendingAssistantOutputsKeepsTextAndToolCallInOneAssistantTurn(t *testing.T) {
|
||||||
raw := `{
|
raw := `{
|
||||||
"id":"1",
|
"id":"1",
|
||||||
|
|||||||
Reference in New Issue
Block a user