You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
opskit go: picker has no column layout and times out after 60s #442
opskit go's fzf picker is hard to read and self-terminates.
No columns.run_fzf_selection feeds fzf f"{rel}\t{path}" with no header and no fixed widths, so fzf expands the tab to the next tab stop and the long absolute path dominates each line — there are no column boundaries.
Times out. fzf is invoked with timeout=60 (bin/opskit-go.py). Leaving the picker open longer than 60s kills fzf, prints "fzf not found or timed out", and exits 1. An interactive picker must not have a timeout.
Expected: a header row and fixed-width columns NAME | BRANCH | STATUS | RESUME, the absolute path hidden but still returned as the selection, and no timeout. Branch/dirty are computed lazily for the visible (enabled) list only, so the scan stays cheap.
The fzf picker in opskit go must be readable and must not self-terminate.
Two defects:
No columns.run_fzf_selection fed fzf f"{rel}\t{path}" with no header and
no fixed widths, so fzf expanded the tab to a tab stop and the long absolute path
dominated each line — no column boundaries.
Times out. fzf was invoked with timeout=60; leaving the picker open longer
killed it, printed "fzf not found or timed out", and exited 1.
Design
Pure, unit-testable helpers plus a rewritten run_fzf_selection:
_picker_header() → header using the same fixed column widths: NAME | BRANCH | STATUS | RESUME.
_picker_row(proj, git_lookup) → one display row, space-padded to fixed widths,
long values truncated with …; branch/status from git_lookup(path).
build_picker(projects, git_lookup=_git_info) → (header, lines); each line is f"{path}\t{display}" — field 1 is the selection value, field 2 the display.
run_fzf_selection(projects):
fzf args add --delimiter=\t --with-nth=2 --accept-nth=1 --header=<header>
(display field 2; return field 1 = path; show a header). Keeps --height=40% --reverse --prompt=opskit> .
Security: unchanged input surface (paths from cache/config); argv list, no shell;
fixed widths, no format injection.
Stability: fzf missing → clear error; empty input handled by the caller; no
timeout means an interactive wait can never be cut short.
Maintainability: formatting isolated in pure helpers → tested without git/fzf.
Effectiveness/Value: fixes both complaints; no scan-cost regression.
Flaw → resolution: truncating a long name could hide a distinguishing suffix.
Resolution: rel is already the unique-ish selector and we now search it; accepted.
Tests
tests/test_tui_picker.py: header names the columns; header/row column offsets align; [resume] shown only when present; long names truncate to the column width; each line's
field 1 is the path; run_fzf_selection passes notimeout and passes the column
options; cancel and empty selection return None. Full suite: make test.
Activity
growlf commentedon Oct 13, 2026
PLAN:
opskit gopicker — column layout and no timeoutCloses #442.
Goal
The fzf picker in
opskit gomust be readable and must not self-terminate.Two defects:
run_fzf_selectionfed fzff"{rel}\t{path}"with no header andno fixed widths, so fzf expanded the tab to a tab stop and the long absolute path
dominated each line — no column boundaries.
timeout=60; leaving the picker open longerkilled it, printed "fzf not found or timed out", and exited 1.
Design
Pure, unit-testable helpers plus a rewritten
run_fzf_selection:_picker_header()→ header using the same fixed column widths:NAME | BRANCH | STATUS | RESUME._picker_row(proj, git_lookup)→ one display row, space-padded to fixed widths,long values truncated with
…; branch/status fromgit_lookup(path).build_picker(projects, git_lookup=_git_info)→(header, lines); each line isf"{path}\t{display}"— field 1 is the selection value, field 2 the display.run_fzf_selection(projects):--delimiter=\t --with-nth=2 --accept-nth=1 --header=<header>(display field 2; return field 1 = path; show a header). Keeps
--height=40% --reverse --prompt=opskit>.timeout— interactive picker; Esc/Ctrl+C cancels (nonzero rc →None).stdout.split("\t")[0], so it still works on a build that ignores--accept-nth._git_infofor the displayed rows only.Notes from implementation
--with-nthscopes the fuzzy search to the displayed columns, so thehidden path is not searchable — the visible
NAME(therelselector) is. Fine.--accept-nthneeds fzf ≥ 0.48 (0.67 here); the defensive split covers older.Critique
fixed widths, no format injection.
timeout means an interactive wait can never be cut short.
Resolution:
relis already the unique-ish selector and we now search it; accepted.Tests
tests/test_tui_picker.py: header names the columns; header/row column offsets align;[resume]shown only when present; long names truncate to the column width; each line'sfield 1 is the path;
run_fzf_selectionpasses notimeoutand passes the columnoptions; cancel and empty selection return
None. Full suite:make test.