@@ -372,9 +372,15 @@ void AppCommandlineArgs::_buildFocusTabParser()
372372 auto * prevOpt = subcommand->add_flag (" -p,--previous" ,
373373 _focusPrevTab,
374374 RS_A (L" CmdFocusTabPrevArgDesc" ));
375+ auto * sessionOpt = subcommand->add_option (" -s,--session" ,
376+ _focusTabSession,
377+ RS_A (L" CmdFocusTabSessionArgDesc" ));
375378 nextOpt->excludes (prevOpt);
376379 indexOpt->excludes (prevOpt);
377380 indexOpt->excludes (nextOpt);
381+ sessionOpt->excludes (indexOpt);
382+ sessionOpt->excludes (nextOpt);
383+ sessionOpt->excludes (prevOpt);
378384
379385 // When ParseCommand is called, if this subcommand was provided, this
380386 // callback function will be triggered on the same thread. We can be sure
@@ -384,10 +390,40 @@ void AppCommandlineArgs::_buildFocusTabParser()
384390 // Build the action from the values we've parsed on the commandline.
385391 ActionAndArgs focusTabAction{};
386392
387- if (_focusTabIndex >= 0 )
393+ if (!_focusTabSession. empty () )
388394 {
395+ // Focus tab by session GUID
389396 focusTabAction.Action (ShortcutAction::SwitchToTab);
390- SwitchToTabArgs args{ static_cast <unsigned int >(_focusTabIndex) };
397+ SwitchToTabArgs args{};
398+ const auto str = winrt::to_hstring (_focusTabSession);
399+ // GuidFromPlainString handles GUIDs without braces (e.g. "12345678-...")
400+ // GuidFromString handles GUIDs with braces (e.g. "{12345678-...}")
401+ // Try plain first (most common from WT_SESSION), fall back to braced
402+ winrt::guid id{};
403+ try
404+ {
405+ id = ::Microsoft::Console::Utils::GuidFromPlainString (str.c_str ());
406+ }
407+ catch (...)
408+ {
409+ id = ::Microsoft::Console::Utils::GuidFromString (str.c_str ());
410+ }
411+ args.SessionId (id);
412+ focusTabAction.Args (args);
413+ _startupActions.push_back (focusTabAction);
414+
415+ // When targeting by session ID, route to an existing window on the
416+ // current desktop (if user didn't specify -w explicitly).
417+ // The session ID uniquely identifies a pane in some window.
418+ if (_windowTarget.empty ())
419+ {
420+ _windowTarget = " 0" ;
421+ }
422+ }
423+ else if (_focusTabIndex >= 0 )
424+ {
425+ focusTabAction.Action (ShortcutAction::SwitchToTab);
426+ SwitchToTabArgs args{ static_cast <unsigned int >(_focusTabIndex), winrt::guid{} };
391427 focusTabAction.Args (args);
392428 _startupActions.push_back (focusTabAction);
393429 }
@@ -808,6 +844,7 @@ void AppCommandlineArgs::_resetStateToDefault()
808844 _focusTabIndex = -1 ;
809845 _focusNextTab = false ;
810846 _focusPrevTab = false ;
847+ _focusTabSession.clear ();
811848
812849 _moveFocusDirection = FocusDirection::None;
813850 _swapPaneDirection = FocusDirection::None;
@@ -1014,9 +1051,14 @@ void AppCommandlineArgs::ValidateStartupCommands()
10141051 // If we parsed no commands, or the first command we've parsed is not a new
10151052 // tab action, prepend a new-tab command to the front of the list.
10161053 // (also, we don't need to do this if the only action is a x-save)
1054+ // Note: SwitchToTab with a SessionId is excluded because focus-tab --session
1055+ // targets an existing tab and doesn't need a new tab to be created.
1056+ // We check for non-empty _windowTarget as a proxy for --session being used,
1057+ // since --session sets _windowTarget = "0".
10171058 else if (_startupActions.empty () ||
10181059 (_startupActions.front ().Action () != ShortcutAction::NewTab &&
1019- _startupActions.front ().Action () != ShortcutAction::SaveSnippet))
1060+ _startupActions.front ().Action () != ShortcutAction::SaveSnippet &&
1061+ !(!_windowTarget.empty () && _startupActions.front ().Action () == ShortcutAction::SwitchToTab)))
10201062 {
10211063 // Build the NewTab action from the values we've parsed on the commandline.
10221064 NewTerminalArgs newTerminalArgs{};
0 commit comments