Skip to content

refactor(cli): keep one copy of the task-flag escape rule - #12343

Merged
jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:refactor/one-copy-of-the-task-flag-escape
Aug 23, 2026
Merged

jdx merged 1 commit into
jdx:mainfrom
JamBalaya56562:refactor/one-copy-of-the-task-flag-escape

Conversation

@JamBalaya56562

@JamBalaya56562 JamBalaya56562 commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

src/cli/mod.rs carried the same rule three times: prefix an argument with TASK_ARG_ESCAPE_PREFIX
when it starts_with('-') && arg != "-", otherwise pass it through.

site shape
escape_args_after_separator (466-476) loop over a slice
escape_task_args, separator branch (621-629) loop over a slice
the task-args loop (703-708) one element

This came from review, not from me. CodeRabbit raised it on #12327 and again on #12314. Both
times it fell outside the diff — those PRs change one hunk each in this file, ~200 lines away — so I
declined and said it wanted its own PR. A finding that keeps landing on unrelated PRs is asking
for a home
, and the second report is what turned up the third copy, which the first had not
mentioned.

No behaviour change is intended.

The shape

An iterator-returning helper covers the two slice sites but not the third, which applies the rule to
a single argument inside a larger while. A singular helper covers all three:

fn escape_flag_arg(arg: &str) -> String {
    if arg.starts_with('-') && arg != "-" {
        format!("{TASK_ARG_ESCAPE_PREFIX}{arg}")
    } else {
        arg.to_string()
    }
}

The separator branch then does not need it at all — it can delegate:

let mut result = escape_task_args(cmd, &args[..separator_idx]);
result.extend(escape_args_after_separator(&args[separator_idx..], 0));

One correction to the review note, which said escape_args_after_separator "cannot be reused
directly here". It can. separator_idx is found by matching "--", so for the slice beginning
there, escape_args_after_separator(.., 0) emits slice[..=0] — exactly ["--"] — and then runs
the same escape over the tail. That is what the branch built by hand, element for element.

The predicate now appears once. grep -c for it in the file returns 1.

Tests

Existing unit tests already reach all three sites, and they are in a #[cfg(test)] mod tests with
no #[cfg(unix)], so they run on Windows CI too:

  • test_escape_task_args_preserves_task_separator_tail — run atask -q -- -- --help. Asserts the
    -q before the separator (the task-args loop) and both elements after it (the separator
    branch).
  • test_escape_task_args_preserves_naked_task_separator_tail — the form with no run, which enters
    escape_args_after_separator directly.
  • ..._splits_attached_short_option_values_before_task, ..._preserves_equals_attached_option_values
    and ..._keeps_hyphen_prefixed_attached_value_bound_to_option exercise the task-args loop.

One test is added, for the rule itself. The --is-not-a-flag exception was carried by all three
copies and asserted by none; now that it has one home, it is pinned there — including that a lone
- survives, which is what a task reading stdin depends on.

No e2e: nothing observable changes.

Verification

cargo fmt --all is clean. Nothing here has been compiled or run locally — cargo check does
not work on this machine (libz-ng-sys wants cmake, which is not installed), so for a refactor
this is thinner than I would like and CI is the first thing to type-check it. What stands in for it
is the existing coverage above, and the equivalence argument for the delegating branch, which is a
reading of args[separator_idx] == "--" rather than a run.

Summary by CodeRabbit

  • Bug Fixes

    • Improved command-line argument handling to correctly preserve standalone hyphens.
    • Ensured task flags and separator arguments are consistently escaped and restored.
  • Tests

    • Added coverage for plain arguments, flag escaping, unescaping, and standalone hyphen behavior.

@coderabbitai

coderabbitai Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 019fabac-aea7-428c-b1fb-44930c2564f0

📥 Commits

Reviewing files that changed from the base of the PR and between 50f9b4b and c6411ed.

📒 Files selected for processing (1)
  • src/cli/mod.rs

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.


📝 Walkthrough

Walkthrough

The CLI now uses a shared helper to escape task-side flags. Lone hyphens and ordinary arguments remain unchanged. Separator-tail handling uses the shared path. Unit tests cover escaping and unescaping behavior.

Changes

Task flag escaping

Layer / File(s) Summary
Shared escaping and validation
src/cli/mod.rs
The CLI centralizes task-side flag escaping, preserves lone hyphens, applies escaping after separators, and adds unit tests for escaping and unescaping.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to c6411

