diskr 0.1.70

Lightweight terminal file explorer and disk/storage manager for macOS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
# Changelog

All notable changes to diskr are documented here. The format follows
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow
[Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.1.70] - 2026-08-06

### Added

- The keyboard-help overlay now scrolls on short terminals, balances sections
  across wide columns by rendered height, and shows the visible line range.
- Package-report JSON now includes `project_inaccessible`, and file, history,
  reclaim, and project-dependency reports surface enumeration failures instead
  of silently presenting partial results as complete.

### Changed

- Filenames, paths, and external-command diagnostics are now escaped before
  human terminal rendering, while JSON continues to preserve machine-readable
  values.
- File-info collection and every move-to-Trash operation now run on guarded
  background workers; package uninstall also has a five-minute process-group
  timeout, keeping the TUI responsive on slow volumes and package managers.
- Persistent history and size-cache writers now coordinate across processes,
  merge updates and invalidations under a lock, create state files with
  owner-only permissions, and sync the containing directory after atomic
  replacement.
- File-only and Reclaim-only keys now act only in the pane where their help is
  shown. Unicode layout and truncation are grapheme-aware, including combining
  characters and emoji.
- Updated compatible dependencies, including crossterm 0.29, anyhow 1.0.104,
  ratatui-core 0.1.2, and ratatui-widgets 0.3.2.

### Fixed

- Reclaim totals now aggregate the union of finding paths: nested categories
  are counted once without dropping unrelated bytes from a parent category.
- FSEvents `MustScanSubDirs` replays now fall back to full cache revalidation,
  so cached descendants cannot remain falsely fresh after a recursive event.
- Snapshot-thinning sizes are parsed with checked decimal arithmetic, rejecting
  values above `u64::MAX` instead of rounding and clamping through `f64`.
- Scanner syscall, malformed-record, open, and directory-entry failures now
  make totals explicit lower bounds instead of being treated as successful EOF.
- Filesystems that do not support the native bulk-attribute syscall now fall
  back to compatible directory enumeration instead of returning an empty scan.
- Package warning truncation no longer splits UTF-8 and aborts release builds.
- Repeated baseline saves and overlapping Trash operations are coalesced or
  rejected without discarding marks, and disconnected workers report failure.

## [0.1.69] - 2026-06-15

### Removed

- Removed the `Space` / Quick Look preview action, which added little over
  revealing the file. Reveal in Finder (`f`) — now also shown in the footer —
  Open (`O`), copy path (`y`), and open in Terminal (`s`) are unchanged.

## [0.1.68] - 2026-06-15

### Changed

- The in-progress scanning indicator is now a four-cell braille "beam" that
  sweeps left to right (the "scan" preset from the rattles spinner library) in
  place of the single spinning braille dot. It appears wherever a scan or load
  is running: the files-pane size cell, the reclaim, top-files, disk-info,
  package, and dependency loading labels, and the per-selection status line. The
  larger centered package-data splash spinner is unchanged.

## [0.1.67] - 2026-06-15

### Changed

- The disk-info, file-info, package-detail, and project-deps detail modals now
  share one centered placement and chrome, so they open at a consistent size
  with consistent wrapping instead of four slightly different dimensions.

## [0.1.66] - 2026-06-15

### Added

- On launch, diskr now consults macOS FSEvents to learn which directories
  changed since the previous session and revalidates only those, so unchanged
  directory trees keep their cached sizes with no rescan. The last event id and
  volume UUID are persisted next to the size cache; the replay falls back to the
  age-based revalidation on first run, a different or reformatted volume, or
  dropped/coalesced events.

## [0.1.65] - 2026-06-15

### Added

- The file browser now tags directories the reclaim engine recognizes
  (`node_modules`, `target`, `.venv`, `__pycache__`, `~/Library/Caches`, …) with
  a colored class chip (`cache`/`build`/`keep`), surfacing reclaim knowledge on
  the main surface — name/path matching only, with no extra I/O while rendering.
- A growth sort mode (in the `o` sort cycle) and a per-row ``/`` badge showing
  how much a directory changed since its previous scan, so revalidation surfaces
  what actually grew instead of the same familiar top-N.
- `diskr --packages --json` now includes the canonical project root as `path`,
  matching the other JSON reports.

### Changed

- The top-files and reclaim-paths overlays now share one list-modal component,
  so they have identical placement, chrome, and footer conventions.
- The side column now collapses to a compact one-line-per-disk summary while
  browsing files, giving the files pane the reclaimed width, and expands back to
  full disk gauges when the Disks or Packages pane is focused or packages load.
