夜、個人開発のリポジトリで設定ファイルの中身を Gemini CLI に読ませようとして、@config と打ってからふと手が止まりました。このリポジトリには config.json が二つあり、片方はアプリ側、もう片方はサーバー側のものです。どちらが渡るのか、自分でも説明できなかったのです。
そのとき思い出したのが、少し前に GitHub で読んだ Issue でした。Gemini CLI の @ 参照は、ファイルが見つからないときに glob へ切り替わり、その出力から最初の一件だけを拾う、という内容です。ここでは、報告の中身を整理したうえで、同名ファイルが並ぶ場所で私が行っている点検をお伝えします。
最初にお伝えしておくと、私自身の手元ではこの挙動を再現しておりません。以下の「報告されている挙動」は、Issue に書かれていることをそのまま整理したものです。
報告されている挙動
Issue #29318 には、atCommandProcessor.ts のフォールバック部分について、次の指摘が並んでいます。
glob の出力を改行で割り、決まった行の位置を読んでいるため、壊れやすい作りです。一致が複数あっても、使われるのは最初の一件だけです。フォールバックの経路では、ワークスペースの信頼確認が省かれています。複数のルートを検索するときは、一つずつ待つので遅くなります。
再現の手順は短く、@config のように曖昧な参照を打つと、二件以上が一致していても確認なしに先頭の一件が選ばれる、というものです。期待される動作として、複数一致のときは利用者に選ばせることが挙げられています。
報告時点のコミットは main の 9c1b0a610 で、バージョンや OS の記載はありません。コメントも担当を申し出る一件だけで、回避策は書かれていませんでした。つまり、いまの時点で確実に言えるのは「そう報告されている」ところまでです。
何が困るのか
エラーが出ないことです。違うファイルが文脈に入っても、CLI は普通に答えを返します。しかも答えは、渡ったファイルの内容に基づいて、それらしく書かれます。
たとえば app/config.json のつもりで server/config.json が渡ると、「この設定で問題ありません」という返答が、別のファイルへの評価になってしまいます。気づくのは、実機で挙動が違ったときです。
点検の考え方:打つ前に数える
CLI の挙動を直すことは私にはできません。ですので、@ に名前を書く前に、その名前が何件に当たるかを数える習慣にしました。1件なら進み、2件以上ならパスを全部書きます。
次のスクリプトは、名前の一部を渡すと、一致するファイルとディレクトリを列挙します。node_modules などは除外しています。
#!/usr/bin/env python3
"""@名前 を打つ前に、同じ名前に当たるファイルが何本あるかを数える。"""
import fnmatch
import os
import sys
SKIP = {".git", "node_modules", ".next", "build", "dist"}
def find_matches(root, needle):
hits = []
for base, dirs, files in os.walk(root):
dirs[:] = [d for d in dirs if d not in SKIP]
for name in files + dirs:
if fnmatch.fnmatch(name.lower(), f"*{needle.lower()}*"):
hits.append(os.path.relpath(os.path.join(base, name), root))
return sorted(hits)
if __name__ == "__main__":
needle = sys.argv[1]
root = sys.argv[2] if len(sys.argv) > 2 else "."
hits = find_matches(root, needle)
for h in hits:
print(h)
if len(hits) > 1:
print(f"曖昧: {len(hits)} 件。@ にはパス全体を書いてください", file=sys.stderr)
sys.exit(1)
sys.exit(0 if hits else 2)書いた理由は、ls や目視だと、ディレクトリと同名のファイルを見落とすからです。os.walk で両方をまとめて数えれば、見落としが出ません。終了コードを分けたのは、シェルの関数に包んだときに「曖昧(1)」と「該当なし(2)」を区別できるようにするためです。
小さなツリーで動かしてみた
app/config.json、server/config.json、app/config/dev.yaml、そして node_modules/x/config.json を置いた検証用のツリーで、実際に動かしました。
| 渡した名前 | 出力 | 終了コード |
|---|---|---|
config | app/config、app/config.json、server/config.json の3件 | 1(曖昧) |
dev.yaml | app/config/dev.yaml の1件 | 0 |
node_modules 側の config.json が出ていないこと、ディレクトリの app/config まで数えられていることが分かります。@config と打てば、少なくとも3通りの解釈があり得る場所だったわけです。
運用の線引き
私が決めたのは、次の三つです。
一つ目は、@ にはなるべくパスの全体を書くことです。短い名前で済ませるのは、一件しか当たらないと確かめたときだけにしています。
二つ目は、渡したつもりのファイルについて、答えの冒頭で内容に触れてもらうことです。取り違えがあれば、そこで早めに気づけます。
三つ目は、同名ファイルが増えてきたら、名前を変えるか、.geminiignore で対象から外すことを検討することです。
三つ目は、報告された挙動への対策であると同時に、リポジトリ側の整理でもあります。同名のファイルが何本もあると、人間も取り違えるからです。
なお、既知の不具合をまとめて引ける形にしておく方法は、既知バグ早見表を先に grep する運用に書いております。承認ルールが意図どおり当たっているかの点検は、policy を点検して回った記録で扱いました。
取り違えに気づく手がかり
点検を習慣にしても、うっかり短い名前で渡してしまう日はあります。そのときに気づくための手がかりも、いくつか決めてあります。
答えのなかに、自分が見ていないキーや関数名が出てきたら、渡ったファイルを疑います。逆に、あるはずの項目への言及が一つもないときも同じです。「設定は問題ありません」とだけ返ってきて、具体的な値に一度も触れていないなら、まず何が渡ったのかを聞き直します。
聞き直しは短くて構いません。「いま読んだファイルのパスを一行で教えてください」と頼むだけで、取り違えはたいてい分かります。最初のうちは、この確認を省いて数時間ぶん進んでしまい、あとで戻る羽目になりました。いまは、設定ファイルを渡した直後に一度だけ確かめるようにしています。
修正が入ったあとのこと
Issue の中では、具体的な修正案として、構造化された glob の API を使うこと、複数一致のときに選ばせること、信頼確認を通すことが挙げられていました。修正が入れば、この点検の一部は不要になるでしょう。ただ、同名ファイルの数を知っておくこと自体は、CLI がどう変わっても損にならないと感じています。
次にやっていただくことを一つだけ挙げるなら、ご自分のリポジトリで、よく使う名前(config、index、README など)を一度このスクリプトに渡してみることです。何件に当たるかを見たとき、@ で名前だけ書いてよい場所と、書いてはいけない場所の境目が見えてきます。
名前で渡すのは、一件だと確かめたときだけ。 この線引きを、私はこれからも守るようにしています。