🚚

Gemini CLIでCLI型コーディングエージェント作った

に公開

Gitruck

Gemini CLIがよく分からない動きをするので、GitruckというCLI型コーディングエージェントを作りました。

https://gitlab.com/tkithrta/gitruck

開発には主にGemini CLIを使いました。いわゆるバイブコーディングというやつです。

https://cloud.google.com/blog/ja/topics/developers-practitioners/introducing-gemini-cli

コードは99%AIに書いてもらいましたが、今回の記事は試行錯誤の結果をなるべく私が手を動かしながら書いていきたいと思います。

モチベーション

Gemini CLIを使っていると、以下の問題に直面します。

  • 遅い(入力とか描画とか)
  • レイアウトがよく分からない壊れ方をする(日本語入力する時とか何回もメッセージを送信している時とか)
  • Enterキーを押すと送信される
  • 無料とはいえやたらトークンを消費してくる
  • どのコンテキストファイルが読み込まれているか把握できない
    • 明らかに何らかの履歴を読み込んでいる処理が行われる
  • TypeScript(問題?)
    • Codex CLIもClaude CodeもTypeScriptなので

いくつかの問題はVS Codeの機能を使用することで解決できますが、根本的解決にならないので、自作してみることにしました。

https://zenn.dev/tkithrta/articles/50207678af65a3

とりあえずGitやDockerがない環境でも動くようにしたかったのですが、Gitは使いたかったので、libc依存のないGitを実装しているgo-gitライブラリのあるGoで作ることにしました。

試行錯誤の結果、以下パッケージのみを使うことになりました。

module gitruck

go 1.24.3

require (
	github.com/go-git/go-git/v5 v5.16.2
	github.com/joho/godotenv v1.5.1
	github.com/sergi/go-diff v1.4.0
	github.com/spf13/cobra v1.9.1
	google.golang.org/genai v1.13.0
)

ちなみに開発環境はFirebase Studio上でDevPodのDevcontainerを使用しています。

https://zenn.dev/tkithrta/articles/7a784d521bfe05

Claude Codeの95%以上をClaude自身が生成しているらしいので、ある一定のレベルまで実装してしまえばGemini自身が生成してくれるのでは? と思ったので、ひとまず使いづらいGemini CLIでバイブコーディングすることにしました。

基礎教養

まずAIコーディングエージェントを作成するにあたり、コンピュータとは何ぞや、エージェントとは何ぞやというところから始める必要があります。

コンピュータは以下の3つのことを人間とは比べ物にならないほど超高速に行うことができます。

  • 計算
  • 検索
  • 置換

この3つは以下の順番で処理が行われています。

  1. 入力
  2. 評価
  3. 出力

生成AIは計算が苦手とよく言われますが、それはベクトル演算や行列演算を超高速で行い回答内容を生成しているからであって、別に計算ができないわけではありません。

では検索や置換はどうでしょうか?

これもまたベクトル演算や行列演算を超高速で行っているため、局所的な問題、すなわちどのファイルのどのエンティティのどの行を置換したらよいか、判断するための検索ができていないように思えます。

そこで以前から使っていたAiderの編集フォーマットを参考にしながら論文を読み[1][2][3]、以下の3つの作業をある程度AIで自動化できるようなツールを目指しました。

  • 特定
  • 修正
  • 検証

https://aider.chat/
https://aider.chat/docs/more/edit-formats.html

最初から3つ全て実装するのはバイブコーディングでも無理なので、まずは修正に特化したコード編集を行う実装を作ることにしました。

しかしながら今回作成するのはコーディングエージェントですから、ある程度自律した動きをしてほしいものです。

エージェントとは、意図的に何かをすることができる生体のことである。この定義は、人間やその他の動物、行動を表す動詞の主語、そしてコンピュータ化されたロボットやソフトボットを含めるのに十分広い。しかし、この定義は、animate、capable、doing、purposeといった、同じように意味の難しい単語によって左右される。これらの言葉を定義する作業は、存在論の他のほとんどすべての側面に関わる問題を提起する。

