RTK に issue 立ててプルリク作った話

目次

TL;DR — RTK に立てた issue が1週間放置されたので自分でプルリクを出した。run_in_terminal の未認識バグを修正し、重複関数を統合。ローカル確認では mise と linuxbrew の PATH 競合に引っかかったが解決し、PR #1800 を提出した。

前の記事で「引き続き更新を待つかぁ」と書いたやつの続き。

1週間待ったけど動きがなかった

Issue #1425 を立てたあと、1週間ほど本体の動向を見ていた。メンテナさんも忙しいだろうし、そんなもんかと思っていたけど、なかなか動きがない…

うん。待つの飽きた。

「どうせなら自分でプルリクを出してしまえ」

リファクタしてからプルリクを作る

ローカルには fix(hooks): VS Code Copilot Chat... というコミットがすでにあった。ただそのまま出すのは品質的に不安だったので、upstream/develop ベースでブランチを切り直して cherry-pick し、ついでに既存のコードベースのお作法を調査してリファクタも入れた。

変更の内容は3つ:

  1. detect_format()"run_in_terminal" を追加 VS Code Copilot Chat が渡す tool_name の値で、これが認識されないとフックが PassThrough になっていた。根本的なバグ修正。

  2. handle_vscode()handle_copilot_cli()handle_copilot_rewrite() に統合 ほぼ同じロジックが2関数に分散していたので1つにまとめた。

  3. VS Code 向けのレスポンスを allow/ask + updatedInput から deny + suggestion に変更 前の記事で書いた「承認ループ」の回避。Copilot Chat は updatedInput を承認後に無視するので、Copilot CLI と同じ deny + reason 方式に統一した(詳細は #1425)。

// before: handle_vscode / handle_copilot_cli に重複していたロジック
// after: handle_copilot_rewrite に統合
fn handle_copilot_rewrite(cmd: &str) -> Result<()> {
    let rewritten = match get_rewritten(cmd) {
        Some(r) => r,
        None => return Ok(()),
    };
    let output = json!({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": format!(
                "Token savings: use `{}` instead (rtk saves 60-90% tokens)",
                rewritten
            )
        }
    });
    println!("{}", output);
    Ok(())
}

cargo test は 1819 passed, 0 failed。test_detect_vscode_run_in_terminaltest_copilot_deny_output_vscode_format を新たに追加してあるので、次に繋がるかと思う。

ローカル動作確認で罠にはまった

テストは通っていたけど、一応 VS Code Copilot Chat で実際に動かして確認しようとした。

cargo install --path . でバイナリをインストールして Copilot Chat でコマンドを実行したら、VS Code の Output パネル(「GitHub Copilot Chat Hooks」チャンネル)のログがこうなった:

[PreToolUse] Running: {"command":"rtk hook copilot",...}
[PreToolUse] Completed (Success) in 16ms, no output

フックは呼ばれてるのに no outputrun_in_terminal が認識されていないときの挙動(PassThrough = 無出力)と同じだ。

原因を調べたら、mise の activate bash --shims が linuxbrew の rtk を優先していたcargo install~/.cargo/bin/rtk を更新しても、Copilot Chat が開くインタラクティブシェルでは linuxbrew 側の古いバイナリを見に行っていた。

/tmp に置いたスクリプトで呼び出すと修正済みバイナリが動いて deny JSON が出るのに、rtk hook copilot 直打ちだと動かない、という奇妙な状況だった。

パスの問題と分かれば話は早い。シェルの PATH を整理して新しいバイナリを確認し、Copilot Chat を再起動したらちゃんと動いた:

[PreToolUse] Running: {"command":"rtk hook copilot",...}
[PreToolUse] Completed (Success) in 18ms
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny",
"permissionDecisionReason":"Token savings: use `rtk git status` instead ..."}}

no output だったログに deny JSON が出るようになった。

プルリク提出

動作確認が取れたのでプルリク提出。

fix(hooks): recognize run_in_terminal for VS Code Copilot Chat #1800

upstream のコントリビューションガイドと PR テンプレに沿って英語で書いた。というか、Claude さんに書いてもらった。

upstream へそのままマージされると嬉しいんだけど、どうなることやら。