Skip to content

Prompt History and Draft Recovery

Prompts you launch with sase run or from sase's TUI are recorded in prompt history when they are useful to replay. Normal launch writes skip prompts shorter than five words (terse scraps like y, ok, or fix the bug are not worth replaying), while recovery paths — and a few launch surfaces that opt in with allow_short — can still preserve a short submitted prompt when a launch fails. Current installs write monthly JSON shards under ~/.sase/prompt_history/YYMM.json, with unparseable last-used timestamps grouped into unknown.json. If an old ~/.sase/prompt_history.json file is found before a shard directory exists, it is migrated on first read or write and kept as a legacy-imported-<timestamp>.json.bak backup inside the shard directory. The sase prompt command group is the first-class way to inspect, search, reuse, curate, and clean up that history. It is built to feel like a well-made shell-history tool: fast to scan, exact when printing text, safe around destructive actions, and scriptable through stable JSON.

Unsent drafts saved from the TUI live in Stash, separately from submitted prompt history. Discarding a Stash draft normally moves it to Trash. A copy is kept in the stash archive when a draft permanently leaves Stash or Trash, and when a Stash row is updated in place. Those copies have their own commands. Archived drafts do not appear in sase prompt list or sase prompt search unless the same text was also submitted as a prompt.

sase prompt reads and writes those JSON shards directly - there is no separate database to manage. Each shard row stores text, timestamp, last_used, cancelled, and an optional origin: typed for prompts a human submitted, generated for machine-driven launches. Prompt history is a write policy, not a label: only human submissions are recorded, once per submission, through a human entry point - the TUI prompt bar (including cancelled drafts and failed submits), sase run or sase prompt run from a terminal, the mobile gateway, or Telegram. A machine-originated launch (agents and LaunchApproval, routine/job launches, bead work, plan/epic approval, restarts, monitor commands, gate commands, member-agent relaunches, mentor-apply launches, and sase run invoked inside a tool run) writes no history row, no <placeholder> tags, and no Stash entry: a generated reuse of already-typed text does not even bump that row's last_used. For the tool-run case, sase run treats any nonempty SASE_TOOL_RUN_ID value as automation; an unset or empty value does not suppress history on its own.

Rows written before origins existed carry no origin, and legacy generated rows remain until they are pruned. When the same text is recorded twice, the merge keeps the newest timestamps but never downgrades: typed beats generated beats no origin, just like a successful launch is never downgraded to cancelled. The next-word prediction corpus excludes generated rows. The recorded text is the canonical submitted text: the prompt after project-alias and project-tag canonicalization, but before any swarm, repeat, alt, admission, provider-guard, or force-reuse rewriting. One submission records one row, no matter how many agents it fans out to — a swarm records its #research_swarm(…) invocation rather than its members, %r:N records the parent rather than its %id:<base>.k slots, and a force-reused launch records the pre-rewrite prompt. A user-authored --- multi-prompt still records the whole text plus each long-enough segment. Readers aggregate and deduplicate records across shards, so reusing the same prompt in a later month still shows one newest entry even if older shard copies remain on disk. New launch recordings only touch the current-month shard. Maintenance commands such as delete and prune remove every stored copy of the selected exact prompt text. Replay commands (run, edit, select) route through the same launch machinery as sase run, so multi-prompt, multi-model, and macro behavior stay identical.

Launch Authorship

New agent records store prompt_origin (typed, generated, or unknown) and prompt_source_surface independently of whether the launch qualifies for prompt history. The submitting surface records those values. A launch that never received a classifying stamp is recorded as generated, unless the process already carries a valid origin, which is kept. A blank or unrecognized prompt_source_surface is stored as unknown. A human's short launch can therefore have authorship provenance even when the five-word history rule skips it.

For memory decisions in a plan, SASE uses the original submitted_prompt.md from a human-typed root launch as evidence. It also checks human-authored plan feedback and free-text question answers in the planner's chain. Gate responses record whether the caller was a human or an agent. Automatically resolved responses, generated launches, macro-expanded instructions, model output, and selected question-option labels do not count as a human request. Older records without provenance remain readable but cannot establish that authorization.

Selectors