Source: https://jfsowa.com/ontology/agents.htm[4]
www.DeepL.com/Translator(無料版)で翻訳しました。

そこでGitを組み合わせ、最もよい編集を行った時点で自動的にコミット文章を考え、コミットも行ってくれるところまでを最初の目標にしたいと思います。

Gitruckという名前はAiderのようなペアプログラミングツールを想定しているので、ドライバーにいる私と、ナビゲーターにいるAIが乗っているトラックをイメージして命名しました。

現時点では修正のみ実装していますが、最終的には特定と検証も実装する予定です。

検証はDiffそのものを比較するアプローチで解決しそうですが、特定は以下の3つのサブタスクをAIが処理できるようにする必要があるので、現時点ではシステムプロンプトの改善やモデルの性能に頼る必要がありそうです。

  • どのファイルを修正するか
  • どの関数を修正するか
  • どの行を修正するか

最後に、以前私が書いた以下記事の内容も基礎教養として参考になると思いますので紹介しておきます。

https://zenn.dev/tkithrta/articles/9a6f1f2d249790

特に今回以下の3つの内容は出てきますので覚えておきましょう。

  • Vocabulary (ボキャブラリー / 専門用語)
  • Context file (コンテキストファイル / 文脈ファイル)
  • Schema (JSON Schema / スキーマ)

コード編集

Aiderは一体どのようにコードを編集しているのでしょうか?
初期実装を確認してみましょう。

https://github.com/Aider-AI/aider/blob/v0.5.0/aider/prompts.py

mathweb/flask/app.py
<<<<<<< SEARCH
from flask import Flask
=======
import math
from flask import Flask
>>>>>>> REPLACE

知らないDiffフォーマットが出てきましたが、これはコンフリクトマーカー(Conflict Markers)と呼ばれるGitのコンフリクト時に表示されるものに似た独自のフォーマットのようです。

皆さんがよく見るDiffフォーマットは以下のようなフォーマットだと思いますが、Unified Diffと呼ばれるものです。

--- app1.py
+++ app1.py
@@ -1 +1,2 @@
+import math
 from flask import Flask

--- app2.py
+++ app2.py
@@ -1 +1,2 @@
+import math
 from flask import Flask

ではなぜUnified Diffではなくコンフリクトマーカーに似たフォーマットを使用しているのかというと、以下のようにシンプルに表現できるためUnified Diffより生成AIが生成しやすく、検索と置換も簡単な実装で高速に行えるからです。

diff

ChatGPTでも簡単に生成できます。

file_path
<<<<<<< SEARCH
search_block
=======
replace_block
>>>>>>> REPLACE
  1. ファイルパスの特定: 1行目のmathweb/flask/app.pyを「変更対象のファイルパス」として取得
  2. 検索ブロックの抽出: <<<<<<< SEARCH=======の間に書かれているテキストブロックを「検索対象の文字列(SearchBlock)」として取得
  3. 置換ブロックの抽出: =======>>>>>>> REPLACEの間に書かれているテキストブロックを「置換後の文字列(ReplaceBlock)」として取得
  4. コード編集: ReplaceAllで変更対象のSearchBlockをReplaceBlockで置換

ただGoogle Gemini APIの構造化出力を使えば、上記フォーマットを使わなくても直接フィールドを参照できるので、パースすることなく検索と置換を行うことができます。

https://ai.google.dev/gemini-api/docs/structured-output?hl=ja

