codecompanion.nvimでチャット履歴を保存する方法
NeovimのAIプラグインcodecompanion.nvimでチャットの履歴を保存する方法を解説します。
CodeCompanion全体の使い方は別の記事NeovimのAIプラグインcodecompanion.nvimの使い方に書きました。まだ使ったこと無い人はまずはこちらをご覧ください。
プラグインとしては実装されていない
codecompanion.nvimではチャットの履歴は実装されていません。実は以前は実装されていましたが廃止されました。他の機能を実装する時間が削られてしまうからだそうです。
詳しくはDiscussion#139 Save and restore chatsをご覧ください。
実装が紹介されている!
上記のDiscussionにて、コマンドの実装が紹介されていました。itsfrankさんのcodecompanion-save.luaです。
チャット履歴をMarkdownとして保存し、選択UIでそのMarkdownファイルをすぐに表示できる、というものです。
元の実装ではtelescope.nvimを選択UIとして使っていました。
筆者はこれを参考にsnacks.nvimで動くようにしてみました。更新日が新しい順にソートしてます。
チャット履歴の保存
:CodeCompanionSave foo
と実行するとfoo.md
として保存されます。
保存先は~/.local/share/nvim/cc_saves/
です。
local Path = require("plenary.path")local data_path = vim.fn.stdpath("data")local save_folder = Path:new(data_path, "cc_saves")if not save_folder:exists() then save_folder:mkdir({ parents = true })end
vim.api.nvim_create_user_command("CodeCompanionSave", function(opts) local codecompanion = require("codecompanion") local success, chat = pcall(function() return codecompanion.buf_get_chat(0) end) if not success or chat == nil then vim.notify("CodeCompanionSave should only be called from CodeCompanion chat buffers", vim.log.levels.ERROR) return end if #opts.fargs == 0 then vim.notify("CodeCompanionSave requires at least 1 arg to make a file name", vim.log.levels.ERROR) end local save_name = table.concat(opts.fargs, "-") .. ".md" local save_path = Path:new(save_folder, save_name) local lines = vim.api.nvim_buf_get_lines(0, 0, -1, false) save_path:write(table.concat(lines, "\n"), "w")end, { nargs = "*" })
「保存先を変えたい」「ファイル名を自動で付けたい」という人はハイライト箇所を変更してください。
チャット履歴の閲覧
続いて「チャット履歴ファイルを開くコマンド」の実装です。
要となるのがハイライトの部分です。
vim.api.nvim_create_user_command("CodeCompanionLoad", function() local cwd = save_folder:absolute() require("snacks.picker").files({ cwd = cwd, transform = function(item) item.cwd = cwd item.file = item.text item.mtime = Path:new(save_folder, item.text):_stat().mtime.sec item.title = item.text .. " | 更新日: " .. os.date("%Y-%m-%d %H:%M", item.mtime) end, matcher = { sort_empty = true }, sort = { fields = { "mtime:desc" } }, prompt_title = "Saved CodeCompanion Chats", win = { input = { keys = { ["dd"] = { "delete_file", mode = { "n" } }, }, }, }, actions = { delete_file = function(the_picker) the_picker.preview:reset() for _, item in ipairs(the_picker:selected({ fallback = true })) do if item.file then os.remove(item.cwd .. "/" .. item.file) end end the_picker.list:set_selected() the_picker.list:set_target() the_picker:find() end, }, })end, {})
やっていることは意外と単純です。ざっくりと次のとおりです。
- 保存ファイルを更新日でソートして読み込む
dd
で削除できるようにキーマップを設定
チャットを継続させたい場合
:CodeCompanionLoad
はあくまでも保存した履歴ファイルを開くためのコマンドです。そのままチャットとしては継続できません。
代わりに、そのファイルのバッファをスラッシュコマンド/buffer
で読み込めば、AIに共有できます。
スラッシュコマンドは別の記事NeovimのAIプラグインcodecompanion.nvimの使い方で解説しています。
遅延読み込みに設定
せっかくなのでプラグインの遅延読み込みで:CodeCompanionLoad
を指定しておきます。
次のコードはlazy.nvimでの設定例です。
---@module "lazy"---@type LazyPluginSpecreturn { "olimorris/codecompanion.nvim", cmd = { "CodeCompanion", "CodeCompanionActions", "CodeCompanionChat", "CodeCompanionCmd", "CodeCompanionLoad" }, -- ...}
nbに保存しておくといいかも
メモ管理のCLIであるnbで保存しておくものいいでしょう。
nbを知らない人はエンジニアのためのメモ管理CLIツールnbの使い方をご覧ください。
保存先をnbにするのもありです。何でもかんでも保存したいわけではないため、筆者は次のようによく見る履歴だけをnbに入れています。
cat ~/.local/share/nvim/cc_saves/create-divided-openapi-yaml.md | nb a
ファイルの命名は困るのでnb a
を使ってタイムスタンプにしています。
ファイル名がそのままでいい人はnb import
を使いましょう。
nb import ~/.local/share/nvim/cc_saves/create-divided-openapi-yaml.md
ファイル名のコピーが面倒な人は次のようなキーマップを仕込んでおきましょう。
vim.keymap.set( "n", "sgf", "<Cmd>let @+=expand('%')<CR>:echo 'Clipboard << ' . @+<CR>", { desc = "現在のファイル名をyank", silent = true })
以上、codecompanion.nvimでのチャット履歴の実装と解説でした。