feat(tui): /version slash command for wizard + protocol version visibility#768
Merged
Conversation
5 tasks
Contributor
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: Version text renders as single blob, not separate lines
- Split the
getVersionText()return value with.split('\n')sosetCommandFeedbackreceives astring[]array, giving each version line its own<Box>with proper indent prefix, matching how/diagnosticspasses multi-line feedback.
- Split the
Or push these changes by commenting:
@cursor push f598f482b1
Preview (f598f482b1)
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
@@ -302,7 +302,7 @@
// exiting the TUI to run `amplitude-wizard --version` in a shell.
// Multi-line, so we render it through the same long-lived
// `setCommandFeedback` slot used by /diagnostics.
- store.setCommandFeedback(getVersionText(), 30_000);
+ store.setCommandFeedback(getVersionText().split('\n'), 30_000);
break;
case '/exit':
store.setOutroData({ kind: OutroKind.Cancel, message: 'Exited.' });
diff --git a/src/ui/tui/components/__tests__/ConsoleView.test.tsx b/src/ui/tui/components/__tests__/ConsoleView.test.tsx
--- a/src/ui/tui/components/__tests__/ConsoleView.test.tsx
+++ b/src/ui/tui/components/__tests__/ConsoleView.test.tsx
@@ -245,10 +245,16 @@
// Give the dispatch + setCommandFeedback round-trip a tick.
await new Promise((r) => setTimeout(r, 30));
- const feedback = store.commandFeedback ?? '';
- expect(feedback).toContain('Amplitude Wizard v');
- expect(feedback).toContain('Agent-mode protocol: v');
- expect(feedback).toContain(`Node: ${process.version}`);
+ const feedback = store.commandFeedback;
+ expect(Array.isArray(feedback)).toBe(true);
+ const lines = feedback as string[];
+ expect(lines).toEqual(
+ expect.arrayContaining([
+ expect.stringContaining('Amplitude Wizard v'),
+ expect.stringContaining('Agent-mode protocol: v'),
+ expect.stringContaining(`Node: ${process.version}`),
+ ]),
+ );
unmount();
});You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit ca81bf7. Configure here.
2 tasks
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…o 30s (#777) The "refreshes the MCP Authorization header when the token rotates between attempts" test simulates a 60s cold-start stall plus jittered backoff (2-30s) inside fake timers — up to ~90s of virtual time per attempt. The test logic is correct, but the per-test 15s real-time timeout left no headroom for the scheduler under CI load on Node 20, producing intermittent "Test timed out in 15000ms" failures across multiple PRs (#765, #766, #767, #768, #769, #771). Double the timeout to 30s. No logic change.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
dd8c336 to
98ab797
Compare
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…rtions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…#767) * fix(tui): drop digit-shortcut UI hints on >10-option PickerMenu lists The PickerMenu primitive consumed digit keystrokes 1-9 (+ 0 → index 9) and rendered a `[N]` chip next to each row. The handler only covers the first 10 indices, but the chip was emitted for those first 10 rows regardless of total list length — so on lists with >10 options (e.g. Amplitude users with 20+ orgs, or projects with many envs) items 11+ looked visually identical to shortcut-enabled rows but typing their visible number did nothing, while items 1-10 advertised shortcuts that worked. Now: - options.length <= 10 → render `[1]`-`[9]`/`[0]` chips as before. - options.length > 10 → drop the chips on EVERY row and show a muted "Use arrows + Enter to pick" hint at the bottom of the list. The digit handler itself is unchanged for indices 0-9 so power users who learned the shortcut keep it; the UI just stops advertising it once the affordance would be incomplete. Audit #4 finding (TUI pickers + primitives). Stacks on #728. * fix(bugbot): account for hint row in PickerMenu chrome budget PR A12's new "Use arrows + Enter to pick" footer (rendered when options.length > DIGIT_SHORTCUT_LIMIT) added a row that wasn't budgeted. `CONSTRAINED_CHROME_RESERVE_ROWS = 3` covered the PromptLabel header + 2 scroll indicators, so when both indicators were visible and the hint rendered, total height became `availableRows + 1` and the parent's `overflow="hidden"` clipped the bottom — precisely on the >10-option lists this PR targets. Add a dynamic `hintRows` (0 or 1) to the chrome reserve so the hint fits inside the budget on both constrained (AuthScreen org/project /env pickers) and natural-height paths. * fix(salvage): drop PICKER_FLASH_MS import in PickerMenu test The PICKER_FLASH_MS export came from the unmerged #715 delight pass (selection-confirmation flash). On main, PickerMenu's `onSelect` is synchronous so we just advance fake timers by 5ms. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(test): drain PICKER_FLASH_MS in PickerMenu large-list digit-shortcut tests After merging main, PickerMenu introduced a 250ms selection-confirmation flash before onSelect fires. The large-list tests advanced fake time by only 5ms, so they read the still-null `chosen` slot and failed with `expected null to be 'v3'`. Advance by `PICKER_FLASH_MS + 5` so the flash timer drains and `onSelect` is invoked before the assertion. * fix(test): tolerate 1s tick drift in EventPlanFullScreen elapsed assertions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…te (#769) * feat(tui): register /help command and accept Tab in slash palette Tab in the slash palette now accepts the highlighted suggestion (Raycast/Slack convention) and fills the input with `<cmd> ` so the user can keep typing arguments. The palette stays open; Enter still submits. Outside slash mode, Tab continues to open Ask via ConsoleView's pre-activation handler. Also adds an `↑↓ navigate · Tab/Enter run · Esc cancel` footer hint beneath the candidate list so users don't have to guess which key does what. /help itself was already registered (see prior PR) — this commit completes the paired affordances from the TUI audit T5 wave. * fix(bugbot): correct slash palette footer and tolerate trailing space in filter Bugbot flagged two issues on PR #739: 1. (3220814211) Palette footer read "Tab/Enter run" but Tab only completes the highlighted suggestion (setValue) — Enter is required to actually submit. Footer now reads "Tab complete · Enter run" so the affordance matches the handler at line ~118. 2. (3220814221) Tab fills the input with `<cmd> ` (trailing space, per Raycast/Slack convention so the user can keep typing args). The filter used `value.slice(1)` as the query, so the trailing space caused both `cmd.startsWith(query)` and the description includes-check to fail — `filtered` emptied and the palette tore itself down mid-completion. Picked option B (trim query at filter time): the filter now keys off the first whitespace-delimited word, matching the slash-mode check at `computeIsSlashMode`. Reason: trailing space is the universal palette/shell convention (zsh, fish, Claude Code itself, every modern CLI palette) — it signals "now type your arg". Dropping the space (option A) would force the user to type it themselves before args, which is a worse UX. The filter should be query-trim-tolerant. Verified clampedIndex, scroll math, and Enter-submit behavior still work because they all key off `filtered.length`, which is now non-empty after Tab. Tests extended in SlashCommandInput.tab.test.tsx to lock down the new footer copy and assert the palette stays open after Tab completion. * fix(bugbot): preserve argv when Enter-submitting slash commands Bugbot 3220907967 (high). The Tab-trailing-space fix that landed in 24eb3bd keyed the slash filter off the first whitespace-delimited word, which fixed the palette-collapse-on-trailing-space regression but introduced a worse one: typing /feedback hello world and pressing Enter submitted just /feedback because the Enter handler preferred filtered[clampedIndex].cmd whenever filtered was non-empty, and the new first-word filter kept it non-empty even when args were typed. Affected commands in the registry: /feedback <message> and /create-project <name>. Args were silently dropped. Fix: Enter now distinguishes command-only (no space in value) from command-with-args (space present). Command-only takes the palette pick (so /he + Enter still submits /help). Command-with-args submits the value verbatim. The Tab fill behavior (trailing space) is unchanged because Tab does not call onSubmit. Tests: extends SlashCommandInput.tab.test.tsx with regression coverage for /feedback hello world, /he, /help, and /feedback<space>. * fix(bugbot): detect hasArgs on untrimmed value so trailing space survives `hasArgs = trimmed.includes(' ')` stripped the very signal we wanted — `/cmd ` (trailing space, e.g. from Tab completion) was indistinguishable from `/cmd` and silently palette-picked. Now detect args on the untrimmed value: a trailing space anywhere routes to verbatim submit, matching the universal palette/shell convention. Bugbot 3221028494. * feat(tui): register /help slash command — docs canonical, registry was missing CLAUDE.md lists `/help` as a canonical slash command, but the registry in `src/ui/tui/console-commands.ts` was missing it. Users typing `/help` got nothing — no completion match in the picker, no dispatch case, no feedback. The empty-result path is the worst kind of broken: the command appears unsupported even though it should be the first thing a new user reaches for. Add `/help` to `COMMANDS` and a dispatch case in `ConsoleView.tsx` that prints a categorized list of every other slash command + a one-line key-binding cheatsheet, surfaced through the existing `setCommandFeedback` channel. Regression test in `console-commands.test.ts` asserts `/help` is in the registry and is NOT `requiresIdle` (so it works mid-run). * fix(test): make SlashCommandInput tab tests resilient to new /d* commands The "fills the input with the highlighted command + space" test used `/d` as the prefix and assumed the first cmd-prefix match was `/debug`. Main now contains `/diff` (added by #599) which sorts ahead of `/debug` in the COMMANDS array — once this branch merges with main, the test received `/diff` instead of `/debug`. Tighten the first test to `/de` (matches /debug only). The "completes the second match when the user navigates to it" test relied on DOWN moving from /debug → /diagnostics. With /diff now in the mix, DOWN once landed on /debug instead. Switch to a self-contained two-command list so the test doesn't depend on the production COMMANDS-array ordering at all — the wiring it pins is "DOWN moves to second match, Tab accepts it". * fix(lint): remove duplicate /help case from ConsoleView after merge The salvage branch added an inline `/help` case in ConsoleView's switch; main later landed a separate `/help` case that calls `getHelpText()`. After merge, both coexisted and ESLint flagged the duplicate-case as an error. Drop the inline version. `getHelpText()` is the canonical helper — it's covered by console-commands.test.ts and shared with any future non-TUI surface that wants the same listing. * fix(test): tolerate 1s tick drift in EventPlanFullScreen elapsed assertions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
) * fix(tui): accept Enter on screenError retry shortcut The screen-error banner advertises `[R] retry` but the dormant ConsoleView keyboard handler only matched `r`/`R`. Users focused on a prompt below the banner often press Enter as the default action — accept it as a retry trigger so the banner clears as advertised. Adds a regression test that asserts both `r` (existing path) and Enter (new) call `clearScreenError`. Audit signal from iteration #10 5-subagent TUI wave (auditor #2, RunScreen + console + slash). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(bugbot): gate screenError Enter handler on !pendingPrompt The previous fix accepted Enter as a screenError retry shortcut but didn't account for the case where `screenError` and `pendingPrompt` are simultaneously active (e.g. a `promptChoice` rendering a PickerMenu below the error banner). Both `useInput` handlers were active in that state — the ConsoleView's accepted Enter to clear the error, AND the PickerMenu's `useScreenInput` accepted Enter to commit the focused selection. Pressing Enter once would fire both, clearing the error AND committing an unintended picker option. Split the screenError keystroke matcher into two branches: - `r`/`R` accepted unconditionally (letters don't collide with picker shortcuts) - `key.return` accepted only when `!pendingPrompt` (picker owns Enter while a prompt is in flight) Regression test pins the new behaviour by setting up a `confirm` prompt + screenError, asserting Enter does NOT clear the error, then asserting `R` still does. * fix(test): tolerate 1s tick drift in EventPlanFullScreen elapsed assertions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…t-activity anchor (#771) * feat(tui): surface error-outro hotkeys as accent-coloured pill bar The error outro buries `L`, `C`, and `R` (the only interactive affordances on the screen) inside the muted secondary-text troubleshooting bullets — "(press L to open)", "Press C to write a sanitized bug report", "Press R to retry from where we left off". Users scanning the screen for "what can I press right now?" frequently skim past those bullets because they look like documentation, not buttons. Pull the keys out of prose and render them as a row of high-contrast `[K] label` pills at the foot of the screen — same visual idiom as the global KeyHintBar so the affordance reads as interactive at a glance. The troubleshooting bullets above stay as context but no longer duplicate the hotkey verbiage. The cancel path's "press R now to resume" line is unchanged (it's the only hint there, so prose works). The R pill is gated on the same `showRetryHint` condition as before (suppressed for auth failures and non-interactive runs), so the bar never advertises a hotkey that would be a no-op. The preserve-files prompt suppresses the standard bar entirely — its dedicated K/R prompt would compete with a second pill row. Covered by: - HotkeyPills.test.tsx (4 tests) — render shape + ordering - OutroScreen.error.test.tsx — updates the existing retry-hint regression suite to assert the new `[R] Retry` shape - OutroScreen.snap.test.tsx — error snapshot updated to show the pill bar replacing the inline "press X to …" hints * feat(tui): surface backoff seconds in sustained-storm retry chip RetryState already carries `nextRetryAtMs` (set by `publishRetryBanner` in agent-interface.ts), but `RetryStatusChip` discarded it entirely — the chip's design intent was calm copy ("slowing down to match Amplitude rate limits"), no countdown, spinner-as-liveness. That's correct for transient blips, but once a storm has been going long enough that the chip already escalated to the "(still trying)" suffix (attempt ≥ 5), the user has clearly been staring at the chip long enough to want a concrete number. "Is it actually still working?" is the implicit question; the backoff seconds answer it. Sub-second tails clamp to 1 (avoid a misleading "0s" right before the retry fires); past-due timestamps fall through to plain "(still trying)" so we never advertise a wait the user won't experience. The calm-storm invariants are preserved by design — no X/Y attempt fraction ever surfaces, no raw HTTP code, the chip stays hidden during the grace window, and the backoff tail is gated on the same sustained threshold that already guards the "(still trying)" suffix. `backoffSecondsFromState` is exported as a pure helper so the rounding contract (Math.floor, min 1 while time remains) is covered independently of the chip's full pipeline. Covered by: - 6 new tests on `getRetryStatusText` (sustained + various nextRetryAtMs shapes) - 4 new tests on `backoffSecondsFromState` (rounding, clamping, past-due, unset) - Existing 11 calm-chip tests still pass — design intent preserved * feat(tui): anchor error outro with "Started at … · Step: …" line When a run dies, the user is staring at a wall of suggested next-steps ("Press L to open the log", "--debug for more detail", "Press C to write a bug report") and the log file is a haystack. They have to scroll back through dozens of progress messages to find the point of failure. The session already tracks both `runStartedAt` and the active task (via `store.tasks` derived from agent TodoWrite); we just weren't surfacing them at the moment they'd be most useful. Add a muted sub-line directly under the error message: "Started at 14:23:47 · Step: Install Amplitude" The line gives the user a concrete entry-point into the log file before they open it. Resolution order: 1. Latest in_progress task wins — that's where the agent died. 2. Fall through to the most-recent completed task — "we got through install but failed before reaching plan". 3. If everything is pending OR runStartedAt is null, render nothing — better than a confusing "Step: <empty>" line. Pure helpers (`buildLastActivityFooter`, `formatClockTime`) live in their own module so the resolution order is verifiable independently of the rendering pipeline. Gated to the error outro only — the cancel path leads with "Resume later" and the success path doesn't need a forensic anchor. Snapshot tests still pass unchanged: `makeStoreForSnapshot` defaults runStartedAt to null, so existing fixtures don't pick up the new line. Covered by: - outro-last-activity.test.ts (10 tests) — pure helper contract - 6 new render-level tests in OutroScreen.error.test.tsx — appears/hides for each outro kind, each task-state shape * fix(test): tolerate 1s tick drift in EventPlanFullScreen elapsed assertions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
Lets users grab the wizard version, agent-mode NDJSON protocol version, and Node/platform runtime from inside the TUI instead of exiting to run `amplitude-wizard --version` in a shell. Useful for bug reports filed without leaving an active session. Output: Amplitude Wizard v1.17.0 Agent-mode protocol: v1 Node: v20.11.0 (darwin arm64) Registers /version in the COMMANDS registry without `requiresIdle` so it works mid-run (informational, no side effects). Wires dispatch through the existing setCommandFeedback slot used by /help and /diagnostics. `getVersionText` accepts an injected runtime so unit tests pin the shape against fixed values; the default path reads from `process.*` so the live command shows the user's actual environment. Stacks on #719.
…rtions The revising-screen elapsed timer ticks every 1s via setInterval; on Node 24 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s. Also includes a trivial prettier fix in console-commands.test.ts that the file-level linter flagged.
98ab797 to
5e5dcb7
Compare
kelsonpw
added a commit
that referenced
this pull request
May 14, 2026
…765) * fix(tui): clear sticky currentFile pill when agent activity is quiet The "currently editing X" pill on RunScreen latched `lastFileRef` whenever `rawFile` was truthy but had no clear path — so after the inner agent finished its last write, the pill kept showing that file forever, into the Finalizing phase and beyond. The header lied about what the wizard was doing. Clear the sticky value when either condition holds: 1. The most recent `fileWrites` entry is older than 10s AND its status is terminal (`applied` / `failed`) — quiet enough that "currently editing" is no longer true. 2. `postAgentSteps.length > 0` — the agent has moved past the file-write phase entirely (commit events, create dashboard, …). Clear immediately, no grace period needed. The stale-time check rides the spinner's existing SPINNER_INTERVAL re-render — no extra timer mounted. Computed inline during render rather than through useEffect so a quiet stretch (rawFile null, last entry timestamps unchanged) doesn't skip re-evaluation. Tests pin both clear conditions plus a heartbeat-keepalive regression: a new write before the stale window switches the pill to the new path without clearing. * fix(test): tolerate 1s tick drift in EventPlanFullScreen elapsed assertions Same fix as PR #768. The revising-screen elapsed timer ticks every 1s via setInterval; on Node 22 under CI clock pressure the interval can fire one tick before the test's advanceTimersByTime boundary, so the rendered "elapsed:" string lands at `1m 29s` instead of `1m 30s`. Both readings are correct for the test's intent (verifying tier WIRING, not exact copy), so widen the assertions to a regex that accepts ±1s.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.


