Feature request
Add quack go <query> — a search-driven directory jumper: runs quack's search, aggregates the directories of the top hits, lets the user pick one, and drops their shell into it (like z/zoxide, but driven by quack's search over the indexed space).
Design goal: a stable shell-agnostic core plus a pluggable shell-adapter layer, so supporting a new shell (fish, nushell, pwsh) is a small self-contained PR that never touches core.
UX sketch
A dynamic, rich interactive selector — not a static prompt. The list renders in place; the user navigates with ↑/↓ (highlighted row moves) and confirms with Enter, or jumps instantly by pressing the number key. q/Esc cancels.
$ quack go parser
quack › jump to a directory (↑/↓ move · 1-3 jump · enter select · q cancel)
❯ 1 src/lib/parsing 4 hits tokenizer + AST builder
2 src/lib 3 hits core library modules
3 tests/parsing 2 hits parser unit tests
# (↓ once, Enter — or just press 1)
# shell is now in .../src/lib/parsing
Interaction details:
- Highlighted row is styled (reverse/colour +
❯ marker); arrows move the selection and redraw in place.
- Number keys
1-9 select-and-confirm immediately (no Enter needed) for the visible rows.
- Enter confirms the highlighted row;
q/Esc/Ctrl-C cancels with no cd.
- Optionally show each directory's quack description/hit-count alongside the path (the catalog already has folder descriptions), so the choice is informed, not just a path list.
zsh power-user path: quack go red<TAB> completes against indexed directories (the selector becomes optional — completion bypasses it entirely).
The hard constraint
A child process cannot change its parent shell's CWD. So the binary can't cd directly — it prints the chosen path and a thin shell-function wrapper performs the cd. This is the z/zoxide model and dictates the layering below.
Architecture: core + pluggable shell adapters
Core (shell-agnostic)
search -> aggregate directories -> rank (by hit count / summed score; include folder hits from the where/which-folder routing).
- Interactive selector (below), rendered on tty/stderr. The final absolute path is the ONLY thing on stdout — the inviolable contract that makes the wrapper's
cd "$(...)" safe.
- Exit codes:
0 = path on stdout; non-zero = cancel / empty (wrapper skips the cd).
- Non-interactive:
quack go <query> <index> selects directly; if stdout isn't a tty and no index given, print top path or exit non-zero.
- Likely reuses the existing raw-terminal/ANSI rendering already in
_duck.py rather than adding a TUI dependency.
Interactive selector
- Renders the ranked list in place; ↑/↓ move a styled highlight (
❯ + reverse/colour), redrawing without scrolling.
- Number keys
1-9 select-and-confirm immediately; Enter confirms the highlight; q/Esc/Ctrl-C cancel.
- Each row may show the directory's catalog description + hit count next to the path, so the choice is informed.
Shell-adapter layer (the extensible part)
One adapter per shell behind a SHELL_ADAPTERS registry:
class ShellAdapter(Protocol):
name: str
def detect(self) -> bool: ...
def rc_file(self) -> Path: ...
def init_script(self) -> str: ...
def completion_script(self) -> str | None: ...
supports_completion: bool
supports_push_to_buffer: bool # e.g. zsh `print -z`
quack shell-init <shell> emits the adapter's init_script() (+ completion if supported). Adding a shell = new adapter in shells/<name>.py + registry entry + fixture test; core and other adapters untouched. Mirrors the existing CLIENTS/ClientTarget pattern in mcp_install.py.
Per-shell behavior
| Shell |
init_script() |
Capability extras |
| bash |
cd "$(command quack go "$@")" wrapper |
baseline |
| zsh (first-class) |
autoloaded fn into $fpath |
compdef completion against the catalog (quack go red<TAB>); optional print -z to push cd <dir> onto the next prompt |
| fish (future PR) |
fish function wrapper |
fish completions if contributed |
Capability flags let core degrade gracefully — no completion still gets the selector; no buffer-push just cd's.
Notes
Feature request
Add
quack go <query>— a search-driven directory jumper: runs quack's search, aggregates the directories of the top hits, lets the user pick one, and drops their shell into it (likez/zoxide, but driven by quack's search over the indexed space).Design goal: a stable shell-agnostic core plus a pluggable shell-adapter layer, so supporting a new shell (fish, nushell, pwsh) is a small self-contained PR that never touches core.
UX sketch
A dynamic, rich interactive selector — not a static prompt. The list renders in place; the user navigates with ↑/↓ (highlighted row moves) and confirms with Enter, or jumps instantly by pressing the number key.
q/Esc cancels.Interaction details:
❯marker); arrows move the selection and redraw in place.1-9select-and-confirm immediately (no Enter needed) for the visible rows.q/Esc/Ctrl-C cancels with no cd.zsh power-user path:
quack go red<TAB>completes against indexed directories (the selector becomes optional — completion bypasses it entirely).The hard constraint
A child process cannot change its parent shell's CWD. So the binary can't
cddirectly — it prints the chosen path and a thin shell-function wrapper performs the cd. This is thez/zoxidemodel and dictates the layering below.Architecture: core + pluggable shell adapters
Core (shell-agnostic)
search -> aggregate directories -> rank(by hit count / summed score; include folder hits from the where/which-folder routing).cd "$(...)"safe.0= path on stdout; non-zero = cancel / empty (wrapper skips the cd).quack go <query> <index>selects directly; if stdout isn't a tty and no index given, print top path or exit non-zero._duck.pyrather than adding a TUI dependency.Interactive selector
❯+ reverse/colour), redrawing without scrolling.1-9select-and-confirm immediately; Enter confirms the highlight;q/Esc/Ctrl-C cancel.Shell-adapter layer (the extensible part)
One adapter per shell behind a
SHELL_ADAPTERSregistry:quack shell-init <shell>emits the adapter'sinit_script()(+ completion if supported). Adding a shell = new adapter inshells/<name>.py+ registry entry + fixture test; core and other adapters untouched. Mirrors the existingCLIENTS/ClientTargetpattern inmcp_install.py.Per-shell behavior
cd "$(command quack go "$@")"wrapper$fpathcompdefcompletion against the catalog (quack go red<TAB>); optionalprint -zto pushcd <dir>onto the next promptfunctionwrapperCapability flags let core degrade gracefully — no completion still gets the selector; no buffer-push just cd's.
Notes
fzfas the selector if present, else the built-in one.