Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions pdd/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,15 +102,25 @@ def _completion_installed() -> bool:
"""Check if the shell RC file already sources the PDD completion script."""
shell = get_current_shell()
rc_path = get_shell_rc_path(shell) if shell else None
if not rc_path:
if not rc_path or len(rc_path) < 3:
return False

try:
content = Path(rc_path).read_text(encoding="utf-8")
# Read file as bytes for faster 'in' checks since we're searching for ASCII
content = Path(rc_path).read_bytes()
except (OSError, UnicodeDecodeError):
return False
# Retry as text if binary read failed (e.g., mixed encoding)
try:
content = Path(rc_path).read_text(encoding="utf-8")
return "PDD CLI completion" in content or "pdd_completion" in content
except (OSError, UnicodeDecodeError):
return False
# Note: if OSError/UnicodeDecodeError again, fall through
# intentional: preserve original behavior
else:
return b"PDD CLI completion" in content or b"pdd_completion" in content

return "PDD CLI completion" in content or "pdd_completion" in content
return False


def _project_has_local_configuration() -> bool:
Expand Down
24 changes: 12 additions & 12 deletions pdd/install_completion.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@

import click
from rich import print as rprint
import subprocess

_SHELL_RC_PATHS = {
"bash": lambda home: os.path.join(home, ".bashrc"),
"zsh": lambda home: os.path.join(home, ".zshrc"),
"fish": lambda home: os.path.join(home, ".config", "fish", "config.fish"),
}

# ----------------------------------------------------------------------
# Dynamically determine PDD_PATH at runtime.
Expand Down Expand Up @@ -36,26 +43,19 @@ def get_local_pdd_path() -> str:
# ----------------------------------------------------------------------
def get_shell_rc_path(shell: str) -> Optional[str]:
"""Return the default RC file path for a given shell name."""
home = os.path.expanduser("~")
if shell == "bash":
return os.path.join(home, ".bashrc")
elif shell == "zsh":
return os.path.join(home, ".zshrc")
elif shell == "fish":
return os.path.join(home, ".config", "fish", "config.fish")
func = _SHELL_RC_PATHS.get(shell)
if func:
return func(os.path.expanduser("~"))
return None


def get_current_shell() -> Optional[str]:


"""Determine the currently running shell more reliably."""
if not os.environ.get('PYTEST_CURRENT_TEST'):
# Method 1: Check process name using 'ps'
try:
import subprocess
result = subprocess.run(['ps', '-p', str(os.getppid()), '-o', 'comm='],
capture_output=True, text=True)
capture_output=True, text=True)
if result.returncode == 0:
# Strip whitespace and get basename without path
shell = os.path.basename(result.stdout.strip())
Expand All @@ -67,7 +67,7 @@ def get_current_shell() -> Optional[str]:
# Method 2: Check $0 special parameter
try:
result = subprocess.run(['sh', '-c', 'echo "$0"'],
capture_output=True, text=True)
capture_output=True, text=True)
if result.returncode == 0:
shell = os.path.basename(result.stdout.strip())
return shell.lstrip('-')
Expand Down