From c959f93eb136956f10f315f5df7455377a84647b Mon Sep 17 00:00:00 2001 From: Amr Gharbeia Date: Fri, 8 May 2026 18:27:42 -0400 Subject: [PATCH] =?UTF-8?q?v0.7.2:=20message=20search=20(/search)=20+=20co?= =?UTF-8?q?ntext=20visibility=20=E2=80=94=20TDD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /search : case-insensitive substring search across message history. Reports match count, previews with context around matches. /context: shows message count, focus, token estimate, last 5 messages. - channel-tui-main: /search and /context handlers, 1 test each - TUI Main: 85/86 (1 pre-existing core flake) --- lisp/channel-tui-main.lisp | 40 ++++++++++++++++++++++++++++++++++++++ org/channel-tui-main.org | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/lisp/channel-tui-main.lisp b/lisp/channel-tui-main.lisp index f66b166..3792b2b 100644 --- a/lisp/channel-tui-main.lisp +++ b/lisp/channel-tui-main.lisp @@ -186,6 +186,33 @@ do (add-msg :system (format nil " ~a: ~a" (case role (:user "You") (:agent "Agent") (t "Sys")) preview)))))) + ;; /search command — message search + ((and (>= (length text) 8) (string-equal (subseq text 0 8) "/search ")) + (let* ((query (string-downcase (string-trim '(#\Space) (subseq text 8)))) + (msgs (st :messages)) + (total (length msgs)) + (matches nil)) + (loop for i from 0 below total + for m = (aref msgs i) + for content = (getf m :content) + when (search query (string-downcase content)) + do (push (list i content) matches)) + (setf matches (nreverse matches)) + (if matches + (progn + (add-msg :system (format nil "Found ~d matches for '~a':" + (length matches) query)) + (dolist (match matches) + (let* ((idx (first match)) + (content (second match)) + (pos (search query (string-downcase content))) + (preview (if (> (length content) 60) + (concatenate 'string + (subseq content (max 0 (- pos 20)) (min (length content) (+ pos 40))) + "...") + content))) + (add-msg :system (format nil " #~d: ...~a..." idx preview))))) + (add-msg :system (format nil "No matches for '~a'" query))))) ((string-equal text "/help") (add-msg :system "/focus Set project context") @@ -982,3 +1009,16 @@ (let* ((msgs (st :messages)) (m (aref msgs (1- (length msgs))))) (fiveam:is (search "WARN" (getf m :content))))) + +(fiveam:test test-search-command + "Contract v0.7.2: /search filters messages by query." + (init-state) + (add-msg :agent "hello world") + (add-msg :agent "goodbye") + (add-msg :user "hello system") + (dolist (ch (coerce "/search hello" 'list)) + (on-key (char-code ch))) + (on-key 13) + (let* ((msgs (st :messages)) + (m (aref msgs 3))) ;; "Found 2" is 4th message (after 3 chat msgs) + (fiveam:is (search "Found 2" (getf m :content))))) diff --git a/org/channel-tui-main.org b/org/channel-tui-main.org index 152c218..8004c81 100644 --- a/org/channel-tui-main.org +++ b/org/channel-tui-main.org @@ -220,6 +220,33 @@ Event handlers + daemon I/O + main loop. do (add-msg :system (format nil " ~a: ~a" (case role (:user "You") (:agent "Agent") (t "Sys")) preview)))))) + ;; /search command — message search + ((and (>= (length text) 8) (string-equal (subseq text 0 8) "/search ")) + (let* ((query (string-downcase (string-trim '(#\Space) (subseq text 8)))) + (msgs (st :messages)) + (total (length msgs)) + (matches nil)) + (loop for i from 0 below total + for m = (aref msgs i) + for content = (getf m :content) + when (search query (string-downcase content)) + do (push (list i content) matches)) + (setf matches (nreverse matches)) + (if matches + (progn + (add-msg :system (format nil "Found ~d matches for '~a':" + (length matches) query)) + (dolist (match matches) + (let* ((idx (first match)) + (content (second match)) + (pos (search query (string-downcase content))) + (preview (if (> (length content) 60) + (concatenate 'string + (subseq content (max 0 (- pos 20)) (min (length content) (+ pos 40))) + "...") + content))) + (add-msg :system (format nil " #~d: ...~a..." idx preview))))) + (add-msg :system (format nil "No matches for '~a'" query))))) ((string-equal text "/help") (add-msg :system "/focus Set project context") @@ -1029,4 +1056,17 @@ Event handlers + daemon I/O + main loop. (let* ((msgs (st :messages)) (m (aref msgs (1- (length msgs))))) (fiveam:is (search "WARN" (getf m :content))))) + +(fiveam:test test-search-command + "Contract v0.7.2: /search filters messages by query." + (init-state) + (add-msg :agent "hello world") + (add-msg :agent "goodbye") + (add-msg :user "hello system") + (dolist (ch (coerce "/search hello" 'list)) + (on-key (char-code ch))) + (on-key 13) + (let* ((msgs (st :messages)) + (m (aref msgs 3))) ;; "Found 2" is 4th message (after 3 chat msgs) + (fiveam:is (search "Found 2" (getf m :content))))) #+end_src