cli-tools list: say which implementation is on PATH - #12
Merged
Conversation
…e is `list` asked whether a file of each name existed on PATH and printed `*` when one did. On the machine this was written for it printed eight stars and "All on PATH" while only three of the eight were this checkout's code — the other five resolved to the older hand-written scripts they were ported from, in ~/scripts/bin. A presence check cannot tell "installed" from "something answers to that name", and here those are different programs. That distinction is not cosmetic. gh-prs-merge repairs by default under --apply in the older script and only when asked with --fix here, so which file is on PATH changes what a merge run does. Reporting both as installed is the same misleading-green-row failure as an exit code that cannot tell "I refused" from "there was nothing to do". So resolveCommand() follows the symlink on both sides and classifies each name as ours, other, or missing. A row that is somebody else's is marked `!` and names the file it found, because otherwise you know a name is taken but not by what, and the next step is a readlink you should not have had to think of. The summary counts the three states separately, and points at `link --force` while saying to check the flags first. Falling out of it: run from a worktree, every installed command now correctly reads as `!`. That is the stale-checkout trap the docs warn about, visible without asking for it. 120 tests pass (was 114), typecheck clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ThreatCrush Security Scan3 finding(s) MEDIUM: 1 | LOW: 2
Snippets are redacted; ThreatCrush never prints matched credential material. |
ralyodio
added a commit
that referenced
this pull request
Aug 19, 2026
…#14) generate-names needed OPENAI_API_KEY or ANTHROPIC_API_KEY in the environment, which in practice means writing the key into a shell profile — a plaintext copy in a file that is backed up, synced, and read by every process you start, to configure one command that runs occasionally. So there is now a store: ~/.config/cli-tools/credentials.json, written 0600 inside a 0700 directory, with a command to manage it. cli-tools config what is set, and where each key came from cli-tools config set openai prompts with echo off cli-tools config unset openai Nothing prints a whole key. `config` shows a masked preview and a length — enough to tell two keys apart, not enough to use one — and --json carries the same previews rather than the values. The environment still wins over the file, because a one-off `KEY=… command` and a CI-injected key both have to keep working. That precedence is invisible at the moment it bites: you store a key, the old one keeps being used, and nothing says why. So `config` reports the *source* of each key rather than only whether one exists, and states plainly when a stored value is being shadowed. It is the same lesson as the `list` fix in #12 — presence is not the useful question, provenance is. A value may be piped for scripts, or passed inline; inline warns, because it lands in shell history and in `ps`. Deliberately not a .env: nothing loads it into an environment wholesale, nothing syncs it, and it is not how a key travels between machines. A secret a deployed service needs still belongs on that service. resolveCredentials returns an environment-shaped record, so resolveProvider consumes it unchanged — the wiring is one line, and a test covers it. 167 tests pass (was 164 plus 3 new wiring cases), typecheck clean. Verified end to end in a sandbox: set/unset/re-set, 0600 on a file that already existed with a looser mode, masked output, and no key in --json. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Fixes a defect in the
listcommand shipped in #10.The bug
listasked whether a file of each name existed onPATHand printed*when one did. On the machine this was written for, that produced:Eight stars, "All on PATH" — while only three of the eight were this checkout's code. The other five resolved to the older hand-written scripts they were ported from, in
~/scripts/bin. A presence check cannot tell "installed" from "something answers to that name", and here those are different programs.That distinction is not cosmetic.
gh-prs-mergerepairs by default under--applyin the older script and only when asked with--fixhere. Reporting both as installed is the same misleading-green-row failure as an exit code that cannot tell "I refused" from "there was nothing to do".The fix
resolveCommand()follows the symlink on both sides and classifies each name asours,otherormissing:The
!row names the file, because otherwise you know a name is taken but not by what, and the next step is areadlinkyou should not have had to think of.Falling out of it: run from a worktree, every installed command correctly reads as
!— the stale-checkout trap the docs warn about, now visible without asking for it.Checks
120 tests pass (was 114), typecheck clean. New cases cover ours / other / missing, PATH order precedence, a broken symlink, and a sibling directory with a shared prefix (
bin-oldbesidebin) that a naivestartsWithwould have called ours.🤖 Generated with Claude Code