|
18 | 18 |
|
19 | 19 | from prism import __version__ |
20 | 20 | from prism.analyzer import analyze_project, score_to_grade |
21 | | -from prism.parser import CLAUDE_PROJECTS_DIR, ProjectInfo, discover_projects, parse_session_file |
| 21 | +from prism.parser import ( |
| 22 | + CLAUDE_PROJECTS_DIR, |
| 23 | + ProjectInfo, |
| 24 | + discover_projects, |
| 25 | + parse_session_file, |
| 26 | + project_path_to_encoded_name, |
| 27 | +) |
22 | 28 |
|
23 | 29 | # Force UTF-8 output on Windows so Unicode symbols render correctly |
24 | 30 | if hasattr(sys.stdout, "reconfigure"): |
@@ -227,6 +233,7 @@ def advise_cmd( |
227 | 233 | "--project", |
228 | 234 | "-p", |
229 | 235 | help="Path to a specific project directory.", |
| 236 | + exists=False, |
230 | 237 | ), |
231 | 238 | apply: bool = typer.Option( |
232 | 239 | False, |
@@ -393,22 +400,58 @@ def _resolve_projects( |
393 | 400 | project_path: Path | None, |
394 | 401 | base_dir: Path | None, |
395 | 402 | ) -> list[ProjectInfo]: |
396 | | - """Return a list of ProjectInfo objects based on CLI options.""" |
| 403 | + """Return a list of ProjectInfo objects based on CLI options. |
| 404 | +
|
| 405 | + The ``--project`` flag accepts any of: |
| 406 | +
|
| 407 | + * The actual path to a Claude Code project directory (inside |
| 408 | + ``~/.claude/projects/``), e.g. ``~/.claude/projects/D--jarvis-space``. |
| 409 | + * The real absolute path of the user's workspace on any OS, e.g. |
| 410 | + ``D:\\jarvis\\space`` or ``/home/user/proj``. The path is encoded |
| 411 | + to the Claude Code convention and looked up under *effective_base*. |
| 412 | + * The display name shown in the projects table, e.g. ``D//jarvis/space`` |
| 413 | + or ``/home/user/proj``. Forward-slash normalisation by ``pathlib`` |
| 414 | + means this is equivalent to passing the real path on most systems. |
| 415 | + """ |
397 | 416 | effective_base = base_dir or CLAUDE_PROJECTS_DIR |
398 | 417 |
|
399 | 418 | if project_path is not None: |
400 | | - # User pointed at a specific project directory |
| 419 | + # Strategy 1: the argument is already a Claude Code project directory |
| 420 | + # that contains JSONL session files — use it directly. |
401 | 421 | if project_path.is_dir(): |
402 | | - from prism.parser import ProjectInfo |
403 | | - sessions = sorted(project_path.glob("*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True) |
| 422 | + sessions = sorted( |
| 423 | + project_path.glob("*.jsonl"), |
| 424 | + key=lambda p: p.stat().st_mtime, |
| 425 | + reverse=True, |
| 426 | + ) |
| 427 | + if sessions: |
| 428 | + return [ProjectInfo( |
| 429 | + encoded_name=project_path.name, |
| 430 | + project_dir=project_path, |
| 431 | + session_files=sessions, |
| 432 | + )] |
| 433 | + |
| 434 | + # Strategy 2: interpret the argument as a real path or display name |
| 435 | + # and look up the corresponding encoded directory inside effective_base. |
| 436 | + encoded = project_path_to_encoded_name(str(project_path)) |
| 437 | + candidate = effective_base / encoded |
| 438 | + if candidate.is_dir(): |
| 439 | + sessions = sorted( |
| 440 | + candidate.glob("*.jsonl"), |
| 441 | + key=lambda p: p.stat().st_mtime, |
| 442 | + reverse=True, |
| 443 | + ) |
404 | 444 | return [ProjectInfo( |
405 | | - encoded_name=project_path.name, |
406 | | - project_dir=project_path, |
| 445 | + encoded_name=encoded, |
| 446 | + project_dir=candidate, |
407 | 447 | session_files=sessions, |
408 | 448 | )] |
409 | | - else: |
410 | | - err_console.print(f"[red]Project path is not a directory: {project_path}[/red]") |
411 | | - return [] |
| 449 | + |
| 450 | + err_console.print(f"[red]Project not found: {project_path}[/red]") |
| 451 | + err_console.print( |
| 452 | + f"[dim]Tried encoded name '{encoded}' in {effective_base}[/dim]" |
| 453 | + ) |
| 454 | + return [] |
412 | 455 |
|
413 | 456 | return discover_projects(effective_base) |
414 | 457 |
|
|
0 commit comments