nvim の内蔵ターミナルで動く Claude の状態を親 nvim のタブタイトルへ通知する

個人用のdotfiles設定リポジトリです。 パッケージ管理と Homebrew 管理は別の nix-config リポジトリで行い、このリポジトリは主にシェルやアプリ設定を管理します。
git clone https://github.com/yourusername/dotfiles.git ~/dotfiles
cd ~/dotfiles
cd ~/nix-config
darwin-rebuild switch --flake .#<nix-configで定義したホスト名>
./install.sh
既存の設定ファイルは~/backupディレクトリにバックアップされます。
install.sh: dotfilesのシンボリックリンク作成スクリプトMANUAL_APPS.md: 手動インストールが必要なアプリケーション一覧.config/: 各種アプリケーションの設定ファイル.zshrc: Zshシェルの設定.gitconfig: Git設定.tmux.conf: tmux設定.claude/: Claude Code の設定.codex/: Codex の設定hooks/index.ts 50 lines1import type { EngineInterface, Register } from "claude-code";
2
3type State = "working" | "waiting" | "done" | "none";
4
5async function send($: EngineInterface, state: State) {
6 // nvim の :terminal から起動された時だけ $NVIM に親のソケットが入る
7 const server = await $.env.get("NVIM");
8 if (!server) return;
9 // ターンの進行を nvim の応答待ちで止めないよう await しない
10 $.process
11 .run([
12 "nvim",
13 "--server",
14 server,
15 "--remote-expr",
16 `luaeval('ClaudeTitleState(_A)', '${state}')`,
17 ])
18 .catch(() => {});
19}
20
21export const register: Register = (on) => {
22 on("turn.start", async ($, e, next) => {
23 await send($, "working");
24 return next(e);
25 });
26
27 on("tool.check", async ($, e, next) => {
28 const result = await next(e);
29 // ask はモードの判定者へ回るため、auto モードでは分類器の判定中も一瞬 waiting になる
30 if (e.tool_use_id && result.decision === "ask") await send($, "waiting");
31 return result;
32 });
33
34 on("tool.call", async ($, e, next) => {
35 const result = await next(e);
36 await send($, "working");
37 return result;
38 });
39
40 on("turn.complete", async ($, e, next) => {
41 if (!e.agentId) await send($, "done");
42 return next(e);
43 });
44
45 on("session.end", async ($, e, next) => {
46 await send($, "none");
47 return next(e);
48 });
49};
50