なのでGitruck 0.1.0では一旦コンフリクトマーカーに似たフォーマットを使わずに、以下のようなJSON Schemaを定義してコード編集しています。[5]

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "array",
  "description": "A list of proposed code changes.",
  "items": {
    "type": "object",
    "description": "Represents a single code change operation within a specific file.",
    "properties": {
      "file_path": {
        "type": "string",
        "description": "The relative path to the file that needs modification (e.g., 'mathweb/flask/app.py'). This path must be relative to the project root."
      },
      "search_block": {
        "type": "string",
        "description": "The exact, verbatim block of original code lines to be replaced. This must be a literal copy from the current file content, including all whitespace, indentation, and newlines. It should uniquely identify the code to be replaced."
      },
      "replace_block": {
        "type": "string",
        "description": "The new block of code lines to replace the search_block with. This should be the complete, new code block, including all necessary whitespace, indentation, and newlines."
      },
      "explanation": {
        "type": "string",
        "description": "A detailed explanation of why this change is being made, formatted in Markdown. This explanation should cover the problem, the solution, and any important considerations."
      }
    },
    "required": [
      "file_path",
      "search_block",
      "replace_block"
    ]
  }
}

コード編集に関する説明(explanation)を追加していますが、オプションなのでなくてもいいです。

ここまでの内容を整理すると、最低限以下のパッケージがあればCLI型コーディングエージェントを作成できることが分かります。

  • 生成AIライブラリ(genai, godotenv)
  • Diff, Match, Patchなどの検索、置換を行う処理(go-diff)
  • 文字列操作(strings)
  • ファイル、ディレクトリ全般(os)
  • ファイル、ディレクトリパスの確認(path/filepath)
  • 入出力(io)
  • システムプロンプト生成(text/template, encoding/jsonなど)
  • CLIツール用ユーティリティ(cobraなど)
  • Gitなどのバージョン管理システム(go-git)

もし従来のコーディングエージェントで行っているような特定、検証に関する処理も実装するのであれば、コマンドを実行するためのパッケージが別途必要になります(Goではos/exec)

実装

ある程度仕組みは理解できたので、いざGemini CLIでバイブコーディングをはじめたものの、なかなかうまくいきません。

いくつか失敗した事例と、Gitruck 0.1.0までコミットしていくうえで廃止した機能を紹介します。

  • 数年前のAiderの初期実装のPythonファイルをすべて読み込ませて、同じ機能をGoで再現してもらう
    • Google Gemini APIを使うように指示してもOpenAI APIを使ってくる
    • Gemini CLIはタスク管理ができないので場当たり的に実装していく
    • ディレクトリ構造もごちゃごちゃになる
      • なのでAiderのソースコードを読ませるのをやめて、コマンドの設計とか、コンフリクトマーカーに似たフォーマットの実装とかちゃんと指示すると安定する
  • BubbleTeaでTUIを実装する
    • TUIのデバッグが困難
    • BubbleTeaのレイアウト調整が大変
    • Gemini CLIと同じく、レイアウトがよく分からない壊れ方をする
      • そもそもTUIいる? 作りたいのはCLIツールなのでBubbleTeaやめる
  • cobraの--fileフラグでファイルを渡す
    • 1つしかファイルを渡せない
    • path/filepathパッケージのGlobで頑張る
    • Globの***?[\有無の条件分岐で無駄な処理が増える
      • --fileフラグをやめてgitruck ask [files...]形式にすれば複数のファイルを渡せるしシェルに組み込まれたGlobが使えるので不要になった
      • ちなみに0.2.0で--fileフラグを使いGITRUCK.mdファイルやディレクトリでコンテキストファイルを渡せるようになっています
  • プロンプトのテンプレートとスキーマのフィールドがGoのコード内にハードコーディングされている
    • Frontmatterにする? 意外なことにGoで枯れているライブラリがなかった
    • XMLにする? プロンプトのテンプレートにCDATAセクションを使用する必要があったので面倒になる
      • JSONにしてプロンプトのテンプレートをStringではなくStringのArrayにする妥協案を採用

またよく言われている話ですが、現在のGemini CLIはGemini 2.5 Proでしばらく使っているとすぐGemini 2.5 Flashに弱体化して複雑な実装ができなくなるので注意が必要です。

寝て起きたらGemini 2.5 Proの使用時間が回復していたりするので、Gemini 2.5 Proを使用できる時は複雑なタスクを投げて、Gemini 2.5 Flashに弱体化したらファイルを多めに渡してコンテキストを明確にするとか正確な指示を出すとか使用者側も切り替えていきましょう。

使い方

せっかくなので軽くGitruckの使い方を紹介したいと思います。

gitruck version
0.2.0
gitruck help
Gitruck is a command-line interface (CLI) tool designed to automate the process of processing tasks on your codebase.
It leverages AI to understand tasks and generate appropriate code changes, which are then applied and staged in your Git repository.

Usage:
  gitruck [command]

Available Commands:
  add         Adds file contents to the Git index
  ask         Ask a question about the codebase and get an explanation
  completion  Generate the autocompletion script for the specified shell
  help        Help about any command
  init        Create an empty Git repository or reinitialize an existing one
  patch       Apply a patch to files
  restore     Restores specified paths in the working tree
  task        Process tasks based on provided file paths and a task description
  version     Print the version number of Gitruck

Flags:
  -h, --help   help for gitruck

Use "gitruck [command] --help" for more information about a command.

詳しい使い方はREADME.mdを参照してください。
ちなみにREADME.mdもAIが書いています。

Ask

gitruck askは、指定したファイルの内容をAIが読み込み、それに関するあなたの質問に答えてくれるコマンドです。

  • 他人が書いた複雑なコードの処理内容を理解したいとき
  • 特定の機能がどのファイルで、どのように実装されているか知りたいとき
  • コードレビューで、より良い実装方法についてAIに意見を聞きたいとき

など、コードの「理解」を深める場面で絶大な効果を発揮します。

gitruck ask [対象のファイル...]

質問は、-m--message)フラグで渡すか、標準入力(パイプ |)を使って渡します。

