feat(tui): v3 foundation quick wins — dead enum + /help + Tab autocomplete#712
Conversation
bb289e8 to
c6dc278
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 3 potential issues.
Autofix Details
Bugbot Autofix prepared fixes for all 3 issues found in the latest run.
- ✅ Fixed: Duplicate
/helpswitch case shadows better implementation- Removed the first inline
/helpcase (line 104) from the switch statement so the bettergetHelpText(runActive)handler is now reachable.
- Removed the first inline
- ✅ Fixed: Duplicate
/helpentry in COMMANDS array- Removed the second duplicate
/helpentry from the COMMANDS array, leaving the single entry at the top with description 'List every slash command and what it does'.
- Removed the second duplicate
- ✅ Fixed: Duplicate
longestCommonPrefixalready exists in PathInput- Replaced the duplicate implementation in SlashCommandInput.tsx with an import from PathInput.tsx, re-exporting it for existing consumers.
Or push these changes by commenting:
@cursor push 2e3a993307
Preview (2e3a993307)
diff --git a/src/ui/tui/components/ConsoleView.tsx b/src/ui/tui/components/ConsoleView.tsx
--- a/src/ui/tui/components/ConsoleView.tsx
+++ b/src/ui/tui/components/ConsoleView.tsx
@@ -101,21 +101,6 @@
}
switch (cmd) {
- case '/help': {
- // CLAUDE.md documents `/help` as canonical but the registry was
- // missing it — users typed `/help` and got nothing. Render a
- // categorized list of every slash command + its description.
- const lines = ['Slash commands:', ''];
- for (const c of COMMANDS) {
- if (c.cmd === '/help') continue;
- const idleNote = c.requiresIdle ? ' [paused during runs]' : '';
- lines.push(` ${c.cmd.padEnd(18)} ${c.desc}${idleNote}`);
- }
- lines.push('');
- lines.push('Key bindings: [/] commands · [Tab] ask · [Esc] back · [Ctrl+C] quit');
- store.setCommandFeedback(lines.join('\n'), 30_000);
- break;
- }
case '/region':
store.setRegionForced();
break;
@@ -799,8 +784,8 @@
? eventPlanPromptShowing
? 'Finish the plan above ([Y]/[S]/[F]) — / and Tab resume after.'
: visibleHistory.length > 0
- ? 'Press / for commands · Tab to ask · Esc to hide answer'
- : 'Press / for commands or Tab to ask a question'
+ ? 'Press / for commands · Tab to ask · Esc to hide answer'
+ : 'Press / for commands or Tab to ask a question'
: 'Press / for commands'}
</Text>
)}
diff --git a/src/ui/tui/console-commands.ts b/src/ui/tui/console-commands.ts
--- a/src/ui/tui/console-commands.ts
+++ b/src/ui/tui/console-commands.ts
@@ -70,10 +70,6 @@
cmd: '/status',
desc: 'Show orchestration status: pending choices, verifications, MCP capabilities, next action',
},
- {
- cmd: '/help',
- desc: 'List slash commands with one-line descriptions',
- },
{ cmd: '/snake', desc: 'Play Snake' },
{ cmd: '/exit', desc: 'Exit the wizard' },
];
diff --git a/src/ui/tui/primitives/SlashCommandInput.tsx b/src/ui/tui/primitives/SlashCommandInput.tsx
--- a/src/ui/tui/primitives/SlashCommandInput.tsx
+++ b/src/ui/tui/primitives/SlashCommandInput.tsx
@@ -10,7 +10,10 @@
import { Box, Text, useInput } from 'ink';
import { useState, useEffect } from 'react';
import { Colors, Icons } from '../styles.js';
+import { longestCommonPrefix } from '../components/PathInput.js';
+export { longestCommonPrefix };
+
export interface SlashCommand {
cmd: string;
desc: string;
@@ -33,26 +36,6 @@
);
}
-/**
- * Returns the longest common prefix shared by every input string.
- * Used by Tab autocomplete to extend a partial slash command as far as
- * the input is unambiguous — bash/zsh/fish convention.
- *
- * Exported for unit testing.
- */
-export function longestCommonPrefix(strings: readonly string[]): string {
- if (strings.length === 0) return '';
- if (strings.length === 1) return strings[0];
- let prefix = strings[0];
- for (let i = 1; i < strings.length; i++) {
- while (!strings[i].startsWith(prefix)) {
- prefix = prefix.slice(0, -1);
- if (prefix === '') return '';
- }
- }
- return prefix;
-}
-
interface SlashCommandInputProps {
commands?: SlashCommand[];
isActive: boolean;You can send follow-ups to the cloud agent here.
e993e40 to
764ff8a
Compare
764ff8a to
14d9f97
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Tab autocomplete LCP polluted by description-matched commands
- Filtered the LCP computation to only include commands where c.cmd prefix-matches the input query, excluding description-only matches that would collapse the common prefix.
Or push these changes by commenting:
@cursor push 4464ee8b9d
Preview (4464ee8b9d)
diff --git a/src/ui/tui/primitives/SlashCommandInput.tsx b/src/ui/tui/primitives/SlashCommandInput.tsx
--- a/src/ui/tui/primitives/SlashCommandInput.tsx
+++ b/src/ui/tui/primitives/SlashCommandInput.tsx
@@ -143,7 +143,11 @@
// there's nothing to complete (filtered.length === 0) or when
// the input is already at the LCP.
if (isSlashMode && filtered.length > 0) {
- const lcp = longestCommonPrefix(filtered.map((c) => c.cmd));
+ const prefixMatches = filtered.filter((c) =>
+ c.cmd.slice(1).startsWith(query),
+ );
+ if (prefixMatches.length === 0) return;
+ const lcp = longestCommonPrefix(prefixMatches.map((c) => c.cmd));
if (lcp.length > value.length) {
setValue(lcp);
setSelectedIndex(0);You can send follow-ups to the cloud agent here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed:
/helprenders as single text blob, loses per-line formatting- Changed
lines.join('\n')tolinesso the/helphandler passes the array directly tosetCommandFeedback, matching the pattern used by/debugand/diagnosticsfor proper per-line rendering.
- Changed
Or push these changes by commenting:
@cursor push 96aa9384b6
Preview (96aa9384b6)
diff --git a/src/ui/tui/components/ConsoleView.tsx b/src/ui/tui/components/ConsoleView.tsx
--- a/src/ui/tui/components/ConsoleView.tsx
+++ b/src/ui/tui/components/ConsoleView.tsx
@@ -111,7 +111,7 @@
}
lines.push('');
lines.push('Key bindings: [/] commands · [Tab] ask · [Esc] back · [Ctrl+C] quit');
- store.setCommandFeedback(lines.join('\n'), 30_000);
+ store.setCommandFeedback(lines, 30_000);
break;
}
case '/region':You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 32e449a. Configure here.
The `Options` enum member in `Screen` (src/ui/tui/flows.ts:36) is registered to a `null` component in `screen-registry.tsx:85` and has zero references anywhere else in the codebase. Confirmed with \`grep -rn 'Screen.Options' src/\` — only hit is the null registration site. Dead enum + null component clutter the surface; removing them drops the screen count from 24 → 23 and surfaces a real path to "everything in the registry is a real screen." No tests reference the value. Type-check + lint stay green.
7558fe3 to
c2c12e2
Compare


Summary
First slice of the TUI v3 redesign consensus from a 6-subagent audit. Lowest-risk, ship-able-now wins. Each commit is atomic so a reviewer can bisect.
Changes
refactor(tui): remove unused Screen.Options enum + null registration(04e42ee1)The
Optionsmember ofScreenwas registered to anullcomponent and referenced nowhere else. Dead enum + null component clutter the surface. Drops the screen-registry from 24 → 23 entries.feat(tui): register /help slash command — docs canonical, registry was missing(1e1571bd)`CLAUDE.md` documents `/help` as canonical, but the registry in `console-commands.ts` was missing it. Users typing `/help` got an empty result — the worst kind of broken because the command looks unsupported when it should be the first thing a new user reaches for. Adds a dispatch case that prints every other slash command + a key-bind cheatsheet via `setCommandFeedback`. Regression test asserts `/help` is in the registry and not `requiresIdle`.
`feat(tui): / autocompletes slash command to longest common prefix` (`bb289e8b`)
Bash/zsh/fish convention: Tab extends a partial command to the LCP of matches. The wizard's slash palette swallowed Tab (`SlashCommandInput.tsx:118`) — only Enter could select. Now `Tab` extends the input to `longestCommonPrefix(filtered.map(c => c.cmd))` in slash mode. Outside slash mode Tab is still a no-op (parent owns it). `longestCommonPrefix` exported with 7 unit tests covering empty / single / divergent / identity / real-world (`/d` → `/d` for debug vs diagnostics).
Test plan
Out of scope (follow-up PRs)
This is the foundation slice. The audit surfaced bigger wins that warrant their own PRs:
🤖 Generated with Claude Code
Note
Low Risk
Low risk cleanup that only removes dead screen identifiers/mappings; no behavior changes expected unless something external referenced the deleted enum value.
Overview
Removes the unused
Screen.Optionsentry from theScreenenum and deletes itsnullcomponent mapping fromscreen-registry.tsx, reducing the registered screen surface area.Reviewed by Cursor Bugbot for commit c2c12e2. Bugbot is set up for automated code reviews on this repo. Configure here.