mirror of
https://github.com/lukaszraczylo/claude-mnemonic.git
synced 2026-06-05 23:03:55 +00:00
fixup! fixup! fixup! fixup! chore: update marketplace for v0.11.37
march-improvements
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/lukaszraczylo/claude-mnemonic/pkg/hooks"
|
||||
"github.com/lukaszraczylo/claude-mnemonic/pkg/sanitize"
|
||||
)
|
||||
|
||||
// Input is the hook input from Claude Code.
|
||||
@@ -148,14 +149,14 @@ func handleUserPrompt(ctx *hooks.HookContext, input *Input) (string, error) {
|
||||
obsText += "Key facts:\n"
|
||||
for _, fact := range facts {
|
||||
if factStr, ok := fact.(string); ok {
|
||||
obsText += fmt.Sprintf("- %s\n", factStr)
|
||||
obsText += fmt.Sprintf("- %s\n", sanitize.StripSystemXML(factStr))
|
||||
}
|
||||
}
|
||||
obsText += "\n"
|
||||
}
|
||||
|
||||
if narrative, ok := obsMap["narrative"].(string); ok && narrative != "" {
|
||||
obsText += narrative + "\n\n"
|
||||
obsText += sanitize.StripSystemXML(narrative) + "\n\n"
|
||||
}
|
||||
|
||||
obsTokens := estimateTokens(obsText)
|
||||
|
||||
@@ -15,7 +15,7 @@ require (
|
||||
github.com/smacker/go-tree-sitter v0.0.0-20240827094217-dd81d9e9be82
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/sugarme/tokenizer v0.3.0
|
||||
github.com/yalue/onnxruntime_go v1.25.0
|
||||
github.com/yalue/onnxruntime_go v1.27.0
|
||||
golang.org/x/sync v0.19.0
|
||||
gorm.io/driver/sqlite v1.6.0
|
||||
gorm.io/gorm v1.31.1
|
||||
|
||||
@@ -54,6 +54,8 @@ github.com/sugarme/regexpset v0.0.0-20200920021344-4d4ec8eaf93c h1:pwb4kNSHb4K89
|
||||
github.com/sugarme/regexpset v0.0.0-20200920021344-4d4ec8eaf93c/go.mod h1:2gwkXLWbDGUQWeL3RtpCmcY4mzCtU13kb9UsAg9xMaw=
|
||||
github.com/yalue/onnxruntime_go v1.25.0 h1:nlhVau1BpLZ/BYr+WpPZCJRD/WES0qo6dK7aKyyAs3g=
|
||||
github.com/yalue/onnxruntime_go v1.25.0/go.mod h1:b4X26A8pekNb1ACJ58wAXgNKeUCGEAQ9dmACut9Sm/4=
|
||||
github.com/yalue/onnxruntime_go v1.27.0 h1:c1YSgDNtpf0WGtxj3YeRIb8VC5LmM1J+Ve3uHdteC1U=
|
||||
github.com/yalue/onnxruntime_go v1.27.0/go.mod h1:b4X26A8pekNb1ACJ58wAXgNKeUCGEAQ9dmACut9Sm/4=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@@ -49,6 +49,10 @@ func BuildObservationPrompt(exec ToolExecution) string {
|
||||
inputJSON, _ := json.MarshalIndent(toolInput, " ", " ")
|
||||
outputJSON, _ := json.MarshalIndent(toolOutput, " ", " ")
|
||||
|
||||
// Strip system XML artifacts from tool data before building prompt
|
||||
cleanInput := StripSystemXML(string(inputJSON))
|
||||
cleanOutput := StripSystemXML(string(outputJSON))
|
||||
|
||||
timestamp := time.UnixMilli(exec.CreatedAtEpoch).Format(time.RFC3339)
|
||||
|
||||
var sb strings.Builder
|
||||
@@ -58,8 +62,8 @@ func BuildObservationPrompt(exec ToolExecution) string {
|
||||
if exec.CWD != "" {
|
||||
sb.WriteString(fmt.Sprintf(" <working_directory>%s</working_directory>\n", exec.CWD))
|
||||
}
|
||||
sb.WriteString(fmt.Sprintf(" <parameters>%s</parameters>\n", truncate(string(inputJSON), 3000)))
|
||||
sb.WriteString(fmt.Sprintf(" <outcome>%s</outcome>\n", truncate(string(outputJSON), 5000)))
|
||||
sb.WriteString(fmt.Sprintf(" <parameters>%s</parameters>\n", truncate(cleanInput, 3000)))
|
||||
sb.WriteString(fmt.Sprintf(" <outcome>%s</outcome>\n", truncate(cleanOutput, 5000)))
|
||||
sb.WriteString("</observed_from_primary_session>")
|
||||
|
||||
return sb.String()
|
||||
@@ -84,8 +88,10 @@ func BuildSummaryPrompt(req SummaryRequest) string {
|
||||
sb.WriteString("Write progress notes of what was done, what was learned, and what's next. This is a checkpoint to capture progress so far. The session is ongoing - you may receive more requests and tool executions after this summary. Write \"next_steps\" as the current trajectory of work (what's actively being worked on or coming up next), not as post-session future work. Always write at least a minimal summary explaining current progress, even if work is still in early stages, so that users see a summary output tied to each request.\n\n")
|
||||
|
||||
if req.LastAssistantMessage != "" {
|
||||
// Strip system XML artifacts from captured transcript content
|
||||
cleanMsg := StripSystemXML(req.LastAssistantMessage)
|
||||
sb.WriteString("Claude's Full Response to User:\n")
|
||||
sb.WriteString(truncate(req.LastAssistantMessage, 4000))
|
||||
sb.WriteString(truncate(cleanMsg, 4000))
|
||||
sb.WriteString("\n\n")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
// Package sdk provides SDK agent integration for claude-mnemonic.
|
||||
package sdk
|
||||
|
||||
import "github.com/lukaszraczylo/claude-mnemonic/pkg/sanitize"
|
||||
|
||||
// StripSystemXML removes known Claude Code internal XML blocks from text.
|
||||
// Delegates to the shared sanitize package.
|
||||
var StripSystemXML = sanitize.StripSystemXML
|
||||
@@ -0,0 +1,63 @@
|
||||
// Package sanitize provides content cleaning utilities for stripping
|
||||
// Claude Code internal XML artifacts from captured text.
|
||||
package sanitize
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// systemXMLTags lists Claude Code internal XML tags that should be stripped
|
||||
// from captured content before processing. These are system-level artifacts
|
||||
// that pollute observations and summaries when stored.
|
||||
var systemXMLTags = []string{
|
||||
// Claude Code task/agent system
|
||||
"task-notification",
|
||||
// System reminders injected by Claude Code
|
||||
"system-reminder",
|
||||
// Claude-mnemonic's own context injection
|
||||
"relevant-memory",
|
||||
// Hook output wrappers
|
||||
"user-prompt-submit-hook",
|
||||
// Large output persistence
|
||||
"persisted-output",
|
||||
// Tool loading system
|
||||
"available-deferred-tools",
|
||||
// Fast mode info
|
||||
"fast_mode_info",
|
||||
// Anthropic internal
|
||||
"antml_thinking",
|
||||
"antml_function_calls",
|
||||
}
|
||||
|
||||
// systemXMLRegexps are compiled regexps for each tag, built once at init.
|
||||
var systemXMLRegexps []*regexp.Regexp
|
||||
|
||||
func init() {
|
||||
systemXMLRegexps = make([]*regexp.Regexp, len(systemXMLTags))
|
||||
for i, tag := range systemXMLTags {
|
||||
// Match opening tag (with optional attributes), content (including newlines), and closing tag
|
||||
systemXMLRegexps[i] = regexp.MustCompile(`(?s)<` + regexp.QuoteMeta(tag) + `[^>]*>.*?</` + regexp.QuoteMeta(tag) + `>`)
|
||||
}
|
||||
}
|
||||
|
||||
// StripSystemXML removes known Claude Code internal XML blocks from text.
|
||||
// This prevents system artifacts like <task-notification>, <system-reminder>,
|
||||
// and <relevant-memory> from being stored in observations and summaries.
|
||||
func StripSystemXML(s string) string {
|
||||
// Quick check: if no angle brackets, nothing to strip
|
||||
if !strings.Contains(s, "<") {
|
||||
return s
|
||||
}
|
||||
|
||||
for _, re := range systemXMLRegexps {
|
||||
s = re.ReplaceAllString(s, "")
|
||||
}
|
||||
|
||||
// Clean up resulting double-blank-lines from removed blocks
|
||||
for strings.Contains(s, "\n\n\n") {
|
||||
s = strings.ReplaceAll(s, "\n\n\n", "\n\n")
|
||||
}
|
||||
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package sanitize
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestStripSystemXML(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "no XML tags",
|
||||
input: "Hello, this is plain text with no XML.",
|
||||
expected: "Hello, this is plain text with no XML.",
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "task-notification block",
|
||||
input: "Before\n<task-notification>\n<task-id>abc123</task-id>\n<status>completed</status>\n<summary>Agent completed</summary>\n<result>Some result text</result>\n</task-notification>\nAfter",
|
||||
expected: "Before\n\nAfter",
|
||||
},
|
||||
{
|
||||
name: "system-reminder block",
|
||||
input: "Content before\n<system-reminder>\nSome system reminder text\n</system-reminder>\nContent after",
|
||||
expected: "Content before\n\nContent after",
|
||||
},
|
||||
{
|
||||
name: "relevant-memory block",
|
||||
input: "<relevant-memory>\n# Relevant Knowledge\n## 1. Something\n</relevant-memory>\nActual content",
|
||||
expected: "Actual content",
|
||||
},
|
||||
{
|
||||
name: "system-reminder with attributes",
|
||||
input: "Before <system-reminder type=\"hook\">reminder text</system-reminder> After",
|
||||
expected: "Before After",
|
||||
},
|
||||
{
|
||||
name: "multiple different tags",
|
||||
input: "Start\n<task-notification><status>done</status></task-notification>\nMiddle\n<system-reminder>reminder</system-reminder>\nEnd",
|
||||
expected: "Start\n\nMiddle\n\nEnd",
|
||||
},
|
||||
{
|
||||
name: "persisted-output block",
|
||||
input: "Result: <persisted-output>\nLarge output stored at: /tmp/foo\n</persisted-output> done",
|
||||
expected: "Result: done",
|
||||
},
|
||||
{
|
||||
name: "available-deferred-tools block",
|
||||
input: "<available-deferred-tools>\nTool1\nTool2\n</available-deferred-tools>\nUser message",
|
||||
expected: "User message",
|
||||
},
|
||||
{
|
||||
name: "fast_mode_info block",
|
||||
input: "Text <fast_mode_info>\nFast mode uses same model\n</fast_mode_info> more text",
|
||||
expected: "Text more text",
|
||||
},
|
||||
{
|
||||
name: "preserves non-system XML",
|
||||
input: "<observation><type>bugfix</type><title>Fixed thing</title></observation>",
|
||||
expected: "<observation><type>bugfix</type><title>Fixed thing</title></observation>",
|
||||
},
|
||||
{
|
||||
name: "no angle brackets fast path",
|
||||
input: "Just plain text without any special characters",
|
||||
expected: "Just plain text without any special characters",
|
||||
},
|
||||
{
|
||||
name: "collapses triple newlines",
|
||||
input: "Before\n<system-reminder>x</system-reminder>\n\n\nAfter",
|
||||
expected: "Before\n\nAfter",
|
||||
},
|
||||
{
|
||||
name: "real-world task notification",
|
||||
input: "Here is the analysis:\n<task-notification>\n<task-id>a077fd04aed547ce1</task-id>\n<tool-use-id>toolu_01Kw2GwsArQQR1F9aV3VfzhP</tool-use-id>\n<status>completed</status>\n<summary>Agent \"Analyze agency-agents repo structure\" completed</summary>\n<result>Now I have all the information I need. Let me compile a comprehensive report.\n\n## Agency-Agents Directory Exploration Report\n\n### 1. Agent File Count and Organization\n\n**Total Agent Files (excluding strategy docs):** 61 agents across 9 categories</result>\n</task-notification>\nThe repo has 61 agents.",
|
||||
expected: "Here is the analysis:\n\nThe repo has 61 agents.",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := StripSystemXML(tt.input)
|
||||
assert.Equal(t, tt.expected, result)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkStripSystemXML(b *testing.B) {
|
||||
// Text with no XML (fast path)
|
||||
b.Run("no_xml", func(b *testing.B) {
|
||||
text := "This is plain text without any XML tags at all."
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
StripSystemXML(text)
|
||||
}
|
||||
})
|
||||
|
||||
// Text with system XML to strip
|
||||
b.Run("with_xml", func(b *testing.B) {
|
||||
text := "Before\n<task-notification>\n<task-id>abc</task-id>\n<status>done</status>\n</task-notification>\n<system-reminder>reminder text here</system-reminder>\nAfter"
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
StripSystemXML(text)
|
||||
}
|
||||
})
|
||||
}
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.10.5-15-g385d05a-dirty",
|
||||
"version": "v0.11.43-2-g11fd196-dirty",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.10.5-15-g385d05a-dirty",
|
||||
"version": "v0.11.43-2-g11fd196-dirty",
|
||||
"dependencies": {
|
||||
"vis-data": "^7.1.9",
|
||||
"vis-network": "^9.1.9",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-mnemonic-dashboard",
|
||||
"version": "v0.10.5-15-g385d05a-dirty",
|
||||
"version": "v0.11.43-2-g11fd196-dirty",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user