fix(cli): setup summary reflects actual CLAUDE.md write outcome (True/False return)
This commit is contained in:
parent
c0aa278c67
commit
297d07f2a1
2 changed files with 61 additions and 11 deletions
|
|
@ -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:
|
||||
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")
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue