Skip to content

fix(windows): 隐藏 PTY 黑窗 + 非 mac 剪贴板键位 + 修复终端模板对话框冻结#275

Open
nothingness0db wants to merge 5 commits into
AkaraChen:devfrom
nothingness0db:dev
Open

fix(windows): 隐藏 PTY 黑窗 + 非 mac 剪贴板键位 + 修复终端模板对话框冻结#275
nothingness0db wants to merge 5 commits into
AkaraChen:devfrom
nothingness0db:dev

Conversation

@nothingness0db
Copy link
Copy Markdown

@nothingness0db nothingness0db commented May 19, 2026

嗨,三个 Windows 相关的体验修复打包成一个 PR,按 commit 分开了,可以单独 review。

总览

Commit 类型 影响范围
72abdd3 fix(windows) 启动 git/gh/sh/PowerShell 等子进程时不再一闪 cmd 黑窗
fe5f240 feat(terminal) 非 mac 平台终端支持 Ctrl+C 复制 / Ctrl+V 粘贴,不破坏 ^C SIGINT 透传
c15d12f fix(settings) 创建终端模板后 UI 卡死的 bug;全局模板那一行的删除按钮的复制粘贴 bug

1. 72abdd3 — 隐藏 Windows 子进程黑窗

Windows 上 std::process::Command::new 默认不带 CREATE_NO_WINDOW,每次 spawn 一个无界面的子进程都会闪一下 console 窗。git diff/log/commit history、gh 调用、PowerShell 启动脚本执行……这些点都会闪。

做法:新增 crates/infra/src/no_window.rs,里头一个 silent_command(program) 工厂,Windows 上设 CREATE_NO_WINDOW (0x08000000),非 Windows 上等价于 Command::new。然后把 infra/git.rsinfra/config.rshandler/topbar.rshandler/updater.rs 里所有 Command::new(...) 替换过去(运行时调用 + 单元测试 helper 一起,大概 40+ 处)。

零行为变化,只是 Windows 上不闪了。

2. fe5f240 — 非 mac 平台的剪贴板键位

Mac 用户惯性是 Cmd+C/V,xterm.js 默认就这套。Windows/Linux 用户期望 Ctrl+C 复制、Ctrl+V 粘贴,但 Ctrl+C 又是 SIGINT,必须区分场景。

新增两个 action:

  • copy-selection-or-interrupt:有选区时复制并吞掉事件;无选区时 return true 让 xterm 把 ^C 原样透传给 PTY(保留 SIGINT 行为)
  • paste-clipboard:从剪贴板读文本,writeToPty

只在 Mac 之外平台触发(isMacPlatform() 判断)。keybindings.test.ts 有覆盖 Win32 命中、MacIntel 不命中两种情况。

一个小提醒:粘贴目前是原始写入 PTY,没用 bracketed paste(`\x1b[200~ ... \x1b[201~`)包裹,多行粘贴到启用 bracketed paste 的 zsh/bash 4.4+ 里会逐行执行。这次先不做,因为还要判断 xterm 当前的 `bracketedPasteMode` 状态再决定是否包裹(不然遇到 cat / Windows cmd.exe 等不支持 bracketed paste 的程序,会收到字面 `\x1b[200~`)。后续单独跟。

3. c15d12f — 终端模板对话框冻结

主问题:创建一个全局终端模板(设置→终端模板→添加模板)后,整个页面点不动了,只能刷新。原因是:

```tsx
{draft ? (
<TerminalTemplateDraftDialog draft={draft} isOpen={isOpen} ... />
) : null}
```

保存时 `handleSave` → `closeDialog` → `setDraft(null)`,这一帧 Dialog 直接从 DOM 卸载。但 Chakra v3 的 `Dialog` 打开时给 `` 加了 `overflow: hidden` 和 pointer-events 锁,依赖 `onOpenChange(false)` 触发的退出动画来清理。直接 unmount 让清理 effect 没机会跑 → body 还锁着 + backdrop 残留 → 整页冻结。

修法:拆 `draft` 和 `isOpen` 两个 state。`draft` 永远非 null(默认空 draft,打开时 reset),`isOpen` 独立控制开/关。`closeDialog` 只翻 `isOpen=false`,Dialog 留在 DOM 里走 Chakra 的正常退出动画 → body 锁自然释放。`ProjectTemplatesEditor`(项目级模板编辑)同样写法、同样问题、一起改了。

顺手修的另一个 bug:全局模板列表行内的删除按钮,`onClick` 绑的是 `openEditDialog(template.id)`,复制粘贴 bug —— 点删除其实打开编辑框。改成调一个新的 `removeTemplate(id)`,直接 mutate 删除。`ProjectTemplatesEditor` 那边行内删除写法是对的(直接 filter onChange),没动。


没做的:Windows PowerShell 补全(PSReadLine 注入)

借这个 PR 顺便说明一下我之前折腾了但放弃的方向,免得你想问。