This change centralizes an existing CLI argument-escaping rule without intended behavior changes; no actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: jdx, risu729

Poem

A rabbit guards the task flag gate,
Lone hyphens pass without a wait.
Flags gain a safe escape to wear,
Tests check each path with careful care.
Hop, hop—the CLI is clear!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: centralizing the task-flag escape rule in one helper.
Docstring Coverage ✅ Passed Docstring check was indeterminate for this PR — some files could not be analyzed in time. Not blocking.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Aug 23, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

The PR centralizes task-side flag escaping without changing its intended behavior.

  • Extracts the repeated single-argument escaping predicate into escape_flag_arg.
  • Reuses escape_args_after_separator when processing a task separator and its tail.
  • Adds focused coverage for ordinary arguments, task flags, round-trip unescaping, and the standalone-hyphen exception.

Confidence Score: 5/5

The PR appears safe to merge.

No blocking failure remains.

Important Files Changed

Filename Overview
src/cli/mod.rs Consolidates three equivalent task-argument escaping paths into one helper while preserving separator handling and adding direct unit coverage.

Reviews (2): Last reviewed commit: "refactor(cli): keep one copy of the task..." | Re-trigger Greptile

@JamBalaya56562
JamBalaya56562 force-pushed the refactor/one-copy-of-the-task-flag-escape branch from c6411ed to 107b3bf Compare August 23, 2026 16:05
@JamBalaya56562
JamBalaya56562 marked this pull request as ready for review August 23, 2026 22:49
@jdx
jdx merged commit e14c046 into jdx:main Aug 23, 2026
29 checks passed
@JamBalaya56562
JamBalaya56562 deleted the refactor/one-copy-of-the-task-flag-escape branch August 23, 2026 23:41
jdx pushed a commit that referenced this pull request Aug 24, 2026
### 🚀 Features

- **(bootstrap)** add package plugin uninstall support by @jdx in
[#12332](#12332)

### 🐛 Bug Fixes

- **(brew-cask)** recognize Homebrew-owned casks by @donbeave in
[#12346](#12346)
- **(cli)** report a cd target it cannot enter instead of panicking by
@JamBalaya56562 in [#12314](#12314)
- **(config)** skip disabled idiomatic tracked configs by @xqm32 in
[#12194](#12194)
- **(config)** keep comments when saving from mise edit by @Marukome0743
in [#12319](#12319)
- **(config)** strip a byte-order mark before reading a version file by
@JamBalaya56562 in [#12325](#12325)
- **(doctor)** report installs that provide no executables by
@Marukome0743 in [#12321](#12321)
- **(env)** say why an age SSH identity could not be used by
@Marukome0743 in [#12339](#12339)
- **(env)** name the key and file behind an unexpanded $VAR by
@Marukome0743 in [#12316](#12316)
- **(go)** don't hand go install a GOROOT from another Go by
@Marukome0743 in [#12342](#12342)
- **(task)** treat a SIGINT-killed child as an interruption by
@Marukome0743 in [#12323](#12323)
- **(task)** preserve string type for usage value flags by @jdx in
[#12355](#12355)
- **(tasks)** tell Windows users why a task file was skipped by
@JamBalaya56562 in [#12324](#12324)
- **(tool-stub)** keep env_with_path instead of rebuilding PATH by @tmkx
in [#12322](#12322)
- **(watch)** restore the controlling terminal from a drop guard by
@Marukome0743 in [#12328](#12328)

### 🚜 Refactor

- **(cli)** keep one copy of the task-flag escape rule by
@JamBalaya56562 in [#12343](#12343)

### 📚 Documentation

- **(tasks)** point extensionless pwsh tasks at MISE_TASK_DIR by
@JamBalaya56562 in [#12313](#12313)

### 📦️ Dependency Updates

- update rust crate aube-registry to v2.1.0 by @renovate[bot] in
[#12348](#12348)
- bump aube to 2.1.0 by @jdx in
[#12351](#12351)
- bump usage to 6.2.0 by @jdx in
[#12354](#12354)

### 📦 Registry

- fix usage version test by @jdx in
[#12349](#12349)
- install oc from a channel directory by @Marukome0743 in
[#12326](#12326)

### Chore

- **(ci)** use github runners for fork prs by @jdx in
[#12345](#12345)
- **(ci)** prevent cache warmer starvation by @jdx in
[#12350](#12350)

### New Contributors

- @tmkx made their first contribution in
[#12322](#12322)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants