🐜

大量のリファクタリングを AI に丸投げする CLI ツールを作った

2024/03/17に公開

プログラミングでは知的な作業ばかりではなく、テキストをゴニョゴニョするだけの単純な作業も多々あることは、みなさん共感いただけると思います。
巷では生成 AI にクリエイティブなコードを書かせることが盛り上がっていますが、むしろ「単純な作業」を「大量」に任せるほうが現状は実用的なのではと常々考えておりました。

そこで今回サッと作ってみたのが https://github.com/HosokawaR/auto-refactor です。OpenAI の API を利用しています。

こんな感じにリファクタリングの指示内容を設定ファイルに書き
設定ファイル
実行すると
実行中の様子
このようにたくさんのファイルをまとめてリファクタリングできます。
リファクタリングの結果

やっていることはファイルを読み取り OpenAI の API を呼び出してファイルを書き換えているだけなので、多分近いうち GitHub とかがやってくれそう(もうやってそう?)です。
が、とりあえずサッと CLI で使えるものが見つからなかったので作りました。

どう活用する?

ちょっとしたリファクタリングを大量にやる場面に向いています。
例えば

  • 命名を camelCase から snake_case に変えたい
  • JSX でテキストを全て <Text> コンポーネントで囲いたい
  • Ruby3 への移行でハッシュ引数をキーワード引数に治す

などなど…

生成 AI を使わずとも、正規表現での置換や AST をイジイジするスクリプトを書くのも楽しいですが、楽に終わらす方法があるならそうしたいのも本音です。

もちろん生成 AI の出力結果は信用できないのでそのままは使えません。が、今回のような作業補助としては十分です。

使い方

yaml で書かれた設定ファイルを用意します。
例えば以下は、日本語が含まれる tsx ファイルの文字部分を <Text> コンポーネントで囲むリファクタリングです。ただし <Button> 部分はそのままにするように指示しています。

instruction.yaml
- targetFileGlob: src/**/*.tsx
  filterFunction: |-
    (filePath, content) => {
      // only match Japanese characters
      return content.match(/[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/);
    }
  instruction: |
    Wrap the text with `<Text>` component.
    DO NOT wrap the text wrapped with `<Button>` component.

設定ファイルを用意したら OPENAI_API_KEY 環境変数に OpenAI の API key をセットしたうえで、下記のように deno でリファクタリングを開始します。

export OPENAI_API_KEY=your_api_key
deno run --allow-env --allow-read --allow-write --allow-net https://deno.land/x/auto_refactor/main.ts ./instruction.yaml

ちなみに targetFileGlobdeno を実行したパスからの相対位置なので、あやまって変な箇所で実行して関係ないファイルをリファクタリングしないようにしましょう(n 敗)。

詳しくは https://github.com/HosokawaR/auto-refactor の README に書いてます。

実行コスト

意外と低いです。デフォルトでは OpenAI の gpt-3.5-turbo-16k-0613 というモデルを使用しています。
300 ファイル程度のリファクタリングで $3 ぐらいでした。
ただし API の呼び出し時間がかかるので、300 ファイル(回)だと 2 時間ぐらい要します。
(リクエストを並列化すればもっと速度が出るかもしれない)

無論状況によって変わるので、みなさんがお手元で試されるときは OpenAI API の limit を厳し目にして試すことを推奨します。

使ってみて

我ながらそこそこ便利だなという感想です(すごいのは OpenAI)。
もちろん生成 AI なので結果に修正が必要ですし、指示を丁寧に書くのも大変ですが、リファクタリング対象が数十個を超えてくると苦労がペイするかなと思っています。

ただ「リファクタリングしない」という判断をするのは難しいようです。
例えば「 <button> の中の文字は <text> で囲まない」と指示しても囲ってしまったり、修正箇所がないファイルも無理やり修正してしまう例が散見されました。

これらに対しては、Git で過剰な修正を戻せるので(戻すのは簡単)許容したり、設定ファイルの filterFunction でうまく事前に対象ファイルを機械的に絞り込むことで一定対処しました。

プロンプトを頑張ったり GPT-4 を使えばもっと精度が向上するかもしれません。

Discussion