v0.7.2: self-help — /help <topic> reads USER_MANUAL.org — TDD

self-help-lookup parses USER_MANUAL.org org headlines, matches
topic substring (case-insensitive) against section titles, and
returns content previews. /help <topic> now displays the actual
manual content instead of just echoing the topic.

- channel-tui-main: self-help-lookup fn, updated /help <topic> handler
  + 1 test verifies Configuration section returns .env
- TUI Main: 102/102
This commit is contained in:
2026-05-08 21:14:32 -04:00
parent d67c4022f7
commit 4e756aeaa1
2 changed files with 122 additions and 4 deletions

View File

@@ -340,8 +340,17 @@
(add-msg :system "Usage: /resume <number>"))))
;; /help <topic> — search user manual
((and (>= (length text) 6) (string-equal (subseq text 0 6) "/help "))
(let ((topic (string-trim '(#\Space) (subseq text 6))))
(add-msg :system (format nil "Topic: ~a — use read-file to query USER_MANUAL.org" topic))))
(let ((topic (string-trim '(#\Space) (subseq text 6)))
(sections (self-help-lookup (string-trim '(#\Space) (subseq text 6)))))
(if sections
(dolist (entry sections)
(let* ((title (car entry))
(content (cdr entry))
(preview (if (> (length content) 300)
(concatenate 'string (subseq content 0 297) "...")
content)))
(add-msg :system (format nil "~a: ~a" title preview))))
(add-msg :system (format nil "No manual section found for '~a'" topic)))))
((string-equal text "/help")
(add-msg :system "/eval <expr> Evaluate Lisp")
(add-msg :system "/undo Undo last operation")
@@ -558,6 +567,47 @@
(setf (st :dirty) (list nil t nil))
(loop-finish)))
;; v0.7.2 — self-help-lookup: read USER_MANUAL.org and find matching sections
(defun self-help-lookup (topic)
"Search USER_MANUAL.org for headlines matching TOPIC, return content previews."
(let* ((manual-path (merge-pathnames "projects/passepartout/docs/USER_MANUAL.org"
(merge-pathnames "memex/" (user-homedir-pathname))))
(results nil))
(handler-case
(let* ((text (uiop:read-file-string manual-path))
(lines (uiop:split-string text :separator '(#\Newline)))
(in-section nil)
(section-content nil))
(dolist (line lines)
(let ((trimmed (string-trim '(#\Space #\Tab) line)))
(cond
;; New headline
((and (>= (length trimmed) 2) (eql (char trimmed 0) #\*))
;; Flush previous section if in one
(when (and in-section section-content)
(push (cons in-section (string-trim '(#\Space #\Newline)
(format nil "~{~a~^ ~}" (reverse section-content))))
results))
;; Check if this headline matches topic
(let ((title (string-trim '(#\Space #\*) trimmed)))
(if (search topic title :test #'char-equal)
(setf in-section title
section-content nil)
(setf in-section nil
section-content nil))))
;; Content line in matching section
(in-section
(when (and (> (length trimmed) 0)
(not (eql (char trimmed 0) #\#)))
(push trimmed section-content))))))
;; Flush last section
(when (and in-section section-content)
(push (cons in-section (string-trim '(#\Space #\Newline)
(format nil "~{~a~^ ~}" (reverse section-content))))
results))
(nreverse results))
(error (c) (list (cons "Error" (format nil "Cannot read manual: ~a" c)))))))
(defun on-daemon-msg (msg)
(let* ((payload (getf msg :payload))
(text (getf payload :text))
@@ -1220,3 +1270,12 @@
(fiveam:is (some (lambda (m) (search "IDENTITY" (getf m :content))) msgs))
(fiveam:is (some (lambda (m) (search "LOGS" (getf m :content))) msgs))
(fiveam:is (some (lambda (m) (search "TOOLS" (getf m :content))) msgs))))
(fiveam:test test-help-topic-lookup
"Contract v0.7.2: /help <topic> reads and searches USER_MANUAL.org."
(init-state)
(dolist (ch (coerce "/help configuration" 'list))
(on-key (char-code ch)))
(on-key 13)
(let ((msgs (st :messages)))
(fiveam:is (some (lambda (m) (search ".env" (getf m :content))) msgs))))