Add project-aware chat startup defaults

Make repo-root chat startup native by letting MCP servers carry a default project source for workspace creation. When a chat host starts from a Git checkout, workspace_create can now omit seed_path and inherit the server startup source; explicit --project-path and clean-clone --repo-url/--repo-ref paths are supported as fallbacks.

Add project startup resolution and materialization, surface origin_kind/origin_ref in workspace_seed, update chat-host docs and the repro/fix smoke to use project-aware workspace creation, and switch dist-check to uv run pyro so verification stays stable after uv reinstalls.

Validated with uv lock, focused startup/server/CLI pytest coverage, UV_CACHE_DIR=.uv-cache make check, UV_CACHE_DIR=.uv-cache make dist-check, and real guest-backed smokes for both explicit project_path and bare repo-root auto-detection.
This commit is contained in:
Thales Maciel 2026-03-13 15:51:47 -03:00
parent 9b9b83ebeb
commit 535efc6919
28 changed files with 968 additions and 67 deletions

View file

@ -73,6 +73,12 @@ def test_cli_subcommand_help_includes_examples_and_guidance() -> None:
assert "recommended first profile for most chat hosts" in mcp_help
assert "workspace-core: default for normal persistent chat editing" in mcp_help
assert "workspace-full: larger opt-in surface" in mcp_help
assert "--project-path" in mcp_help
assert "--repo-url" in mcp_help
assert "--repo-ref" in mcp_help
assert "--no-project-source" in mcp_help
assert "pyro mcp serve --project-path ." in mcp_help
assert "pyro mcp serve --repo-url https://github.com/example/project.git" in mcp_help
workspace_help = _subparser_choice(parser, "workspace").format_help()
assert "Use the workspace model when you need one sandbox to stay alive" in workspace_help
@ -2825,25 +2831,30 @@ def test_chat_host_docs_and_examples_recommend_workspace_core() -> None:
assert claude_cmd in readme
assert codex_cmd in readme
assert "examples/opencode_mcp_config.json" in readme
assert "Bare `pyro mcp serve` starts `workspace-core`" in readme
assert "bare `pyro mcp serve` starts `workspace-core`" in readme
assert "auto-detects\nthe current Git checkout" in readme
assert "--project-path /abs/path/to/repo" in readme
assert "--repo-url https://github.com/example/project.git" in readme
assert "## 5. Connect a chat host" in install
assert "uvx --from pyro-mcp pyro mcp serve" in install
assert claude_cmd in install
assert codex_cmd in install
assert "workspace-full" in install
assert "--project-path /abs/path/to/repo" in install
assert claude_cmd in first_run
assert codex_cmd in first_run
assert "--project-path /abs/path/to/repo" in first_run
assert (
"Bare `pyro mcp serve` starts `workspace-core`. That is the product path."
in integrations
)
assert "Bare `pyro mcp serve` starts `workspace-core`." in integrations
assert "auto-detects the current Git checkout" in integrations
assert "examples/claude_code_mcp.md" in integrations
assert "examples/codex_mcp.md" in integrations
assert "examples/opencode_mcp_config.json" in integrations
assert "That is the product path." in integrations
assert "--project-path /abs/path/to/repo" in integrations
assert "--repo-url https://github.com/example/project.git" in integrations
assert "Default for most chat hosts in `4.x`: `workspace-core`." in mcp_config
assert "Use the host-specific examples first when they apply:" in mcp_config
@ -2854,10 +2865,12 @@ def test_chat_host_docs_and_examples_recommend_workspace_core() -> None:
assert claude_cmd in claude_code
assert "claude mcp list" in claude_code
assert "workspace-full" in claude_code
assert "--project-path /abs/path/to/repo" in claude_code
assert codex_cmd in codex
assert "codex mcp list" in codex
assert "workspace-full" in codex
assert "--project-path /abs/path/to/repo" in codex
assert opencode == {
"mcp": {
@ -4020,11 +4033,23 @@ def test_cli_run_json_error_exits_nonzero(
def test_cli_mcp_runs_stdio_transport(monkeypatch: pytest.MonkeyPatch) -> None:
observed: dict[str, str] = {}
observed: dict[str, Any] = {}
class StubPyro:
def create_server(self, *, profile: str) -> Any:
def create_server(
self,
*,
profile: str,
project_path: str | None,
repo_url: str | None,
repo_ref: str | None,
no_project_source: bool,
) -> Any:
observed["profile"] = profile
observed["project_path"] = project_path
observed["repo_url"] = repo_url
observed["repo_ref"] = repo_ref
observed["no_project_source"] = no_project_source
return type(
"StubServer",
(),
@ -4033,12 +4058,27 @@ def test_cli_mcp_runs_stdio_transport(monkeypatch: pytest.MonkeyPatch) -> None:
class StubParser:
def parse_args(self) -> argparse.Namespace:
return argparse.Namespace(command="mcp", mcp_command="serve", profile="workspace-core")
return argparse.Namespace(
command="mcp",
mcp_command="serve",
profile="workspace-core",
project_path="/repo",
repo_url=None,
repo_ref=None,
no_project_source=False,
)
monkeypatch.setattr(cli, "_build_parser", lambda: StubParser())
monkeypatch.setattr(cli, "Pyro", StubPyro)
cli.main()
assert observed == {"profile": "workspace-core", "transport": "stdio"}
assert observed == {
"profile": "workspace-core",
"project_path": "/repo",
"repo_url": None,
"repo_ref": None,
"no_project_source": False,
"transport": "stdio",
}
def test_cli_demo_default_prints_json(