feat(prompts,tui): cache awareness in agent prompt + slash prefix Enter (#573)

Two related polish items wrapped together because both touch how the
user perceives the model's context behavior.

### Cache awareness in the agent prompt

The system prompt's Context Management section already lives inside
the volatile-content-last invariant — but the model never knew *why*
the prompt is shaped that way, or that it has any agency over keeping
the cache hit rate up.

Added a `### Prompt-cache awareness` subsection (Agent / Yolo modes)
with five concrete dos-and-don'ts:
- Append, don't reorder.
- Don't paraphrase quoted content (refer back by path).
- Use `/compact` as a hard reset, not a tweak.
- Read once, refer back instead of re-reading.
- Watch the `cache hit %` chip — red < 40%, yellow < 80%.

The chip itself already exists in the default footer status set
(`StatusItem::Cache`); the prompt addition closes the loop so the model
treats it as a real signal instead of a passive readout.

### #573 — typing `/mo` + Enter activates the first matching command

Previously a partial slash command + Enter sent the literal `/mo` as a
turn. The popup was already showing `/model` highlighted, so the user
expectation (and the OPENCODE behavior the issue cites) is that Enter
runs the highlight. The fix routes Enter through
`apply_slash_menu_selection` first when the popup is open and the input
starts with `/`. If the popup is empty (no matches) the legacy submit
path still fires — Enter on a non-slash line is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hunter Bown
2026-05-04 04:22:38 -05:00
parent 351ca4f3e6
commit 874e8b4b78
2 changed files with 21 additions and 1 deletions
+8 -1
View File
@@ -357,7 +357,14 @@ pub fn system_prompt_for_mode_with_context_skills_and_session(
1. Use `/compact` to summarize earlier context and free up space\n\
2. The system will preserve important information (files you're working on, recent messages, tool results)\n\
3. After compaction, you'll see a summary of what was discussed and can continue seamlessly\n\n\
If you notice context is getting long (>80%), proactively suggest using `/compact` to the user."
If you notice context is getting long (>80%), proactively suggest using `/compact` to the user.\n\n\
### Prompt-cache awareness\n\n\
DeepSeek caches the longest *byte-stable prefix* of every request and charges roughly 100× less for cache-hit tokens than miss tokens. The system prompt above is layered most-static-first specifically so the prefix stays stable turn-over-turn. To keep cache hits high:\n\
- **Append, don't reorder.** New context goes at the end (latest user / tool messages). Reshuffling earlier messages or rewriting their content invalidates the cache for everything after the change.\n\
- **Don't paraphrase quoted content.** If you've already read a file, refer to it by path or line range instead of re-quoting it with different formatting.\n\
- **Use `/compact` as a hard reset, not a tweak.** Compaction is meant for when the cache is already losing — it intentionally rewrites the prefix to a shorter summary. Don't trigger it for small wins.\n\
- **Read once, refer back.** Re-reading the same file produces a different tool-result envelope than the prior read; it's cheaper to scroll back than to re-fetch.\n\
- **Footer chip:** the `cache hit %` chip turns red below 40% and yellow below 80%. If it's been red for several turns, that's a signal to consolidate."
);
}
+13
View File
@@ -2381,6 +2381,19 @@ async fn run_event_loop(
}
}
KeyCode::Enter => {
// #573: when the user typed a slash-command prefix that
// the popup is matching (e.g. `/mo` → `/model`), Enter
// should run the *highlighted match* rather than
// sending the literal `/mo` text. Only kick in when the
// popup has at least one entry; otherwise fall through
// to the legacy submit path.
if slash_menu_open
&& !slash_menu_entries.is_empty()
&& app.input.starts_with('/')
&& apply_slash_menu_selection(app, &slash_menu_entries, false)
{
app.close_slash_menu();
}
if let Some(input) = app.submit_input() {
if handle_plan_choice(app, &engine_handle, &input).await? {
continue;