Windows 自带 PowerShell 5.1 的 PSReadLine 是 2.0.0,没有 prediction / ghost text。我之前尝试做一套和 macOS `ZDOTDIR`/`.zshenv` 等价的机制:

  • 在 Tauri `resources/` 里捆绑 PSReadLine 2.4.5(~570KB,含 .dll / .psd1 / .psm1 / .ps1xml)
  • 新增 `crates/infra/scripts/default_init.ps1`,里头 `Import-Module -Force` 加载捆绑的 PSReadLine,设置 `PredictionSource HistoryAndPlugin` / `PredictionViewStyle InlineView`,再把 `~/.2code/bin` 写入 PATH、生成 claude wrapper 等
  • 把 `prepare_init_dir` 改成返回 `InitDirResult { dir, profile_path }`,Unix 写 `.zshenv`,Windows + PowerShell 写 `profile.ps1`,Windows + cmd 退化为 noop
  • PTY 启动时通过 stdin 注入 `. 'profile.ps1'` dot-source 它;前面加 `\x1b[1;1R` 假冒一个 cursor-position 应答,绕过 PowerShell 启动时发 `\x1b[6n` 等响应的死锁
  • `bridge.rs` 里用 Tauri path resolver 找 `.psd1`,并 strip 掉 `?\` extended-length 前缀(不然 PowerShell 把它当 UNC 路径,触发签名信任弹窗)
  • 同时把前端默认 shell 从 `powershell.exe -NoLogo -NoProfile` 改成 `powershell.exe -NoLogo`,因为 `-NoProfile` 会跳过用户 `$PROFILE`,而很多人 PSReadLine 个性化都写在那
  • 顺手加了 `pwsh.exe -NoLogo` 选项给装了 PS 7 的人
  • `ProjectConfig` 拆 `init_script` / `init_script_unix` / `init_script_windows`,对应 UI 加两个 textarea

结果:跑起来出问题。三个 tab 并存时 PTY 会陷入循环喷 prompt,日志里能看到三个 session_id 各自高频 `read: bytes from PTY n=1 / n=36 / n=39`,对应屏幕上是 `PS E:\workspace\open-source\2code>` 一行接一行无限刷。怀疑是某个东西(PSReadLine 注入 / DSR 应答 / xterm 自动回复某个序列)形成了反馈环,但没排查彻底。

考虑到:

  1. 这一摊改动量大(3500+ 行,含资源文件),涉及面广(shell 启动路径 / 资源捆绑 / Tauri 配置 / 前端默认值 / config schema 全动了)
  2. 真要做也得先把循环喷 prompt 那个 root cause 查清楚,不能带着 bug 上
  3. `-NoProfile` 移除会拖慢启动(用户 `$PROFILE` 加载 OhMyPosh / Terminal-Icons / posh-git 普遍 200ms~1s)
  4. ZDOTDIR 风格的初始化对 MSYS2/Cygwin 的 zsh/bash 也会被影响(虽然不致命,但是脏的)

所以这条线整体回退了。如果你对 Windows 补全有想法可以聊聊,我那份代码还能从 git reflog 翻出来当起点。


测试

CI 等价命令本地都跑过:

  • `bun run test` → 53 files / 429 tests pass
  • `cargo test --manifest-path src-tauri/Cargo.toml` → 36 + 26 + 4 + 0 doctests pass

UI 验证:

  • 设置 → 终端模板 → 添加模板 → 保存:页面不再冻结 ✓
  • 全局模板列表行内点删除:直接删除,不再开编辑框 ✓
  • Windows 上启动 dev / 用 git 操作:不闪黑窗 ✓
  • Windows 终端 Ctrl+C 无选区:^C 正常透传杀进程 ✓
  • Windows 终端 Ctrl+C 有选区:复制 ✓
  • Windows 终端 Ctrl+V:粘贴(单行 OK,多行的 bracketed paste 问题见上面 commit 2 的备注)

Summary by CodeRabbit

  • New Features

    • Terminal: use clipboard shortcuts — copy selection with Ctrl+C and paste with Ctrl+V on Windows/Linux.
  • Improvements

    • Background processes now run without opening extra console windows.
    • Added explicit clipboard support and permissions for the app.
    • Shell integration expanded and hardened for Bash, Zsh, Fish, and PowerShell.
    • Project template editor dialogs updated for more reliable open/edit/close behavior.

Review Change Stack

Eira Hazel added 3 commits May 19, 2026 04:40
Wrap all std::process::Command spawns in a silent_command helper that
sets CREATE_NO_WINDOW on Windows, eliminating the brief black console
window flash when invoking git, gh, sh, powershell, and open from the
git, config, topbar, and updater modules.
Add copy-selection-or-interrupt and paste-clipboard keybindings for
Windows and Linux. Ctrl+C copies the current selection or, when no
selection exists, passes through to xterm so SIGINT (^C) still works.
Ctrl+V reads the clipboard and writes it to the PTY. Mac is unchanged
since Cmd+C/V is handled by the system.
Splitting `draft` and `isOpen` keeps the Dialog mounted while it closes so
Chakra's exit animation can release the pointer-events lock — previously
the page froze after creating a template. Also wires the global templates
trash button to a remove handler instead of opening the edit dialog.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 19, 2026

📝 Walkthrough

Walkthrough

This PR adds a cross-platform silent_command helper and replaces direct Command::new subprocess construction across infra and handlers; implements multi-shell detection and injection for PTY sessions and adds comprehensive shell-integration scripts; adds terminal clipboard copy/paste actions and refactors dialog state; and updates manifests and plugin registration for clipboard support.

Changes

Backend: Silent Command Infrastructure

Layer / File(s) Summary
Silent command helper definition
src-tauri/crates/infra/src/no_window.rs, src-tauri/crates/infra/src/lib.rs
Adds pub fn silent_command(program: &str) that wraps Command::new with Windows CREATE_NO_WINDOW via CommandExt and exports no_window module.
Apply silent_command to config, topbar, and updater handlers
src-tauri/crates/infra/src/config.rs, src-tauri/src/handler/topbar.rs, src-tauri/src/handler/updater.rs
Replaces Command::new with silent_command for script execution, app opening, and gh token retrieval while preserving arguments and behavior.
Apply silent_command throughout git module
src-tauri/crates/infra/src/git.rs
Refactors git/gh invocations (status, diff, commit, branch/worktree ops, blob reads) and test helpers to use silent_command, preserving parsing and error handling.

Shell injection and PTY wiring

Layer / File(s) Summary
Shell detection and injection preparation
src-tauri/crates/infra/src/shell_init.rs
Adds ShellType, ShellInjection, detect_shell_type, and prepare_shell_injection to produce per-shell temp artifacts and init scripts (zsh, bash, fish, pwsh).
PTY command builder and injection application
src-tauri/crates/infra/src/pty.rs
Change create_session to accept ShellInjection, parse shell strings, build injected CommandBuilders, apply injection-specific envs, and add parse_shell_command tests.
Service/session wiring and Windows shell discovery
src-tauri/crates/service/src/pty.rs, src-tauri/src/handler/shell.rs
Prepare ShellInjection in service create_session and pass into session creation; add find_pwsh_path() and expand Windows shell enumeration to prefer pwsh and detect Git Bash/WSL.

Shell integration scripts

Layer / File(s) Summary
Bash integration script
src-tauri/crates/infra/scripts/shellIntegration-bash.sh
Adds a Bash integration script implementing prompt/command lifecycle reporting, env diffing, prompt wrapping, and preexec/precmd integration.
Zsh integration and helpers
src-tauri/crates/infra/scripts/shellIntegration-rc.zsh, ...-env.zsh, ...-login.zsh, ...-profile.zsh, src-tauri/crates/infra/scripts/default_init_zsh.sh
Adds Zsh rc/env/login/profile helpers, injection guard logic, env reporting, preexec/precmd hooks, and a zsh-specific default init snippet.
Fish and PowerShell integration
src-tauri/crates/infra/scripts/shellIntegration.fish, src-tauri/crates/infra/scripts/shellIntegration.ps1
Add Fish and PowerShell integration scripts with prompt wrapping, event hooks, env reporting, nonce handling, and PSReadLine/key mapping support.
Default init common change
src-tauri/crates/infra/scripts/default_init_common.sh
Remove zsh-specific prompt/newline widget from the common init (moved to zsh-specific init).

Frontend: Terminal Clipboard and Dialog State Management

Layer / File(s) Summary
Terminal clipboard keybindings definition and detection
src/features/terminal/keybindings.ts, src/features/terminal/keybindings.test.ts
Extend TerminalShortcutAction and add platform-conditional detection for Ctrl+C/Ctrl+V on non-macOS; add tests covering Windows and macOS behavior.
Terminal clipboard implementation in xterm handler
src/features/terminal/Terminal.tsx
Import clipboard plugin APIs and implement copy-selection-or-interrupt (copy when selection, else allow SIGINT) and paste-clipboard (read clipboard and write to PTY).
Dialog state refactor in template components
src/features/projects/components/ProjectTemplatesEditor.tsx, src/features/settings/GlobalTerminalTemplatesSettings.tsx
Refactor dialog state from nullable draft to always-mounted draft with explicit isOpen control; add removeTemplate helper and update handlers.

Misc: manifests, plugin, and ignore

Layer / File(s) Summary
Add clipboard plugin and capabilities; register plugin
package.json, src-tauri/Cargo.toml, src-tauri/capabilities/default.json, src-tauri/src/lib.rs
Add @tauri-apps/plugin-clipboard-manager dependency, add tauri-plugin-clipboard-manager crate dependency, include clipboard permissions in capabilities, and register the plugin in Tauri builder.
.gitignore update
src-tauri/.gitignore
Ignore tauri.conf.dev.json in src-tauri .gitignore.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • AkaraChen/2code#142: Changes src-tauri/crates/infra/src/config.rs to alter shell command invocation logic, related to silent_command usage here.

Poem

🐰 A quiet helper in my paw I bring,
No console window — subprocesses sing.
Shells get injected, scripts now align,
Ctrl+C and V make clipboard shine.
Dialogs open clean with isOpen true,
A rabbit's hop — code tidy and new.

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 45.11% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The PR title in Chinese focuses on three Windows/terminal fixes but uses non-descriptive phrasing (隐藏, 键位, 冻结) that obscures the main changes and lacks context for English-speaking reviewers. Consider using English or adding more specific English context (e.g., 'Hide PTY console windows + clipboard shortcuts on non-macOS + fix terminal template dialog freezing').
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 and usage tips.

Eira Hazel added 2 commits May 19, 2026 21:00
Replace navigator.clipboard with tauri-plugin-clipboard-manager so
Ctrl+C/V hit the native OS clipboard API instead of WebView2's
JS shim, which is unreliable on Windows.
Extend shell init injection from zsh-only to bash, fish, and PowerShell
by vendoring VS Code's MIT-licensed shell integration scripts.

Shell init (shell_init.rs):
- Split monolithic default_init.sh into common (POSIX-sh) and zsh-only parts
- New ShellType enum with detect_shell_type() for automatic shell detection
- New ShellInjection enum describing per-shell injection strategy:
  - Bash: --init-file flag
  - Zsh: ZDOTDIR redirection (existing approach, now with .zshrc/.zprofile/.zlogin)
  - Fish: --init-command source
  - Pwsh: -noexit -command dot-source
  - Unknown: no injection (graceful fallback)
- prepare_shell_injection() dispatches to per-shell prepare functions
- All shells (including fish/pwsh) now receive project init scripts from 2code.json
- Delete legacy prepare_init_dir() (no remaining callers)

PTY layer (pty.rs):
- build_injected_command() replaces old command_builder(), constructs
  shell-specific args (e.g. --init-file for bash, -i for zsh)
- parse_shell_command() handles Windows paths with spaces in shell commands
  (e.g. "C:\Program Files\...\pwsh.exe -NoLogo -NoProfile")
- Set TERM_PROGRAM=vscode and VSCODE_INJECTION=1 env vars so vendored
  VS Code scripts activate correctly
- New tests for parse_shell_command()

Windows shell detection (handler/shell.rs):
- find_pwsh_path() probes standard install locations + PATH fallback
- Default shell prefers pwsh 7 over powershell 5.1
- load_windows_shells() now lists: pwsh, powershell, cmd, Git Bash, WSL

Vendor scripts (scripts/):
- shellIntegration-bash.sh, shellIntegration-rc.zsh, shellIntegration-env.zsh,
  shellIntegration-profile.zsh, shellIntegration-login.zsh (zsh),
  shellIntegration.fish, shellIntegration.ps1
- default_init_common.sh: POSIX-sh notify hook, claude wrapper, PATH setup
- default_init_zsh.sh: zle keybindings, unsetopt PROMPT_SP

Other:
- .gitignore: add tauri.conf.dev.json for local dev config override
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src-tauri/crates/infra/scripts/shellIntegration-bash.sh`:
- Around line 104-108: The activation failure reporting currently records the
negated return code because the `! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"`
inverts `$?`; change the logic so you capture the actual exit code immediately
after `builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"` (e.g. into
`__vsc_activation_status`) and then test/negate that value for the `if`
condition; update the `printf` to use the captured `__vsc_activation_status` so
the real Python activation exit code (from `VSCODE_PYTHON_BASH_ACTIVATE`) is
logged instead of 0.