Prompts are addressed by a stable content ID derived from the exact prompt text, not by a recency index that would change every time you launch something new:

  • ph_<sha256[:12]> — the canonical ID printed by sase prompt list.
  • A bare hash prefix (for example 8f3a9c0d) — any unambiguous leading slice of the SHA-256 digest.
  • sha256:<full_hash> — the fully qualified digest when you need to be unambiguous.

If a short prefix matches more than one prompt, the command prints the colliding IDs and asks for a longer selector. Adding newer prompts never changes an existing prompt's ID.

Command Reference

The generated prompt CLI reference lists every subcommand and option.

list, show, search, stats, and doctor are read-only. list, search (default compact format), stats, and doctor never print full prompt text — they show previews only — so they stay bounded even on a history with thousands of entries. show, export, copy, and search -f json|full are the intentional full-text escape hatches.

Run sase prompt <command> --help for the full flag list of any subcommand.

Recover a Stashed Draft

Stash, Trash, and the stash archive are three different stores.

  • Stash holds unsent drafts. Ctrl+S saves the active non-empty pane. An empty Ctrl+S opens Stash instead of saving. gs saves every non-empty pane, in order, as one Stash row. When the panes are empty and frontmatter is present, gs saves that frontmatter.
  • Trash holds drafts you discarded from Stash. Enter there puts a row back in Stash and does not launch it. Moving a row into Trash does not archive it. With ace.prompt_stash.trash_limit set to 0, a discard never enters Trash and is archived immediately.
  • The stash archive is ~/.sase/prompt_stash_archive.jsonl, next to ~/.sase/prompt_stash.jsonl. It keeps a full copy when a draft permanently leaves Stash or Trash, and when a Stash row's text, frontmatter, or cursor is replaced. It is not the canonical agents archive that sase prompt search reads.

list -r names why the copy was written:

Reason When the copy is written
popped An unpinned Stash row was loaded into the prompt bar and removed from Stash. Loading a pinned row leaves it in Stash, so that load is not archived.
purged A Trash row was permanently deleted.
evicted Trash dropped the row to stay within its limit. A discard while the limit is 0 is archived as evicted and never appears in Trash.
overwritten A Stash row's text, frontmatter, or cursor was replaced. gS does this when it updates a pinned draft. The row stays in Stash; the archive keeps the previous version. Changing only the pin does not archive.
sase prompt stash-archive                        # newest 20 archived drafts
sase prompt stash-archive list -q parser         # draft text and frontmatter only
sase prompt stash-archive list -r popped -n 50   # one reason, up to 50 rows
sase prompt stash-archive list -j                # JSON, including each row's full text
sase prompt stash-archive show <ID>              # newest copy: full text and metadata
sase prompt stash-archive restore <ID>           # append the newest copy back to Stash

Bare sase prompt stash-archive is list. show prints one draft. restore appends one or more drafts back onto Stash. It does not launch an agent and it does not open the prompt bar. Load the returned row from Stash with @ or the Prompts overlay. @ loads a row immediately only when it is the sole unpinned draft; otherwise @ opens the overlay.

show and restore take a full draft id or a prefix that matches exactly one archived id. The list's ID column prints the first eight characters. If the id is already in Stash, restore skips it with already in Stash. If it is in Trash, restore skips it with in Trash: restore it from the Trash view instead. When the same id was archived more than once, show and restore use the newest copy. Older copies remain in list -j. -n changes how many rows are printed. -q and -r search the newest 100,000 archive rows, then print up to that limit. -q does not match the id, project, or reason. See Stash and Trash in the TUI for the keys and Trash limits for retention settings.

sase prompt list -q filters the local history only. sase prompt search is the broader tool: it searches two stores at once and ranks canonical archived prompts first, so it answers "I remember a prompt about X — find it, whether it was archived from an agent run or just ran once last month."

  • Canonical agents archive — prompts published by agent-backed commits and approved planner runs in the agents sidecar under prompts/<YYYYMM>/. Search resolves the current project's archive the same way sase agent prompts does, reads each entry's primary body, and ranks these curated records first. For a commit publication that body is the pre-expansion Macro; for an approved planner publication it is the dry-expanded plan snapshot.
  • Local prompt history — the machine-wide ~/.sase/prompt_history/ shard store: every prompt ever submitted on this machine, across all repos.