複数のファイルにまたがる機能について質問してみましょう。

gitruck ask main.go internal/core/llm.go -m "How does the LLM integration work?"
  • gitruck ask: AIに質問を開始します。
  • main.go internal/core/llm.go: AIにコンテキストとして読み込ませるファイルを指定します。
  • -m "...": "How does the LLM integration work?"(LLMの統合はどのように機能しますか?)という質問をAIに投げかけています。

以下のコマンドは、長文の質問や、他のコマンドの出力を利用したい場合に便利です。

echo "Explain the purpose of the persona system" | gitruck ask internal/core/persona.go
  • echo "...": "Explain the purpose of the persona system"(ペルソナシステムの目的を説明して)という文字列を出力します。
  • |: echoコマンドの出力を、次のgitruckコマンドの入力(質問内容)として渡します。
  • gitruck ask internal/core/persona.go: persona.goファイルをコンテキストとして、渡された質問に答えます。

gitruckの面白い機能の一つに「ペルソナ」があります。-pフラグでペルソナを指定すると、AIがそのキャラクターになりきって回答してくれます。

cat GITRUCK.md | gitruck ask old.txt new.txt -p novelist
  • cat GITRUCK.md |: GITRUCK.mdファイルの内容をgitruckコマンドの入力として渡しています。これは、より詳細なコンテキストや、ペルソナの振る舞いを定義する情報を与えるために使われることが多いです。
  • gitruck ask old.txt new.txt: old.txtnew.txtの差分について質問するようなシナリオが想定されます。
  • -p novelist: AIに「小説家(novelist)」のペルソナを指定しています。これによりテキストファイルの中身について、まるで物語のように、比喩を交えた表現で返ってくるかもしれません。

Task

gitruck taskは、askから一歩踏み込み、AIに実際のコード編集を依頼するコマンドです。指示に基づいてAIがコードを修正し、その変更を自動的に保存してくれます。

  • 面倒なリファクタリング作業(変数名の一括変更、関数の分割など)
  • すべての公開関数にコメントを自動で追加する
  • エラーハンドリングの定型的なコードを追加する

など、手間のかかる作業をAIに丸投げできます。

gitruck task [対象のファイル...]

タスクの指示内容はaskと同様に、-mフラグか標準入力で渡します。

main.goファイルにあるmain関数のエラーハンドリングを改善してもらいましょう。

