Navigate a directory full of directories, identifying repos and worktrees
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

add doc/pick-iter.md: fast scan + fuzzel + deep inspect pipeline design

rektide de6b58e5 f6e24ee5

+185
+185
doc/pick-iter.md
··· 1 + # pick-iter: fast candidate generation + interactive multi-select + deep inspection 2 + 3 + A design for combining `is-tree-scan-priority` and `is-tree-fuzzel-pipeline` into a single fluid workflow: generate tree candidates quickly, interactively pick the ones you care about, then inspect those in depth. 4 + 5 + ## The problem 6 + 7 + `is-tree --all` against a large `~/src` is slow. It scans every directory, resolves jj bookmarks, fetches commit dates, and only then prints results. For interactive use you want the opposite: 8 + 9 + 1. **Fast candidate list** — just directory names, streamed as discovered. 10 + 2. **Pick what matters** — multi-select from that list via fuzzel (or any picker). 11 + 3. **Deep inspect the few** — run full is-tree with all columns on just the selected paths. 12 + 13 + The current tooling makes this clunky because is-tree doesn't separate "discovery" from "inspection." 14 + 15 + ## The dream pipeline 16 + 17 + ```bash 18 + is-tree --scan | fuzzel --dmenu --multi | is-tree --format all 19 + ``` 20 + 21 + Three stages, three processes, one pipe. Let's unpack what each stage needs. 22 + 23 + ## Stage 1: fast candidate generation 24 + 25 + ```bash 26 + is-tree --scan 27 + ``` 28 + 29 + `--scan` is a new mode. It does the minimum work to determine that a directory is a tree (git or jj) and emits its path. No commit dates, no bookmark resolution, no remote queries. Just: 30 + 31 + - readdir the parent 32 + - stat `.git` / `.jj` in each child 33 + - print matching paths, one per line 34 + 35 + This maps onto `is-tree-scan-priority` — but instead of "priority ordering," it's "stream immediately, skip everything expensive." The key insight: discovery is cheap, inspection is expensive. `--scan` only does discovery. 36 + 37 + ### Streaming 38 + 39 + `--scan` should stream: print each path as it's found, don't buffer. This lets fuzzel start showing candidates before the scan finishes. A large `~/src` with hundreds of entries becomes interactive from the first few results. 40 + 41 + ### What about type info? 42 + 43 + Emitting bare paths works, but we could optionally include the status: 44 + 45 + ``` 46 + jj ~/src/is-tree 47 + git ~/src/compfuzor 48 + ``` 49 + 50 + This lets fuzzel show context without slowing down discovery. The status is free — we already check `.jj`/`.git` to classify. A `--scan --format "{status} {directory}"` or `--scan -s` flag could control this. But for the pipeline use case, bare paths are the default since they pipe directly into is-tree positional args. 51 + 52 + ## Stage 2: interactive multi-select 53 + 54 + ```bash 55 + fuzzel --dmenu --multi 56 + ``` 57 + 58 + This is existing fuzzel behavior. It reads lines from stdin, presents a searchable multi-select UI, and emits selected lines to stdout. No work for us. 59 + 60 + For terminal-only environments, alternatives work identically: 61 + 62 + ```bash 63 + is-tree --scan | fzf --multi | is-tree --format all 64 + is-tree --scan | sk --multi | is-tree --format all 65 + ``` 66 + 67 + The pipeline is picker-agnostic. 68 + 69 + ## Stage 3: deep inspection 70 + 71 + ```bash 72 + is-tree --format all 73 + ``` 74 + 75 + This is where the selected paths come in as positional arguments. is-tree already supports this: 76 + 77 + ```bash 78 + is-tree ~/src/is-tree ~/src/compfuzor --format all 79 + ``` 80 + 81 + For a handful of selected paths, this is fast — full column resolution on 5-10 trees instead of 500. 82 + 83 + ## Does is-tree need changes? 84 + 85 + ### What already works 86 + 87 + - **Positional args**: `is-tree path1 path2 path3` already works. The pipe `fuzzel | xargs is-tree` would work today if fuzzel emitted paths. 88 + - **`--format all`**: already supported. 89 + - **Discovery**: the core detection logic exists. 90 + 91 + ### What doesn't work yet 92 + 93 + 1. **No streaming/fast-scan mode.** `is-tree --all` does full inspection on every directory before printing anything. We need `--scan` (or similar) that emits paths immediately with minimal work. 94 + 95 + 2. **stdin as positional input.** The pipeline `fuzzel | is-tree` doesn't work because is-tree reads positional args from argv, not stdin. We'd need either: 96 + - `xargs` glue: `is-tree --scan | fuzzel --multi | xargs is-tree --format all` — works today but xargs breaks on paths with spaces unless you use `-d '\n'`. 97 + - A `--stdin` flag on is-tree to read paths from stdin, one per line. This is cleaner: 98 + ```bash 99 + is-tree --scan | fuzzel --multi | is-tree --stdin --format all 100 + ``` 101 + - Or: detect piped stdin automatically (if stdin is a pipe and no positional args given, read paths from stdin). This is the most ergonomic — zero extra flags. 102 + 103 + 3. **Recurse-on-selected.** Once you've picked trees, you might want to go deeper — per-file stats inside those trees. That's `is-tree-per-file-stats`, which would compose naturally: 104 + ```bash 105 + is-tree --scan | fuzzel --multi | is-tree --stdin --files --format all 106 + ``` 107 + 108 + ### Recommendation: `--stdin` (or auto-detect) 109 + 110 + The smallest change with the biggest payoff: 111 + 112 + - Add stdin reading when no positional args are given and stdin is a pipe (isatty check). 113 + - Or an explicit `--stdin` flag if implicit behavior feels too magical. 114 + 115 + This turns the three-stage pipeline into a first-class workflow without adding subcommands or restructuring is-tree. 116 + 117 + ## The full pipeline, revised 118 + 119 + With `--scan` + stdin support: 120 + 121 + ```bash 122 + # pick trees, inspect deeply 123 + is-tree --scan | fuzzel --dmenu --multi | is-tree --format all 124 + 125 + # pick trees, see per-file staleness 126 + is-tree --scan | fuzzel --dmenu --multi | is-tree --stdin --files --older-than 30d 127 + 128 + # pick trees, push them 129 + is-tree --scan | fuzzel --dmenu --multi | reforrest push 130 + 131 + # scoped to a subdirectory 132 + is-tree --scan ~/src | fuzzel --dmenu --multi | is-tree --format all 133 + ``` 134 + 135 + ## Design decisions to resolve 136 + 137 + ### `--scan` output format 138 + 139 + Default: bare paths. Optionally include status with a flag. Rationale: bare paths are universally pipeable. Anything else breaks composability. 140 + 141 + ```bash 142 + is-tree --scan # one path per line 143 + is-tree --scan --show-type # "jj ~/src/is-tree" — useful for visual scanning but not for piping back 144 + ``` 145 + 146 + ### `--scan` vs `--all --format "{directory}"` 147 + 148 + These overlap. `--all --format "{directory}"` already emits just paths. The difference is: 149 + 150 + - `--all --format "{directory}"` does full inspection on every tree, then strips output to just the path column. Slow. 151 + - `--scan` skips inspection entirely. Fast. 152 + 153 + We could make `--all --format "{directory}"` fast by detecting that only `directory` is requested and short-circuiting inspection. But `--scan` is a clearer intent signal and doesn't require format parsing to optimize. 154 + 155 + **Recommendation**: add `--scan` as a distinct fast-path. It's self-documenting and unambiguous. 156 + 157 + ### Per-file recursion after picking 158 + 159 + The `--files` flag (from `is-tree-per-file-stats`) would make the final `is-tree` call recurse into each selected tree and report per-file stats. This is where the pipeline really shines: you pick 3 stale projects from hundreds, then immediately see which files in those projects are gathering dust. 160 + 161 + ```bash 162 + is-tree --scan | fuzzel --dmenu --multi | is-tree --format all --files --sort age- 163 + ``` 164 + 165 + ### Session state 166 + 167 + Could we persist a "pick session" — e.g., a temporary file with selected paths — so you can re-inspect without re-picking? 168 + 169 + ```bash 170 + is-tree --scan | fuzzel --multi | tee /tmp/picked | is-tree --format all 171 + # later: 172 + is-tree --stdin --format all --files < /tmp/picked 173 + ``` 174 + 175 + This works with plain Unix tooling. No special session mechanism needed. 176 + 177 + ## Summary of required is-tree changes 178 + 179 + | Change | Purpose | Scope | 180 + |--------|---------|-------| 181 + | `--scan` flag | Fast path-only discovery, streamed output | New flag, reuses detection logic, skips inspection | 182 + | Stdin path reading | Accept selected paths from pipe | Auto-detect (isatty) or explicit `--stdin` flag | 183 + | (future) `--files` flag | Per-file stats on selected trees | Separate ticket (`is-tree-per-file-stats`) | 184 + 185 + Two flags. That's it. The rest is Unix.