- Files-pane density pass: the pane title now summarizes item count, total size,
  scan coverage, the active sort, and any marked-items total; sizes are colored
  by magnitude (dim sub-MiB, green MiB, bold amber GiB+); the modified column
  shows whenever the pane is wide enough; marked rows keep their type glyph next
  to the check; the app header drops the always-on "sort/hidden off" defaults;
  and the `?` overlay gained a size-marker legend.

- The footer help strip is now context-sensitive: it lists the keys for the
  focused pane, truncates to fit narrow terminals, and always keeps `?` visible
  instead of rendering one long static line. Destructive keys (`d`, `E`, `x`)
  are highlighted in red in both the footer and the `?` overlay, and the README
  key table now documents every binding and is checked against the keymap by a
  test so the two cannot drift.

### Fixed

- Background history-diff and top-files scans now stop early when they are
  superseded or closed — navigating between baselined directories, or pressing
  Esc on the top-files modal — instead of running a full directory walk to
  completion only to discard the result, so redundant walks no longer pile up.
- Moving the cursor onto an unsized directory while a scan is running no longer
  cancels the in-flight batch: such directories are queued and scanned once the
  batch finishes, so spinners and progress are not wiped out by a premature
  "scan complete".
- A fully-cached directory view is no longer a frozen snapshot. When nothing is
  missing, diskr now revalidates the oldest stale directories in the background
  (bounded, oldest first) and reports honest status ("sizes from cache (oldest
  …) · verifying N") instead of "cache hit · all sizes known", so a folder that
  grew since the last scan stops sitting at its old rank.
- The Reclaim pane's status line and the Space/f/O/y/s actions now target the
  highlighted finding once the paths modal is closed, instead of a path from a
  previously-opened finding.
- The batch-delete confirmation now counts marked files, not just cached
  directories, and shows the total as a lower bound (``) when a marked
  directory has no cached size, so the common "mark some files" case no longer
  reads as zero bytes.
- Package navigation and actions can no longer reach hidden packages through a
  not-yet-built visibility cache: an empty or unbuilt package filter now
  consistently exposes zero rows everywhere.
- On short terminals the packages pane is given rows whenever it is focused or
  loaded, so `Tab`/`p` can no longer move focus onto an invisible, zero-height
  packages pane.
- `--thin-snapshots` now validates its path the same way `--space` does (it
  must exist and be a directory, and is canonicalized) and resolves the owning
  volume before invoking `tmutil`, so it no longer accepts regular files,
  relative paths, or symlinks, and the preview shows the resolved mount.
- `size-cache.json` and `history.json` are now written atomically (a temp file
  is fsynced and renamed over the target), so a crash or full disk during a
  save leaves the previous file intact instead of a truncated one. A corrupt or
  unreadable `history.json` now surfaces a startup status warning the way the
  size cache already does, instead of silently presenting "no baselines".
- `--save` and `--diff` now report unreadable directories: saved baselines
  record an unreadable-directory count, and both the text and JSON output warn
  when permission errors limited the scan, matching the TUI, `--top`, and
  `--reclaim`. Baselines saved by older versions load unchanged.
- pip package sizing now derives the package list and the site-packages
  directories from the same Python interpreter and also searches the per-user
  site-packages, so `pip install --user` packages are sized and Homebrew, CLT,
  and pyenv interpreter splits no longer mismatch. dist-info matching now
  requires a version digit after the package name, so `sentry` is no longer
  mis-sized from `sentry-sdk`'s metadata.
- Directory scans now flag more unreadable cases instead of silently
  undercounting: a subtree whose path grows past the platform length limit
  during the walk, and individual entries whose attributes the kernel cannot
  read, are counted toward the unreadable total so reported sizes stay honest
  lower bounds.
- Mount-boundary skipping now covers volumes outside `/Volumes`. Scanning `/`
  no longer folds the APFS Preboot/VM/Update helper volumes into the system
  total, and network or FUSE mounts at custom paths are skipped rather than
  walked as local data; the firmlinked Data volume is still counted. Skipped
  mounts are now described as "mounted volumes on other devices".

## [0.1.64] - 2026-06-15

### Fixed

- TUI history baseline saves now persist only after the background scan result
  is validated as current, so a stale save worker can no longer overwrite a
  newer saved baseline on disk after the UI has discarded its result.

## [0.1.63] - 2026-06-13

### Changed

- Relaxed the `ratatui-core` and `ratatui-widgets` dependency pins from exact
  patch versions to caret ranges, so diskr can pick up compatible upstream
  fixes without a manifest edit while keeping the existing custom crossterm
  backend path.

## [0.1.62] - 2026-06-13

### Changed

- Saved history baselines now retain only the newest 512 directories on disk
  and in memory, so `history.json` and the TUI baseline cache cannot grow
  without bound as more paths are saved.

## [0.1.61] - 2026-06-13

### Fixed

- Reclaim findings now reserve separate list and detail regions, so the
  selected finding details no longer cover the middle of the findings list.

### Changed

- Moved private maintenance materials out of the public repository, including
  issue tracking, audit notes, and agent workflow instructions. CI and release
  jobs now reject those files if they are accidentally tracked again.

## [0.1.60] - 2026-06-13

### Fixed

- History baselines now stay parsed in memory for the TUI session, and
  baseline diffs index the prior child list once instead of linearly
  rescanning it per entry, which removes repeated `history.json` parses on
  navigation and avoids quadratic diff time on large directories.

## [0.1.59] - 2026-06-13

### Fixed

- Package-manager command timeouts now start each manager probe in its own
  process group and kill the whole group on deadline, so a helper process that
  inherits stdout/stderr can no longer keep `--packages` or the TUI package
  scan hung past the timeout. Regression coverage locks the shell-background
  case that previously blocked forever.

## [0.1.58] - 2026-06-13

### Fixed

- Package-manager scans now time out after 10 seconds (30 seconds for slow
  `brew cask` metadata) instead of blocking forever, and failed or timed-out
  managers surface a diagnostic warning in the TUI status line, CLI text output,
  and JSON reports instead of silently showing "0 packages."

- Resolved the audit paper-cut grab bag covering Reclaim refresh routing,
  package-pane focus behavior, diff labels, file-row alignment/truncation,
  disk labels, Full Disk Access rechecks, Empty Trash duplicate-request
  status, input cursor visibility, background batch delete, stale dead-code
  allowances, `HOMEBREW_PREFIX`, and PEP 621 dependency arrays.

## [0.1.57] - 2026-06-13

### Fixed

- Cache invalidation now drops cached sizes, stale markers, inaccessible
  counts, and cache ages for the changed path and every cached descendant, so
  deleting and recreating a directory cannot resurrect stale child sizes from
  the old tree.

## [0.1.56] - 2026-06-12

### Fixed

- Project dependency reports now merge multiple manifests that point at the
  same dependency directory, so Python projects with both `requirements.txt`
  and `pyproject.toml` count one `.venv` once while preserving both manifest
  labels.
- Size bars and the `%` column in the files pane now show each entry's share
  of the full directory rather than the scroll window, so percentages no longer
  change as you scroll and the largest item in view is no longer always
  full-width.

## [0.1.54] - 2026-06-12

### Fixed

- Package filter mode now treats plain `j` and `k` as query text, so package
  names like `jq` and `kubectl` can be typed while arrow keys still navigate.

## [0.1.55] - 2026-06-12

### Fixed

- PageUp/PageDown now jump by the active pane's visible rows in Files,
  Disks, Packages, and Reclaim, and page moves clamp at the first/last row
  instead of wrapping unexpectedly through the list.

## [0.1.53] - 2026-06-12

### Fixed

- Search and package-filter Enter now keeps the narrowed view active while
  leaving input mode; Esc and Ctrl+C clear the kept filter.
- `S` and the selected-directory scan path now rescan stale cached directory
  sizes instead of only directories with no cached size, so one suspicious
  row can be verified without invalidating the whole view.

## [0.1.52] - 2026-06-12

### Fixed

- `E` in the Reclaim pane only arms the Empty Trash confirmation when the
  loaded reclaim report actually lists a Trash finding, and the modal now
  shows that finding's path alongside its size. Without a Trash finding the
  status reports "Trash is not in this reclaim report" instead of arming a
  global Finder Empty Trash detached from the visible report.

## [0.1.51] - 2026-06-12

### Added

- Added a `?` keyboard help overlay backed by the shared TUI keymap, and
  shortened the footer to advertise `? help` instead of trying to fit every
  shortcut on one line.

## [0.1.50] - 2026-06-12

### Changed

- Replaced rayon with a purpose-built std-only work-stealing pool
  (`src/pool.rs`): per-worker deques with steal-oldest balancing, batched
  task spawns, idle-gated wakeups, and sync waiters that help drain the
  queue. Scan results and wall-clock throughput are unchanged (verified
  against the rayon build on wide, deep, and /Applications-shaped trees);
  rayon, rayon-core, crossbeam-deque, crossbeam-epoch, and crossbeam-utils
  leave the dependency graph (81 -> 76 locked crates).
- The walker now descends directory chains on one worker, accumulating
  results locally and merging into shared scan state once per chain instead
  of locking a global aggregate once per directory; chain descent opens
  children with `openat(2)` relative to the held parent fd, resolving one
  path component instead of re-walking the full path.
- Release builds use fat LTO instead of thin; with the dependency removal
  the binary shrinks ~8% (1,449,840 -> 1,333,392 bytes on Apple Silicon).

## [0.1.49] - 2026-06-12

### Fixed

- Rename keeps the cursor on the renamed entry after reloads in sorted views,
  instead of falling back to the old row index; restored the missing
  regression test.

