Skip to content

Commit 12d4add

Browse files
committed
session_search get: return message content; glob: support ** patterns
Two fixes: 1. handleGet now returns actual session messages (role + content) in a session_messages field, not just a count. The LLM can finally read what was actually said in past sessions instead of only seeing 2-line buffer summaries. 2. glob tool now supports ** (recursive globstar) patterns. Go's filepath.Match/Glob don't support ** — they treat it as literal '*'. Convert ** patterns to equivalent regex when detected so queries like '**/*.json' actually find files in subdirectories.
1 parent f4ffc8a commit 12d4add

2 files changed

Lines changed: 67 additions & 8 deletions

File tree

cmd/odek/file_tool.go

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,38 @@ func (t *globTool) Call(argsJSON string) (result string, err error) {
10701070

10711071
// If pattern has path separators, use filepath.Walk
10721072
if strings.Contains(args.Pattern, "/") || strings.Contains(args.Pattern, "\\") {
1073+
// Support ** (recursive globstar) by converting to regex.
1074+
// filepath.Match does NOT support ** — it treats it as literal "*".
1075+
// When ** is present, compile a regex and use it for matching.
1076+
var globRe *regexp.Regexp
1077+
if strings.Contains(args.Pattern, "**") {
1078+
// Convert glob pattern to regex:
1079+
// ** -> .* (match any chars including path separators)
1080+
// * -> [^/]* (match any chars except path separators)
1081+
// ? -> [^/] (match any single char except path separator)
1082+
// Escape other regex meta-characters.
1083+
var reStr strings.Builder
1084+
reStr.WriteString("^")
1085+
for i := 0; i < len(args.Pattern); i++ {
1086+
ch := args.Pattern[i]
1087+
switch {
1088+
case ch == '*' && i+1 < len(args.Pattern) && args.Pattern[i+1] == '*':
1089+
reStr.WriteString(".*")
1090+
i++
1091+
case ch == '*':
1092+
reStr.WriteString("[^/]*")
1093+
case ch == '?':
1094+
reStr.WriteString("[^/]")
1095+
case ch == '.' || ch == '+' || ch == '^' || ch == '$' || ch == '|' || ch == '(' || ch == ')' || ch == '[' || ch == ']' || ch == '{' || ch == '}' || ch == '\\':
1096+
reStr.WriteByte('\\')
1097+
reStr.WriteByte(ch)
1098+
default:
1099+
reStr.WriteByte(ch)
1100+
}
1101+
}
1102+
reStr.WriteString("$")
1103+
globRe, _ = regexp.Compile(reStr.String())
1104+
}
10731105
filepath.Walk(args.Path, func(path string, info os.FileInfo, err error) error {
10741106
if err != nil || info == nil {
10751107
return nil
@@ -1079,12 +1111,21 @@ func (t *globTool) Call(argsJSON string) (result string, err error) {
10791111
if info.Mode()&os.ModeSymlink != 0 {
10801112
return nil
10811113
}
1082-
match, _ := filepath.Match(args.Pattern, path)
1083-
if !match {
1084-
// Also try relative to root
1085-
rel, err := filepath.Rel(args.Path, path)
1086-
if err == nil {
1087-
match, _ = filepath.Match(args.Pattern, rel)
1114+
var match bool
1115+
if globRe != nil {
1116+
// Use regex matching for ** patterns.
1117+
rel, _ := filepath.Rel(args.Path, path)
1118+
if rel != "" {
1119+
match = globRe.MatchString(rel)
1120+
}
1121+
} else {
1122+
match, _ = filepath.Match(args.Pattern, path)
1123+
if !match {
1124+
// Also try relative to root
1125+
rel, err := filepath.Rel(args.Path, path)
1126+
if err == nil {
1127+
match, _ = filepath.Match(args.Pattern, rel)
1128+
}
10881129
}
10891130
}
10901131
if match {

cmd/odek/session_search_tool.go

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,17 @@ type sessionSearchResult struct {
5454
UpdatedAt string `json:"updated_at,omitempty"`
5555
Model string `json:"model,omitempty"`
5656
Buffer []string `json:"buffer,omitempty"`
57-
Messages int `json:"messages,omitempty"`
57+
Messages int `json:"messages,omitempty"`
58+
SessionMessages []sessionMessage `json:"session_messages,omitempty"`
5859
Error string `json:"error,omitempty"`
5960
}
6061

62+
// sessionMessage is a single message in a session.
63+
type sessionMessage struct {
64+
Role string `json:"role"`
65+
Content string `json:"content"`
66+
}
67+
6168
func (t *sessionSearchTool) Schema() any {
6269
return map[string]any{
6370
"type": "object",
@@ -338,7 +345,17 @@ func (t *sessionSearchTool) handleGet(id string) (string, error) {
338345
})
339346
}
340347

341-
msgCount := len(sess.Messages)
348+
// Build session messages for the LLM to read.
349+
var sessionMessages []sessionMessage
350+
for _, m := range sess.Messages {
351+
if m.Role == "user" || m.Role == "assistant" {
352+
sessionMessages = append(sessionMessages, sessionMessage{
353+
Role: m.Role,
354+
Content: m.Content,
355+
})
356+
}
357+
}
358+
msgCount := len(sessionMessages)
342359
return jsonResult(sessionSearchResult{
343360
Action: "get",
344361
ID: sess.ID,
@@ -349,6 +366,7 @@ func (t *sessionSearchTool) handleGet(id string) (string, error) {
349366
Model: sess.Model,
350367
Buffer: sess.Buffer,
351368
Messages: msgCount,
369+
SessionMessages: sessionMessages,
352370
})
353371
}
354372

0 commit comments

Comments
 (0)