fix: add helpful error message when Python dependencies are missing (ACS-145) (#755)

* fix: add helpful error message when Python dependencies are missing

When running runner scripts (spec_runner, insights_runner, etc.) without
the virtual environment activated, users would get a cryptic
ModuleNotFoundError for 'dotenv' or other dependencies.

This fix adds a try-except around the dotenv import that provides a clear
error message explaining:
- The issue is likely due to not using the virtual environment
- How to activate the venv (Linux/macOS/Windows)
- How to install dependencies directly
- Shows the current Python executable being used

Also fixes CLI-USAGE.md which had incorrect paths for spec_runner.py
(the file is in runners/, not the backend root).

Related to: ACS-145

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* fix: improve error messages with explicit package name and requirements path

- cli/utils.py: Explicitly mention 'python-dotenv' and add 'pip install python-dotenv' option
- insights_runner.py: Use full path 'apps/backend/requirements.txt' for clarity

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* refactor: centralize dotenv import error handling

- Create shared import_dotenv() function in cli/utils.py
- Update all runner scripts to use centralized function
- Removes ~73 lines of duplicate code across 6 files
- Ensures consistent error messaging (mentions python-dotenv explicitly)
- Fixes path inconsistency in insights_runner.py

Addresses CodeRabbit feedback about DRY principle violations.

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* style: fix import ordering to satisfy ruff I001 rule

Add blank lines to separate local imports and function calls from
third-party imports, properly delineating import groups.

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* style: auto-fix ruff I001 import ordering

Ruff auto-fixed by adding blank line after 'from cli.utils import import_dotenv'
to properly separate the import from the function call.

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* style: apply ruff formatting to cli/utils.py

- Add blank line after import statement
- Use double quotes instead of single quotes

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

* refactor: return load_dotenv instead of mutating sys.modules

- Change import_dotenv() to return load_dotenv callable
- Remove sys.modules mutation for cleaner approach
- Update callers to do: load_dotenv = import_dotenv()
- Fixes ruff I001 import ordering violations
- Preserves same error message on ImportError

Addresses CodeRabbit feedback about import-order complexity.

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>

---------

Signed-off-by: StillKnotKnown <stillknotknown@users.noreply.github.com>
Co-authored-by: StillKnotKnown <stillknotknown@users.noreply.github.com>
Co-authored-by: Alex <63423455+AlexMadera@users.noreply.github.com>
This commit is contained in:
StillKnotKnown
2026-01-07 08:19:24 +02:00
committed by GitHub
parent f406959094
commit 31519c2a10
8 changed files with 73 additions and 22 deletions
+40 -1
View File
@@ -15,7 +15,46 @@ if str(_PARENT_DIR) not in sys.path:
sys.path.insert(0, str(_PARENT_DIR))
from core.auth import get_auth_token, get_auth_token_source
from dotenv import load_dotenv
def import_dotenv():
"""
Import and return load_dotenv with helpful error message if not installed.
This centralized function ensures consistent error messaging across all
runner scripts when python-dotenv is not available.
Returns:
The load_dotenv function
Raises:
SystemExit: If dotenv cannot be imported, with helpful installation instructions.
"""
try:
from dotenv import load_dotenv as _load_dotenv
return _load_dotenv
except ImportError:
sys.exit(
"Error: Required Python package 'python-dotenv' is not installed.\n"
"\n"
"This usually means you're not using the virtual environment.\n"
"\n"
"To fix this:\n"
"1. From the 'apps/backend/' directory, activate the venv:\n"
" source .venv/bin/activate # Linux/macOS\n"
" .venv\\Scripts\\activate # Windows\n"
"\n"
"2. Or install dependencies directly:\n"
" pip install python-dotenv\n"
" pip install -r requirements.txt\n"
"\n"
f"Current Python: {sys.executable}\n"
)
# Load .env with helpful error if dependencies not installed
load_dotenv = import_dotenv()
from graphiti_config import get_graphiti_status
from linear_integration import LinearManager
from linear_updater import is_linear_enabled
+4 -2
View File
@@ -56,8 +56,10 @@ if sys.platform == "win32":
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
# Load .env file
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent.parent / ".env"
if env_file.exists():
+4 -2
View File
@@ -26,8 +26,10 @@ from pathlib import Path
# Add backend to path
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
# Load .env file
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent.parent / ".env"
if env_file.exists():
+4 -2
View File
@@ -26,8 +26,10 @@ from pathlib import Path
# Add auto-claude to path
sys.path.insert(0, str(Path(__file__).parent.parent))
# Load .env file from auto-claude/ directory
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent / ".env"
if env_file.exists():
+4 -2
View File
@@ -15,8 +15,10 @@ from pathlib import Path
# Add auto-claude to path
sys.path.insert(0, str(Path(__file__).parent.parent))
# Load .env file from auto-claude/ directory
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent / ".env"
if env_file.exists():
+4 -2
View File
@@ -20,8 +20,10 @@ from pathlib import Path
# Add auto-claude to path
sys.path.insert(0, str(Path(__file__).parent.parent))
# Load .env file from auto-claude/ directory
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent / ".env"
if env_file.exists():
+9 -7
View File
@@ -26,11 +26,11 @@ The AI considers:
- Risk factors and edge cases
Usage:
python auto-claude/spec_runner.py --task "Add user authentication"
python auto-claude/spec_runner.py --interactive
python auto-claude/spec_runner.py --continue 001-feature
python auto-claude/spec_runner.py --task "Fix button color" --complexity simple
python auto-claude/spec_runner.py --task "Simple fix" --no-ai-assessment
python runners/spec_runner.py --task "Add user authentication"
python runners/spec_runner.py --interactive
python runners/spec_runner.py --continue 001-feature
python runners/spec_runner.py --task "Fix button color" --complexity simple
python runners/spec_runner.py --task "Simple fix" --no-ai-assessment
"""
import sys
@@ -81,8 +81,10 @@ if sys.platform == "win32":
# Add auto-claude to path (parent of runners/)
sys.path.insert(0, str(Path(__file__).parent.parent))
# Load .env file
from dotenv import load_dotenv
# Load .env file with centralized error handling
from cli.utils import import_dotenv
load_dotenv = import_dotenv()
env_file = Path(__file__).parent.parent / ".env"
dev_env_file = Path(__file__).parent.parent.parent / "dev" / "auto-claude" / ".env"
+4 -4
View File
@@ -74,16 +74,16 @@ All commands below should be run from the `apps/backend/` directory:
source .venv/bin/activate
# Create a spec interactively
python spec_runner.py --interactive
python runners/spec_runner.py --interactive
# Or with a task description
python spec_runner.py --task "Add user authentication with OAuth"
python runners/spec_runner.py --task "Add user authentication with OAuth"
# Force a specific complexity level
python spec_runner.py --task "Fix button color" --complexity simple
python runners/spec_runner.py --task "Fix button color" --complexity simple
# Continue an interrupted spec
python spec_runner.py --continue 001-feature
python runners/spec_runner.py --continue 001-feature
```
### Complexity Tiers