goat -- キモいgoのコードが書きたい

キモいgoのコードが書きたい
関数の引数をastでパースしコマンドラインオプションの対応コードを生成をする。go runやgo buildは遅い。go/packagesも遅い。パッケージのimportがeagerなのが問題なきがする。
enumやdefault対応にミニインタプリタを作る。goの構文はすべて受け入れるが一部のコードだけ対応。

日記

2025-06-02
- ずっとscaffoldだけ用意していて放置していた。実行した。
- buildが通らないようなコードが生成されるらしい。悲しい。直した。

こんなのが動くように。
とりあえずヘルプメッセージの生成て情報の収集部分の動作確認。
go run ./cmd/goat/ examples/hello/main.go
2025/06/03 02:37:53 Goat: Analyzing examples/hello/main.go with runFunc=run, optionsInitializer=
2025/06/03 02:37:53 Goat: Command metadata extracted for command: main
2025/06/03 02:37:53 Goat: Skipping options initializer interpretation (initializer name or options struct not found/specified).
-------------------- Generated Help Message --------------------
main -
Usage:
main [flags]
Flags:
--version bool Print version information (required)
--help bool Show help message (required)
--config-file string Path to the configuration file (required)
-h, --help Show this help message and exit

次にやることはなんだろう?
- helpUsageを埋め込み
- main()の書き換え、本丸だ。
- テスト直す
まずはmain()の書き換え?
ペンディング
- TextVarの対応
- Optionsをimport
- フィールドをimport
- ネスト
- 埋め込み
- ミニ言語
- default
- enum
- いろんなバリエーション
- import
- (シンボルからファイルのキャッシュ)
- サブコマンドに分ける
- ヘルプメッセージ
- mainの差し替え
- 解析結果の確認
- (injectionをサブコマンドでやってもいいかも)

2025-06-03
- julesに書いてもらっていた
- codegenを実装してもらった
- 💭意外とコード中の余分なコメントが機能している
全体としてコードは綺麗なように見えて構造がグチャグチャ。初期段階のscaffoldのせいではある。

2025-06-04
- 夜から深夜の作業
- ほぼスマホからjules
- mainの差し替え
- サブコマンドを実装した(init, scan, emit, help-message)
- initはダミー
- 外部のパッケージも見れるようにanalyzerを調整
- 埋め込みとかにも対応した
ℹ️ 実際に動かして試せてはいない。
初期段階のscaffoldでTODOコメントを残しまくるのは後のjulesでの実装に役立ったかも?
PC触れるときにはexamplesとかの実行して動作確認が良さそう。

気になるのは
- flagsのPrintDefault()でusageは十分では?
- UnquoteUsage()とか

2025-06-06
- いろんな機能がごてごて入った
- ヘルプメッセージ自体の文字列をコードに埋め込む
- 環境変数の解析
- 順序を整える
- 😇一度テストコードの更新を怠って進めた環境変数とか対応のコードが複雑な実装をし始めた
- 何回やってもjulesがテストコードを直せなくなった
- 仕方がないのでまじめに仕様的なものを書きシンプルな実装にしてもらった
- テストコードを0から書き直すタスクも並行させてた(使わず)
- あとjulesはissueを読んでくれない
- ドキュメントを作らせるみたいなこともやってみた
- いわゆる普通のドキュメントではないかも
- https://github.com/podhmo/goat/pull/28
- ドキュメントを作らせるみたいなこともやってみた
その後、以下などを追加してきた。
- Textvar対応
- パッケージimport周り対応
💭 なんかコードが複雑化してきたけどこれはもういけるところまでいってみるというのも面白いのかもしれない。
ああそうそうコードの出力の常として出力されたコードが気に食わなくて調整みたいなタスクも投げた(slices.Containsを使うだとか)。あとやっぱりきれいな出力を目指すとtext/templateは無しかも。
examplesの再生成とか常にやってもらいたいかも?