Use sase agent prompts list and sase agent prompts validate to inspect or validate the canonical archive directly. sase agent prompts show <prompt> prints the archived Markdown document.

Matching is a case-insensitive substring test of the literal query (no regex or globbing) against these searchable fields: title, body, locator/ID, archive path, plan label, and tags. Each hit can therefore report why it matched. Results are ranked deterministically: archive before local, a title/locator/path hit before a body-only hit, newer before older, with a stable tiebreak so output is byte-identical across runs. search is read-only: it never writes or locks either store, so it is safe to run against a corrupt or unreadable history.

sase prompt search auth                  # both stores, compact, most-relevant first
sase prompt search auth -s archive       # only canonical archived prompts
sase prompt search auth -s sdd           # deprecated alias for -s archive
sase prompt search auth -s local -x      # only cancelled local prompts
sase prompt search auth -t review        # only prompts tagged "review" (repeatable; ORs)

Output is bounded by default

The local history can hold tens of thousands of entries, so search shows the 20 best matches by default and never silently truncates — every format reports the full match count, and -n 0 returns an unlimited result set.

-f compact (default) groups hits by source under dim ── Archived prompts (N) ── / ── Local history (N) ── headers, prints one entry per hit with the matched term highlighted plus a one-line snippet (or a plan: "…" / tag: "…" line when the match was off-body), and ends with a footer such as 27 matches (3 archive · 24 local) · showing 20.

-f json emits a stable, never-colored envelope — query, count, total, per-source counts, and a results array carrying the full prompt text — so editors and scripts can build on it. count versus total makes truncation explicit:

sase prompt search auth -f json | jq '.total, (.results | length)'

-f full prints each hit completely, divider-separated: a local hit reuses the exact sase prompt show -f markdown rendering, and an archived hit shows its available path, plan, artifact-count, tag, and local-history metadata plus the primary archive body with the match highlighted.

Filtering by date, tag, source, and status

  • -a|--after / -b|--before keep prompts within an inclusive date window. Both accept 2026-06-01, 202606 (YYYYMM), 260601 (YYmmdd), a full 260601_143000 SASE timestamp, or a relative offset 30d / 2w / 6m / 1y; an unparseable date is a usage error. Archived prompts are dated by archive frontmatter first, falling back to the YYYYMM path segment, then the file mtime; local prompts use their last-used time.
  • -t|--tag keeps prompts carrying a matching tag — archived prompt_tags frontmatter plus the embedded #macro chips parsed from the prompt body. Low-signal runner-control % directives (%model, %id, %wait, …) are execution mechanics, not content tags, so they are deliberately excluded. Repeats OR together (-t review -t auth matches either).
  • -s|--source scopes to archive, local, or all (default). The deprecated sdd spelling remains as an alias for archive for one compatibility window.
  • -x|--cancelled restricts local results to cancelled prompts; it has no effect on archived prompts, which have no cancelled state.
  • -c|--color is auto (colorize on a TTY unless NO_COLOR is set), always, or never; JSON is never colored.

An empty or whitespace-only query is a usage error (exit 2). A query that matches nothing is not an error (exit 0): compact/full print No prompts match "<query>". and json returns an envelope with count: 0. When -s all finds the same authored prompt in both stores after normalizing whitespace and canonical-archive macro links, the local copy collapses into the archive hit, annotated also in local history.

Common Workflows

Find a recent prompt

For the TUI workflow, see Find and replay an old prompt.

list defaults to the 20 most recent non-cancelled prompts, newest first. Narrow it with a case-insensitive substring filter:

sase prompt list
sase prompt list -q auth          # only prompts whose text contains "auth"
sase prompt list -l 50            # widen the window to 50 entries
sase prompt list -j               # stable JSON for scripts and editor integrations

The table shows the prompt ID, last-used time, status, character count, project/macro/directive hint chips, and a one-line preview — never the full text.