In `@src-tauri/crates/infra/scripts/shellIntegration-rc.zsh`:
- Around line 21-33: After the injected zsh startup block finishes (the branch
that sets HISTFILE, VSCODE_ZDOTDIR, ZDOTDIR and sources . $USER_ZDOTDIR/.zshrc),
unset the VSCODE_INJECTION environment variable so child/nested zsh sessions do
not re-enter the injection path; specifically, add an unset or export -n for
VSCODE_INJECTION immediately after the sourcing step in the branch that checks
[[ "$VSCODE_INJECTION" == "1" ]] to ensure VSCODE_INJECTION is cleared for
subsequent shells while leaving the rest of the HISTFILE/ZDOTDIR/VSCODE_ZDOTDIR
logic intact.

In `@src-tauri/crates/infra/scripts/shellIntegration.fish`:
- Around line 52-83: The guard variable __vsc_applied_env_vars is declared
global at top but set locally inside function __vsc_apply_env_vars (using set
-l), which shadows the global and leaves the global flag at 0 so the guard never
blocks repeated execution; change the assignment in __vsc_apply_env_vars to set
the global flag instead (use set -g __vsc_applied_env_vars 1) so the initial
check (if test $__vsc_applied_env_vars -eq 1) correctly prevents re-applying
VSCODE_ENV_REPLACE/PREPEND/APPEND on subsequent prompts.

