fix(plugins): strip empty-string args in resolver so no-arg slash commands work#47
Conversation
…mands work
Every plugin command card renders `<subcommand> "$ARGUMENTS"`. When the user
runs a command with no arguments, Claude Code substitutes an empty $ARGUMENTS,
leaving a literal empty positional:
bash resolve-code-oz.sh doctor ""
The engine's 0.21.1 subcommand dispatcher (doctor split into
providers|tools|git|run) treats "" as a subcommand name, matches nothing, and
exits 1 ("unknown subcommand ''"). doctor and init break on every no-arg
invocation; run and resume carry a latent trailing empty. The engine already
does the right thing with zero positionals — the empty string is what breaks
it.
Fix at the layer that introduces the artifact: the resolver drops empty-string
positionals before both the PATH-exec and npx branches. An empty positional is
never meaningful to code-oz, so `doctor ""` becomes `doctor`. Array form
preserves args containing spaces; ${a[@]+"${a[@]}"} keeps the empty-array
expansion safe under `set -u` on bash 3.2 (macOS default). RED-first: two
empty-strip tests (PATH + npx branches) plus a spaces-preservation guard.
Verified on bash 3.2.57: `resolve-code-oz.sh doctor ""` now exits 0; `init ""`
is byte-identical to `init`. 3815 offline tests pass; typecheck clean.
A separate, carefully-scoped engine-level normalization (treat an empty
subcommand token as absent, without a blanket argv filter that would eat empty
flag values) remains a recommended follow-up to harden non-plugin callers.
|
Warning Review limit reached
More reviews will be available in 17 minutes and 45 seconds. Learn how PR review limits work. Your organization has run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates resolve-code-oz.sh to filter out empty-string positional arguments before executing code-oz or invoking npx, preventing errors with empty arguments. Corresponding tests have been added to verify this behavior. Feedback suggests replacing a cryptic bash array expansion with a more readable conditional check to improve maintainability and robustness.
| for arg in "$@"; do | ||
| [ -n "${arg}" ] && filtered_args+=("${arg}") | ||
| done | ||
| set -- ${filtered_args[@]+"${filtered_args[@]}"} |
There was a problem hiding this comment.
The expression ${filtered_args[@]+"${filtered_args[@]}"} is highly cryptic and relies on subtle bash expansion and quoting rules that can be difficult to maintain or easily broken by future changes (e.g., if someone tries to wrap the outer expression in double quotes, which would introduce an empty string argument when the array is empty).
A much more readable, standard, and robust approach that is fully compatible with set -u and older bash versions (like bash 3.2 on macOS) is to check the array length explicitly using ${#filtered_args[@]}.
| set -- ${filtered_args[@]+"${filtered_args[@]}"} | |
| if [ ${#filtered_args[@]} -gt 0 ]; then | |
| set -- "${filtered_args[@]}" | |
| else | |
| set -- | |
| fi |
There was a problem hiding this comment.
Pull request overview
This PR fixes the plugin resolver so Claude Code slash command cards that pass an empty "$ARGUMENTS" no longer forward a literal empty string to the code-oz engine.
Changes:
- Adds empty-string argument filtering in
resolve-code-oz.shbefore PATH and npx resolution. - Adds regression tests covering PATH execution, npx fallback, and preservation of arguments containing spaces.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
plugins/code-oz/scripts/resolve-code-oz.sh |
Filters empty positional arguments before invoking the engine. |
tests/plugins/bootstrap-resolver.test.ts |
Adds resolver tests for empty argument stripping and space-containing argument preservation. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Why
Found by dogfooding the freshly-published plugin:
/code-oz-doctor(and/code-oz-init) fail on every no-arg invocation.Each command card renders
<subcommand> "$ARGUMENTS". With no user args, Claude Code substitutes an empty$ARGUMENTS, so the shell line becomes... resolve-code-oz.sh doctor "". The engine's 0.21.1 subcommand dispatcher (which splitdoctorintoproviders|tools|git|run) treats""as a subcommand name and exits 1 (unknown subcommand ''). The engine handles zero positionals fine — the empty string is the problem.doctordoctor ""unknown subcommand ''→ exit 1 ✗doctor/initbreak every time;run/resumecarry a latent trailing empty.What
Fix at the layer that introduces the artifact — the resolver strips empty-string positionals before both the PATH-exec and npx branches. An empty positional is never meaningful to code-oz, so
doctor ""becomesdoctor.${a[@]+"${a[@]}"}keeps the empty-array expansion safe underset -uon bash 3.2 (macOS default).Verification
resolve-code-oz.sh doctor ""now exits 0;init ""is byte-identical toinit(its exit 1 is the legit ".code-oz/already exists" guard).Follow-up (not in this PR)
A carefully-scoped engine-level normalization — treat an empty subcommand token as absent, not a blanket argv filter (which would eat legitimate empty flag values like
run --message "") — would harden direct-CLI and other non-plugin callers. Tracked as a recommended follow-up.