fix(tool-hint): handle quoted paths in exec hints
Preserve path folding for quoted exec command paths with spaces so hint previews do not fall back to mid-path truncation. Add regression coverage for quoted Unix and Windows path cases. Made-with: Cursor
This commit is contained in:
@@ -19,9 +19,11 @@ _TOOL_FORMATS: dict[str, tuple[list[str], str, bool, bool]] = {
|
|||||||
"list_dir": (["path"], "ls {}", True, False),
|
"list_dir": (["path"], "ls {}", True, False),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Matches file paths embedded in shell commands (Windows drive, ~/, or absolute after space)
|
# Matches file paths embedded in shell commands, including quoted paths with spaces.
|
||||||
_PATH_IN_CMD_RE = re.compile(
|
_PATH_IN_CMD_RE = re.compile(
|
||||||
r"(?:[A-Za-z]:[/\\]|~/|(?<=\s)/)[^\s;&|<>\"']+"
|
r'"(?P<double>(?:[A-Za-z]:[/\\]|~/|/)[^"]+)"'
|
||||||
|
r"|'(?P<single>(?:[A-Za-z]:[/\\]|~/|/)[^']+)'"
|
||||||
|
r"|(?P<bare>(?:[A-Za-z]:[/\\]|~/|(?<=\s)/)[^\s;&|<>\"']+)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -92,9 +94,14 @@ def _fmt_known(tc, fmt: tuple) -> str:
|
|||||||
|
|
||||||
def _abbreviate_command(cmd: str, max_len: int = 40) -> str:
|
def _abbreviate_command(cmd: str, max_len: int = 40) -> str:
|
||||||
"""Abbreviate paths in a command string, then truncate."""
|
"""Abbreviate paths in a command string, then truncate."""
|
||||||
abbreviated = _PATH_IN_CMD_RE.sub(
|
def _replace_path(match: re.Match[str]) -> str:
|
||||||
lambda m: abbreviate_path(m.group(), max_len=25), cmd
|
if match.group("double") is not None:
|
||||||
)
|
return f'"{abbreviate_path(match.group("double"), max_len=25)}"'
|
||||||
|
if match.group("single") is not None:
|
||||||
|
return f"'{abbreviate_path(match.group('single'), max_len=25)}'"
|
||||||
|
return abbreviate_path(match.group("bare"), max_len=25)
|
||||||
|
|
||||||
|
abbreviated = _PATH_IN_CMD_RE.sub(_replace_path, cmd)
|
||||||
if len(abbreviated) <= max_len:
|
if len(abbreviated) <= max_len:
|
||||||
return abbreviated
|
return abbreviated
|
||||||
return abbreviated[:max_len - 1] + "\u2026"
|
return abbreviated[:max_len - 1] + "\u2026"
|
||||||
|
|||||||
@@ -72,6 +72,22 @@ class TestToolHintKnownTools:
|
|||||||
result = _hint([_tc("exec", {"command": cmd})])
|
result = _hint([_tc("exec", {"command": cmd})])
|
||||||
assert "\u2026/" in result
|
assert "\u2026/" in result
|
||||||
|
|
||||||
|
def test_exec_abbreviates_quoted_linux_paths_with_spaces(self):
|
||||||
|
"""Quoted Unix paths with spaces should still be folded."""
|
||||||
|
cmd = 'cd "/home/user/My Documents/project" && pytest tests/'
|
||||||
|
result = _hint([_tc("exec", {"command": cmd})])
|
||||||
|
assert "\u2026/" in result
|
||||||
|
assert '"/home/user/My Documents/project"' not in result
|
||||||
|
assert '"' in result
|
||||||
|
|
||||||
|
def test_exec_abbreviates_quoted_windows_paths_with_spaces(self):
|
||||||
|
"""Quoted Windows paths with spaces should still be folded."""
|
||||||
|
cmd = 'cd "C:/Program Files/Git/project" && git status'
|
||||||
|
result = _hint([_tc("exec", {"command": cmd})])
|
||||||
|
assert "\u2026/" in result
|
||||||
|
assert '"C:/Program Files/Git/project"' not in result
|
||||||
|
assert '"' in result
|
||||||
|
|
||||||
def test_exec_short_command_unchanged(self):
|
def test_exec_short_command_unchanged(self):
|
||||||
result = _hint([_tc("exec", {"command": "npm install typescript"})])
|
result = _hint([_tc("exec", {"command": "npm install typescript"})])
|
||||||
assert result == "$ npm install typescript"
|
assert result == "$ npm install typescript"
|
||||||
|
|||||||
Reference in New Issue
Block a user