Restores the dim prompt suggestion after running a slash command like /copy

A Claude Code mod that restores the dim prompt suggestion after you run a slash command like /copy.
Normally, running a local slash command clears the suggested next prompt that Claude Code shows in the input box. This mod remembers the last suggestion that was shown and re-proposes it once the command finishes. A new model turn discards it, since that turn produces a fresh suggestion of its own. So do /clear, /resume and /rewind, which move to a conversation the old suggestion no longer fits.
From inside Claude Code:
/plugin marketplace add mmorearty/sticky-suggestion
/plugin install sticky-suggestion@sticky-suggestion
Or clone the repo and point Claude Code at it:
git clone https://github.com/mmorearty/sticky-suggestion.git
claude --plugin-dir ./sticky-suggestion
claude plugin validate .
claude plugin test .
MIT
hooks/register.ts 53 lines1import type { Register } from 'claude-code'
2
3// The engine's last suggestion that actually showed, until a new turn makes it stale.
4let pending: string | undefined
5
6// Commands that move the conversation somewhere the old suggestion no longer
7// fits. /clear and /resume need no entry: they end the session (below).
8const staleAfter = new Set(['rewind'])
9
10export const register: Register = on => {
11 on('prompt.suggest', async ($, e, next) => {
12 const shown = await next(e)
13 if (e.origin.kind === 'suggestion' && shown.isShown) pending = e.text
14 return shown
15 })
16
17 // A model turn (a plain prompt, or a slash command that prompts the model)
18 // will produce a fresh suggestion of its own, so the old one is done.
19 on('turn.start', ($, e, next) => {
20 pending = undefined
21 return next(e)
22 })
23
24 // /clear, /resume and /logout end the session; its suggestion goes with it.
25 on('session.end', ($, e, next) => {
26 pending = undefined
27 return next(e)
28 })
29
30 on('command.run', async ($, e, next) => {
31 if (staleAfter.has(e.command)) pending = undefined
32 const text = pending
33 const ran = await next(e)
34 if (text === undefined || pending !== text) return ran
35
36 // Core won't show a suggestion while the command is still running, and it
37 // counts as running until this hook returns. So re-propose from a timer,
38 // retrying a few times; suggest() itself waits out a dialog (/copy's picker).
39 const delays = [0, 50, 200, 500, 1000]
40 const attempt = (i: number) => {
41 const delay = delays[i]
42 if (delay === undefined) return
43 $.clock.after(delay, async () => {
44 if (pending !== text) return
45 const { isShown } = await $.prompt.suggest({ text })
46 if (!isShown) attempt(i + 1)
47 })
48 }
49 attempt(0)
50 return ran
51 })
52}
53