Module: Pikuri::Workspace::Search::Utils
- Defined in:
- lib/pikuri/workspace/search/utils.rb
Overview
The two guards every path-listing tool needs and none should own: an oversized-root refusal, and a byte cap on the rendered result.
Class Method Summary collapse
-
.denied_globs(filesystem:, root:) ⇒ Array<String>
rg --glob '!<rel>'for each denied root underroot, pruning the subtree before rg ever walks it. -
.denied_prefixes(filesystem:, root:) ⇒ Array<String>
Search-root-relative paths of every denied root that falls under
root— the only ones an rg walk rooted there could reach. -
.head_truncate(raw, max_bytes:, hint:) ⇒ Array(String, String)
Head-truncate
rawtomax_bytes, cutting at the last newline so the final row is never partial. -
.normalize_root(path) ⇒ String
Symlink-resolved absolute path; falls back to a lexical cleanup when the path doesn't exist on disk.
-
.oversized_root_refusal(resolved, filesystem:, verb:) ⇒ String?
Guard against a search/list rooted at
/, the user's whole home dir, or any parent of home (+/home+, which holds every user's home) — each walks an enormous tree and, under anAllowAllworkspace, floods rg with unreadable-path diagnostics the model pays context for. -
.reject_denied_lines(raw, prefixes:) ⇒ String
Drop every output line rg emitted for a denied path.
Class Method Details
.denied_globs(filesystem:, root:) ⇒ Array<String>
rg --glob '!<rel>' for each denied root under root, pruning the
subtree before rg ever walks it. Verified: a negative glob on a
directory prunes its contents and survives --no-ignore, the same
property Glob's !.git/* relies on.
This is the optimization, never the guarantee — it goes silently partial whenever a denied root sits outside the search root, and an rg flag change could weaken it without a test noticing. The Filesystem#denied? filter on the results is what fails closed.
134 135 136 137 |
# File 'lib/pikuri/workspace/search/utils.rb', line 134 def self.denied_globs(filesystem:, root:) denied_prefixes(filesystem: filesystem, root: root) .flat_map { |rel| ['--glob', "!#{rel}"] } end |
.denied_prefixes(filesystem:, root:) ⇒ Array<String>
Search-root-relative paths of every denied root that falls under
root — the only ones an rg walk rooted there could reach.
denied_prefixes(filesystem: fs, root: Pathname.new('/home/alice'))
# => [".aws", ".gnupg", ".ssh"]
111 112 113 114 115 116 117 118 119 |
# File 'lib/pikuri/workspace/search/utils.rb', line 111 def self.denied_prefixes(filesystem:, root:) base = normalize_root(root) filesystem.denied_roots.filter_map { |denied| absolute = denied.exist? ? denied.realpath.to_s : denied.to_s next unless absolute.start_with?(base + File::SEPARATOR) absolute.delete_prefix(base + File::SEPARATOR) }.sort end |
.head_truncate(raw, max_bytes:, hint:) ⇒ Array(String, String)
Head-truncate raw to max_bytes, cutting at the last newline so
the final row is never partial.
89 90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/pikuri/workspace/search/utils.rb', line 89 def self.head_truncate(raw, max_bytes:, hint:) total = raw.bytesize return [raw, ''] if total <= max_bytes head = raw.byteslice(0, max_bytes) last_nl = head.rindex("\n") head = head.byteslice(0, last_nl) if last_nl omitted = total - head.bytesize marker = "\n\n... [#{omitted} bytes omitted; total was #{total} bytes; " \ "#{hint}] ..." [head, marker] end |
.normalize_root(path) ⇒ String
Returns symlink-resolved absolute path; falls back to a lexical cleanup when the path doesn't exist on disk.
74 75 76 77 78 |
# File 'lib/pikuri/workspace/search/utils.rb', line 74 def self.normalize_root(path) Pathname.new(path).realpath.to_s rescue SystemCallError Pathname.new(path)..cleanpath.to_s end |
.oversized_root_refusal(resolved, filesystem:, verb:) ⇒ String?
Guard against a search/list rooted at /, the user's whole home dir,
or any parent of home (+/home+, which holds every user's home) —
each walks an enormous tree and, under an AllowAll workspace, floods
rg with unreadable-path diagnostics the model pays context for. The
refused set is home and everything at-or-above it: blocking ~ while
waving through its strictly-broader parent /home would be no guard
at all. Refused regardless of any glob: rg traverses the tree before
the glob narrows which files it searches. Lesser roots (a project dir,
~/work) return nil and proceed.
Rather than dead-end at "narrow it", the refusal hands back a one-level listing of the root's own entries, so the model can pick a subdirectory and retry without another tool call or a shell:
Error: refusing to search from /home/alice — the whole home
directory is too broad. Pick one of its entries below as `path` and
search there instead (directories end with `/`):
.config/ [3 dirs, 2 files]
.ssh/ [denied]
work/ [12 dirs, 1 file]
todo.txt 412B
The listing is deliberately one level deep: rendering a tree would
re-do the very traversal this guard exists to prevent. (The child
summary does open each entry once, but that's bounded by the root's
own entry count — nothing like rg's walk.) It falls back to a plain
"narrow path" line if the directory is empty or unreadable.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/pikuri/workspace/search/utils.rb', line 43 def self.oversized_root_refusal(resolved, filesystem:, verb:) normalized = normalize_root(resolved) home = normalize_root(Pathname.new(Dir.home)) root_kind = if normalized == normalize_root(Pathname.new('/')) 'the whole filesystem root' elsif normalized == home 'the whole home directory' elsif home.start_with?(normalized + File::SEPARATOR) # e.g. /home — a parent of ~ is strictly broader than ~ itself. # (+/+ is a parent too, but its "//"-prefix test never matches, # so it stays owned by the first branch and its clearer message.) 'a parent of your home directory' end return nil unless root_kind listing = root_listing(normalized, filesystem: filesystem) guidance = if listing.empty? 'Narrow `path` to a specific subdirectory.' else "Pick one of its entries below as `path` and #{verb} there " \ "instead (directories end with `/`):\n#{listing}" end "Error: refusing to #{verb} from #{resolved} — #{root_kind} is too " \ "broad. #{guidance}" end |
.reject_denied_lines(raw, prefixes:) ⇒ String
Drop every output line rg emitted for a denied path. rg puts the path
first on the line and never follows symlinks (no -L in either
tool's argv), so a leading-prefix test is exact for the path segment
— no need to find where the path ends, which a filename containing
: would make ambiguous. It may over-match (a .netrc.bak line goes
too); over-filtering is the safe direction for a credential guard.
149 150 151 152 153 154 155 |
# File 'lib/pikuri/workspace/search/utils.rb', line 149 def self.reject_denied_lines(raw, prefixes:) return raw if prefixes.empty? raw.lines.reject { |line| prefixes.any? { |rel| line.start_with?(rel) } }.join end |