Add /search command to search session history
ober
5b1f05286c99994c4996982d80f0f4d12d12128c
--- a/lib/jcode/core/session.sls +++ b/lib/jcode/core/session.sls @@ -4,7 +4,7 @@ (library (jcode core session) (export session-init-db session-create session-load session-list - session-add-message session-get-messages + session-search session-add-message session-get-messages session-update-title session-delete session-id session-title session-created session-messages) (import @@ -119,6 +119,21 @@ "UPDATE sessions SET title = ?, updated_at = ? WHERE id = ?" title (timestamp-now) session-id) (sqlite-close db))) + (def (session-search term) + "Search all messages for TERM. Returns list of (session-title role snippet)." + (let* ([db (open-db)] + [pattern (string-append "%" term "%")] + [rows (sqlite-query + db + "SELECT s.title, m.role, m.content\n FROM messages m\n JOIN sessions s ON s.id = m.session_id\n WHERE m.content LIKE ?\n ORDER BY m.created_at DESC\n LIMIT 50" + pattern)]) + (sqlite-close db) + (map (lambda (row) + (list + (vector-ref row 0) + (vector-ref row 1) + (vector-ref row 2))) + rows))) (def (session-delete session-id) (let ([db (open-db)]) (sqlite-eval --- a/lib/jcode/ui/tui-input.sls +++ b/lib/jcode/ui/tui-input.sls @@ -34,6 +34,7 @@ ("/tools" . "List available tools") ("/clear" . "Start new session") ("/sessions" . "List saved sessions") + ("/search" . "Search session history") ("/compact" . "Compact conversation") ("/quit" . "Exit jcode") ("/theme" . "Switch color theme") ("/sidebar" . "Toggle sidebar")))) --- a/lib/jcode/ui/tui.sls +++ b/lib/jcode/ui/tui.sls @@ -338,6 +338,7 @@ " /tools List available tools" " /clear Start new session" " /sessions List sessions" + " /search <term> Search session history" " /theme Cycle theme (Ctrl-T)" " /sidebar Toggle sidebar (Ctrl-B)" " /quit Exit" "" @@ -408,6 +409,41 @@ (session-title s))) sessions) "\n")))))] + [(string-prefix? "search " cmd) + (let* ([term (string-trim + (substring cmd 7 (string-length cmd)))] + [results (session-search term)]) + (add-message! + state + (msg-block-system + (if (null? results) + (format "No matches for \"~a\"." term) + (let ([lines (map (lambda (r) + (let* ([title (car r)] + [role (cadr r)] + [content (caddr r)] + [snippet (if (> (string-length + content) + 120) + (string-append + (substring + content + 0 + 120) + "...") + content)]) + (format + " [~a] (~a) ~a" + title + role + snippet))) + results)]) + (string-append + (format + "Search: \"~a\" (~a matches)\n" + term + (length results)) + (string-join lines "\n")))))))] [#t (add-message! state --- a/src/jcode/core/session.ss +++ b/src/jcode/core/session.ss @@ -4,6 +4,7 @@ session-create session-load session-list + session-search session-add-message session-get-messages session-update-title @@ -150,6 +151,25 @@ title (timestamp-now) session-id) (sqlite-close db))) +(def (session-search term) + "Search all messages for TERM. Returns list of (session-title role snippet)." + (let* ((db (open-db)) + (pattern (string-append "%" term "%")) + (rows (sqlite-query db + "SELECT s.title, m.role, m.content + FROM messages m + JOIN sessions s ON s.id = m.session_id + WHERE m.content LIKE ? + ORDER BY m.created_at DESC + LIMIT 50" + pattern))) + (sqlite-close db) + (map (lambda (row) + (list (vector-ref row 0) + (vector-ref row 1) + (vector-ref row 2))) + rows))) + (def (session-delete session-id) (let ((db (open-db))) (sqlite-eval db "DELETE FROM messages WHERE session_id = ?" session-id) --- a/src/jcode/ui/tui-input.ss +++ b/src/jcode/ui/tui-input.ss @@ -43,6 +43,7 @@ ("/tools" . "List available tools") ("/clear" . "Start new session") ("/sessions" . "List saved sessions") + ("/search" . "Search session history") ("/compact" . "Compact conversation") ("/quit" . "Exit jcode") ("/theme" . "Switch color theme") --- a/src/jcode/ui/tui.ss +++ b/src/jcode/ui/tui.ss @@ -437,6 +437,7 @@ " /tools List available tools" " /clear Start new session" " /sessions List sessions" + " /search <term> Search session history" " /theme Cycle theme (Ctrl-T)" " /sidebar Toggle sidebar (Ctrl-B)" " /quit Exit" @@ -496,6 +497,26 @@ (map (lambda (s) (format " ~a ~a" (session-id s) (session-title s))) sessions) "\n")))))) + ((string-prefix? "search " cmd) + (let* ((term (string-trim (substring cmd 7 (string-length cmd)))) + (results (session-search term))) + (add-message! state + (msg-block-system + (if (null? results) + (format "No matches for \"~a\"." term) + (let ((lines + (map (lambda (r) + (let* ((title (car r)) + (role (cadr r)) + (content (caddr r)) + (snippet (if (> (string-length content) 120) + (string-append (substring content 0 120) "...") + content))) + (format " [~a] (~a) ~a" title role snippet))) + results))) + (string-append + (format "Search: \"~a\" (~a matches)\n" term (length results)) + (string-join lines "\n")))))))) (#t (add-message! state (msg-block-system (format "Unknown command: /~a" cmd)))))))