fix: SFTP paths on Windows — double-slash for remote, remove broken MSYS env

- Remote paths in upload/download require // prefix on Windows/Git Bash
- Removed useless MSYS_NO_PATHCONV from Python (must be shell-level)
- Removed broken bash wrapper
- Updated skill docs with // path rule and examples

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
chrome-storm-c442
2026-02-24 07:14:49 -05:00
parent d7101a12d2
commit e75afddb99
2 changed files with 12 additions and 7 deletions

View File

@@ -48,13 +48,15 @@ python ~/.server-connections/ssh.py ALIAS --no-sudo "command"
### Загрузить файл на сервер
```bash
python ~/.server-connections/ssh.py ALIAS --upload /local/path /remote/path
python ~/.server-connections/ssh.py ALIAS --upload "D:/path/local/file" //remote/path/file
```
**ВАЖНО (Windows/Git Bash):** remote path ОБЯЗАТЕЛЬНО с двойным слешем `//home/...`, `//tmp/...`. Одинарный `/` будет сконвертирован Git Bash в Windows-путь и сломает SFTP.
### Скачать файл с сервера
```bash
python ~/.server-connections/ssh.py ALIAS --download /remote/path /local/path
python ~/.server-connections/ssh.py ALIAS --download //remote/path/file "D:/path/local/file"
```
Remote path тоже с `//`.
### Установить SSH-ключ на сервер
```bash

View File

@@ -1,6 +1,4 @@
#!/usr/bin/env python3
# Disable MSYS path conversion to prevent Windows path conversion issues
os.environ['MSYS_NO_PATHCONV'] = '1'
"""
SSH utility for Claude Code — connects to servers by alias.
Credentials stored locally in servers.json (encrypted), NEVER exposed to AI API.
@@ -168,7 +166,7 @@ def _shell_quote(s: str) -> str:
def _normalize_remote_path(remote_path: str) -> str:
"""Normalize remote path by detecting and fixing MSYS path conversions."""
# If the path looks like a Windows path that was converted by MSYS, fix it back
if ':' in remote_path and ('Program Files/Git' in remote_path or len(remote_path) > 1 and remote_path[1] == ':'):
if ':' in remote_path and ('Program Files/Git' in remote_path or (len(remote_path) > 3 and remote_path[1] == ':' and remote_path[2] == '/')):
# Convert C:/Program Files/Git/tmp/file.txt back to /tmp/file.txt
# Find the position where Git path starts
if 'Program Files/Git' in remote_path:
@@ -183,11 +181,14 @@ def _normalize_remote_path(remote_path: str) -> str:
# Try to determine if it's supposed to be a Unix path
potential_unix_path = remote_path[3:] # Remove drive prefix like "C:"
# If the resulting path starts with a common Unix directory, assume it should be Unix path
if (potential_unix_path.startswith('/tmp/') or potential_unix_path.startswith('/home/') or potential_unix_path.startswith('/etc/') or potential_unix_path.startswith('/var/') or potential_unix_path.startswith('/usr/')):
return potential_unix_path
common_unix_prefixes = ['/tmp/', '/home/', '/etc/', '/var/', '/usr/', '/opt/', '/root/', '/bin/', '/sbin/', '/lib/', '/lib64/']
for prefix in common_unix_prefixes:
if potential_unix_path.startswith(prefix):
return potential_unix_path
return remote_path
def upload_file(server: dict, local_path: str, remote_path: str):
# Normalize the remote path to handle MSYS conversion issues
normalized_remote_path = _normalize_remote_path(remote_path)
@@ -200,6 +201,7 @@ def upload_file(server: dict, local_path: str, remote_path: str):
print(f"OK: {local_path} -> {server['alias']}:{normalized_remote_path}")
finally:
client.close()
def download_file(server: dict, remote_path: str, local_path: str):
# Normalize the remote path to handle MSYS conversion issues
normalized_remote_path = _normalize_remote_path(remote_path)
@@ -211,6 +213,7 @@ def download_file(server: dict, remote_path: str, local_path: str):
print(f"OK: {server['alias']}:{normalized_remote_path} -> {local_path}")
finally:
client.close()
def install_key(server: dict):
pub_key_path = SSH_KEY_PATH + ".pub"
if not os.path.exists(pub_key_path):