feat: interactive session with tabular output - #9
Merged
Conversation
Checking one path meant relaunching the script and retyping every answer.
Once a result is printed, ask for another path instead of exiting, reusing the
class and permission already chosen, until the user presses q. Only the fully
interactive run loops; supplying everything via flags still checks once and
exits, which is what keeps the tool usable from cron and CI.
Add an "all" option to the permission prompt and to --perm, matching the one
the who prompt already had. Combined with --all this gives the full 3x3 matrix
of every permission against every class.
Report results as a bordered table with headers rather than flat lines, one row
per path, shading alternate rows so neighbouring rows stay distinct. Checking
and rendering are now separate: rows are collected first so the table can size
each column to the widest value it is about to print. Column widths are
computed from explicit display widths rather than ${#s}, which counts bytes
outside a UTF-8 locale and would skew every border once a check mark or an em
dash appears in a cell.
Also stop mistaking a path for a list of paths. Reading /etc/passwd as a list
of paths and reporting that "root:*:0:0:..." does not exist is useless, so a
regular file is only read as a list when it looks like one — its first
meaningful line being a path. --file still forces the list reading.
Cover the table, the matrix, the disambiguation, and the session loop, and add
pty cases for the loop, the q exit, and the row striping.
iamteedoh
pushed a commit
that referenced
this pull request
Jul 17, 2026
🤖 I have created a release *beep* *boop* --- ## [0.3.0](v0.2.0...v0.3.0) (2026-07-17) ### Features * check paths directly, with tilde/variable expansion and prompt history ([#7](#7)) ([c7f3389](c7f3389)) * interactive session with tabular output ([#9](#9)) ([e1d5e08](e1d5e08)) ### Bug Fixes * report the real version from --version ([#10](#10)) ([0d87357](0d87357)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
7 tasks
iamteedoh
added a commit
that referenced
this pull request
Jul 17, 2026
## What does this PR do? Three releases of behavior landed (#7, #9, #10) and parts of the README still described the tool as it was before. This audits the whole file against what v0.3.0 actually does. ### Documents tab completion The prompt has completed paths ever since readline arrived alongside the arrow-key history, and nothing said so — which means nobody would ever try it. Adds a keys table under **Interactive**: `Tab`, `↑`/`↓`, `Ctrl-A`/`E`/`W`/`U`/`R`, `q`. The table records what was **verified over a pty**, not what readline is assumed to do. Two claims were corrected during testing: - An earlier draft said "press Tab twice to list the candidates". That is false for `read -e` — it fills the common prefix and beeps without listing. Re-tested on a 120×40 pty in case readline was suppressing the list on a 0×0 terminal; it was not. - The session's history never touches your shell history (`HISTFILE` is cleared), so that is now stated rather than left to guesswork. ### Corrects what had gone stale | Was | Now | |---|---| | Overview: prompts "for the input file and the permission to check" | a session taking a path *or* a list file, looping until `q` | | Feature bullet advertising `owner rwx \| group r-x \| other r--` | that format no longer exists — replaced with the `Mode` column | | "Interactive prompts guide the user to select the input file" | predated `--path` | | "prompts for the rest when a terminal is attached" | had drifted under the **Paths vs. lists** heading during earlier edits, reading as though it were about disambiguation — moved back | | Prerequisites | now name the Bash 3.2 floor (what macOS ships), the tty the session needs, and that `getent`/`dscl` are only for *another* user's `~` | | Installation: "save the script content to a file" | predated the repo having releases — clone or download the release | ### Adds an Exit codes section The README recommends the tool for cron and CI but never documented that **a denied permission exits `0`** — only usage errors exit `2`. Anyone writing `dirPathPerms.sh … || alert` would get silence forever. Now documented, with a grep-based recipe for acting on the result. ## Related issue DIR-5 ## Validation Documentation-only, so the value is in whether the claims are **true**. Every runnable claim was executed rather than eyeballed: - **All 16 documented flags** exercised (`-P`, `--path`, `-f`, `--file`, `-w` × 4, `-p` × 5, `-a`/`--all`, `--no-color`, `-h`, `-V`, positional, repeatable `-P`) — all pass. - **Exit codes verified** against the new table: denied → `0`; missing list file, unknown option, `--file`+`--path`, and no-tty-no-path → `2`. - **The CI recipe was run**: it fires on a group-writable directory and stays silent when nothing matches. - **Tab completion driven over a pty** for `/etc/pass`, `/var/lo`, `~/Doc`. - All README internal anchors resolve. - Repo gate unchanged and green: shellcheck, `bash -n`, 47/47 tests, gitleaks. ## Notes for review Two things deliberately **not** done here: - **Symlink caveat.** Verifying a README example surfaced a real bug — `/tmp` reports the symlink's mode (`lrwxr-xr-x`) instead of the target's (`drwxrwxrwt`), so the tool answers "not group-writable" when it is. Raised as **DIR-6**. Documenting it as a caveat would enshrine a bug that should be fixed instead; say the word if you'd rather ship the caveat. - **The hero image** in `assets/` still shows `v0.1.0` and the old `File path:` prompt — the exact UX #7 removed. It needs re-capturing from a real terminal; there is no terminal-to-image tooling available to regenerate it here. `docs:` — README-only, so release-please will not bump the version for it. ## Checklist - [x] `git ls-files '*.sh' | xargs shellcheck` passes - [x] `git ls-files '*.sh' | xargs -n1 bash -n` passes - [x] gitleaks reports no secrets in Git history - [x] Scripts and programs include a GPL-3.0-or-later SPDX header - [x] No secrets, tokens, credentials, or private infrastructure details are committed - [x] Documentation is updated for user-visible or operational changes - [x] The PR title follows Conventional Commits
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Follow-on to #7. Once a path resolved, the tool was still awkward to actually use: every single check meant relaunching the script and retyping every answer, and the results were flat lines that got hard to scan past a handful of paths.
Changes
q. Only the fully interactive run loops — supplying everything via flags still checks once and exits, which is what keeps the tool usable from cron and CI.alloption for permissions, matching the one the who prompt already had.--perm all(oraat the prompt) checks read, write and execute together; combined with--allit gives the full 3×3 matrix of every permission against every class.YES/NOremain green/red.A bug this surfaced
Driving the session interactively caught something worth calling out, because it refines a rule #7 had just documented. #7 said "a regular file is read as a list of paths" — so typing
/etc/passwd, the single most obvious thing anyone would permission-check, parsed its contents as paths:A regular file is now only read as a list when it looks like one — its first meaningful line being a path (
/,~,$).--filestill forces the list reading unconditionally. The README documents the rule.Design notes for review
${#s}.${#s}counts bytes outside a UTF-8 locale, so an em dash would measure 3 columns instead of 1 and skew every border. Alignment is verified underLC_ALL=C.Related issue
DIR-3
Validation
qexit, the table, and the row striping only exist with a real terminal, sotests/interactive_test.pydrives them through a pseudo-terminal.YES/NOline-format assertions the previous suite used, since this changes that output format.q.mainafter feat: check paths directly, with tilde/variable expansion and prompt history #7 merged; main's post-squash tree was identical to the pre-squash tree, so the replay was conflict-free. Verified green on the new base.Checklist
git ls-files '*.sh' | xargs shellcheckpassesgit ls-files '*.sh' | xargs -n1 bash -npasses