fix(builtins): honor --help argv order and rewrite pflag errors to GNU style#238
Conversation
…U style Two follow-ups from PR #205 (df builtin) review: 1. `cmd --help --bogus` now prints help and exits 0, matching GNU coreutils' left-to-right `--help` short-circuit. Previously pflag scanned the whole argv and failed on `--bogus` first. Implemented in `builtins/builtins.go` via a pre-parse args trim, scoped to commands that register a `help` flag. 2. pflag's default flag-error wording (`unknown flag: --foo`, `unknown shorthand flag: 'X' in -...`, `flag does not allow an argument`, `flag needs an argument: --foo`) is now translated to GNU's `unrecognized option '--foo'`, `invalid option -- 'X'`, `option '--foo' doesn't allow an argument`, and `option '--foo' requires an argument`. A `Try 'cmd --help' for more information.` footer is appended when the command exposes `--help`. Several df scenarios that previously carried `skip_assert_against_bash` solely because of wording divergence now match GNU byte-for-byte and have been de-skipped. Updated all sibling scenarios and Go tests (ping, pwd, ip, strings, uniq, sed, du) that asserted on the old pflag wording. Refs: - #205 (comment) - #205 (comment) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 25059712c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex review caught that `df --no-such=value` emitted `df: unrecognized option '--no-such'` while GNU emits the full token `'--no-such=value'`. pflag strips the `=value` suffix from its error string, so `rewritePflagError` now takes the parsed argv and scans it to recover the original token. Added a unit-test case (--no-such=value, --no-such=, and `--` boundary) plus a scenario test that asserts byte-for-byte match against GNU. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c85098992
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught a second wording divergence: pflag emits two different
payloads for a missing argument depending on flag form:
long form: `flag needs an argument: --foo`
short form: `flag needs an argument: 'X' in -Y`
Previously the rewriter inlined the payload into a single template,
producing `option ''n' in -n' requires an argument` for `head -n`.
GNU uses two separate templates:
long: `option '--foo' requires an argument`
short: `option requires an argument -- 'X'`
`rewritePflagError` now branches on the payload shape via a small
`shortMissingArg` helper. Added unit-test cases for the single
shorthand and the combined-shorthand form (`-an`) plus scenario tests
under `tests/scenarios/cmd/head/errors/missing_arg_{short,long}.yaml`
that assert against GNU.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: cb77a039a0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex flagged that the previous trim "args at first --help" was too aggressive: `head -n nope --help --bogus` would discard `--bogus`, let pflag parse `-n nope --help` cleanly, then short-circuit on --help and exit 0 — silently swallowing both the unknown later flag and the invalid earlier value. GNU instead surfaces the earlier validation error. Restricted the trim to the case where `--help` is the very first argument. This still covers the cases the original reviewer cited (`df --help --sync`, `df --help --bad`) and every existing test, but keeps full pflag validation when `--help` appears after another flag — preserving GNU's "fail on the leftmost bad option" semantics. Added a regression scenario at `tests/scenarios/cmd/head/errors/help_after_value_does_not_swallow_unknown_flag.yaml` that locks in: `head -n nope --help --bogus` exits 1 (does not print help). skip_assert_against_bash is set because GNU's failure reason (invalid -n value) and rshell's (unknown --bogus) differ — both exit 1 and neither prints help, which is the property we care about. The underlying value-validation gap is a pre-existing issue out of scope. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
GNU getopt parses `-h=true` as the no-arg shorthand `-h` followed by `=` interpreted as another shorthand char (which isn't registered), emitting `df: invalid option -- '='`. pflag instead treats `=` as the value separator and routes the value through Set, which our no-arg guards reject. Detect the `-X=value` argv shape in the rewriter and substitute GNU's wording when the descriptor's shorthand char matches X. Closes the last wording-only divergence flagged in PR #205's follow-up audit (`tests/scenarios/cmd/df/flags/rejected_h_equals_true.yaml`); skip removed and stderr now matches GNU byte-for-byte. Updated unit tests: - new case: -h=true with descriptor `-h, --human-readable` → `'='` - existing -k=true case (short-only descriptor) updated — GNU also emits `invalid option -- '='` for that, not `option '-k' doesn't allow an argument` Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f55983930b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex flagged that GNU honors --help even after earlier valid options — `head -q --help --bogus` and `df -h --help --bogus` print help and exit 0. The previous "first-only" trim was too narrow. The new `safeHelpTrimIndex` scans tokens before --help and trims only when every preceding token is a registered no-argument flag (long form or shorthand cluster). Tokens that could fail validation (value-takers, =value forms, positionals, unknown flags, end-of-flags `--`) all preclude the trim — keeping the earlier-failure semantics from the prior round (`head -n nope --help --bogus` still errors instead of silently swallowing the invalid value). Added unit tests for `safeHelpTrimIndex` covering each token class and a regression scenario at `tests/scenarios/cmd/df/basic/help_short_circuits_after_no_arg_flag.yaml` (`df -h --help --bogus` exits 0 with usage). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
CI's `Fuzz (pwd)` and `Fuzz (du)` jobs caught a panic in the new `isNoArgFlagToken`: for a token like `-˞`, the loop calls `fs.ShorthandLookup(string(a[i]))`. When `a[i]` is a UTF-8 continuation byte (≥ 0x80), `string(byte)` produces the 2-byte UTF-8 encoding of that byte interpreted as a rune (e.g. 0xCB → "Ë"), and pflag panics with `can not look up shorthand which is more than one ASCII character`. The interpreter recovers and reports `internal error` from api.go's panic guard. Guard the loop: any byte outside printable ASCII (≤ ' ' or > 0x7E) disqualifies the token as a no-arg cluster — non-ASCII bytes can't be valid shorthand chars anyway. Captured the failing input as a permanent corpus seed (`builtins/pwd/testdata/fuzz/FuzzPwdArgs/9386e59311458487`) and added a unit-test case to TestSafeHelpTrimIndex. Verified locally: 30s fuzz on both pwd and du now passes. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 18e763ab39
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Closes the trade-off codex flagged on the prior round ([3202837999](#238 (comment))). GNU coreutils process options left-to-right, so `head -n 5 --help --bogus` prints help (valid -n value, --help short-circuits) while `head -n nope --help --bogus` errors (invalid -n value surfaces before --help is reached). The prior no-arg-only trim only matched GNU for the no-arg-before-help case. Three changes work together: 1. `builtins/builtins.go` — replaced the syntactic no-arg-only `safeHelpTrimIndex` with `trialHelpTrimIndex`, which trial-parses the prefix on a fresh FlagSet. If pflag accepts every preceding option, the suffix is safely discardable. This now permits trims after value-takers, but defers the safety guarantee to per-builtin value validation. 2. `builtins/head/head.go` — moved the -n/-c value validation to BEFORE the `--help` short-circuit. Switched the error wording from Go-style `%q` (double quotes) to single-quoted `'%s'` so the message matches GNU byte-for-byte (`head: invalid number of lines: 'nope'`). 3. `builtins/tail/tail.go` — removed the no-op validation from `modeFlag.Set` (it returned `errors.New("invalid count")` and pflag wrapped it as `invalid argument "X" for "-n, --lines" flag: invalid count`). Validation now lives upfront in the handler, before `--help`, with the same GNU-matching format as head. Both explicit -n and -c values are validated even when one later overrides the other, matching GNU. Tests: - `TestSafeHelpTrimIndex` replaced with `TestTrialHelpTrimIndex`, covering no-arg flags, value-takers (long, short, `=value`), positionals, `--`, unknown flags, and the non-ASCII fuzz case. - Updated `tests/scenarios/cmd/head/errors/help_after_value_does_not_swallow_unknown_flag.yaml` to assert GNU's "invalid number of lines: 'nope'" wording and dropped its `skip_assert_against_bash` (now byte-for-byte with bash). - Updated `tests/scenarios/cmd/tail/errors/minint64_suffix.yaml` substring from "invalid count" to "invalid number of lines". - Added four scenarios locking in the four target cases: `head/basic/help_short_circuits_after_value_taker.yaml`, `head/errors/invalid_lines_value.yaml`, `tail/basic/help_short_circuits_after_value_taker.yaml`, `tail/errors/invalid_lines_value.yaml`. Verified end-to-end against bash with `RSHELL_BASH_TEST=1` plus 15s fuzz on pwd and du. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Pure refactor — no behaviour change. Moves the error-rewriting and
trial-parse helpers out of builtins.go into a dedicated package so
the file is back to focusing on builtin registration.
What moved:
- rewritePflagError → flagparser.RewriteError
- trialHelpTrimIndex → flagparser.TrialHelpTrimIndex
- extractFlagDescriptor, longFlagName, recoverLongFlagToken,
shortFlagEqualsValueIn, shortFlagFromDescriptor, shortMissingArg
(all unexported helpers)
builtins.go now imports flagparser and delegates. The trial-parse
helper's signature changed from `func(*FlagSet) HandlerFunc` to
`func(*FlagSet)` so the package doesn't need to know about
builtins.HandlerFunc — the caller wraps factory in a one-line
closure that discards the returned handler.
Tests moved with the code (builtins/internal/flagparser/flagparser_test.go).
analysis/symbols_internal.go gained a flagparser entry plus
io.Discard, strings.CutPrefix, strings.HasSuffix, and the three
pflag symbols used by the trial parser.
Addresses [3204000991](#238 (comment))
option #2. Keeps option #1 (own parser) as a future possibility:
if we ever swap pflag for a hand-written GNU-getopt implementation,
the swap is local to this package.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
Alignment-only diff — gofmt rebalanced the comment columns after the previous commit added new entries for the flagparser package. CI's gofmt check picked this up locally; running gofmt -w resolved it. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8873b509aa
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught two bugs in TrialHelpTrimIndex's success condition. The
trial parse could succeed even when pflag consumed `--help` in a way
that did NOT set the help flag:
1. As the value of a preceding value-taker. `grep -e --help --bogus`
makes `--help` the pattern for -e; the trial parsed fine but help
was never requested. The old code trimmed `--bogus` away and grep
then ran with pattern `--help` instead of reporting the bad flag.
2. As a positional, for FlagSets that called `SetInterspersed(false)`
(xargs, tr, read). After the first positional, pflag stops parsing
flags. `xargs echo --help --bogus` left both `--help` and `--bogus`
as positional args for the child command, but our trim discarded
`--bogus`.
Fix: after a successful trial parse, also require
`trial.Lookup("help").Changed == true`. If pflag didn't actually call
Set on the help flag, we leave args alone and let the real parse
report any later error.
Tests:
- New cases in TestTrialHelpTrimIndex for both shapes (`-n --help`,
`-e --help`, plus a dedicated SetInterspersed(false) sub-test for
the xargs/tr/read style).
- Verified end-to-end: `grep -e --help --bogus` now emits
`grep: unrecognized option '--bogus'` (matching GNU);
`xargs echo --help --bogus` correctly passes `--help --bogus` to
echo.
- All previously-fixed cases (head -q/-n/-h/--help-first variants,
df invocations) still match GNU.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3e6601a666
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex flagged that `sort --check=bogus --help` printed help and exited 0 instead of erroring on the invalid value. Same class of bug as head/tail had: sort defers `--check` value validation, `-cC` conflict detection, `-t`/`-k` parsing, and `-d`/`-n`/`-h` global-flag conflicts to its handler — but the handler checked `*help` first, so any `--help` later in argv silently masked these earlier validations. GNU sort processes options left-to-right; an invalid earlier option exits non-zero even when `--help` follows. Moved all GNU-style validations to BEFORE the `--help` short-circuit in builtins/sort/sort.go. The rshell-specific rejections (`--output`, `--temporary-directory`, `--compress-program`) stay AFTER `--help` because GNU accepts those flags and prints help when `--help` is also set — we mirror that ordering and only reject when no help short- circuit fires. Verified end-to-end: - `sort --check=bogus --help` → exit 2, "invalid argument" (was: help, 0) - `sort -c -C --help` → exit 2, "'-cC' are incompatible" (was: help, 0) - `sort -t '' --help` → exit 2, "empty tab" (was: help, 0) - `sort -t XX --help` → exit 2, "multi-character tab" (was: help, 0) - `sort -k bad --help` → exit 2, "invalid key option" (was: help, 0) - `sort --output=/tmp/x --help` → help, 0 (still, since --output is rshell-rejected after --help — matches GNU which accepts --output) - `sort --help` → help, 0 (unchanged) Added two regression scenarios: - `tests/scenarios/cmd/sort/errors/invalid_check_with_help.yaml` - `tests/scenarios/cmd/sort/errors/check_conflict_with_help.yaml` Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 918125a0d6
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught that GNU sort treats the global ordering-flag conflicts as deferred validation: `sort -d -n --help`, `sort -d -h --help`, and `sort -h -n --help` all print help and exit 0. My previous reorder was too aggressive — it moved EVERY validation before --help, including these. Moved the -dn/-dh/-hn global-conflict checks back to AFTER --help. The genuinely-parse-time validations (--check value, -c+-C, -t length, -k parsing) stay BEFORE --help, matching GNU's left-to-right rule. Verified GNU/rshell match on all cases: | invocation | GNU/rshell | |-------------------------------------|------------| | sort --check=bogus --help | error, 2 | | sort -c -C --help | error, 2 | | sort -t XX --help | error, 2 | | sort -k bad --help | error, 2 | | sort -d -n --help | help, 0 | | sort -d -h --help | help, 0 | | sort -h -n --help | help, 0 | | sort -d --help -n | help, 0 | | sort --help | help, 0 | Added a regression scenario at `tests/scenarios/cmd/sort/basic/help_short_circuits_over_global_conflicts.yaml` to lock in the deferred-validation behavior. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 642d0c6697
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught that the previous code scanned the entire argv for any `-X=value` token whose shorthand matched the failing flag, even when the actual failing token was the long form earlier in argv. Example regression: `df --human-readable=true -h=x` should emit `option '--human-readable' doesn't allow an argument` (GNU), but rshell emitted `invalid option -- '='` because the later `-h=x` matched the shorthand scan. pflag stops at the FIRST bad option in argv, so the rewriter now walks args left-to-right and uses whichever form (long `--LONG=val` or short `-X=val`) appears first. Removed the now-unused `shortFlagEqualsValueIn` helper; the lookup is inlined into the no-arg branch of `RewriteError` so it can return immediately on either match. Two new unit-test cases lock in the left-to-right rule: - long --foo=val before -X=val → long-form error wins - short -X=val before --LONG=val → short-form error wins (the reverse, even though no real builtin has flags ordered that way). Verified end-to-end: - df --human-readable=true -h=x → `option '--human-readable' doesn't allow an argument` (matches GNU; was: `invalid option -- '='`) - df -h=x → `invalid option -- '='` (unchanged) - df --human-readable=true → `option '--human-readable' doesn't allow an argument` (unchanged) Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Lines 406 to 408 in 1b32a67
When the same mode flag is repeated, this setter overwrites the previous value and the handler only validates the final one. As a result, head -n nope -n 1 now succeeds and reads stdin, while GNU head rejects the first invalid -n value before considering later options; tracking the first invalid value (as sort does for --check) would preserve the intended left-to-right validation without losing the --help short-circuit.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught that `tail -n nope -n 1` (and the same for head) exited 0 and printed the last line, masking the earlier invalid value. The deferred-validation approach lost track of the FIRST bad input because modeFlag.Set always overwrote val and the handler only validated the final value. Added `invalid` / `hasInvalid` fields to both head's and tail's modeFlag. Set captures the first value rejected by parseCount but keeps accepting later writes (so val/offsetSeen/pos still track the last call). The bound handlers in both builtins now check `hasInvalid` and report the captured `invalid` value, matching GNU which validates each option as it sees it. Verified end-to-end: - `tail -n nope -n 1` → `tail: invalid number of lines: 'nope'` (was: prints last line, exit 0) - `head -n nope -n 1` → `head: invalid number of lines: 'nope'` (was: prints first line, exit 0) - `head -c bad -n 5` → `head: invalid number of bytes: 'bad'` - `head -n 1` / `tail -n 1` → still work (no regression) Added two regression scenarios: - `tests/scenarios/cmd/head/errors/invalid_lines_overridden.yaml` - `tests/scenarios/cmd/tail/errors/invalid_lines_overridden.yaml` Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8ab6703b14
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex P1 flagged that the shared trial-parse trim in builtins/builtins.go
silently masks handler-time validation errors for builtins that check
`*help` before their value-taker checks. Three builtins were
specifically called out:
- `cut -d xy --help --bogus` printed help instead of erroring on the
multi-byte delimiter.
- `uniq -f nope --help --bogus` printed help instead of erroring on
the invalid skip-fields count.
- `strings -n 0 --help --bogus` printed help instead of erroring on
the out-of-range minimum length.
Same fix pattern as head/tail/sort: move the value validation BEFORE
the `if *help` short-circuit. For cut, the delimiter check is now
guarded by `fs.Changed("delimiter")` so `cut --help` (with the default
"\t") still short-circuits cleanly. For uniq and strings, default
flag values pass the existing checks unchanged.
Verified end-to-end:
- `cut -d xy --help --bogus` → `cut: the delimiter must be a single character`, exit 1
- `uniq -f nope --help --bogus` → `uniq: nope: invalid number of fields to skip`, exit 1
- `strings -n 0 --help --bogus` → `strings: invalid minimum string length 0`, exit 1
- `cut --help` / `uniq --help` / `strings --help` → help, 0 (no regression)
- `cut -d , --help --bogus` / `uniq -f 1 --help --bogus` / `strings -n 4 --help --bogus` → help, 0 (value valid)
Added regression scenarios at
`tests/scenarios/cmd/{cut,uniq,strings}/errors/invalid_*_with_help.yaml`.
(Docker is currently unavailable so the bash-comparison test was not
re-run for these specific scenarios; the Go-level scenarios all pass.
Prior bash-comparison runs were green on the rest of the suite.)
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
PR #237 (cd builtin) merged to main after this PR branched and brought tests that assert pflag's pre-rewrite wording: `unknown flag` / `unknown shorthand flag`. After merging main into this branch the shared rewriter in builtins/internal/flagparser/ translates those to GNU's `unrecognized option` / `invalid option -- 'X'`, plus the `Try 'cmd --help' for more information.` footer. Updated: - builtins/cd/cd_test.go: TestCdUnknownLongFlag substring assertion switched to "cd: unrecognized option". - tests/scenarios/cmd/cd/errors/unknown_flag_short.yaml: stderr now matches "cd: invalid option -- 'x'\nTry 'cd --help' for more information.". - tests/scenarios/cmd/cd/errors/unknown_flag_long.yaml: stderr now matches "cd: unrecognized option '--no-such-flag'\nTry 'cd --help' for more information.". Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
CI's bash-comparison flagged that GNU cut appends `Try 'cut --help' for more information.` after the "delimiter must be a single character" error; rshell only emitted the error line. Updated cut's handler to emit the footer too and updated the regression scenario `tests/scenarios/cmd/cut/errors/invalid_delimiter_with_help.yaml` to match. Other cut error scenarios use `stderr_contains` substring match and are unaffected by the extra line. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e91315756
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught that head/tail's mode-flag validation always reported
linesFlag's error before bytesFlag's, regardless of argv order. For
`head -c bad -n nope` (and tail), GNU reports the byte error since
-c came first; rshell reported the line error because the handler
checked linesFlag first.
Added an `invalidPos` field to each builtin's modeFlag that captures
the shared seq counter value WHEN hasInvalid is first set. Added a
small `reportLinesInvalidFirst` helper in each handler that picks the
mode flag with the smaller invalidPos when both are invalid, so the
emitted error matches whichever bad value GNU's leftmost-bad-option
rule would surface.
Verified end-to-end:
- `head -c bad -n nope` → `head: invalid number of bytes: 'bad'` (was: lines)
- `head -n nope -c bad` → `head: invalid number of lines: 'nope'` (unchanged)
- `tail -c bad -n nope` → `tail: invalid number of bytes: 'bad'` (was: lines)
- `tail -n nope -c bad` → `tail: invalid number of lines: 'nope'` (unchanged)
- single-flag cases (`head -n nope`, `head -c bad`, `head -n 5 -c bad`,
prior `-n nope -n 1` regression) → all match GNU.
Added regression scenarios at
`tests/scenarios/cmd/{head,tail}/errors/invalid_bytes_before_lines.yaml`.
Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
|
@codex review |
|
Codex Review: Didn't find any major issues. More of your lovely PRs please. ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Two follow-ups from PR #205 (df builtin) review feedback:
cmd --help --bogusnow prints help and exits 0, matching GNU coreutils' left-to-right--helpshort-circuit. Previously pflag scanned the whole argv and failed on--bogusfirst. Implemented inbuiltins/builtins.govia a pre-parse args trim, scoped to commands that register ahelpflag.pflag flag-error wording now matches GNU getopt byte-for-byte:
unknown flag: --foo→unrecognized option '--foo'unknown shorthand flag: 'X' in -...→invalid option -- 'X'flag does not allow an argument→option '--foo' doesn't allow an argumentflag needs an argument: --foo→option '--foo' requires an argumentTry 'cmd --help' for more information.footer appended when the command exposes--help.Several
dfscenarios that previously carriedskip_assert_against_bashsolely because of wording divergence now match GNU byte-for-byte and have been de-skipped. Sibling scenarios and Go tests for ping, pwd, ip, strings, uniq, sed, and du were updated to the new wording.Refs:
Test plan
🤖 Generated with Claude Code