-
Notifications
You must be signed in to change notification settings - Fork 52.7k
feat(cli): show session state in classic terminal title #74654
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
konsisumer
wants to merge
1
commit into
NousResearch:main
Choose a base branch
from
konsisumer:feat/classic-cli-terminal-title
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,105 @@ | ||
| """Best-effort terminal tab and window title updates for the classic CLI.""" | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import os | ||
| import re | ||
| import sys | ||
| import threading | ||
|
|
||
|
|
||
| _CONTROL_CHARS = re.compile(r"[\x00-\x1f\x7f-\x9f]") | ||
| _MAX_TITLE_LENGTH = 200 | ||
| _WRITE_LOCK = threading.Lock() | ||
|
|
||
|
|
||
| def sanitize_terminal_title(value: object) -> str: | ||
| """Return a printable, bounded title that cannot inject terminal escapes.""" | ||
| text = _CONTROL_CHARS.sub("", str(value or "")) | ||
| return " ".join(text.split())[:_MAX_TITLE_LENGTH] | ||
|
|
||
|
|
||
| def terminal_title_symbol(response_label: object, fallback: str = "⚕") -> str: | ||
| """Extract the skin's leading symbol from its response-panel label.""" | ||
| label = sanitize_terminal_title(response_label) | ||
| return label.split(maxsplit=1)[0] if label else fallback | ||
|
|
||
|
|
||
| def compose_terminal_title( | ||
| response_label: object, | ||
| session_title: object = "", | ||
| *, | ||
| busy: bool = False, | ||
| ) -> str: | ||
| """Compose the short tab title for an idle or active classic CLI session.""" | ||
| parts = [terminal_title_symbol(response_label)] | ||
| title = sanitize_terminal_title(session_title) | ||
| if title: | ||
| parts.append(title) | ||
| if busy: | ||
| parts.append("⏳") | ||
| return " ".join(parts) | ||
|
|
||
|
|
||
| def _set_windows_console_title(title: str) -> bool: | ||
| """Set the native Windows console title without relying on OSC support.""" | ||
| try: | ||
| import ctypes | ||
|
|
||
| return bool(ctypes.windll.kernel32.SetConsoleTitleW(title)) | ||
| except Exception: | ||
| return False | ||
|
|
||
|
|
||
| def _is_interactive_output(output: object) -> bool: | ||
| """Check the underlying stream for prompt_toolkit Output instances.""" | ||
| stream = getattr(output, "stdout", output) | ||
| try: | ||
| return bool(stream.isatty()) | ||
| except Exception: | ||
| return False | ||
|
|
||
|
|
||
| def _write_osc_terminal_title(output: object, title: str) -> bool: | ||
| """Write OSC title sequences, returning whether the terminal accepted them.""" | ||
| try: | ||
| with _WRITE_LOCK: | ||
| sequence = f"\033]1;{title}\a\033]2;{title}\a" | ||
| write_raw = getattr(output, "write_raw", None) | ||
| if callable(write_raw): | ||
| write_raw(sequence) | ||
| else: | ||
| output.write(sequence) | ||
| output.flush() | ||
| return True | ||
| except Exception: | ||
| return False | ||
|
|
||
|
|
||
| def write_terminal_title(title: object, output: object | None = None) -> bool: | ||
| """Set an interactive terminal's tab and window title. | ||
|
|
||
| On Windows, this uses ``SetConsoleTitleW`` for classic conhost and also | ||
| writes OSC 1/2 for terminal emulators such as mintty and VS Code. Elsewhere, | ||
| OSC 1 updates a terminal icon/tab label and OSC 2 updates the window title. | ||
| Prompt_toolkit ``Output`` objects are supported so callers can bypass | ||
| ``patch_stdout`` safely. The writer deliberately avoids logging failures | ||
| because it may be called from an agent callback. | ||
| """ | ||
| if os.environ.get("TERM", "").lower() == "dumb": | ||
| return False | ||
|
|
||
| try: | ||
| output = output if output is not None else sys.stdout | ||
| if output is None or not _is_interactive_output(output): | ||
| return False | ||
| clean_title = sanitize_terminal_title(title) | ||
| if not clean_title: | ||
| return False | ||
| if sys.platform == "win32": | ||
| native_updated = _set_windows_console_title(clean_title) | ||
| osc_updated = _write_osc_terminal_title(output, clean_title) | ||
| return native_updated or osc_updated | ||
| return _write_osc_terminal_title(output, clean_title) | ||
| except Exception: | ||
| return False | ||
Oops, something went wrong.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The classic interactive loop is inside
patch_stdout(cli.py:17185), andcli.py:3060documents that raw ANSI written to itsStdoutProxyis swallowed. Please write this sequence through the real terminal/raw prompt_toolkit output path and add a regression test; otherwise the title lifecycle can be invisible in the primary classic-CLI path.