決めたキーを押している間だけ録音し、話した内容を入力欄に入れる

kokot が作った Claude Code 用の mod 集です。
| mod | できること |
|---|---|
| usage-band | 入力欄の上に、残りの使用量(5時間枠・週枠・コンテキスト)とリセットまでの時間を帯で表示する。7日間のトークンを何に使ったかの円グラフも出す。チャット名の色分けもする |
| chat-time | LINE のように、各メッセージの下に送った日時を表示する |
| ja-questions | Claude からの質問が英語なら、日本語に訳して表示する |
| push-to-talk | 決めたキーを押している間だけ録音して、話した内容を入力欄に入れる |
| shield | 危ない操作(フォルダごと削除・履歴の上書き・秘密ファイルの書き換えなど)は止めて確認し、Claude が書いたファイルは自動で構文チェックする。ほかの作業中のチャットも表示し、名前を押すとそのチャットへ飛べる。守るもの・守れないものは shield/SAFETY.md |
ログインはいりません。ほしい mod だけ選んで入れられます。
claude plugin marketplace add kokotosu/claude-mods
claude plugin install usage-band@kokot-claude-mods
claude plugin install chat-time@kokot-claude-mods
claude plugin install ja-questions@kokot-claude-mods
claude plugin install push-to-talk@kokot-claude-mods
claude plugin install shield@kokot-claude-mods
Claude に頼むなら「GitHub の kokotosu/claude-mods から chat-time だけ入れて」のように言えば入れてくれる。
claude plugin marketplace update kokot-claude-mods
そのあと claude plugin update <mod名>@kokot-claude-mods を打つ。
~/.claude/mods/chat-colors.json もコピーするhooks/register.ts 94 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// The key is watched by helper/ptt.ps1, which drives the app's own voice
4// input; this module starts it and shows where it stands.
5// One helper runs for every session: it writes the time to
6// %TEMP%\push-to-talk.alive every 3 seconds, and each session looks at that
7// file every 15 and starts a helper only once it goes stale. A helper that
8// finds another one running answers BUSY and exits.
9// A reload of this module restarts the helper, so an edit to ptt.ps1 alone
10// takes effect only with a real change here too: a reload follows content,
11// and an edit undone before the turn ends reloads nothing.
12// The helper writes what it saw to %TEMP%\push-to-talk-diag.txt when it
13// cannot find the button.
14const CHECK_MS = 15_000
15const STALE_MS = 10_000
16
17let isRunning = false
18
19async function isAnotherAlive($: EngineInterface, heartbeat: string) {
20 try {
21 const at = Number(await $.fs.read(heartbeat))
22 return (await $.clock.now()) - at < STALE_MS
23 } catch {
24 return false
25 }
26}
27
28// Resolves once the helper exits: true when it ran here, false on BUSY.
29async function runHelper($: EngineInterface, key: string, idle: string) {
30 const helper = $.process.spawn({
31 argv: [
32 'powershell.exe', '-NoProfile', '-ExecutionPolicy', 'Bypass',
33 '-File', `${$.plugin.root}/helper/ptt.ps1`, '-Key', key,
34 ],
35 })
36
37 let hasRun = false
38 let pending = ''
39 for await (const { stream, text } of helper) {
40 if (stream === 'stderr') {
41 $.ui.log(text, { to: 'debug' })
42 continue
43 }
44 pending += text
45 const lines = pending.split(/\r?\n/)
46 pending = lines.pop() ?? ''
47
48 for (const line of lines) {
49 if (line === 'READY') hasRun = true
50 if (line === 'REC') $.ui.status('🎙 録音中')
51 else if (line === 'READY' || line === 'DONE') $.ui.status(idle)
52 else if (line === 'NOBUTTON') $.ui.toast('録音ボタンが見つかりませんでした Claudeの入力欄を表示してから押してください')
53 else if (line.startsWith('ERR ')) {
54 $.ui.status(idle)
55 $.ui.toast(`音声入力のエラー:${line.slice(4)}`)
56 }
57 }
58 }
59 return hasRun
60}
61
62async function ensureHelper($: EngineInterface, heartbeat: string, key: string, idle: string) {
63 if (isRunning) return
64 if (await isAnotherAlive($, heartbeat)) {
65 $.ui.status(idle)
66 return
67 }
68 isRunning = true
69 try {
70 const hasRun = await runHelper($, key, idle)
71 if (hasRun) $.ui.toast('音声入力の補助プログラムが止まりました 15秒以内に起動し直します')
72 } finally {
73 isRunning = false
74 }
75}
76
77export const register: Register = (on, options) => {
78 const key = typeof options.key === 'string' ? options.key : 'RightCtrl'
79 const idle = `${key} 長押しで音声入力`
80
81 on('session.start', async ($, e, next) => {
82 const started = await next(e)
83
84 const temp = await $.env.get('TEMP')
85 if (temp === undefined) return started
86 const heartbeat = `${temp}/push-to-talk.alive`
87
88 void ensureHelper($, heartbeat, key, idle)
89 $.clock.every(CHECK_MS, () => void ensureHelper($, heartbeat, key, idle))
90
91 return started
92 })
93}
94