fix(tui): rank exact slash aliases first
This commit is contained in:
+41
-2
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [0.8.40] - 2026-05-18
|
||||
|
||||
### Added
|
||||
|
||||
- **Configurable sub-agent per-step API timeout.** A new
|
||||
`[subagents] api_timeout_secs` setting in `~/.deepseek/config.toml`
|
||||
controls how long each sub-agent step will wait on a DeepSeek
|
||||
`create_message` response before falling back. The value is clamped to
|
||||
`1..=1800`; `0` or unset preserves the legacy 120-second default, so
|
||||
existing installs see no behavior change. Long-thinking children (e.g.
|
||||
heavy plan or review work behind `agent_open`) can extend the timeout
|
||||
without recompiling (#1806, #1808).
|
||||
- **Delegated file-write permissions for write-capable sub-agent roles.**
|
||||
`implementer` and `custom` sub-agents may now run `Suggest`-level write
|
||||
tools (`write_file`, `edit_file`, `apply_patch`) without the parent
|
||||
runtime being auto-approved. Read-only stances (`explore`, `plan`,
|
||||
`review`, `verifier`) and the default `general` role still bounce
|
||||
approval-gated tools so they can't quietly mutate the workspace, and
|
||||
`Required`-level tools (shell, etc.) still need parent auto-approve
|
||||
regardless of role. Pick `implementer` (or pass an explicit `custom`
|
||||
allowlist) when the delegated task needs to land file changes
|
||||
(#1828, #1833).
|
||||
|
||||
### Fixed
|
||||
|
||||
- **WSL2 and headless Linux startup no longer blocks on clipboard init.** The
|
||||
@@ -73,6 +94,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
to persisted active turns after the long-connection client starts, and text
|
||||
chunking no longer splits emoji or other multi-code-unit characters.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Slash-command autocomplete ranks exact alias matches first.** Typing
|
||||
`/q` now surfaces `/exit` (whose alias `q` is an exact match) above
|
||||
`/clear` (which only matches by the longer pinyin alias `qingping`).
|
||||
Within each rank tier the menu still falls back to alphabetical name
|
||||
order for deterministic display (#1811).
|
||||
|
||||
### Thanks
|
||||
|
||||
Thanks to **jayzhu ([@zlh124](https://github.com/zlh124))** for the WSL2
|
||||
@@ -85,8 +114,18 @@ composer work in #1748/#1749, plus the per-process log filename follow-up in
|
||||
passthrough, reasoning replay, thinking-only turn, and Windows quoting fixes
|
||||
in #1740, #1743, #1742, and #1744. Thanks to **Nightt
|
||||
([@nightt5879](https://github.com/nightt5879))** for the Ctrl+C prompt restore
|
||||
fix in #1764. Thanks to **Bevis** and the community reports that surfaced the
|
||||
compaction failure mode addressed in this release.
|
||||
fix in #1764. Thanks to **Ling ([@LING71671](https://github.com/LING71671);
|
||||
commits as `www17 <ivonrust@gmail.com>`)** for the configurable sub-agent API
|
||||
timeout in #1808, harvested with `1..=1800` clamping and a fail-fast guard
|
||||
so a stray `api_timeout_secs = 0` keeps the legacy 120-second default.
|
||||
Thanks to **[@knqiufan](https://github.com/knqiufan)** for the sub-agent
|
||||
file-write delegation work in #1833, harvested with structured approval-
|
||||
gate semantics (`Implementer` and `Custom` only, never `Required`-level
|
||||
tools) so write-capable children can actually land code without bypassing
|
||||
the `Required` approval class. Thanks to **[@IIzzaya](https://github.com/IIzzaya)**
|
||||
for the exact-alias-first slash-completion ordering idea in #1811, landed
|
||||
with a focused regression test. Thanks to **Bevis** and the community reports
|
||||
that surfaced the compaction failure mode addressed in this release.
|
||||
|
||||
## [0.8.39] - 2026-05-17
|
||||
|
||||
|
||||
+41
-2
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
## [0.8.40] - 2026-05-18
|
||||
|
||||
### Added
|
||||
|
||||
- **Configurable sub-agent per-step API timeout.** A new
|
||||
`[subagents] api_timeout_secs` setting in `~/.deepseek/config.toml`
|
||||
controls how long each sub-agent step will wait on a DeepSeek
|
||||
`create_message` response before falling back. The value is clamped to
|
||||
`1..=1800`; `0` or unset preserves the legacy 120-second default, so
|
||||
existing installs see no behavior change. Long-thinking children (e.g.
|
||||
heavy plan or review work behind `agent_open`) can extend the timeout
|
||||
without recompiling (#1806, #1808).
|
||||
- **Delegated file-write permissions for write-capable sub-agent roles.**
|
||||
`implementer` and `custom` sub-agents may now run `Suggest`-level write
|
||||
tools (`write_file`, `edit_file`, `apply_patch`) without the parent
|
||||
runtime being auto-approved. Read-only stances (`explore`, `plan`,
|
||||
`review`, `verifier`) and the default `general` role still bounce
|
||||
approval-gated tools so they can't quietly mutate the workspace, and
|
||||
`Required`-level tools (shell, etc.) still need parent auto-approve
|
||||
regardless of role. Pick `implementer` (or pass an explicit `custom`
|
||||
allowlist) when the delegated task needs to land file changes
|
||||
(#1828, #1833).
|
||||
|
||||
### Fixed
|
||||
|
||||
- **WSL2 and headless Linux startup no longer blocks on clipboard init.** The
|
||||
@@ -73,6 +94,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
to persisted active turns after the long-connection client starts, and text
|
||||
chunking no longer splits emoji or other multi-code-unit characters.
|
||||
|
||||
### Changed
|
||||
|
||||
- **Slash-command autocomplete ranks exact alias matches first.** Typing
|
||||
`/q` now surfaces `/exit` (whose alias `q` is an exact match) above
|
||||
`/clear` (which only matches by the longer pinyin alias `qingping`).
|
||||
Within each rank tier the menu still falls back to alphabetical name
|
||||
order for deterministic display (#1811).
|
||||
|
||||
### Thanks
|
||||
|
||||
Thanks to **jayzhu ([@zlh124](https://github.com/zlh124))** for the WSL2
|
||||
@@ -85,8 +114,18 @@ composer work in #1748/#1749, plus the per-process log filename follow-up in
|
||||
passthrough, reasoning replay, thinking-only turn, and Windows quoting fixes
|
||||
in #1740, #1743, #1742, and #1744. Thanks to **Nightt
|
||||
([@nightt5879](https://github.com/nightt5879))** for the Ctrl+C prompt restore
|
||||
fix in #1764. Thanks to **Bevis** and the community reports that surfaced the
|
||||
compaction failure mode addressed in this release.
|
||||
fix in #1764. Thanks to **Ling ([@LING71671](https://github.com/LING71671);
|
||||
commits as `www17 <ivonrust@gmail.com>`)** for the configurable sub-agent API
|
||||
timeout in #1808, harvested with `1..=1800` clamping and a fail-fast guard
|
||||
so a stray `api_timeout_secs = 0` keeps the legacy 120-second default.
|
||||
Thanks to **[@knqiufan](https://github.com/knqiufan)** for the sub-agent
|
||||
file-write delegation work in #1833, harvested with structured approval-
|
||||
gate semantics (`Implementer` and `Custom` only, never `Required`-level
|
||||
tools) so write-capable children can actually land code without bypassing
|
||||
the `Required` approval class. Thanks to **[@IIzzaya](https://github.com/IIzzaya)**
|
||||
for the exact-alias-first slash-completion ordering idea in #1811, landed
|
||||
with a focused regression test. Thanks to **Bevis** and the community reports
|
||||
that surfaced the compaction failure mode addressed in this release.
|
||||
|
||||
## [0.8.39] - 2026-05-17
|
||||
|
||||
|
||||
@@ -2125,7 +2125,32 @@ pub(crate) fn slash_completion_hints(
|
||||
}
|
||||
}
|
||||
|
||||
entries.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
// Rank exact-alias matches above prefix/alias matches so e.g. typing
|
||||
// `/q` ranks `/exit` (alias `q` is an exact hit) above `/clear` (alias
|
||||
// `qingping` only matches by prefix). Inside each tier, fall back to
|
||||
// alphabetical name order for deterministic display (#1811).
|
||||
let rank = |entry: &SlashMenuEntry| -> u8 {
|
||||
if entry.is_skill {
|
||||
return 3;
|
||||
}
|
||||
let command_key = entry.name.trim_start_matches('/');
|
||||
if command_key.eq_ignore_ascii_case(&prefix_lower) {
|
||||
return 0;
|
||||
}
|
||||
if let Some(info) = commands::get_command_info(command_key)
|
||||
&& info
|
||||
.aliases
|
||||
.iter()
|
||||
.any(|a| a.eq_ignore_ascii_case(&prefix_lower))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if command_key.to_ascii_lowercase().starts_with(&prefix_lower) {
|
||||
return 1;
|
||||
}
|
||||
2
|
||||
};
|
||||
entries.sort_by(|a, b| rank(a).cmp(&rank(b)).then_with(|| a.name.cmp(&b.name)));
|
||||
entries.dedup_by(|a, b| a.name == b.name);
|
||||
entries.into_iter().take(limit).collect()
|
||||
}
|
||||
@@ -2484,6 +2509,50 @@ mod tests {
|
||||
assert!(hints.iter().any(|hint| hint.name == "/links"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slash_completion_hints_rank_exact_alias_above_prefix_alias() {
|
||||
// `/q` should rank `/exit` (exact alias `q`) above `/clear` (alias
|
||||
// `qingping` only matches by prefix). Before #1811 the entries were
|
||||
// sorted alphabetically, so `/clear` shadowed `/exit` even though
|
||||
// the user typed the exact alias for `/exit`.
|
||||
let hints = slash_completion_hints("/q", 128, &[], Locale::En, None, ApiProvider::Deepseek);
|
||||
let names: Vec<&str> = hints.iter().map(|h| h.name.as_str()).collect();
|
||||
let exit_pos = names
|
||||
.iter()
|
||||
.position(|n| *n == "/exit")
|
||||
.expect("/exit should appear when typing /q (alias `q`)");
|
||||
let clear_pos = names
|
||||
.iter()
|
||||
.position(|n| *n == "/clear")
|
||||
.expect("/clear should still appear when typing /q (alias `qingping`)");
|
||||
assert!(
|
||||
exit_pos < clear_pos,
|
||||
"expected /exit to rank above /clear for prefix /q, got {names:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slash_completion_hints_keep_prefix_match_alphabetical_within_tier() {
|
||||
// Within the same rank tier (no exact-alias match), entries fall
|
||||
// back to alphabetical name order, same as the prior behavior.
|
||||
let hints =
|
||||
slash_completion_hints("/co", 128, &[], Locale::En, None, ApiProvider::Deepseek);
|
||||
let names: Vec<&str> = hints
|
||||
.iter()
|
||||
.map(|h| h.name.as_str())
|
||||
.filter(|n| n.starts_with("/co"))
|
||||
.collect();
|
||||
let sorted = {
|
||||
let mut copy = names.clone();
|
||||
copy.sort();
|
||||
copy
|
||||
};
|
||||
assert_eq!(
|
||||
names, sorted,
|
||||
"tied entries (no exact-alias match) should stay alphabetical"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn slash_completion_hints_exclude_set_and_deepseek_commands() {
|
||||
let hints = slash_completion_hints("/", 128, &[], Locale::En, None, ApiProvider::Deepseek);
|
||||
|
||||
Reference in New Issue
Block a user