Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/autoskillit/cli/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
from autoskillit.core import (
RecipeSource,
atomic_write,
claude_code_project_dir,
)
from autoskillit.execution import _has_active_execution_marker

Expand Down Expand Up @@ -152,11 +151,11 @@ def serve(*, verbose: Annotated[bool, Parameter(name=["--verbose", "-v"])] = Fal
try:
backend_options = {"use_uvloop": sys.platform != "win32"}

_marker_dir: Path | None = None
try:
_marker_dir = claude_code_project_dir(str(ctx.project_dir))
except OSError:
pass
_marker_dir: Path | None = (
ctx.backend.session_locator().project_log_dir(str(ctx.project_dir))
if ctx.backend is not None
else None
)

async def _guarded_serve() -> None:
await serve_with_signal_guard(
Expand Down
66 changes: 65 additions & 1 deletion tests/cli/test_app_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@

from __future__ import annotations

import asyncio
import importlib
import sys
from pathlib import Path
from unittest.mock import MagicMock

import pytest

pytestmark = [pytest.mark.layer("cli"), pytest.mark.small]
pytestmark = [pytest.mark.layer("cli"), pytest.mark.medium]


def test_main_does_not_call_app_after_update_restart(monkeypatch: pytest.MonkeyPatch) -> None:
Expand Down Expand Up @@ -36,3 +39,64 @@ def fake_run_update_checks(**kwargs: object) -> None:
pass

assert not app_called, "app() must not be called when update path exits the process"


def test_serve_activity_check_uses_backend_derived_marker_dir(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
) -> None:
"""serve() must derive _marker_dir via backend.session_locator().project_log_dir()."""
app_module = importlib.import_module("autoskillit.cli.app")

monkeypatch.chdir(tmp_path)

expected_dir = tmp_path / "backend-marker"

mock_backend = MagicMock()
mock_backend.session_locator.return_value.project_log_dir.return_value = expected_dir

mock_ctx = MagicMock()
mock_ctx.project_dir = tmp_path
mock_ctx.backend = mock_backend
mock_ctx.fleet_lock = None

mock_cfg = MagicMock()
mock_cfg.logging.level = "INFO"
mock_cfg.logging.json_output = None
mock_cfg.safety.protected_branches = []

monkeypatch.setattr("autoskillit.config.load_config", lambda _: mock_cfg)
monkeypatch.setattr("autoskillit.core.configure_logging", lambda **kw: None)
monkeypatch.setattr("autoskillit.server.make_context", lambda *a, **kw: mock_ctx)
monkeypatch.setattr("autoskillit.server._initialize", lambda ctx: None)

captured_activity_check: list = []

async def fake_serve_guard(mcp_instance, *, activity_check=None, **kw):
captured_activity_check.append(activity_check)

monkeypatch.setattr(app_module, "serve_with_signal_guard", fake_serve_guard)
monkeypatch.setattr("anyio.run", lambda fn, **kw: asyncio.run(fn()))

from autoskillit.cli.app import serve

serve()

mock_backend.session_locator.return_value.project_log_dir.assert_called_once_with(
str(tmp_path)
)

assert len(captured_activity_check) == 1, "serve_with_signal_guard was not called exactly once"

seen_marker_dirs: list = []
monkeypatch.setattr(
app_module,
"is_server_active",
lambda md, fl: (seen_marker_dirs.append(md), False)[-1],
)

captured_activity_check[0]()
assert seen_marker_dirs == [expected_dir], (
f"activity_check should pass backend-derived marker_dir ({expected_dir}), "
f"got {seen_marker_dirs}"
)
Loading