Skip to content

Commit 36d989e

Browse files
committed
[#24244] Added logic to restore terminator profile once VulcanAI exec finish
Signed-off-by: danipiza <dpizarrogallego@gmail.com>
1 parent 9504de1 commit 36d989e

2 files changed

Lines changed: 138 additions & 13 deletions

File tree

src/vulcanai/console/terminal_session.py

Lines changed: 137 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,120 @@ def _run(*args: str) -> bool:
190190

191191
return completed.returncode == 0
192192

193+
@staticmethod
194+
def _run_capture(*args: str) -> Optional[str]:
195+
"""
196+
Execute a command and return trimmed stdout on success.
197+
198+
@return stdout without trailing whitespace, or ``None`` on failure/empty output.
199+
"""
200+
try:
201+
completed = subprocess.run(
202+
[*args],
203+
check=False,
204+
capture_output=True,
205+
text=True,
206+
)
207+
except Exception:
208+
return None
209+
210+
if completed.returncode != 0:
211+
return None
212+
213+
output = (completed.stdout or "").strip()
214+
return output or None
215+
216+
@staticmethod
217+
def _parse_profile_name(raw_output: Optional[str]) -> Optional[str]:
218+
"""
219+
Extract profile name from ``remotinator`` output.
220+
"""
221+
if not raw_output:
222+
return None
223+
224+
lines = [line.strip() for line in raw_output.splitlines() if line.strip()]
225+
if not lines:
226+
return None
227+
228+
candidate = lines[-1].strip("'\"")
229+
lower_candidate = candidate.lower()
230+
for prefix in ("current profile:", "profile:", "profile="):
231+
if lower_candidate.startswith(prefix):
232+
candidate = candidate[len(prefix) :].strip().strip("'\"")
233+
break
234+
235+
return candidate or None
236+
237+
def _get_current_profile(self, terminal_uuid: str) -> Optional[str]:
238+
"""
239+
Query current profile for a terminal UUID using ``remotinator``.
240+
"""
241+
output = self._run_capture("remotinator", "get_profile", "-u", terminal_uuid)
242+
return self._parse_profile_name(output)
243+
244+
@staticmethod
245+
def _profiles_block_bounds(lines: list[str]) -> Optional[tuple[int, int]]:
246+
"""
247+
Locate the [profiles] block bounds in Terminator config lines.
248+
"""
249+
profiles_start = next((i for i, line in enumerate(lines) if line.strip() == "[profiles]"), None)
250+
if profiles_start is None:
251+
return None
252+
253+
profiles_end = len(lines)
254+
for index in range(profiles_start + 1, len(lines)):
255+
if TerminatorTerminalAdapter._TOP_LEVEL_SECTION_RE.match(lines[index]) and lines[index].strip() != "[profiles]":
256+
profiles_end = index
257+
break
258+
259+
return (profiles_start, profiles_end)
260+
261+
@classmethod
262+
def _list_profiles(cls, config_path: Path) -> list[str]:
263+
"""
264+
Return profile names defined in the Terminator config.
265+
"""
266+
try:
267+
lines = config_path.read_text(encoding="utf-8").splitlines(keepends=True)
268+
except Exception:
269+
return []
270+
271+
bounds = cls._profiles_block_bounds(lines)
272+
if not bounds:
273+
return []
274+
275+
profiles_start, profiles_end = bounds
276+
names: list[str] = []
277+
for index in range(profiles_start + 1, profiles_end):
278+
header_match = cls._PROFILE_HEADER_RE.match(lines[index].rstrip("\r\n"))
279+
if header_match:
280+
names.append(header_match.group(2).strip())
281+
return names
282+
283+
def _fallback_restore_profile(self, config_path: Path) -> Optional[str]:
284+
"""
285+
Pick a best-effort restore profile when runtime query is unavailable.
286+
"""
287+
profiles = [name for name in self._list_profiles(config_path) if name != self._config.terminator_profile_hidden]
288+
if not profiles:
289+
return None
290+
291+
configured_base = self._config.terminator_profile_base
292+
if configured_base in profiles and configured_base != "default":
293+
return configured_base
294+
295+
non_default = [name for name in profiles if name != "default"]
296+
if configured_base == "default" and len(non_default) == 1 and "default" in profiles:
297+
return non_default[0]
298+
299+
if configured_base in profiles:
300+
return configured_base
301+
if len(non_default) == 1:
302+
return non_default[0]
303+
if "default" in profiles:
304+
return "default"
305+
return profiles[0]
306+
193307
@staticmethod
194308
def _config_path() -> Path:
195309
"""
@@ -212,15 +326,10 @@ def _ensure_hidden_profile(cls, config_path: Path, base_profile: str, hidden_pro
212326
except Exception:
213327
return False
214328

215-
profiles_start = next((i for i, line in enumerate(lines) if line.strip() == "[profiles]"), None)
216-
if profiles_start is None:
329+
bounds = cls._profiles_block_bounds(lines)
330+
if not bounds:
217331
return False
218-
219-
profiles_end = len(lines)
220-
for index in range(profiles_start + 1, len(lines)):
221-
if cls._TOP_LEVEL_SECTION_RE.match(lines[index]) and lines[index].strip() != "[profiles]":
222-
profiles_end = index
223-
break
332+
profiles_start, profiles_end = bounds
224333

225334
profile_headers: list[tuple[str, int, str]] = []
226335
for index in range(profiles_start + 1, profiles_end):
@@ -298,7 +407,7 @@ def detect(self) -> bool:
298407
def apply(self) -> Optional[tuple[str, str]]:
299408
"""
300409
@brief Switch current Terminator tab to hidden-scroll profile.
301-
@return ``(uuid, base_profile)`` when switching succeeds, else ``None``.
410+
@return ``(uuid, restore_profile)`` when switching succeeds, else ``None``.
302411
"""
303412
terminal_uuid = os.environ.get("TERMINATOR_UUID")
304413
if not terminal_uuid:
@@ -311,12 +420,28 @@ def apply(self) -> Optional[tuple[str, str]]:
311420
if not config_path.is_file():
312421
return None
313422

423+
restore_profile = self._get_current_profile(terminal_uuid)
424+
if not restore_profile:
425+
# Older remotinator versions do not expose the active profile.
426+
# Use a config-based best effort so profile switching still works.
427+
restore_profile = self._fallback_restore_profile(config_path)
428+
if not restore_profile:
429+
return None
430+
314431
if not self._ensure_hidden_profile(
315432
config_path=config_path,
316-
base_profile=self._config.terminator_profile_base,
433+
base_profile=restore_profile,
317434
hidden_profile=self._config.terminator_profile_hidden,
318435
):
319-
return None
436+
# Fallback to configured profile in case runtime query is unavailable or untrusted.
437+
if restore_profile == self._config.terminator_profile_base:
438+
return None
439+
if not self._ensure_hidden_profile(
440+
config_path=config_path,
441+
base_profile=self._config.terminator_profile_base,
442+
hidden_profile=self._config.terminator_profile_hidden,
443+
):
444+
return None
320445

321446
switched = self._run(
322447
"remotinator",
@@ -329,7 +454,7 @@ def apply(self) -> Optional[tuple[str, str]]:
329454
if not switched:
330455
return None
331456

332-
return (terminal_uuid, self._config.terminator_profile_base)
457+
return (terminal_uuid, restore_profile)
333458

334459
def restore(self, state: Optional[tuple[str, str]]) -> None:
335460
"""

src/vulcanai/core/plan_types.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def __str__(self) -> str:
115115
+ f"<{color_value}>{node.timeout_ms} ms</{color_value}>"
116116
)
117117
if node.success_criteria:
118-
# Succes Criteria: <node.success_criteria>
118+
# Success Criteria: <node.success_criteria>
119119
lines.append(
120120
f"\t<{color_tool}>Success Criteria</{color_tool}>: "
121121
+ f"<{color_value}>{node.success_criteria}</{color_value}>"

0 commit comments

Comments
 (0)