API Reference¶
The agentgrep.ui subpackage holds the streaming Textual explorer.
Textual is imported lazily inside
build_streaming_ui_app() via
importlib.import_module, so bare import agentgrep does not pull
Textual into the importing process; the import error is deferred to
the moment the factory is called.
The subpackage’s __init__ re-exports run_ui()
and build_streaming_ui_app() at the
agentgrep.ui namespace for convenience, and the top-level
agentgrep package provides matching lazy wrappers so callers can
use agentgrep.run_ui() without reaching into
agentgrep.ui.app.
Textual TUI subpackage for agentgrep.
This subpackage holds the streaming Textual explorer run_ui and the
build_streaming_ui_app() factory. It is imported lazily by the
top-level agentgrep package — bare import agentgrep does not
load Textual. Anyone who imports agentgrep.ui (or calls
agentgrep.run_ui()) requires Textual to be installed.
Streaming Textual app entry points — run_ui and the app factory.
This module is the Textual-free factory facade: it builds the
UiContext, wires the engine seam, validates the
selected layout and workflow against agentgrep.ui.registry, and constructs
the ExplorerApp shell. The shell and the layouts
import Textual at module scope, so they are imported lazily here and the import
error is deferred to the moment a UI is actually built — keeping a bare import
agentgrep Textual-free (ADR 0010).
Argument type¶
Entry points¶
-
agentgrep.ui.app.run_ui(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)¶agentgrep.ui.app.run_ui(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)¶
Launch the streaming Textual explorer for
query.Thin wrapper that builds the app via
build_streaming_ui_app()and callsapp.run(). The factory split lets tests construct the app for a TextualPilotsmoke test without entering the blocking run loop.- Parameters:
home (
pathlib.Path) – User home directory, passed through to the search engine.query (
SearchQuery) – Search to run. Emptytermsmeans “all records” (browse mode).control (
SearchControl) – Shared cooperative-cancel flag seeding the first search.initial_search_text (
str|None) – Initial value of the layout’s primary input. WhenNone, defaults to the space-joinedquery.terms.layout (
str) – The layout to launch into (seeagentgrep.ui.registry.LAYOUTS).workflow (
str) – The workflow to drive it (seeagentgrep.ui.registry.WORKFLOWS).
- Return type:
-
agentgrep.ui.app.build_streaming_ui_app(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)¶agentgrep.ui.app.build_streaming_ui_app(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)¶
Construct the streaming Textual app without entering its run loop.
Returns the constructed
ExplorerAppshell (typedobjectso this module need not import Textual). Callers invoke.run()for a real session or.run_test()for a Pilot smoke test. The shell and the layouts are imported lazily so the eagerimport agentgreppath stays Textual-free (ADR 0010).- Parameters:
home (
pathlib.Path) – User home directory, passed through to the search engine.query (
SearchQuery) – Search to run. Emptytermsmeans “all records” (browse mode).control (
SearchControl) – Shared cooperative-cancel flag seeding the first search.initial_search_text (
str|None) – Initial value of the layout’s primary input; defaults to the space-joinedquery.termswhenNone.layout (
str) – The layout to launch into; validated against the registry.workflow (
str) – The workflow to drive it; validated against the registry.
- Raises:
ValueError– Iflayoutorworkflownames an unregistered component.- Return type:
Filter and display helpers¶
-
agentgrep.cached_haystack(record)¶agentgrep.cached_haystack(record)¶
Return the casefolded haystack for
record, memoized byid.The filter worker scans every loaded record on every keystroke; recomputing
build_search_haystack(...).casefold()per record per pass dominates filter latency once the result set grows past a few thousand records. Memoizing byidis safe because the app retains every record inAgentGrepApp.all_recordsfor the lifetime of one search, so Python cannot recycle a collected record’s id while its entry sits in_HAYSTACK_CACHE.Callers that need to invalidate (because a new search will allocate new records) should call
clear_haystack_cache().- Parameters:
record (
SearchRecord)- Return type:
-
agentgrep.clear_haystack_cache()¶agentgrep.clear_haystack_cache()¶
Drop every memoized haystack — call before allocating a new record set.
- Return type:
-
agentgrep.compute_filter_matches(records, text)¶agentgrep.compute_filter_matches(records, text)¶
Return the subset of
recordswhose haystack containstext(case-fold).Used by the TUI’s filter worker. Pure function so the filter logic is directly unit-testable without spinning up a Textual app.
- Parameters:
records (
Sequence[SearchRecord]) – Records to test.text (
str) – Filter text. Whitespace-trimmed and case-folded before matching. An empty (or whitespace-only)textreturns all records.
- Returns:
Matching records in input order.
- Return type:
tuple[SearchRecord, ...]
-
agentgrep.format_timestamp_tig(value)¶agentgrep.format_timestamp_tig(value)¶
Render an ISO-8601 timestamp as
YYYY-MM-DD HH:MM ±HHMM(tig style).Localizes to the system timezone before formatting so the displayed time matches what the user expects to see — tig’s main view does the same. Returns
""forNone/ empty input and a clipped raw string for unparseable input so callers can pad consistently.Examples
>>> format_timestamp_tig(None) '' >>> format_timestamp_tig("") '' >>> # An ISO timestamp with explicit timezone — formatted result keeps >>> # the offset for the system's local timezone (whose exact value >>> # varies by host, so we just check shape here). >>> sample = format_timestamp_tig("2026-05-17T11:59:12+00:00") >>> len(sample) 22 >>> sample[4], sample[7], sample[10], sample[13], sample[16] ('-', '-', ' ', ':', ' ') >>> format_timestamp_tig("not-a-real-timestamp") 'not-a-real-timestamp'
-
agentgrep.ui.format.scroll_percent(scroll_y, max_scroll_y)¶agentgrep.ui.format.scroll_percent(scroll_y, max_scroll_y)¶
Return an integer scroll percent clamped to
[0, 100].Returns
100when there is no scrollable region (everything fits) and0when scrolled to the very top. Mirrors tig’s bottom-status convention where a fully visible view reads as100%.