fix(environment): improve bash shell detection using shutil.which#1134
fix(environment): improve bash shell detection using shutil.which#1134yumesha wants to merge 2 commits intoMoonshotAI:mainfrom
Conversation
Use shutil.which() as the primary method for detecting bash location, falling back to hardcoded paths only when necessary. This enables proper shell detection on systems where bash is not located at standard paths like /bin/bash. Also updates tests to normalize bash paths for cross-platform compatibility.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 97c00b97c1
ℹ️ 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".
| bash_in_path = shutil.which("bash") | ||
| if bash_in_path: | ||
| shell_name = "bash" | ||
| shell_path = KaosPath(bash_in_path) |
There was a problem hiding this comment.
Normalize which() result to an absolute shell path
shutil.which("bash") may return a relative path when PATH contains relative entries (for example, bin/bash), and this value is stored directly as Environment.shell_path. Because the runtime later switches cwd to session.work_dir before executing shell commands, Shell can end up trying to exec a path that no longer resolves, causing Shell tool calls to fail when startup cwd and session cwd differ. Converting bash_in_path to an absolute path before storing it avoids this regression.
Useful? React with 👍 / 👎.
When using kimi -c "prompt" or kimi --command "prompt", the CLI now automatically runs in print mode (non-interactive) similar to python -c or claude -c behavior. This makes the -c flag work as expected for single-shot command execution without requiring --print flag.
| # Track if -c/--command was explicitly used (vs --prompt/-p) | ||
| # to auto-enable print mode for command-style usage | ||
| command_mode = False | ||
| for arg in sys.argv[1:]: | ||
| if arg in ("-c", "--command"): | ||
| command_mode = True | ||
| break | ||
|
|
||
| if prompt is not None: | ||
| prompt = prompt.strip() | ||
| if not prompt: | ||
| raise typer.BadParameter("Prompt cannot be empty", param_hint="--prompt") | ||
|
|
||
| ui: UIMode = "shell" | ||
| if print_mode: | ||
| if print_mode or command_mode: | ||
| ui = "print" |
There was a problem hiding this comment.
🔴 command_mode bypasses UI conflict detection, silently overriding --acp/--wire and enabling yolo
When a user runs kimi -c "do something" --acp or kimi -c "do something" --wire, the command_mode flag silently overrides the explicitly requested UI mode and implicitly enables yolo (auto-approve all actions).
Root Cause
The command_mode variable is determined by raw sys.argv scanning at lines 393-397, but it is not included in the conflict_option_sets check at src/kimi_cli/cli/__init__.py:357-382. That check only examines print_mode:
conflict_option_sets = [
{
"--print": print_mode, # only checks print_mode, not command_mode
"--acp": acp_mode,
"--wire": wire_mode,
},
...
]So when the user passes -c with --acp, the conflict check sees --print: False and --acp: True — only one active option — and passes. Then at line 405, command_mode takes priority:
if print_mode or command_mode: # command_mode is True
ui = "print" # overrides --acp / --wire
elif acp_mode:
ui = "acp" # never reachedFurther, at line 497, ui == "print" implicitly enables yolo: yolo=yolo or (ui == "print"), meaning all tool actions are auto-approved without user consent.
Impact: The user's explicitly requested --acp or --wire mode is silently ignored, and dangerous auto-approval is enabled without the user's knowledge.
Prompt for agents
The command_mode flag must be integrated into the existing conflict detection system. In src/kimi_cli/cli/__init__.py, either:
1. Move the command_mode detection (lines 391-397) to BEFORE the conflict_option_sets block (line 357), and include command_mode in the conflict set alongside print_mode. For example, change the first conflict set to:
{"--print": print_mode, "--command": command_mode, "--acp": acp_mode, "--wire": wire_mode}
This ensures that -c/--command conflicts with --acp and --wire are detected and raise a clear error.
2. Alternatively, merge command_mode into print_mode before the conflict check:
if command_mode:
print_mode = True
Then the existing conflict detection handles it naturally.
Was this helpful? React with 👍 or 👎 to provide feedback.
| command_mode = False | ||
| for arg in sys.argv[1:]: | ||
| if arg in ("-c", "--command"): | ||
| command_mode = True | ||
| break |
There was a problem hiding this comment.
🟡 Raw sys.argv scan for -c can match option values, causing false command_mode activation
The sys.argv scan at lines 394-397 iterates over all argv entries without understanding option-value pairing, so -c appearing as a value to another option (e.g., kimi --session -c) is incorrectly detected as command_mode = True.
Detailed Explanation
The code scans sys.argv[1:] linearly:
for arg in sys.argv[1:]:
if arg in ("-c", "--command"):
command_mode = True
breakConsider kimi --session -c -p "hello". Typer parses --session as expecting a string value, so it consumes -c as the session ID (i.e., session_id = "-c"). The prompt parameter gets its value from -p "hello". However, the raw sys.argv scan finds -c at index 2 and sets command_mode = True, causing ui = "print" and implicitly enabling yolo at src/kimi_cli/cli/__init__.py:497. The user intended shell mode with session "-c" but instead gets print mode with auto-approval.
Impact: Unexpected UI mode and implicit yolo activation when -c happens to appear as a value to another option.
Was this helpful? React with 👍 or 👎 to provide feedback.
Related Issue
N/A - This is a compatibility fix for systems with non-standard bash locations.
Description
This PR improves bash shell detection by using
shutil.which()as the primary method, falling back to hardcoded paths only when necessary.Changes:
src/kimi_cli/utils/environment.py
shutil.which("bash")as primary detection method/bin/bash,/usr/bin/bash,/usr/local/bin/bashhardcoded paths only whenwhichfailstests/conftest.py
shutil.which("bash")for shell pathtests/core/test_default_agent.py
_normalize_bash_path()helper to normalize bash paths in tool descriptionstests/tools/test_tool_descriptions.py
tests/utils/test_pyinstaller_utils.py
tests/utils/test_utils_environment.py
Why:
On some systems or container environments, bash may not be located at
/bin/bash. The previous hardcoded path approach would fail on these systems. Usingshutil.which()allows the shell to be found regardless of its location in$PATH.Checklist
make gen-changelogto update the changelog. (Not needed for this fix)make gen-docsto update the user documentation. (Not needed for this fix)