2025-06-07
悲しい事実に気づいた
- いつの間にかgo/packagesが混入してた
- いつの間にか新しい機能が勝手に追加されてた

いろいろな抱えている問題点を整理する
- go/packagesが使われている(排除したい) -> analyzer v3で対応
- TextMarshaler/TextUnmarshallerの解析がうまくいかない
- 別パッケージに定義された型を参照できない
- initializerを取得できていない
- go listって遅いのだっけ?(遅いかはともかく壊れやすい。go.modとgoのversionがズレて壊れるとかがある)
- usage欄でのコマンド名がパッケージ名のフルパスになっている
- enumの例が整理されていない
- toolsディレクティブを使えていない (go.mod)
- globパターン対応とか余分な機能が含まれている
- デバッグログが少ない(壊れた時の情報が少ない)
- 自身でgoat/main.goが定義されていない
開発的な話
- 常にjuleseが読むruleを作りたい
- go/packagesは使わない
- (不要なコメントを追加しない)
- buildが通ることを確認する
- goimportsを実行してフォーマットする

2025/06/11
- internal/loader/lazyloadをinternal/loaderに変更 (既存のinternal/loaderを削除)
- internal/analyzerのOptionsAnalyzeV2を削除する
- cmd/goatで
--analyzer-version
を削除する
internal/loader/lazyloadのパッケージ名をinternal/loaderに変更してください。利用しているコードも全て直してください。ドキュメントも更新してください
具体的な変更箇所は以下です
- internal/loaderパッケージの削除
- internal/loader/lazyloadパッケージをinternal/loaderパッケージに名称の変更
- 変更後のLoaderを作成する関数の名前をNewLoaderからNewに変更(loader.NewLoaderからloader.Newに変更)
必ず docs/ja/llm.md に従って作業をしてください

2025/06/11
- とりあえずenumのexamplesが動くようになった
- 生成されるコードはコンパイルエラー

go.modのパーサーを使うべきじゃない?

2025-06-13
- go listを使わないほうのLocatorをデフォルトにした
- enumをinterpreter側でもサポートした
- log/slogに統一した

2025-06-14
- loaderにキャッシュを追加した
- interpreterでキャッシュを利用するようにした
- context付きのslogに切り替えた(便利) https://github.com/podhmo/goat/pull/98
- とりあえず動く感じになったのでv0.1.0として公開した。

jules

