npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tylerho/pi-prompt-stash

v0.1.0

Published

Saves and restores the editor's draft text in a single slot via ctrl+s, Claude Code style.

Readme

@tylerho/pi-prompt-stash

Saves and restores the editor's draft text in a single slot via ctrl+s, Claude Code style.

Install

pi install npm:@tylerho/pi-prompt-stash


Prompt Stash

Claude Code-style prompt stash: ctrl+s (or /stash) saves the editor's current draft to a single in-memory slot and clears the editor; pressing it again with an empty editor restores the draft verbatim. A footer status (prompt-stash) shows a one-line preview while a stash exists. Single file (index.ts, 84 lines) plus its test.

Key concepts

  • Single slot, overwrite semantics — exactly Claude Code's chat:stash. Editor has text → stash it raw and untrimmed (leading/trailing whitespace and newlines preserved), clear the editor. Editor empty or whitespace-only + stash exists → restore it and clear the slot. Editor empty + no stash → no-op. A second stash overwrites the first: there is no stack.
  • Module-level state, not session state. stashed is a module-level string | undefined, deliberately outside any session: the stash survives session rebinds (/new, /resume, /fork) within the process, mirroring CC's app-level React state. A full /reload re-imports the module and drops the stash — also like CC losing it on process restart.
  • Key-repeat suppression. A held ctrl+s autorepeats (kitty CSI-u repeat events, or legacy raw bytes) and pi filters key releases but not repeats, so without a guard the shortcut would cycle stash → restore → stash while the key is held. toggleStash ignores toggles closer than REPEAT_WINDOW_MS = 300 ms apart, and always updates lastToggleAt — even on a suppressed toggle — so a long hold stays suppressed until the key is released.
  • Pure decision logic. The handler is a thin wrapper over three exported pure functions (decideStashAction, shouldToggle, stashPreview) so all semantics are unit-testable without a UI.
  • Text only. pi's editor API exposes no pasted images; the stash holds exactly what getEditorText() returns.
  • Feedback asymmetry. Stash sets status prompt-stashstashed: <preview> (clearing the editor is its own visible feedback, so no notification). Restore clears the status and fires ctx.ui.notify("Draft restored", "info").

API

No tools (registerTool), no events (pi.on), no config files, no settings references. Two registrations plus three exported pure functions.

Shortcut: ctrl+s

pi.registerShortcut("ctrl+s", { description: "Stash the current prompt draft, or restore the stashed draft", handler: toggleStash }). Handler signature (ctx: ExtensionContext) => Promise<void> | void (pi's declared type; toggleStash is async); fires from the default editor's key handling (editor-scoped), and ctrl+s is not in pi's reserved-for-extensions conflict list.

Command: /stash

pi.registerCommand("stash", { description: "Stash the current prompt draft, or restore the stashed draft (same as ctrl+s)", handler: async (_args, ctx) => toggleStash(ctx) }). Args ignored; identical behavior to the shortcut. Useful when ctrl+s is captured by a picker-scoped builtin.

UI contract used (via ctx.ui)

  • getEditorText(): string — read the current draft.
  • setEditorText(text) — clear on stash, fill on restore.
  • setStatus("prompt-stash", string | undefined) — footer status line while a stash exists (any footer-rendering extension, e.g. the CC-style footer, shows it on the status line).
  • notify("Draft restored", "info") — restore feedback.
  • Guard: if (!ctx.hasUI) return; — headless sessions never toggle.

Exported pure functions

| Export | Signature | Behavior | |---|---|---| | shouldToggle | (now: number, lastToggleAtMs: number) => boolean | now - lastToggleAtMs >= REPEAT_WINDOW_MS (300). | | decideStashAction | (editorText: string, stashedText: string \| undefined) => { action: "stash", text: string } \| { action: "restore", text: string } \| { action: "noop" } | Trim-based decision; stash text is the raw untrimmed editor text. | | stashPreview | (text: string, max = 40) => string | Squashes whitespace runs to single spaces, truncates to max chars ending with . |

Default export: (pi: ExtensionAPI) => void — registers the shortcut and command. Importing ./index.ts (as the test does) is side-effect-free: the default export is defined but never invoked at import time.

Examples

  1. Stash a half-written prompt. Mid-draft, the user wants to run something else: ctrl+s saves the draft (raw, untrimmed), clears the editor, and the footer shows prompt-stash: stashed: <first ~40 chars, whitespace-squashed>…. Later, with an empty editor, ctrl+s restores it byte-for-byte and shows a "Draft restored" notification.
  2. /stash as the explicit form. Same behavior, no key binding — for discoverability, or as the fallback when ctrl+s is claimed by a picker-scoped builtin while a selector is open.
  3. Overwrite, not stack. Draft A is stashed; the user types draft B and hits ctrl+s again — A is gone, B occupies the single slot. Restore yields B.
  4. No-op cases. Empty (or whitespace-only) editor with no stash: ctrl+s does nothing — no status change, no notification. Whitespace-only editor text with a stash counts as "empty" and restores.