@@ -4512,6 +4512,10 @@ def post_read(payload: HookPayload) -> HookResponse:
45124512_VERBOSE_TEST_MIN_LINES : int = 80
45134513#: Minimum line count before cargo compilation output compression fires.
45144514_CARGO_COMPILE_MIN_LINES : int = 40
4515+ #: Minimum line count before tsc output compression fires.
4516+ _TSC_MIN_LINES : int = 50
4517+ #: Matches position-less tsc --build errors/warnings (no ``(row,col)`` token).
4518+ _TSC_BARE_DIAG_RE : _re .Pattern [str ] = _re .compile (r"^(error|warning) TS\d+:" )
45154519#: Matches the base command name of directory-exploration invocations (ls, eza, tree, fd).
45164520_RECON_CMD_RE : _re .Pattern [str ] = _re .compile (r"^(?:ls|ll|la|eza|exa|tree|fd|fdfind)\b" )
45174521
@@ -6039,6 +6043,99 @@ def post_bash(payload: HookPayload) -> HookResponse:
60396043 except Exception : # noqa: BLE001 — fail-soft; never block the hook
60406044 _LOG .debug ("post-bash: cargo compile compression failed" , exc_info = True )
60416045
6046+ # tsc compression: strip timestamp/watch noise, keep diagnostics + summary; fires at >= _TSC_MIN_LINES lines.
6047+ if stdout and len (stdout .splitlines ()) >= _TSC_MIN_LINES :
6048+ try :
6049+ import re as _re_tsc # noqa: PLC0415
6050+ import shlex as _shlex_tsc # noqa: PLC0415
6051+ import sys as _sys_tsc # noqa: PLC0415
6052+
6053+ from .bash_compress import _is_tsc_cmd as _tsc_check # noqa: PLC0415
6054+
6055+ _tsc_argv = _shlex_tsc .split (display_cmd , posix = (_sys_tsc .platform != "win32" ))
6056+ if _tsc_argv and _tsc_check (_tsc_argv ):
6057+ _TSC_DIAG_RE = _re_tsc .compile (r"^[^\s].+\(\d+,\d+\): (error|warning) TS\d+:" )
6058+ _TSC_SUMMARY_RE = _re_tsc .compile (r"^Found \d+ errors?\." )
6059+ _tsc_lines = stdout .splitlines ()
6060+ _tsc_total = len (_tsc_lines )
6061+ _tsc_diag_lines : list [str ] = []
6062+ _tsc_noise_lines : list [str ] = []
6063+ _tsc_summary : str | None = None
6064+ for _tsc_line in _tsc_lines :
6065+ if _TSC_SUMMARY_RE .match (_tsc_line ):
6066+ _tsc_summary = _tsc_line
6067+ elif _TSC_DIAG_RE .match (_tsc_line ) or _TSC_BARE_DIAG_RE .match (_tsc_line ):
6068+ _tsc_diag_lines .append (_tsc_line )
6069+ else :
6070+ _tsc_noise_lines .append (_tsc_line )
6071+
6072+ if len (_tsc_noise_lines ) == 0 :
6073+ pass # nothing to suppress — fall through
6074+ else :
6075+ _tsc_out_id : str | None = None
6076+ if session_id :
6077+ from . import bash_cache as _bc_tsc # noqa: PLC0415
6078+ with contextlib .suppress (Exception ):
6079+ _tsc_meta = _bc_tsc .store_output (
6080+ session_id , display_cmd , stdout , stderr , exit_code ,
6081+ cwd = cwd , min_cache_bytes = 0 ,
6082+ )
6083+ if _tsc_meta is not None :
6084+ _bc_tsc .write_sidecar (_tsc_meta )
6085+ _tsc_out_id = _tsc_meta .output_id
6086+ _tsc_recall = (f"\n [Full output: bash-output { _tsc_out_id } ]" if _tsc_out_id else "" )
6087+
6088+ if not _tsc_diag_lines and exit_code in (None , 0 ):
6089+ # Clean build with verbose/timestamp noise only
6090+ _tsc_summary_line = _tsc_summary or next (
6091+ (_l for _l in reversed (_tsc_lines ) if _l .strip ()), None
6092+ )
6093+ _tsc_body = (_tsc_summary_line + "\n " ) if _tsc_summary_line else ""
6094+ if not stdout .endswith (("\n " , "\r \n " )) and _tsc_body .endswith ("\n " ):
6095+ _tsc_body = _tsc_body .rstrip ("\n " )
6096+ _tsc_suppressed = _tsc_total - (1 if _tsc_summary_line else 0 )
6097+ _tsc_msg = (
6098+ f"[token-goat] tsc: 0 errors, 0 warnings"
6099+ f" ({ _tsc_suppressed } /{ _tsc_total } lines suppressed)\n "
6100+ + _tsc_body
6101+ + _tsc_recall
6102+ )
6103+ else :
6104+ # Has diagnostics — keep all, strip noise lines
6105+ _tsc_error_count = sum (
6106+ 1 for _l in _tsc_diag_lines if _re_tsc .search (r": error TS\d+:" , _l )
6107+ )
6108+ _tsc_warn_count = sum (
6109+ 1 for _l in _tsc_diag_lines
6110+ if _re_tsc .search (r": warning TS\d+:" , _l )
6111+ )
6112+ _tsc_body_lines = list (_tsc_diag_lines )
6113+ if _tsc_summary and (
6114+ not _tsc_body_lines or _tsc_body_lines [- 1 ] != _tsc_summary
6115+ ):
6116+ _tsc_body_lines .append (_tsc_summary )
6117+ _tsc_body = "\n " .join (_tsc_body_lines )
6118+ if stdout .endswith (("\n " , "\r \n " )):
6119+ _tsc_body += "\n "
6120+ _tsc_suppressed = _tsc_total - len (_tsc_body_lines )
6121+ _tsc_msg = (
6122+ f"[token-goat] tsc: { _tsc_error_count } errors,"
6123+ f" { _tsc_warn_count } warnings"
6124+ f" ({ _tsc_suppressed } /{ _tsc_total } lines suppressed)\n "
6125+ + _tsc_body
6126+ + _tsc_recall
6127+ )
6128+ _LOG .info (
6129+ "post-bash: tsc compressed lines=%d diag=%d cmd=%.60s" ,
6130+ _tsc_total , len (_tsc_diag_lines ), display_cmd ,
6131+ )
6132+ if _sess_mod is not None and _session_cache is not None :
6133+ with contextlib .suppress (Exception ):
6134+ _sess_mod .save (_session_cache )
6135+ return {"continue" : True , "systemMessage" : _tsc_msg }
6136+ except Exception : # noqa: BLE001 — fail-soft; never block the hook
6137+ _LOG .debug ("post-bash: tsc compression failed" , exc_info = True )
6138+
60426139 # Pytest failure traceback suppression (Iter 18):
60436140 # Fires when pytest output is large (>= _PYTEST_COMPRESS_MIN_BYTES) and contains
60446141 # FAILED markers. Stores full output in bash-cache first so ``bash-output <id>``
0 commit comments