gitruck task main.go -m "Refactor the main function to improve error handling"
  • gitruck task: AIにコード編集タスクを依頼します。
  • main.go: 編集対象のファイルを指定します。
  • -m "...": "Refactor the main function to improve error handling"(main関数をリファクタリングしてエラーハンドリングを改善して)という具体的なタスクを指示しています。

以下のinternalディレクトリ配下のすべてのGoファイル(.go)にある、エクスポートされた(公開されている)関数にコメントを追加してもらいます。

echo "Add comments to all exported functions" | gitruck task internal/**/*.go
  • echo "..." |: "Add comments to all exported functions"(すべての公開関数にコメントを追加して)という指示を渡します。
  • gitruck task internal/**/*.go: internalディレクトリ以下のすべてのサブディレクトリに含まれる、拡張子が.goのファイルを対象にします。シェルのワイルドカード(**/*)が非常に便利です。

askと同様にペルソナが使えます。さらに、forループと組み合わせることで、AIに繰り返しタスクを試行させ、無限に小説を書くこともできます。

for i in {1..5}; do cat GITRUCK.md | gitruck task new.txt -p novelist; done
  • for i in {1..5}; do ...; done: ...の部分を5回繰り返します。
  • cat GITRUCK.md | gitruck task new.txt -p novelist: 「小説家」ペルソナに、何らかのコンテキスト(GITRUCK.md)を基にnew.txtを編集させるタスクです。

AIの出力は毎回少しずつ変わることがあるため、同じ指示を複数回実行することで、最も良い結果を選択したり、段階的に洗練させたりする、といった高度な使い方が可能です。

コマンド実行回数 = Google Gemini APIの呼び出し回数ですし、ファイルの読み込みと書き込みも柔軟に宣言できるため、トークンの消費を自由にコントロールすることができます。

そして何と言っても完全なCLIツールなので、エディタを選ばないですし、TUIツールで発生する様々な問題に悩まされる必要もありません!

nano

このようにnanoでもパイプで簡単にメッセージを渡すことができます。

今後

今後も引き続きGemini CLIとGitruckでバイブコーディングをやっていき、実装予定のGitコマンドや便利な機能の追加を行っていきたいと思います。

ただ……作っていて思いました。

これGitいる?

GitはVS Codeにも搭載されている非常に便利なバージョン管理システムですが、正確にはソースコード管理システムなので、生成AIのコード編集についてバージョン管理するには機能があまりにも多すぎます。

Goもビルド、テスト、リンター、フォーマッターが標準でついてきますし、libcのような依存関係もなく様々な環境で動きますが、クロスコンパイルするとWindows Defenderで誤検知にあったりするので、普段Windowsを使用している私のユースケースにあわないことに気づき始めました。

なのでAiderと同じくPythonで作り直すかも……。
PythonならLiteLLMで様々なモデルが使えるようになりますし。
バイブコーディングは一度慣れてしまえば同じものを作り直すのが楽でいいですね。

脚注
  1. Source: https://github.com/codefuse-ai/Awesome-Code-LLM ↩︎

  2. Xia, C. S., Deng, Y., Dunn, S., & Zhang, L. (2024). Agentless: Demystifying LLM-based Software Engineering Agents. arXiv preprint arXiv:2407.01489. https://doi.org/10.48550/arXiv.2407.01489 ↩︎

  3. Ma, Z., Peng, C., Gao, P., Meng, X., Zou, Y., & Xie, B. (2025). SoRFT: Issue Resolving with Subtask-oriented Reinforced Fine-Tuning. arXiv preprint arXiv:2502.20127. https://doi.org/10.48550/arXiv.2502.20127 ↩︎

  4. Source: https://jfsowa.com/ontology/agents.htm Copyright ©2001, 2005, by John F. Sowa. Permission is hereby granted for anyone to make verbatim copies of these documents for teaching, self-study, or other noncommercial purposes provided that the copies cite the author, the URL of this document, and this permission statement. ↩︎

  5. Sroposed: https://github.com/Aider-AI/aider/issues/3713 ↩︎

Discussion