Class: Pikuri::Os::FileindexSearch
- Inherits:
-
Tool
- Object
- Tool
- Pikuri::Os::FileindexSearch
- Defined in:
- lib/pikuri/os/fileindex_search.rb
Overview
The fileindex_search tool — a fast, cheap keyword lookup over a
desktop file index, returning matching paths each with the single
snippet the index provides. A Tool in the
Workspace::Search::Grep shape (shells out through the backend,
fails loud at construction if the binary is missing).
The backend: is a stateless module satisfying the file-index duck
type (see #initialize) — LocalSearch or Recoll, whichever
Extension finds usable. Backend-specific text (the blind-spot note,
the error label) is read from the backend, so a new index arm ships
its own without touching the tool.
It's a locator, not a window: the index reports at most one match per file, so the tool previews one match each and tells the LLM to read the file for full content / other matches. It sees inside PDFs and office docs (indexed at extract time), and being an index lookup is cheap to run repeatedly.
Sharing: P_stateless — the backend is a stateless module and each call
shells out to its own query, so one instance serves any number of agents.
Constant Summary collapse
- DEFAULT_LIMIT =
Returns default / max matching files returned.
10- MAX_LIMIT =
25- MAX_BYTES =
Returns hard byte cap on combined output (matches Workspace::Search::Grep::MAX_BYTES).
50 * 1024
- MAX_BYTES_LABEL =
Returns human-readable form of MAX_BYTES.
"#{MAX_BYTES / 1024} KB"- DESCRIPTION =
Returns opencode-shape description (summary + Usage), backend-neutral. The runtime tool description is this constant plus the backend's
#limitations, appended at construction (see #initialize) — and it is that appended text, not this constant, that states which folders the index actually covers and what it misses (recoll lists itstopdirs; localsearch notes it ranks across the whole home). So keep coverage claims out of here; they belong with the backend that knows them. <<~DESC Find files whose contents contain your words or phrase, using the desktop search index. Usage: - Searches inside PDFs, Word/ODT/spreadsheet documents and plain text — reach for this when the answer may live in a file's contents. - Keyword match (and word stems); type the actual words that would appear in the file. - The index returns at most ONE match per file and may be slightly stale — this is a locator, not the full story. To read a file's full, current text, read it (see fileindex_read). - Cheap: it queries a prebuilt index rather than walking the filesystem, so it barely touches the disk of the machine the user is on — fine to run many times to narrow down. - Only indexed folders are covered; system paths like /etc or /var are not — search those with a text/regex tool instead. - Output is truncated to #{MAX_BYTES_LABEL}; narrow the query if it ends in a truncation marker. DESC
Class Method Summary collapse
-
.search(query:, backend:, limit: DEFAULT_LIMIT) ⇒ String
Formatted hits + disclaimer, a no-match message, or
"Error: ...".
Instance Method Summary collapse
- #initialize(backend:) ⇒ FileindexSearch constructor
Constructor Details
#initialize(backend:) ⇒ FileindexSearch
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/pikuri/os/fileindex_search.rb', line 63 def initialize(backend:) @backend = backend backend.check_binaries! super( name: 'fileindex_search', description: "#{DESCRIPTION}\n#{backend.limitations}", parameters: Parameters.build { |p| p.required_string :query, 'Words or a phrase to find in file contents, ' \ 'e.g. "tax return 2024".' p.optional_integer :limit, "Max number of matching files to return " \ "(default #{DEFAULT_LIMIT}, max #{MAX_LIMIT}), e.g. 10." }, execute: lambda { |query:, limit: DEFAULT_LIMIT| FileindexSearch.search(query: query, limit: limit, backend: backend) }, # Private and hard untrusted, both fixed rather than derived: this searches # the *whole machine's* file index, which is the AllowAll case by # construction — a reachable set nobody can vouch for, holding whatever the # user keeps on disk. trifecta_legs: Pikuri::Tool::TrifectaLegs.new(private: true, untrusted: :hard, egress_payload_review: :no_egress) ) end |
Class Method Details
.search(query:, backend:, limit: DEFAULT_LIMIT) ⇒ String
Returns formatted hits + disclaimer, a no-match message,
or "Error: ...".
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/pikuri/os/fileindex_search.rb', line 93 def self.search(query:, backend:, limit: DEFAULT_LIMIT) q = query.to_s.strip return 'Error: empty query.' if q.empty? limit = clamp(limit, 1, MAX_LIMIT) hits = backend.search(query: q, limit: limit) return (q) if hits.empty? content, marker = head_truncate(hits.map { |hit| render_hit(hit) }.join("\n")) [content.chomp, '', disclaimer(hits.size) + marker].join("\n") rescue backend::CommandError => e # Cap the error too — a raw subprocess error returned verbatim is # the shape that overflows the model's context; share the success # path's ceiling. content, marker = head_truncate(e.) "Error: #{backend.label}: #{content}#{marker}" end |