コメントが邪魔…。でもこの試みは失敗した 🫠
対話履歴
ユーザー:
あなたをコーディングエージェントとして利用しあるコードに機能を追加したりバグを取ってもらったりしました。動作はするのですが不満があります。以下のような不満なのですがどうすれば解決するのでしょうか?
- 余分なコメントが残される
- 不要なコードが削除されずコメントアウトされる
おそらく実験用のコードや問題を解くコーディング試験用のコードや解説用のコードなら問題ないのでしょうが実際の開発のコードでは邪魔なだけです。これを抑制するプロンプトを追加したいです。
具体的には、goのコードを書いてるときに
import (
"fmt"
"go/printer"
)
から
import (
"fmt"
// "go/printer" // 不要になった
"go/ast" // Added
)
みたいなコメント達です。この場合は以下が望ましいです
import (
"fmt"
"go/ast"
)
AI:
ご不満な点、よく理解できます。AIがコードを生成する際に、思考の過程や変更の意図を示すためにコメントを残したり、安全策として古いコードをコメントアウトしたりすることがあります。これは、対話形式で段階的に問題を解決していく場合や、教育的な文脈では有用な場合もありますが、おっしゃる通り、実際の開発プロジェクトではノイズになりがちです。
この問題を解決するためには、AIに対する指示(プロンプト)をより明確にすることが効果的です。具体的には、以下のような指示をプロンプトの冒頭や、コード生成を依頼する際に常に含めることをお勧めします。
提案するプロンプトの追加指示:
「あなたはプロのソフトウェアエンジニアとして、本番環境にデプロイされることを想定した、高品質でクリーンなGoコードを生成してください。以下の点に特に注意してください。
-
コメントの最小化:
- コードの動作を理解するために本当に必要なコメントのみを残してください。
- 思考プロセス、デバッグ情報、一時的なメモ、変更理由(例:「不要になった」「Added」など)を示すコメントは、最終的なコードに一切含めないでください。 これらはバージョン管理システム(例: Gitのコミットメッセージ)で管理されるべき情報です。
- 特に、既存のコードを修正する際に、削除するコードやインポート文はコメントアウトするのではなく、完全に削除してください。
-
コードのクリーンネス:
-
使用されなくなったインポート文、変数、関数、その他のコード片は、完全に削除してください。コメントアウトして残さないでください。
-
例えば、import文の変更は以下のようにしてください。
変更前:
import ( "fmt" // "go/printer" // 不要になった "go/ast" // Added )
期待する変更後 (これが正しい形式です):
import ( "fmt" "go/ast" )
-
-
最終成果物としての意識:
- 生成するコードは、そのまま本番コードとして利用できる状態を目指してください。実験的なコードや解説用のコードのような冗長な表現は避けてください。
これらの指示を常に守り、可読性と保守性の高い、プロダクションレディなコードを提供してください。」
このプロンプトのポイント:
- 役割の明確化: 「プロのソフトウェアエンジニア」「本番環境」「高品質でクリーンなコード」といった言葉で、期待する品質レベルを伝えます。
- 禁止事項の明示: 「一切含めないでください」「完全に削除してください」といった強い言葉で、望まない行動を具体的に抑制します。
- 具体例の提示: ご提示いただいたimport文の例をそのままプロンプトに含めることで、AIが期待するフォーマットをより正確に理解しやすくなります。
- 代替手段の示唆: 「バージョン管理システムで管理されるべき情報」と伝えることで、AIがなぜその種のコメントを省略すべきかを理解しやすくなります。
このプロンプトを、あなたがAIにコーディングを依頼する際の基本指示として追加してみてください。これにより、生成されるコードのクリーンさが向上し、ご不満が解消される可能性が高まります。
もし、このプロンプトを使ってもまだ改善が見られない場合は、さらに具体的なフィードバック(例えば、「まだ『// Added』というコメントが残っています」など)をAIに与えることで、徐々に期待する振る舞いに近づけていくことができます。
(返答終了理由: STOP)
メタデータ
{
"runSettings": {
"temperature": 1,
"model": "models/gemini-2.5-pro-preview-05-06",
"topP": 0.95,
"topK": 64,
"maxOutputTokens": 65536,
"safetySettings": [
{
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "OFF"
},
{
"category": "HARM_CATEGORY_HATE_SPEECH",
"threshold": "OFF"
},
{
"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT",
"threshold": "OFF"
},
{
"category": "HARM_CATEGORY_DANGEROUS_CONTENT",
"threshold": "OFF"
}
],
"responseMimeType": "text/plain",
"enableCodeExecution": false,
"enableSearchAsATool": false,
"enableBrowseAsATool": false,
"enableAutoFunctionResponse": false
},
"systemInstruction": {}
}


あとドキュメントは作っても指定しなければ読まないし更新しない。
なので古い記述が残ることもあるし。ドキュメントに書かれてることを認識せずに作業をすることがある。

あとはgithub issueは一切参考にならない。リポジトリの中に情報を埋め込む必要がある(連携はないのでそれはそう)。

あとjulesはパッケージの移動に弱い。gomvpkgで済むような作業が失敗する。
たぶん削除が禁止されてる。
cp -r で終られたと思ったら途中で終わるし。パッケージ名も変えないし。import pathも変えない。テストが通るまで修正してと別タスクでお願いしたがパッケージ名は変えなかった(internal/helpgenをimportしたがhelp.でアクセスするまま)