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

class agentgrep.UIArgs
class
class
class agentgrep.UIArgs

Bases: object

Typed arguments for agentgrep ui.

Entry points

agentgrep.ui.app.run_ui(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)
function
function
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 calls app.run(). The factory split lets tests construct the app for a Textual Pilot smoke 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. Empty terms means “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. When None, defaults to the space-joined query.terms.

  • layout (str) – The layout to launch into (see agentgrep.ui.registry.LAYOUTS).

  • workflow (str) – The workflow to drive it (see agentgrep.ui.registry.WORKFLOWS).

Return type:

None

agentgrep.ui.app.build_streaming_ui_app(home, query, *, control, initial_search_text=None, layout=registry.DEFAULT_LAYOUT, workflow=registry.DEFAULT_WORKFLOW)
function
function
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 ExplorerApp shell (typed object so 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 eager import agentgrep path stays Textual-free (ADR 0010).

Parameters:
  • home (pathlib.Path) – User home directory, passed through to the search engine.

  • query (SearchQuery) – Search to run. Empty terms means “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-joined query.terms when None.

  • layout (str) – The layout to launch into; validated against the registry.

  • workflow (str) – The workflow to drive it; validated against the registry.

Raises:

ValueError – If layout or workflow names an unregistered component.

Return type:

object

Filter and display helpers

agentgrep.cached_haystack(record)
function
function
agentgrep.cached_haystack(record)

Return the casefolded haystack for record, memoized by id.

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 by id is safe because the app retains every record in AgentGrepApp.all_records for 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:

str

agentgrep.clear_haystack_cache()
function
function
agentgrep.clear_haystack_cache()

Drop every memoized haystack — call before allocating a new record set.

Return type:

None

agentgrep.compute_filter_matches(records, text)
function
function
agentgrep.compute_filter_matches(records, text)

Return the subset of records whose haystack contains text (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) text returns all records.

Returns:

Matching records in input order.

Return type:

tuple[SearchRecord, ...]

agentgrep.format_timestamp_tig(value)
function
function
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 "" for None / 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'
Parameters:

value (str | None)

Return type:

str

agentgrep.ui.format.scroll_percent(scroll_y, max_scroll_y)
function
function
agentgrep.ui.format.scroll_percent(scroll_y, max_scroll_y)

Return an integer scroll percent clamped to [0, 100].

Returns 100 when there is no scrollable region (everything fits) and 0 when scrolled to the very top. Mirrors tig’s bottom-status convention where a fully visible view reads as 100%.

Parameters:
Return type:

int