## [0.1.48] - 2026-06-12

### Changed

- Overhauled development docs: added this changelog, structured private
  maintenance tracking, and a repeatable agent workflow protocol.

### Fixed

- Character keys carrying Control/Alt/Super no longer match plain-key
  actions: Ctrl+C cannot trigger rename, Ctrl+D cannot arm Trash deletion,
  and modified characters are not inserted into text inputs. Ctrl+C now
  cancels the active input mode, confirmation, or overlay like Esc; README
  and `--help` key lists updated.
- A panic now restores the terminal (raw mode off, alternate screen left)
  before the panic message prints, including release builds where
  `panic = "abort"` skips destructors.
- Reclaim reports no longer double-count nested fixed cache categories:
  parent categories containing other findings are marked as `[subtotal]`
  roll-up rows and excluded from the report total; JSON findings gain a
  `rollup` field.
- The package detail modal in the Projects view shows the selected project
  dependency row instead of an unrelated system package.
- `empty_trash` runs `osascript` through an injectable runner; the test suite
  can no longer touch the real Trash, and its doc comment no longer claims
  emptying is reversible.

## [0.1.47] - 2026-06-12

### Added

- File info popup: `i` in the Files pane opens a modal with full path, type,
  logical vs. allocated size (with APFS clone/sparse/compression note),
  created/modified/accessed timestamps, owner/group, permissions, hard-link
  count, and xattr count with quarantine flagging. Action keys for Quick Look,
  Finder reveal, Open, and Trash.

## [0.1.46] - 2026-06-12

### Fixed

- Homebrew cask rows now act on the real `.app` bundle: detail, Finder reveal,
  and Open follow the app bundle while uninstall keeps using the cask token.
  The Caskroom metadata path is shown separately when it differs.

## [0.1.45] - 2026-06-12

### Fixed

- Stabilized the package visibility cache regression test.

## [0.1.44] - 2026-06-12

### Fixed

- Package pane no longer rebuilds visibility indices on every rendered row
  (O(n^2) per frame); visible indices and lowercase search text are cached.
- npm global package sizing resolves from the active `npm root -g` first, so
  package lists and size lookups stay aligned under nvm/fnm.
- Top-files and reclaim-paths modals page by visible height and render their
  footers without clipping.

## [0.1.43] - 2026-06-12

### Fixed

- Size-sorted scan results no longer land on the wrong row: `apply_sort()`
  rebuilds the path-to-index map, so a directory size arriving after a
  mid-scan resort cannot be written onto an unrelated file.

## 0.1.34 - 0.1.42 (2026-06-11 - 2026-06-12)

Rapid audit-driven fix releases. Reconstructed summary; detailed provenance
lives in git history.

### Added

- Full-subtree scan mode: `S` scans every visible directory whose size is
  missing without invalidating known sizes (0.1.38).
- Clipboard copy (`y`) and open-in-Terminal (`s`) shortcuts (0.1.39).

### Fixed

- `Esc` no longer quits from the Files pane; `q` remains quit (0.1.36).
- Replaced the redundant outer scanner thread layer with a single rayon
  dispatch thread (0.1.37).
- Stale background package/reclaim results from a previous directory are
  discarded instead of being shown for the current one.
- Multi-select marks render a checkmark, clear on directory changes, and batch
  delete confirmations list the marked items.
- Rename follows the renamed entry after reload. This later regressed and was
  restored in 0.1.49.
- Empty Trash requires an explicit confirmation and runs on a background
  worker.
- Dependency-leaf graph covers casks, npm, cargo, and bun, not just brew
  formulae and pip.
- Baseline header chip formatting (duplicate "ago", missing separator).
- Reverse pane navigation from Files lands on Reclaim.

## 0.1.21 - 0.1.33 (2026-06-10 - 2026-06-11)

First audit wave. Highlights:

- Search-mode selection corruption fixes, spinner animation during quiet
  scans, permission failures surfaced as lower-bound sizes instead of
  silent 0 B, scan-result salvage and cancellation, `r` rescans all
  visible directories, hard-link and firmlink double-counting fixes,
  removed unused mouse capture, accurate brew cask and pip
  sizing, mtime display, project-deps rescan caching,
  TUI surfaces for reclaim/top-files/disk-details/history-diff,
  file operations: rename, mkdir, multi-select, batch trash, per-row
  size-share bars, persistent size cache.

## 0.1.0 - 0.1.20 (2026-05-01 - 2026-06-10)

Initial development: TUI file browser with `getattrlistbulk(2)` bulk scanner,
disks and packages panes, CLI reports (`--top`, `--reclaim`, `--save`/`--diff`,
`--space`, `--packages`, `--thin-snapshots`), automated release workflow.