In `@src-tauri/crates/infra/src/pty.rs`:
- Around line 183-194: The Bash and Zsh branches (ShellInjection::Bash and
ShellInjection::Zsh) drop the caller's existing_args when constructing the
CommandBuilder, so preserve and re-add existing_args (from
parse_shell_command()) to the CommandBuilder created by
CommandBuilder::new(program) before adding the injection flags; for Bash, add
existing_args then append "--init-file" and init_file.to_string_lossy(), and for
Zsh add existing_args then append "-i" (or other injection behavior), ensuring
the original flags such as "-l" or "-f" are retained.

In `@src-tauri/crates/infra/src/shell_init.rs`:
- Around line 30-40: The current detect_shell_type splits on whitespace which
misparses unquoted Windows paths like "C:\Program Files\...\pwsh.exe -NoLogo";
update detect_shell_type to first extract the executable token robustly: if
shell_cmd starts with a quote, take the content up to the closing quote;
otherwise search for the earliest occurrence of an executable filename by
looking for a path separator and a known executable extension (e.g.,
case-insensitive ".exe", ".com", ".bat", ".cmd", ".pwsh", ".sh") and take the
substring up to that extension, falling back to the original split_whitespace()
behavior only if no match is found; then continue using
Path::new(...).file_stem() on that extracted exe to compute basename and
classification.
- Around line 42-45: The match currently maps "sh" to ShellType::Bash which
causes Bash-specific init flags to be used for /bin/sh; change the match in
shell_init.rs so that "bash" alone maps to ShellType::Bash and remove "sh" from
that arm, and add an explicit "sh" mapping (e.g., ShellType::Sh or
ShellType::Posix) or map it to the generic/unknown variant used by your code;
then update downstream code that handles ShellType (the code that constructs the
invocation using ShellType) to treat ShellType::Sh/Posix as a plain POSIX sh and
avoid Bash-only flags/--init-file usage so that dash/BusyBox sh do not receive
incompatible arguments.

In `@src-tauri/src/handler/shell.rs`:
- Around line 68-72: The Windows-only "where pwsh" probe currently uses
std::process::Command::new("where").arg("pwsh").output(), which can cause a
console flash; replace that call with the project's no-window helper (e.g., use
no_window_command("where").arg("pwsh").output() or the actual helper name
provided in the repo) so the probe is spawned without a visible window; keep the
same .arg("pwsh") and .output().ok()? handling and ensure the helper returns a
Command or equivalent so the rest of the code is unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0df1ffa5-5e14-4623-8255-a7107f2df939

📥 Commits

Reviewing files that changed from the base of the PR and between c15d12f and f81b776.

