Skip to content

Commit 44a83fc

Browse files
shuvebclaude
andcommitted
Fix auth commands requiring project root and silent ralph exit
Auth commands (login, logout, status, refresh) now fall back to the current working directory when no project root is found, instead of erroring out. This allows `mfbt auth login` to work from any directory. Fix typer.Exit (click.exceptions.Exit) not being caught by `except SystemExit` in the ralph command and handle_errors, which caused silent exit with code 1 and no error message. Bump version to 0.1.2. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 5f6bb1f commit 44a83fc

File tree

5 files changed

+24
-20
lines changed

5 files changed

+24
-20
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "mfbt-cli"
7-
version = "0.1.0"
7+
version = "0.1.2"
88
description = "CLI tool for the mfbt platform — interactive TUI and subcommands for managing mfbt projects"
99
readme = "README.md"
1010
requires-python = ">=3.10"

src/mfbt/cli.py

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,14 @@ def login(
134134
start_callback_server,
135135
wait_for_callback,
136136
)
137+
from pathlib import Path
138+
137139
from mfbt.config import find_project_root, init_mfbt_dir, resolve_config
138140

139-
# Resolve configuration
141+
# Resolve configuration — fall back to cwd if no project root found
140142
project_root = find_project_root()
141143
if project_root is None:
142-
console.print(
143-
"[red]Error:[/red] Could not find a project root. "
144-
"Run this command from within a project directory."
145-
)
146-
raise typer.Exit(code=1)
144+
project_root = Path.cwd()
147145

148146
init_mfbt_dir(project_root)
149147
config = resolve_config(project_root)
@@ -244,17 +242,15 @@ def login(
244242
def _resolve_project() -> tuple:
245243
"""Resolve project root, init .mfbt dir, and return (project_root, config).
246244
247-
Exits with code 1 if no project root found.
245+
Falls back to the current working directory if no project root is found.
248246
"""
247+
from pathlib import Path
248+
249249
from mfbt.config import find_project_root, init_mfbt_dir, resolve_config
250250

251251
project_root = find_project_root()
252252
if project_root is None:
253-
console.print(
254-
"[red]Error:[/red] Could not find a project root. "
255-
"Run this command from within a project directory."
256-
)
257-
raise typer.Exit(code=1)
253+
project_root = Path.cwd()
258254

259255
init_mfbt_dir(project_root)
260256
config = resolve_config(project_root)

src/mfbt/commands/ralph/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ def ralph(
163163
# Resolve phase
164164
try:
165165
phase_id, phase_title = resolve_phase_id(client, project_id, phase)
166-
except SystemExit:
166+
except (SystemExit, typer.Exit):
167167
if phase is not None:
168168
display.error(
169169
f"Phase '{phase}' not found. "

src/mfbt/error_handler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import traceback
1111
from collections.abc import Iterator, Sequence
1212

13+
import click.exceptions
1314
import typer
1415
from rich.console import Console
1516

@@ -179,6 +180,8 @@ def handle_errors(
179180
if verbose:
180181
_print_verbose(console, exc)
181182
raise typer.Exit(code=1) from exc
183+
except click.exceptions.Exit:
184+
raise
182185
except Exception as exc:
183186
# Import here to avoid circular dependency
184187
from mfbt.auth import AuthError

tests/unit/test_cli.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,15 @@ def test_no_args_shows_help(self) -> None:
5656

5757

5858
class TestLoginCommand:
59-
def test_no_project_root_exits(self) -> None:
60-
with patch("mfbt.config.find_project_root", return_value=None):
59+
def test_no_project_root_falls_back_to_cwd(self) -> None:
60+
"""Auth login should not error with 'project root' when none found."""
61+
with (
62+
patch("mfbt.config.find_project_root", return_value=None),
63+
patch("mfbt.auth.start_callback_server", side_effect=Exception("stop")),
64+
):
6165
result = runner.invoke(app, ["auth", "login"])
62-
assert result.exit_code == 1
63-
assert "project root" in result.output.lower()
66+
# Should not fail with "project root" error — it falls back to cwd
67+
assert "project root" not in result.output.lower()
6468

6569

6670
# ---------------------------------------------------------------------------
@@ -263,10 +267,11 @@ def test_successful_logout(self, mock_resolve: MagicMock, tmp_path: Path) -> Non
263267
assert result.exit_code == 0
264268
assert "Logged out" in result.output
265269

266-
def test_logout_no_project_root(self) -> None:
270+
def test_logout_no_project_root_falls_back_to_cwd(self) -> None:
271+
"""Auth logout should not error out when no project root is found."""
267272
with patch("mfbt.config.find_project_root", return_value=None):
268273
result = runner.invoke(app, ["auth", "logout"])
269-
assert result.exit_code == 1
274+
assert "project root" not in result.output.lower()
270275

271276

272277
# ---------------------------------------------------------------------------

0 commit comments

Comments
 (0)