zudotext: コンフィグ駆動のAIライティングアシスタントを作った
概要
日本語のビジネスコミュニケーション(メール、Slackメッセージ等)を書くのを手伝ってくれるElectronデスクトップアプリ「zudotext」を作った。Claude Codeをターミナルに組み込んで、雑に書いた下書きをいい感じの文章にしてくれる。設定ファイル1つで異なるアプリインスタンスを生成できるコンフィグ駆動のアーキテクチャになっている。そのまとめ。
モチベーション
日本語のビジネスメールやSlackメッセージを書くのは地味に手間がかかる。敬語のレベルを揃える、語彙を統一する、構成を整える。内容自体は決まっているのに、体裁を整えるのに時間を取られる。毎日何通も書くものだと、この作業が積み重なる。
AIに任せれば良いのでは、というのは自然な発想だが、ただClaude Codeに「丁寧にして」と頼むだけではうまくいかない。自分の文体のルールがある。使う語彙にも好みがある。敬語のレベルも場面ごとに違う。こういったコンテキストをちゃんと渡してやらないと、出力がAIっぽい汎用的な丁寧文になってしまう。
ということで、ライティングルールやスタイルガイドを自動的にClaude Codeに読ませつつ、快適に文章を書ける環境を作ることにした。
何ができるのか
zudotextの基本的な使い方はこう。
エディタ側に雑なテキストを書く。箇条書きでもメモ書きでも構わない。ターミナル側でClaude Codeが動いていて、コマンドを打つとエディタの内容を読み取って、ライティングルールに基づいた文章に整えてくれる。整った文章はエディタに直接反映される。
UIは左右の分割ペイン構成で、片方がCodeMirrorベースのエディタ、もう片方がxterm.jsベースのターミナル。ターミナルでClaude Codeが動いていて、エディタのファイルを直接編集する。つまりClaude Codeのファイル編集能力をそのまま使っている。
メッセージのライフサイクルとしては以下の流れになる。
- エディタで下書きを書く
- Claude Codeに整えてもらう
- レンダリングされたMarkdownをプレビューで確認する
- 完成したらタイムスタンプ付きファイル名でアーカイブする
アーカイブされたメッセージはYYYYMMDD-HHMM-description.mdの命名規則で保存され、全文検索もできる。タブは最大99個まで開けるので、複数のメッセージを同時に書ける。
コンフィグ駆動のアーキテクチャ
zudotextの設計で特徴的なのは、1つのアプリケーションコードから異なるアプリインスタンスを生成できるコンフィグ駆動のアーキテクチャ。
各アプリインスタンスは.zudotext.settings.jsonという設定ファイルで定義される。設定ファイルで制御できるのは以下。
- カラースキーム
- エディタの設定(フォント、テーマ等)
- ターミナルの設定
- キーボードショートカット
- レイアウト
- Pins(サイドバーに表示するディレクトリ)
ワークスペースは~/Documents/zudo-text/<appname>/にアプリ名ごとに自動生成される。初回起動時にスキャフォールドされるので、設定だけ書けば新しいアプリインスタンスが使える。
たとえばmail用とnotes用で別々のインスタンスを作ることができる。それぞれカラースキームもルールも別。パーアプリの設定は~/.config/zudotext/<appname>/config.jsonに保存される。
共有パッケージの@takazudo/app-defaultsで設定スキーマとデフォルト値とバリデーションを定義していて、@takazudo/app-scaffoldでアプリ環境のスキャフォールドを行う。設定の型定義と実際の挙動が一箇所で管理されているので、新しい設定項目を追加するときも一貫性が保てる。
Pinsシステム
サイドバーに表示される「Pins」は、任意のディレクトリを指すエントリ。デフォルトではmessage-workspace/.claude/skillsがPinとして登録されており、ライティング用のスキル(コマンド)が表示される。
ユーザーは自由にPinを追加できる。よく参照するノート、テンプレート集、参考資料など、書きながら見たいものを何でもサイドバーに出せる。
なぜElectron + Claude Codeなのか
AI連携の書き方としては、APIを直接叩いてチャットUIを作る方法もある。ただ、そうするとAIとのやり取りのレイヤーを自分で作らなければならない。ファイル編集のロジック、コンテキスト管理、ツール呼び出し。これらは全部Claude Codeが既に持っている機能で、再実装する意味がない。
zudotextのアプローチは、Claude Codeをそのままターミナルに組み込んで、その周りに快適な書き物環境を作るというもの。アプリ側は「薄いラッパー」に徹する。Claude Codeのファイル編集能力、ツール使用能力、コンテキスト認識能力をそのまま使える。カスタムAIインテグレーションのレイヤーを作らなくて良い。
Electronを選んだのは、CodeMirrorとxterm.jsの両方をネイティブアプリとして動かせるから。Webベースだとターミナルの統合がやりにくい。
余談
zudotextは元々自分用のツールとして作り始めたもので、設計も「自分が毎日使うもの」として決めている。コンフィグ駆動にしたのは、メール用と個人メモ用で文体のルールが違うから。ライティングの文脈ごとにアプリインスタンスを分けられるのは、実際に使ってみるとかなり便利だった。