[BUG] Mods: prompt box draws the built-in edit before a prompt.edit hook's answer
Preflight Checklist
- [x] I have searched existing issues and this hasn't been reported yet
- [x] This is a single bug report
- [x] I am using the latest version of Claude Code
What's Wrong?
Since 2.1.261, Ctrl+W in the prompt deletes back to the last whitespace, as in Bash, and keybindingFlavor no longer has any effect. Japanese and Chinese are written without spaces between words, so one Ctrl+W now deletes the whole sentence. Before, the default classic flavor (2.1.238) stopped between words.
Mods looked like the way to get that back, so I wrote a mod whose prompt.edit hook handles Ctrl+W itself and stops where the script changes (kanji, hiragana, katakana, Latin, punctuation). 東京タワーへ行きたい ("I want to go to Tokyo Tower") splits as 東京|タワー|へ|行|きたい, so each Ctrl+W deletes one of those.
The text it deletes is right. But on every press, the prompt box first draws the cursor where the built-in Ctrl+W would have put it, and only then the hook's answer (▏ is the cursor):
before: 東京タワーへ行きたい▏
flash: ▏東京タワーへ行きたい built-in target: no space in the draft, so the start
after: 東京タワーへ行▏ the hook's answer: きたい deleted
When the draft has a space earlier, the cursor flashes right after that space on every press:
before: 明日は Tokyo で東京タワーへ行きたい▏
flash: 明日は Tokyo ▏で東京タワーへ行きたい
after: 明日は Tokyo で東京タワーへ行▏
With a multi-line draft, the whole box jumps, because the lines rewrap around the flashed cursor.
It happens both when the hook calls next({ ...e, start, end }) with its own range and when it answers synchronously with { text, cursor }, calling neither next nor anything on $. So it is not the hook being slow; the box seems to draw the built-in edit before it asks the hook.
What Should Happen?
The box draws only the hook's answer, with no frame of the built-in edit before it. Otherwise a mod cannot replace a built-in editing key without the box flickering, which is what a mod needs to restore word boundaries for languages without spaces.
Steps to Reproduce
- Save this as a mod, with
.claude-plugin/plugin.jsonandhooks/hooks.json({ "modules": ["./register.ts"] }):
// hooks/register.ts
import type { Register } from 'claude-code'
export const register: Register = on => {
on('prompt.edit', ($, e, next) => {
if (e.key?.ctrl !== true || e.key.key !== 'w') return next(e)
// Delete one character, so the answer differs from the built-in Ctrl+W.
const start = Math.max(0, e.cursor - 1)
return { text: e.text.slice(0, start) + e.text.slice(e.cursor), cursor: start }
})
}
- Start
claude --plugin-dir <mod dir>. - Type a few lines of text with spaces in them, and press Ctrl+W a few times at the end.
The cursor flashes at the start of the last word before it lands one character back.
Is this a regression?
I don't know
Claude Code Version
Operating System
macOS 27.0.1
Terminal/Shell
Ghostty, inside herdr (a terminal multiplexer), zsh
Additional Information
There seems to be no event before prompt.edit that a mod could take the key at. Binding Ctrl+W in keybindings.json to a mod command would keep it from the editor, but $.prompt.fill cannot place the cursor except at the end, so it cannot replace a kill in the middle of the draft or a word move.