引数なしの /rename で、会話内容から日本語のセッション名を生成する。

Claude Code の /rename を引数なしで実行したときに、会話の内容から日本語のセッション名を付ける mod です。
組み込みの /rename は、英小文字の kebab-case(例: fix-login-bug)で名前を生成します。この mod は、その名前を日本語に置き換えます。
| 入力 | mod なし | mod あり |
|---|---|---|
/rename | typescript-remove-duplicates | 配列の重複除去方法 |
/rename 好きな名前 | 好きな名前 | 好きな名前 |
名前を指定した /rename の動作は変わりません。
この mod は、Claude Code の function hooks を使います。function hooks は early access の機能で、既定では無効です。
~/.claude/settings.json の env に次の 1 行を追加して、Claude Code を再起動してください。
{
"env": {
"CLAUDE_CODE_ENABLE_FUNCTION_HOOKS": "1"
}
}
1 回だけ試す場合は、環境変数を付けて起動します。
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude
動作を確認した Claude Code のバージョンは 2.1.286 です。これより古いバージョンは、mod が使う API の形が異なるため、動かない可能性があります。
claude plugin marketplace add tomatoaiu/rename-ja
claude plugin install rename-ja@tomatoaiu-mods
インストール後に Claude Code を再起動すると、mod が読み込まれます。 配布元の stable ブランチは、GitHub Release として公開したコミットだけを指します。 開発中の main は、通常のインストールには使いません。
/rename を引数なしで実行します。画面には /rename <生成された名前> と表示されます。mod が生成した名前を引数に入れてから、組み込みの /rename に渡しているためです。
名前を生成するときに Haiku へ渡す指示文(プロンプト)を変更できます。設定しない場合、mod は 20 文字以内の日本語名を生成する既定のプロンプトを使います。
| 項目 | 型 | 内容 |
|---|---|---|
prompt | 1 行の文字列 | Haiku へ渡す指示文。会話の抜粋は、ユーザーメッセージとして別に渡されます。 |
設定方法は 3 通りあります。どの方法でも、設定後に Claude Code を再起動すると反映されます。
Claude Code の中で設定する場合は、次のコマンドを実行します。
/plugin configure rename-ja@tomatoaiu-mods
シェルから設定する場合は、JSON を標準入力で渡します。
echo '{"prompt":"ユーザーメッセージは会話の抜粋です。その主題を表すセッション名を1つだけ出力してください。必ず絵文字1つで始め、続けて10文字以内の日本語を書く。説明文なし。"}' \
| claude plugin configure rename-ja@tomatoaiu-mods --values-stdin
~/.claude/settings.json に直接書く場合は、pluginConfigs に追加します。
{
"pluginConfigs": {
"rename-ja@tomatoaiu-mods": {
"options": {
"prompt": "Output exactly one English session name in Title Case, at most 4 words, no punctuation, no explanation. The user message is a conversation excerpt."
}
}
}
}
プロンプトの内容にかかわらず、mod は Haiku の返答の 1 行目だけを採用し、前後の引用符を取り除き、40 文字で切ります。
mod は command.run イベントを rename コマンドに限定して hook します。
/rename が英語名を付けます。/rename は組み込みの英語名に戻ります。/rename は組み込みの英語名に戻ります。/rename を引数なしで 1 回実行するたびに、Haiku の呼び出しが 1 回発生します。マーケットプレイスとプラグインを更新した後、Claude Code を再起動してください。 以前の配布元(main の相対パス)からインストールした場合も、同じコマンドで公開版の配布元へ切り替わります。
claude plugin marketplace update tomatoaiu-mods
claude plugin update rename-ja@tomatoaiu-mods
アンインストールする場合は、次のコマンドを実行します。
claude plugin uninstall rename-ja@tomatoaiu-mods
Node.js 24 以降で、バージョン情報の整合性と hook のテストを実行できます。 依存パッケージのインストールは不要です。
npm run check
Claude Code がある環境では、マニフェストも検証できます。 この検証と上記のテストだけでは、early access の function hooks が実際に読み込まれることまでは確認できません。
claude plugin validate .claude-plugin/plugin.json --strict
claude plugin validate .claude-plugin/marketplace.json --strict
CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1 claude --plugin-dir .
バージョンは SemVer(0.1.0 など)で管理します。 Release Please が Conventional Commits から次のバージョンと変更履歴を含む PR を作成します。 所有者がその PR をマージすると、Release workflow が検証、ZIP の作成、署名付き provenance の生成を実行し、ZIP とチェックサムを Immutable Release として公開します。 workflow は公開版の署名を検証した後、stable を更新します。 初回公開と GitHub の設定は、リリース手順を参照してください。 変更履歴は CHANGELOG.md に記録します。
hooks/register.ts 42 lines1import type { EngineInterface, On, PluginOptions } from 'claude-code'
2
3const HEAD_CHARS = 2000
4const TAIL_CHARS = 4000
5const MAX_NAME_CHARS = 40
6
7function cleaned(raw: string): string {
8 const firstLine = raw.split('\n').find(line => line.trim() !== '') ?? ''
9 return firstLine.replace(/^["'「『\s]+|["'」』\s。]+$/g, '').slice(0, MAX_NAME_CHARS)
10}
11
12function excerpt(transcript: string): string {
13 if (transcript.length <= HEAD_CHARS + TAIL_CHARS) return transcript
14 return `${transcript.slice(0, HEAD_CHARS)}\n…\n${transcript.slice(-TAIL_CHARS)}`
15}
16
17async function generated($: EngineInterface, system: string): Promise<string> {
18 const transcript = (await $.session.messages())
19 .filter(message => message.text !== '')
20 .map(message => `${message.role}: ${message.text}`)
21 .join('\n')
22 if (transcript === '') return ''
23
24 const reply = await $.model.complete({
25 model: 'haiku',
26 system,
27 prompt: excerpt(transcript),
28 maxTokens: 64,
29 })
30 return reply.isAnswered ? cleaned(reply.text) : ''
31}
32
33export function register(on: On, options: PluginOptions): void {
34 const system = String(options.prompt)
35
36 on('command.run', { command: 'rename' }, async ($, e, next) => {
37 if (e.args.trim() !== '') return next(e)
38 const name = await generated($, system)
39 return next(name === '' ? e : { ...e, args: name })
40 })
41}
42