Inside sase's TUI, the same history is one keystroke away: press Ctrl+K in the prompt bar (or ,. from any tab) to open the Prompts overlay. Its filter accepts a leading project:<name> qualifier, and Ctrl+K pre-scopes it to the project named by the current prompt's workspace reference.

show -f raw writes the exact bytes of the prompt with no added or stripped newline, so it is safe in command substitution or a pipe:

sase prompt show ph_8f3a9c0d12ab -f raw
sase prompt show ph_8f3a9c0d12ab -f raw | wc -c
sase prompt show ph_8f3a9c0d12ab -f markdown   # metadata header + body
sase prompt show ph_8f3a9c0d12ab -f json       # metadata + full text

Replay a prompt

For the find-and-replay workflow, see Find and replay an old prompt. edit and run -e abort without launching when the saved buffer is empty.

Replay under another VCS prefix

--prefix reuses the existing VCS-tag replacement logic, so a prompt captured under one workspace can be replayed under another without retyping it. Prefix replacement happens before the editor opens, so what you see is what will run:

sase prompt run ph_8f3a9c0d12ab -P "#gh:bob-cli"     # reuse a "#gh:sase" prompt elsewhere
sase prompt edit ph_8f3a9c0d12ab -P "#gh:bob-cli"    # adjust details after re-prefixing
sase prompt run ph_8f3a9c0d12ab -P "+bob-cli"        # a project tag works as the prefix

--prefix replaces # workspace refs that start a line (after optional %directive tokens). When it finds none, it prepends the new prefix instead. Launched prompts are stored with any project tags already expanded to their #<workflow>:<directory-key> form, so --prefix finds and replaces them. A cancelled prompt, or one rejected by tag validation, keeps the +<project> tags you typed. --prefix does not replace those tags; it prepends the new prefix, so the replay fails with Only one workspace target is allowed per launch unit. For those prompts, run sase prompt edit <id> without -P and change the tag by hand. The prompt-history picker in sase's TUI expands tags before it swaps the prefix, so it does not have this limitation.

This is the shared, drift-free implementation behind the sase run "#vcs:ref ." compatibility path.

Pick interactively with fzf

sase prompt select                 # fzf over launched prompts, newest first
sase prompt select -q auth -e      # filter to "auth", edit the choice, then launch
sase prompt select -a              # include cancelled prompts as candidates

If fzf is not installed, select points you at sase prompt list and sase prompt run instead of failing silently.

Recover a cancelled prompt

Prompts you typed but did not launch (or whose launch failed) are stored as cancelled so you can recover them. They are hidden by default:

sase prompt list -c                # only cancelled prompts
sase prompt list -a                # launched and cancelled together
sase prompt run ph_8f3a9c0d12ab    # relaunch a recovered prompt by ID

Delete a secret

If a prompt captured something sensitive, remove exactly that one prompt. delete confirms on a TTY unless you pass --yes; in a non-interactive shell it refuses to act without --yes:

sase prompt delete ph_8f3a9c0d12ab        # confirms before removing
sase prompt delete ph_8f3a9c0d12ab -y     # skip the prompt (e.g. in scripts)

Because IDs are content-addressed, deleting a prompt removes every stored copy of that exact text.

Prune cancelled or old prompts

prune cleans up by objective criteria. Predicates intersect, and --keep is a hard floor that always preserves the newest N entries. Always preview with --dry-run first:

sase prompt prune -c --dry-run            # show which cancelled prompts would go
sase prompt prune -c -y                   # remove all cancelled prompts
sase prompt prune -k 500 -y               # keep only the 500 newest prompts
sase prompt prune -b 2026-01-01 --dry-run # preview removing entries older than a date

--before accepts YYYY-MM-DD, YYmmdd, or a full YYmmdd_HHMMSS SASE timestamp. A dry run never mutates the store, and neither delete nor prune will rewrite a corrupt or unreadable store.

One-time cleanup of machine rows

Rows written before origins existed carry no origin, and legacy generated rows remain until they are pruned. --generated removes rows whose merged origin is generated (a typed copy anywhere protects every duplicate, because removal works by exact text). --legacy additionally removes origin-less rows the generated-text heuristic flags (routine tribe bindings, bead-work segments, deferred launches, and single-turn agent headers), and requires --generated. Both intersect with --before, --cancelled, and the --keep floor. Every apply first writes a timestamped .json.bak backup of the shard files (which never matches the *.json shard glob) and prints the backup path:

sase prompt prune --generated --legacy --dry-run  # preview counts per tier + samples
sase prompt prune --generated --legacy --yes      # back up, then remove

Save a prompt as a reusable macro

For the TUI workflow, see Reuse a prompt with macros.

sase prompt save ph_8f3a9c0d12ab -n fix-auth-review -t review
sase run "#fix-auth-review"               # the existing loader resolves it

sase prompt save ph_8f3a9c0d12ab -g       # write to ~/sase/macros/ instead of project sase/macros/
sase prompt save ph_8f3a9c0d12ab -p bob   # namespace under ~/sase/macros/bob/

With no --name, save derives a deterministic slug from the prompt preview. It never overwrites an existing macro file unless you pass --force.

SASE's TUI prompt bar also saves drafts with gX or Ctrl+G X; the save panel starts in macro mode, and Ctrl+X switches to snippet mode. A stack saves its non-empty panes as one ----separated macro; snippet mode saves only the active pane. See Prompt Stacks and Creating Macros for the other keybindings and save locations.

Export a prompt to a file

export is a local-history escape hatch. It prints a stored prompt byte-exactly, wraps it in metadata frontmatter, or writes either form to an arbitrary path. It does not create canonical archive entries: archived prompts come from agent runs with captured provenance in the agents sidecar. The old --sdd flag remains parseable only so it can fail with an actionable message naming --out and sase agent prompts.

sase prompt export ph_8f3a9c0d12ab -o prompt.md    # write to an arbitrary path
sase prompt export ph_8f3a9c0d12ab -m              # stdout, wrapped in frontmatter
sase prompt export ph_8f3a9c0d12ab                 # stdout, byte-exact (like show -f raw)
sase prompt export ph_8f3a9c0d12ab -s              # exits with retired-flag guidance

Existing destination files are never silently overwritten — pass --force to replace one.

Health and Scripting

doctor is a read-only diagnostic that never echoes full prompt text. It reports the shard directory path, aggregate store size, shard count, and parseability; entry and cancelled counts; origin counts (typed / generated / missing) plus how many origin-less rows the generated-text heuristic flags; invalid or duplicate entries; oversized prompts; prompts shorter than the five-word recording minimum that were saved through recovery paths; and whether fzf and a clipboard command are available:

sase prompt doctor          # pretty report
sase prompt doctor -j       # stable JSON for editor integrations
sase prompt stats           # length percentiles, largest prompts, top chips
sase prompt stats -j

The -j JSON output of list, stats, and doctor uses a stable schema and degrades cleanly under NO_COLOR or when piped to a non-TTY, making it safe to build tooling on top of.

sase chat

sase chat discovers and inspects saved agent chat transcripts. With no subcommand, it defaults to sase chat list. Subcommands:

Moved to sase chat.

Moved to Prompt History and Draft Recovery.

Command behavior

_[project]: A sase project is a named unit of work registered with SASE. _[workspace]: A sase workspace is a numbered clone of a project's primary repo, managed by the workspace store and tracked in that project's registry.json. _[agent]: A sase agent is an agent session or a single agent that does not belong to a session. _[prompt]: A prompt is the text SASE hands to a provider to start an agent turn. _[macro]: A macro is a reusable prompt triggered with #name in agent prompts. _[Patch]: A Patch is SASE's local unit of change. _[bead]: A bead is one tracked work item in SASE's git-native issue tracker. _[plan]: A plan is a persisted, approved artifact recording the intent behind agent work. _[TUI]: The TUI is SASE's terminal interface for running agents and reviewing results. _[epic]: An epic is an executable multi-phase plan run with sase bead work. _[gate]: A sase gate is a durable, command-backed request for one user decision, created when work must pause for the user. _[monitor]: A sase monitor is a monitor turn that runs one long command under a detached supervisor, so the command outlives the agent that started it. *[stitch]: A stitch is the lightweight ordered change record inside a Patch's STITCHES: section.