fix(cli): setup summary reflects actual CLAUDE.md write outcome (True/False return)

This commit is contained in:
ZdenekSrotyr 2026-05-04 07:17:37 +02:00
parent c0aa278c67
commit 297d07f2a1
2 changed files with 61 additions and 11 deletions

View file

@ -301,11 +301,15 @@ def _init_claude_workspace(
workspace: Path,
server_url: str = "",
token: str = "",
) -> None:
) -> bool:
"""Initialise the .claude/ directory with placeholder files and hooks.
Writes CLAUDE.md from the server (GET /api/welcome) unless ``server_url``
or ``token`` are empty, or the request fails (graceful degradation).
Returns True if CLAUDE.md was written from the server, False otherwise
(skipped because caller passed empty server_url/token, or fetch failed
in the latter case a warning was already printed to stderr).
"""
local_md = workspace / ".claude" / "CLAUDE.local.md"
if not local_md.exists():
@ -326,15 +330,19 @@ def _init_claude_workspace(
# Write CLAUDE.md from the server
if server_url and token:
_write_claude_md(workspace, server_url, token)
return _write_claude_md(workspace, server_url, token)
return False
def _write_claude_md(workspace: Path, server_url: str, token: str) -> None:
def _write_claude_md(workspace: Path, server_url: str, token: str) -> bool:
"""Fetch the rendered CLAUDE.md from the server and write it to the workspace.
Gracefully handles:
- 404: older server without the endpoint skip with warning.
- Other HTTP errors / network errors skip with warning.
Returns True iff a non-empty CLAUDE.md was successfully written; False on
any skipped or failed path (the caller already saw a stderr warning).
"""
from urllib.parse import urlencode
import httpx
@ -353,27 +361,30 @@ def _write_claude_md(workspace: Path, server_url: str, token: str) -> None:
"Warning: server does not support CLAUDE.md generation (older version). Skipping.",
err=True,
)
return
return False
if resp.status_code == 401 or resp.status_code == 403:
typer.echo(
f"Warning: CLAUDE.md fetch failed ({resp.status_code} {resp.reason_phrase}). Skipping.",
err=True,
)
return
return False
resp.raise_for_status()
data = resp.json()
content = data.get("content", "")
if content:
(workspace / "CLAUDE.md").write_text(content, encoding="utf-8")
else:
typer.echo("Warning: server returned empty CLAUDE.md content. Skipping.", err=True)
return True
typer.echo("Warning: server returned empty CLAUDE.md content. Skipping.", err=True)
return False
except httpx.HTTPStatusError as e:
typer.echo(
f"Warning: CLAUDE.md fetch failed (HTTP {e.response.status_code}). Skipping.",
err=True,
)
return False
except Exception as e:
typer.echo(f"Warning: CLAUDE.md fetch failed: {e}. Skipping.", err=True)
return False
# ---------------------------------------------------------------------------
@ -443,7 +454,7 @@ def setup(
# 7. Initialise Claude workspace (.claude/ hooks + placeholder + CLAUDE.md)
typer.echo("Initializing Claude workspace...")
_init_claude_workspace(
claude_md_written = _init_claude_workspace(
workspace,
server_url=server_url if not no_claude_md else "",
token=token if not no_claude_md else "",
@ -456,8 +467,12 @@ def setup(
typer.echo(f" Tables : {n_downloaded} downloaded, {total_rows} total rows")
typer.echo(f" Workspace: {workspace}")
typer.echo(f" Hooks : SessionStart/End installed in {workspace}/.claude/settings.json")
if not no_claude_md:
if no_claude_md:
typer.echo(f" CLAUDE.md: skipped (--no-claude-md)")
elif claude_md_written:
typer.echo(f" CLAUDE.md: written from server template")
else:
typer.echo(f" CLAUDE.md: skipped (see warnings above)")
typer.echo("")
typer.echo("Next steps:")
typer.echo(" da sync — refresh data")

View file

@ -153,7 +153,8 @@ class TestInitClaudeWorkspace:
)
def test_writes_claude_md_when_server_returns_200(self, tmp_workspace):
"""When /api/welcome returns 200, CLAUDE.md is written."""
"""When /api/welcome returns 200, CLAUDE.md is written and the helper
returns True so the caller can label its summary line accurately."""
from cli.commands.analyst import _create_workspace, _init_claude_workspace
from unittest.mock import MagicMock, patch
@ -165,12 +166,46 @@ class TestInitClaudeWorkspace:
mock_resp.raise_for_status = MagicMock()
with patch("cli.commands.analyst.httpx.get", return_value=mock_resp):
_init_claude_workspace(tmp_workspace, server_url="https://example.com", token="tok")
written = _init_claude_workspace(
tmp_workspace, server_url="https://example.com", token="tok"
)
assert written is True
claude_md = tmp_workspace / "CLAUDE.md"
assert claude_md.exists()
assert "My CLAUDE.md" in claude_md.read_text(encoding="utf-8")
def test_returns_false_when_server_returns_404(self, tmp_workspace):
"""When the server is too old for /api/welcome (404), the helper
returns False so the caller can render the summary as 'skipped' instead
of contradicting its own stderr warning."""
from cli.commands.analyst import _create_workspace, _init_claude_workspace
from unittest.mock import MagicMock, patch
_create_workspace(tmp_workspace)
mock_resp = MagicMock()
mock_resp.status_code = 404
mock_resp.raise_for_status = MagicMock()
with patch("cli.commands.analyst.httpx.get", return_value=mock_resp):
written = _init_claude_workspace(
tmp_workspace, server_url="https://example.com", token="tok"
)
assert written is False
assert not (tmp_workspace / "CLAUDE.md").exists()
def test_returns_false_when_no_server_url(self, tmp_workspace):
"""Caller passing empty server_url/token (e.g. --no-claude-md) gets False back."""
from cli.commands.analyst import _create_workspace, _init_claude_workspace
_create_workspace(tmp_workspace)
written = _init_claude_workspace(tmp_workspace, server_url="", token="")
assert written is False
assert not (tmp_workspace / "CLAUDE.md").exists()
def test_does_not_write_claude_md_when_no_claude_md_flag(self, tmp_workspace):
"""When server_url/token are empty (--no-claude-md path), CLAUDE.md is not written."""
from cli.commands.analyst import _create_workspace, _init_claude_workspace