Summary
Salvaged from #725 by cherry-picking its unique commit onto a fresh branch from
main. The original PR was stacked on the closedfeat/v2-tui-redesign(#696); its git history carried ~35 commits of v2 orchestration scaffolding that couldn't be rebased onto main's parallel orchestration implementation.Original commits preserved:
8cf86032Skipped:
f6b09583— this followup wrapsprocess.exitinsrc/utils/wizard-abort.ts, which is on the salvage-policy "do not touch" list (load-bearing). The vitest unhandled-rejection bug it patches can be re-addressed in a dedicated PR if it surfaces on CI.Notes:
/versioncommit also touched the slash-command registry to add/statusand/help(from later v3 PRs). Only/versionwas kept —/statusbelongs to the orchestration overlay work and/helpis salvaged separately in PR feat(tui): register /help command and accept Tab in slash palette #739.Test plan
Generated with Claude Code
Note
Low Risk
Low risk: changes are isolated to TUI command registration/dispatch and add read-only version formatting with test coverage, without affecting auth, data writes, or run orchestration.
Overview
Adds a new read-only
/versionslash command to the TUI that surfaces wizard version, agent-mode wire protocol version, and Node/platform runtime info viagetVersionText().Wires
/versionintoConsoleViewcommand dispatch (shown viasetCommandFeedback) and extends unit/render tests to ensure the command stays registered and remains available mid-run.Reviewed by Cursor Bugbot for commit 5e5dcb7. Bugbot is set up for automated code reviews on this repo. Configure here.