Class: Pikuri::Thunderbird::CalendarSearch
- Inherits:
-
Pikuri::Tool
- Object
- Pikuri::Tool
- Pikuri::Thunderbird::CalendarSearch
- Defined in:
- lib/pikuri/thunderbird/calendar_search.rb
Overview
The thunderbird_calendar_search tool — search local + cached
Thunderbird calendars by title/description/location. Inbound-only; its
read half is thunderbird_calendar_read.
Recall-first (words OR-matched, ranked by matched-word count) on purpose, mirroring MailSearch: tighten it to AND and the model — primed by the forgiving mail search — reads the sparser hits as "no such event".
Empty-corpus guidance (Q6)
Thunderbird keeps a CalDAV calendar's events in memory only unless "Offline Support" is enabled, so a calendar can look full in the UI yet have nothing on disk. When no events are cached, this tool returns a self-fixing observation telling the user the one-click fix rather than a bare "no results" — and the same fact rides the description, so the LLM can advise proactively before any empty search.
Sharing: P_shared_locked — no state of its own, and the Calendar
backend it queries locks; see that class's == Sharing.
Constant Summary collapse
- DEFAULT_LIMIT =
15- MAX_LIMIT =
50- OFFLINE_HINT =
Returns the enable-Offline-Support guidance, reused by the description and the empty-corpus observation.
'No calendar events are cached to disk. If Thunderbird shows events but ' \ 'search finds none, enable Offline Support on each calendar ' \ '(right-click the calendar → Properties → Offline Support) so its events ' \ 'are stored locally and become searchable.'
- DESCRIPTION =
Returns opencode-shape description.
<<~DESC Search the user's local and cached Thunderbird calendars by title, description or location. Usage: - Free-text query and/or a date range — both optional; supply at least one. - To list everything on a day or in a span (e.g. "tomorrow's meetings"), pass a broad or empty query with after/before; a single day is after=before=that date. - Each query word is matched independently (across title, description, and location) and events matching more of the words rank first — so extra words broaden the search and surface partial matches, they don't require every word. Matching ignores accents and case. To narrow, add a date range rather than more words. - Optionally restrict to recurring events (a repeating series) or single one-off events with the recurrence filter; omit it to get both. - Reads Thunderbird's on-disk calendar store — it never connects to any calendar server. - Only events cached to disk are visible. A CalDAV calendar keeps events in memory unless its "Offline Support" is enabled (right-click the calendar → Properties → Offline Support); if a search comes back empty but the calendar looks full, that setting is why. - Read an event in full with thunderbird_calendar_read. DESC
Class Method Summary collapse
Instance Method Summary collapse
- #initialize(backend:) ⇒ CalendarSearch constructor
Constructor Details
#initialize(backend:) ⇒ CalendarSearch
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/pikuri/thunderbird/calendar_search.rb', line 52 def initialize(backend:) @backend = backend super( name: 'thunderbird_calendar_search', description: DESCRIPTION, parameters: Parameters.build { |p| p.optional_string :query, 'Words to find in event title/description/location, e.g. "standup". Omit to list everything in the date range.' p.optional_string :after, 'Only events on/after this date, e.g. "2026-01-01".' p.optional_string :before, 'Only events on/before this date, e.g. "2026-12-31".' p.optional_enum :recurrence, 'Restrict by repetition: "recurring" for repeating series only, "single" for one-off events only. Omit for both.', values: %w[recurring single] p.optional_integer :limit, "Max events (default #{DEFAULT_LIMIT}, max #{MAX_LIMIT}), e.g. 15." }, execute: lambda { |query: nil, after: nil, before: nil, recurrence: nil, limit: DEFAULT_LIMIT| CalendarSearch.run_search(backend: @backend, query:, after:, before:, recurrence:, limit:) }, trifecta_legs: Pikuri::Thunderbird::INBOUND_LEGS ) end |
Class Method Details
.run_search(backend:, query:, after:, before:, recurrence: nil, limit:) ⇒ String
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/pikuri/thunderbird/calendar_search.rb', line 74 def self.run_search(backend:, query:, after:, before:, recurrence: nil, limit:) # Refuse a match-everything call: with no query and no date bound this # would dump every cached event. "What's on my calendar" should name a # span — steer the model to add after/before (e.g. this week). A lone # recurrence filter is still too broad to stand in for a span. if [query, after, before].all? { |v| v.to_s.strip.empty? } return 'Error: give a query and/or a date range (after/before) to search for' end limit = limit.to_i.clamp(1, MAX_LIMIT) recurring = { 'recurring' => true, 'single' => false }[recurrence] # Fetch one past the cap so a full page is distinguishable from an # exhausted calendar (see {MailSearch.run_search}); limit+1 back means # more exist and {header} says so, rather than let the model read a # full page as the whole calendar. hits = backend.search(query:, limit: limit + 1, recurring:, after: DateHelpers.parse_after(after), before: DateHelpers.parse_before(before)) return offline_or_no_match(backend, query) if hits.empty? has_more = hits.size > limit hits = hits.first(limit) [header(shown: hits.size, has_more:), *hits.map { |h| render(h) }].join("\n\n") rescue ArgumentError => e "Error: bad date filter (#{e.})." end |