SLOPSHOPPER

sticky-suggestion

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

newprompttimer
v0.1.1MITupdated 2026-10-03mmorearty/sticky-suggestion
A shopper browsing a rack in a slop shop
README

sticky-suggestion

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.

Install

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

Development

claude plugin validate .
claude plugin test .

License

MIT

Source 1 files
hooks/register.ts 53 lines
1import 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