fix: also prefix next system block with Claude Code banner
This commit is contained in:
@@ -2,6 +2,7 @@ package service
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -134,6 +135,8 @@ func TestSystemIncludesClaudeCodePrompt(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInjectClaudeCodePrompt(t *testing.T) {
|
||||
claudePrefix := strings.TrimSpace(claudeCodeSystemPrompt)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
body string
|
||||
@@ -162,7 +165,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
||||
system: "Custom prompt",
|
||||
wantSystemLen: 2,
|
||||
wantFirstText: claudeCodeSystemPrompt,
|
||||
wantSecondText: "Custom prompt",
|
||||
wantSecondText: claudePrefix + "\n\nCustom prompt",
|
||||
},
|
||||
{
|
||||
name: "string system equals Claude Code prompt",
|
||||
@@ -178,7 +181,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
||||
// Claude Code + Custom = 2
|
||||
wantSystemLen: 2,
|
||||
wantFirstText: claudeCodeSystemPrompt,
|
||||
wantSecondText: "Custom",
|
||||
wantSecondText: claudePrefix + "\n\nCustom",
|
||||
},
|
||||
{
|
||||
name: "array system with existing Claude Code prompt (should dedupe)",
|
||||
@@ -190,7 +193,7 @@ func TestInjectClaudeCodePrompt(t *testing.T) {
|
||||
// Claude Code at start + Other = 2 (deduped)
|
||||
wantSystemLen: 2,
|
||||
wantFirstText: claudeCodeSystemPrompt,
|
||||
wantSecondText: "Other",
|
||||
wantSecondText: claudePrefix + "\n\nOther",
|
||||
},
|
||||
{
|
||||
name: "empty array",
|
||||
|
||||
@@ -2479,6 +2479,10 @@ func injectClaudeCodePrompt(body []byte, system any) []byte {
|
||||
"text": claudeCodeSystemPrompt,
|
||||
"cache_control": map[string]string{"type": "ephemeral"},
|
||||
}
|
||||
// Opencode plugin applies an extra safeguard: it not only prepends the Claude Code
|
||||
// banner, it also prefixes the next system instruction with the same banner plus
|
||||
// a blank line. This helps when upstream concatenates system instructions.
|
||||
claudeCodePrefix := strings.TrimSpace(claudeCodeSystemPrompt)
|
||||
|
||||
var newSystem []any
|
||||
|
||||
@@ -2490,16 +2494,32 @@ func injectClaudeCodePrompt(body []byte, system any) []byte {
|
||||
if strings.TrimSpace(v) == "" || strings.TrimSpace(v) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
||||
newSystem = []any{claudeCodeBlock}
|
||||
} else {
|
||||
newSystem = []any{claudeCodeBlock, map[string]any{"type": "text", "text": v}}
|
||||
// Mirror opencode behavior: keep the banner as a separate system entry,
|
||||
// but also prefix the next system text with the banner.
|
||||
merged := v
|
||||
if !strings.HasPrefix(v, claudeCodePrefix) {
|
||||
merged = claudeCodePrefix + "\n\n" + v
|
||||
}
|
||||
newSystem = []any{claudeCodeBlock, map[string]any{"type": "text", "text": merged}}
|
||||
}
|
||||
case []any:
|
||||
newSystem = make([]any, 0, len(v)+1)
|
||||
newSystem = append(newSystem, claudeCodeBlock)
|
||||
prefixedNext := false
|
||||
for _, item := range v {
|
||||
if m, ok := item.(map[string]any); ok {
|
||||
if text, ok := m["text"].(string); ok && strings.TrimSpace(text) == strings.TrimSpace(claudeCodeSystemPrompt) {
|
||||
continue
|
||||
}
|
||||
// Prefix the first subsequent text system block once.
|
||||
if !prefixedNext {
|
||||
if blockType, _ := m["type"].(string); blockType == "text" {
|
||||
if text, ok := m["text"].(string); ok && strings.TrimSpace(text) != "" && !strings.HasPrefix(text, claudeCodePrefix) {
|
||||
m["text"] = claudeCodePrefix + "\n\n" + text
|
||||
prefixedNext = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
newSystem = append(newSystem, item)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user