⛔ Files ignored due to path filters (2)
  • bun.lock is excluded by !**/*.lock
  • src-tauri/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (19)
  • package.json
  • src-tauri/.gitignore
  • src-tauri/Cargo.toml
  • src-tauri/capabilities/default.json
  • src-tauri/crates/infra/scripts/default_init_common.sh
  • src-tauri/crates/infra/scripts/default_init_zsh.sh
  • src-tauri/crates/infra/scripts/shellIntegration-bash.sh
  • src-tauri/crates/infra/scripts/shellIntegration-env.zsh
  • src-tauri/crates/infra/scripts/shellIntegration-login.zsh
  • src-tauri/crates/infra/scripts/shellIntegration-profile.zsh
  • src-tauri/crates/infra/scripts/shellIntegration-rc.zsh
  • src-tauri/crates/infra/scripts/shellIntegration.fish
  • src-tauri/crates/infra/scripts/shellIntegration.ps1
  • src-tauri/crates/infra/src/pty.rs
  • src-tauri/crates/infra/src/shell_init.rs
  • src-tauri/crates/service/src/pty.rs
  • src-tauri/src/handler/shell.rs
  • src-tauri/src/lib.rs
  • src/features/terminal/Terminal.tsx
💤 Files with no reviewable changes (1)
  • src-tauri/crates/infra/scripts/default_init_common.sh
✅ Files skipped from review due to trivial changes (4)
  • src-tauri/.gitignore
  • package.json
  • src-tauri/capabilities/default.json
  • src-tauri/Cargo.toml

Comment on lines +104 to +108
if [ -n "${VSCODE_PYTHON_BASH_ACTIVATE:-}" ] && [ "$TERM_PROGRAM" = "vscode" ]; then
# Prevent crashing by negating exit code
if ! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"; then
__vsc_activation_status=$?
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

fd -t f "shellIntegration-bash.sh" -x cat -n {} | head -120

Repository: AkaraChen/2code

Length of output: 4547


🏁 Script executed:

# Verify bash semantics: does $? get the negated status or original?
cat > /tmp/test_bash_semantics.sh << 'EOF'
#!/bin/bash

echo "=== Test 1: Capturing $? after negated failing command ==="
if ! (exit 5); then
  result=$?
  echo "Inside 'then': \$? = $result (should be 0 from negation)"
fi

echo ""
echo "=== Test 2: Capturing exit code before negation ==="
(exit 5)
exit_code=$?
if [ "$exit_code" -ne 0 ]; then
  echo "Captured before negation: exit_code = $exit_code (should be 5)"
fi

echo ""
echo "=== Test 3: Using the suggested pattern ==="
(exit 7)
capture=$?
if [ "$capture" -ne 0 ]; then
  echo "Captured status: $capture (should be 7)"
fi
EOF
bash /tmp/test_bash_semantics.sh

Repository: AkaraChen/2code

Length of output: 352


Capture the Python activation exit code before negating it.

Currently, $? in the then branch reflects the negated status from ! builtin eval ..., so activation failures are reported as exit code 0 instead of the actual error code. Capture the exit code before the negation to preserve diagnostic information.

Suggested fix
-		if ! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"; then
-			__vsc_activation_status=$?
+		builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"
+		__vsc_activation_status=$?
+		if [ "$__vsc_activation_status" -ne 0 ]; then
 			builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
 		fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [ -n "${VSCODE_PYTHON_BASH_ACTIVATE:-}" ] && [ "$TERM_PROGRAM" = "vscode" ]; then
# Prevent crashing by negating exit code
if ! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"; then
__vsc_activation_status=$?
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
if [ -n "${VSCODE_PYTHON_BASH_ACTIVATE:-}" ] && [ "$TERM_PROGRAM" = "vscode" ]; then
# Prevent crashing by negating exit code
builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"
__vsc_activation_status=$?
if [ "$__vsc_activation_status" -ne 0 ]; then
builtin printf '\x1b[0m\x1b[7m * \x1b[0;103m VS Code Python bash activation failed with exit code %d \x1b[0m' "$__vsc_activation_status"
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/scripts/shellIntegration-bash.sh` around lines 104 -
108, The activation failure reporting currently records the negated return code
because the `! builtin eval "$VSCODE_PYTHON_BASH_ACTIVATE"` inverts `$?`; change
the logic so you capture the actual exit code immediately after `builtin eval
"$VSCODE_PYTHON_BASH_ACTIVATE"` (e.g. into `__vsc_activation_status`) and then
test/negate that value for the `if` condition; update the `printf` to use the
captured `__vsc_activation_status` so the real Python activation exit code (from
`VSCODE_PYTHON_BASH_ACTIVATE`) is logged instead of 0.

Comment on lines +21 to +33
if [[ "$VSCODE_INJECTION" == "1" ]]; then
HISTFILE=$USER_ZDOTDIR/.zsh_history
fi

# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
if [[ "$VSCODE_INJECTION" == "1" ]]; then
if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
VSCODE_ZDOTDIR=$ZDOTDIR
ZDOTDIR=$USER_ZDOTDIR
# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
. $USER_ZDOTDIR/.zshrc
fi
fi
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

find src-tauri/crates/infra/scripts -name "shellIntegration*" -type f

Repository: AkaraChen/2code

Length of output: 449


🏁 Script executed:

# Read the zsh script to confirm current behavior
cat -n src-tauri/crates/infra/scripts/shellIntegration-rc.zsh

Repository: AkaraChen/2code

Length of output: 12849


🏁 Script executed:

# Read the bash script to see if it clears VSCODE_INJECTION
cat -n src-tauri/crates/infra/scripts/shellIntegration-bash.sh

Repository: AkaraChen/2code

Length of output: 18098


Unset VSCODE_INJECTION once the injected startup path is done.

The bash variant clears this flag at line 58, but the zsh path leaves it exported for child shells. Nested zsh sessions will detect it as "1" and re-enter the special startup block, unexpectedly re-sourcing ~/.zshrc and re-applying history/dotdir configuration.

Suggested fix
 if [[ "$VSCODE_INJECTION" == "1" ]]; then
 	if [[ $options[norcs] = off  && -f $USER_ZDOTDIR/.zshrc ]]; then
 		VSCODE_ZDOTDIR=$ZDOTDIR
 		ZDOTDIR=$USER_ZDOTDIR
 		# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
 		. $USER_ZDOTDIR/.zshrc
 	fi
+	builtin unset VSCODE_INJECTION
 fi
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if [[ "$VSCODE_INJECTION" == "1" ]]; then
HISTFILE=$USER_ZDOTDIR/.zsh_history
fi
# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
if [[ "$VSCODE_INJECTION" == "1" ]]; then
if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
VSCODE_ZDOTDIR=$ZDOTDIR
ZDOTDIR=$USER_ZDOTDIR
# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
. $USER_ZDOTDIR/.zshrc
fi
fi
if [[ "$VSCODE_INJECTION" == "1" ]]; then
HISTFILE=$USER_ZDOTDIR/.zsh_history
fi
# Only fix up ZDOTDIR if shell integration was injected (not manually installed) and has not been called yet
if [[ "$VSCODE_INJECTION" == "1" ]]; then
if [[ $options[norcs] = off && -f $USER_ZDOTDIR/.zshrc ]]; then
VSCODE_ZDOTDIR=$ZDOTDIR
ZDOTDIR=$USER_ZDOTDIR
# A user's custom HISTFILE location might be set when their .zshrc file is sourced below
. $USER_ZDOTDIR/.zshrc
fi
builtin unset VSCODE_INJECTION
fi
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/scripts/shellIntegration-rc.zsh` around lines 21 - 33,
After the injected zsh startup block finishes (the branch that sets HISTFILE,
VSCODE_ZDOTDIR, ZDOTDIR and sources . $USER_ZDOTDIR/.zshrc), unset the
VSCODE_INJECTION environment variable so child/nested zsh sessions do not
re-enter the injection path; specifically, add an unset or export -n for
VSCODE_INJECTION immediately after the sourcing step in the branch that checks
[[ "$VSCODE_INJECTION" == "1" ]] to ensure VSCODE_INJECTION is cleared for
subsequent shells while leaving the rest of the HISTFILE/ZDOTDIR/VSCODE_ZDOTDIR
logic intact.

Comment on lines +52 to +83
set -g __vsc_applied_env_vars 0
function __vsc_apply_env_vars
if test $__vsc_applied_env_vars -eq 1;
return
end
set -l __vsc_applied_env_vars 1
# Apply EnvironmentVariableCollections if needed
if test -n "$VSCODE_ENV_REPLACE"
set ITEMS (string split : $VSCODE_ENV_REPLACE)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")
end
set -e VSCODE_ENV_REPLACE
end
if test -n "$VSCODE_ENV_PREPEND"
set ITEMS (string split : $VSCODE_ENV_PREPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")"$$split[1]" # avoid -p as it adds a space
end
set -e VSCODE_ENV_PREPEND
end
if test -n "$VSCODE_ENV_APPEND"
set ITEMS (string split : $VSCODE_ENV_APPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" "$$split[1]"(echo -e "$split[2]") # avoid -a as it adds a space
end
set -e VSCODE_ENV_APPEND
end
end
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

# First, let's confirm the file exists and read the relevant section
cat -n src-tauri/crates/infra/scripts/shellIntegration.fish | head -90 | tail -40

Repository: AkaraChen/2code

Length of output: 1607


🏁 Script executed:

# Let's verify fish shell's variable scoping behavior with a simple test
fish <<'EOF'
# Simulate the problematic pattern
set -g test_var 0

function test_func
  if test $test_var -eq 1
    echo "Guard worked: early return"
    return
  end
  set -l test_var 1
  echo "Function executed with local shadow"
end

# First call
echo "First call:"
test_func

# Second call - will it return early?
echo "Second call:"
test_func

# Check global state
echo "Global test_var is: $test_var"
EOF

Repository: AkaraChen/2code

Length of output: 104


🌐 Web query:

fish shell set -l vs set -g variable scoping shadow global

💡 Result:

In fish, -l (local) sets a variable scope that ends when the current block ends; -g (global) sets a variable scope that’s available to all functions running in the same shell. When you reference a variable name, fish uses the smallest (narrowest) scoped variable of that name; so a locally-scoped variable will shadow a global one with the same name within its scope.[1][2] Key behaviors 1) set -l NAME VALUE (or set --local) - “Sets a locally-scoped variable in this block. It is erased when the block ends.” Outside a block, it behaves like function scope.[1] - In the language docs, local variables are “specific to the current block of commands” and “automatically erased when a specific block goes out of scope.” Outside of a block, local is like function scope.[2] 2) set -g NAME VALUE (or set --global) - “Sets a globally-scoped variable. Global variables are available to all functions running in the same shell. They can be modified or erased.”[1] 3) Shadowing / precedence rules - fish can have multiple variables with the same name but different scopes. - When you use a variable, “the smallest scoped variable of that name will be used. If a local variable exists, it will be used instead of the global or universal variable of the same name.”[2] Practical implication for “set -l vs set -g” - If you do set -g foo 1, then later inside a function/block do set -l foo 2, any use of $foo inside that block will resolve to 2 (the local), not the global.[2] - After the block ends, the local foo is erased, so $foo resolves back to the global foo value (assuming no other narrower scope is active).[1][2] Notes about function scope vs local flag - If scope is not explicitly set, fish uses a different default (function-scope) behavior: “If a variable’s scope is not explicitly set… the variable will be local to the currently executing function. Note that this is different from using the -l or --local flag, in which case the variable will be local to the most-inner currently executing block.”[1] Example pattern (block-local shadowing) - Use set -l inside conditionals/loops/inner blocks to prevent temporarily overriding a global outside that block, since locals will shadow globals only within the active block.[1][2] Sources: fish set documentation and fish language variable scope/shadowing rules.[1][2]

Citations:


Fix the env-var application guard by using global scope.

Line 57 uses set -l __vsc_applied_env_vars 1, which creates a local variable that shadows the global flag initialized at line 52. The global remains 0, so the guard check at line 54 always fails. This allows the prepend/append/replace logic to run on every prompt, repeatedly duplicating environment variable values.

Suggested fix
-	set -l __vsc_applied_env_vars 1
+	set -g __vsc_applied_env_vars 1
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
set -g __vsc_applied_env_vars 0
function __vsc_apply_env_vars
if test $__vsc_applied_env_vars -eq 1;
return
end
set -l __vsc_applied_env_vars 1
# Apply EnvironmentVariableCollections if needed
if test -n "$VSCODE_ENV_REPLACE"
set ITEMS (string split : $VSCODE_ENV_REPLACE)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")
end
set -e VSCODE_ENV_REPLACE
end
if test -n "$VSCODE_ENV_PREPEND"
set ITEMS (string split : $VSCODE_ENV_PREPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")"$$split[1]" # avoid -p as it adds a space
end
set -e VSCODE_ENV_PREPEND
end
if test -n "$VSCODE_ENV_APPEND"
set ITEMS (string split : $VSCODE_ENV_APPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" "$$split[1]"(echo -e "$split[2]") # avoid -a as it adds a space
end
set -e VSCODE_ENV_APPEND
end
end
set -g __vsc_applied_env_vars 0
function __vsc_apply_env_vars
if test $__vsc_applied_env_vars -eq 1;
return
end
set -g __vsc_applied_env_vars 1
# Apply EnvironmentVariableCollections if needed
if test -n "$VSCODE_ENV_REPLACE"
set ITEMS (string split : $VSCODE_ENV_REPLACE)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")
end
set -e VSCODE_ENV_REPLACE
end
if test -n "$VSCODE_ENV_PREPEND"
set ITEMS (string split : $VSCODE_ENV_PREPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" (echo -e "$split[2]")"$$split[1]" # avoid -p as it adds a space
end
set -e VSCODE_ENV_PREPEND
end
if test -n "$VSCODE_ENV_APPEND"
set ITEMS (string split : $VSCODE_ENV_APPEND)
for B in $ITEMS
set split (string split -m1 = $B)
set -gx "$split[1]" "$$split[1]"(echo -e "$split[2]") # avoid -a as it adds a space
end
set -e VSCODE_ENV_APPEND
end
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/scripts/shellIntegration.fish` around lines 52 - 83,
The guard variable __vsc_applied_env_vars is declared global at top but set
locally inside function __vsc_apply_env_vars (using set -l), which shadows the
global and leaves the global flag at 0 so the guard never blocks repeated
execution; change the assignment in __vsc_apply_env_vars to set the global flag
instead (use set -g __vsc_applied_env_vars 1) so the initial check (if test
$__vsc_applied_env_vars -eq 1) correctly prevents re-applying
VSCODE_ENV_REPLACE/PREPEND/APPEND on subsequent prompts.

Comment on lines +183 to +194
ShellInjection::Bash { init_file } => {
// bash --init-file /path/to/shellIntegration-bash.sh
let mut cmd = CommandBuilder::new(program);
cmd.arg("--init-file");
cmd.arg(init_file.to_string_lossy().as_ref());
cmd
}
ShellInjection::Zsh { .. } => {
// zsh -i (ZDOTDIR is set via env var, scripts are in the dir)
let mut cmd = CommandBuilder::new(program);
cmd.arg("-i");
cmd
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Preserve the caller's shell flags for Bash/Zsh injection.

parse_shell_command() already keeps existing_args, but these two branches drop them completely. A configured shell like bash -l or zsh -f will silently change behavior as soon as injection is enabled.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/src/pty.rs` around lines 183 - 194, The Bash and Zsh
branches (ShellInjection::Bash and ShellInjection::Zsh) drop the caller's
existing_args when constructing the CommandBuilder, so preserve and re-add
existing_args (from parse_shell_command()) to the CommandBuilder created by
CommandBuilder::new(program) before adding the injection flags; for Bash, add
existing_args then append "--init-file" and init_file.to_string_lossy(), and for
Zsh add existing_args then append "-i" (or other injection behavior), ensuring
the original flags such as "-l" or "-f" are retained.

Comment on lines +30 to +40
pub fn detect_shell_type(shell_cmd: &str) -> ShellType {
// Take the basename of the first token (the executable)
let exe = shell_cmd
.split_whitespace()
.next()
.unwrap_or(shell_cmd);
let basename = Path::new(exe)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or(exe)
.to_lowercase();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Handle executable paths with spaces before classifying the shell.

This logic breaks on commands like C:\Program Files\PowerShell\7\pwsh.exe -NoLogo -NoProfile: the first whitespace token becomes C:\Program, so the basename resolves to program and the function returns Unknown. The service layer then falls back to ShellInjection::None, which disables integration for the preferred Windows pwsh path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/src/shell_init.rs` around lines 30 - 40, The current
detect_shell_type splits on whitespace which misparses unquoted Windows paths
like "C:\Program Files\...\pwsh.exe -NoLogo"; update detect_shell_type to first
extract the executable token robustly: if shell_cmd starts with a quote, take
the content up to the closing quote; otherwise search for the earliest
occurrence of an executable filename by looking for a path separator and a known
executable extension (e.g., case-insensitive ".exe", ".com", ".bat", ".cmd",
".pwsh", ".sh") and take the substring up to that extension, falling back to the
original split_whitespace() behavior only if no match is found; then continue
using Path::new(...).file_stem() on that extracted exe to compute basename and
classification.

Comment on lines +42 to +45
match basename.as_str() {
"zsh" => ShellType::Zsh,
"bash" | "sh" => ShellType::Bash,
"fish" => ShellType::Fish,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Don't route generic sh through the Bash injection path.

/bin/sh is offered in shell discovery, but this branch classifies it as Bash. Downstream that becomes sh --init-file ..., which is Bash-specific and can break startup on systems where sh is dash/BusyBox instead of Bash.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/crates/infra/src/shell_init.rs` around lines 42 - 45, The match
currently maps "sh" to ShellType::Bash which causes Bash-specific init flags to
be used for /bin/sh; change the match in shell_init.rs so that "bash" alone maps
to ShellType::Bash and remove "sh" from that arm, and add an explicit "sh"
mapping (e.g., ShellType::Sh or ShellType::Posix) or map it to the
generic/unknown variant used by your code; then update downstream code that
handles ShellType (the code that constructs the invocation using ShellType) to
treat ShellType::Sh/Posix as a plain POSIX sh and avoid Bash-only
flags/--init-file usage so that dash/BusyBox sh do not receive incompatible
arguments.

Comment on lines +68 to +72
// Fallback: check PATH via `where`
let output = std::process::Command::new("where")
.arg("pwsh")
.output()
.ok()?;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use the no-window helper for the where pwsh probe.

This Windows-only lookup is still spawned with plain Command::new, so listing shells can reintroduce the same console flash the no-window helper was meant to eliminate.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/handler/shell.rs` around lines 68 - 72, The Windows-only "where
pwsh" probe currently uses
std::process::Command::new("where").arg("pwsh").output(), which can cause a
console flash; replace that call with the project's no-window helper (e.g., use
no_window_command("where").arg("pwsh").output() or the actual helper name
provided in the repo) so the probe is spawned without a visible window; keep the
same .arg("pwsh") and .output().ok()? handling and ensure the helper returns a
Command or equivalent so the rest of the code is unchanged.

@nothingness0db
Copy link
Copy Markdown
Author

根据 CodeRabbit 的 review 意见,修复了以下问题:

  • bash: Python 激活退出码在取反前捕获,避免报 0
  • zsh: injection 块结束后 unset VSCODE_INJECTION,防止嵌套 shell 重复进入
  • fish: set -g 替代 set -l,env-var guard 实际生效
  • pty: Bash/Zsh 注入分支保留 existing_args(如 bash -l-l
  • shell_init: detect_shell_type 正确处理含空格的 Windows 路径
  • shell_init: sh 不再走 Bash 路径,避免 dash/BusyBox 收到 --init-file
  • shell: where pwsh 改用 silent_command 消除 Windows 控制台闪烁

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

@nothingness0db 感谢这些细致的修复!所有改动看起来都很有针对性,修复了之前 review 中指出的问题。我来对更新后的代码进行新一轮完整 review。

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@nothingness0db
Copy link
Copy Markdown
Author

根据 review 意见继续更新:

代码质量修复(上次 push 后)

  • 解析器统一:将 detect_shell_typeparse_shell_command 合并为同一套 exe 提取逻辑(extract_exe),消除两套独立解析器
  • AvailableShell 增加 supports_integration 字段,cmd.exe / wsl.exe 标记为不支持,ShellPicker 下拉显示提示
  • app 退出时清理所有 2code-init-* 临时目录
  • CLAUDE.md 补全 tauri-plugin-clipboard-manager 插件记录

功能 bug 修复

  • Fish 和 Pwsh 会话现在包含 2code common init(之前缺失,导致 notify hook 和 claude wrapper 不可用)
    • Rust 层在 session 创建时写好 notify.sh / claude wrapper / claude-settings.json
    • fish init 用 fish_add_path 更新 PATH,pwsh init 用 $env:PATH 更新
  • Handler 层重构:shell 检测逻辑从 handler/shell.rs 移到 infra/shell_detect.rs,handler 现在是薄层(9 行)

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 20, 2026

@nothingness0db 感谢这一轮的持续改进!以下几点改动看起来特别有价值:

  • extract_exe 统一解析器 — 消除了 detect_shell_typeparse_shell_command 之间潜在的行为不一致,是非常好的重构。
  • supports_integration 字段 — 为 cmd.exe / wsl.exe 明确标记不支持集成,在 UI 层给用户清晰的提示,比静默降级更友好。
  • 退出时清理临时目录 — 防止 2code-init-* 目录在异常退出后堆积,是重要的资源管理改进。
  • Fish / Pwsh 补全 common init — 修复了 notify hook 和 claude wrapper 在这两个 shell 下不可用的功能缺陷,覆盖面更完整。
  • Handler 层瘦身 — 将 shell 检测移到 infra/shell_detect.rs 符合分层架构原则,handler 只做薄层调度。

我来对更新后的代码进行新一轮完整 review。

(=^・ω・^=)🐇

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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.

1 participant