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
22 changes: 21 additions & 1 deletion mureo/byod/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,30 @@
_USER_DIR_NAME = ".mureo"
_BYOD_SUBDIR = "byod"
_MANIFEST_NAME = "manifest.json"
_BYOD_DIR_ENV = "MUREO_BYOD_DIR"


def byod_data_dir() -> Path:
"""Return ``~/.mureo/byod/`` (does not create it)."""
"""Return the BYOD data directory (does not create it).

Resolution precedence:
1. ``MUREO_BYOD_DIR`` environment variable. Set by the
install-desktop wrapper to ``<workspace>/byod`` for the MCP
runtime, and also temporarily set by ``install_desktop()``
itself during ``--with-demo`` seeding. Each Claude Desktop
workspace sees its own BYOD store and demo data does not
leak across workspaces.
2. Legacy ``~/.mureo/byod/`` for existing CLI / Claude Code
users — preserves backward compatibility.

An empty or whitespace-only env var is treated as 'unset' so a
stray ``MUREO_BYOD_DIR=`` (or `` `` from a malformed shell rc)
cannot silently redirect BYOD into the cwd or a path named
``" "``. ``~`` in the value is expanded.
"""
override = os.environ.get(_BYOD_DIR_ENV)
if override and override.strip():
return Path(override.strip()).expanduser()
return Path.home() / _USER_DIR_NAME / _BYOD_SUBDIR


Expand Down
91 changes: 91 additions & 0 deletions mureo/cli/install_desktop_cmd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"""``mureo install-desktop`` — onboarding for Claude Desktop chat users.

This top-level command exists separately from the ``mureo setup *``
group because it targets non-engineer users. The discoverability of
``install-desktop`` matters more than consistency with the existing
host-specific setup commands.
"""

from __future__ import annotations

from pathlib import Path

import typer

from mureo.desktop_installer import (
DesktopConfigCorruptError,
DesktopInstallExistsError,
DesktopInstallUnsupportedPlatformError,
format_next_steps,
install_desktop,
)

install_desktop_app = typer.Typer(
name="install-desktop",
help="Wire mureo into Claude Desktop chat (macOS).",
invoke_without_command=True,
no_args_is_help=False,
)


@install_desktop_app.callback() # type: ignore[untyped-decorator, unused-ignore]
def install_desktop_cmd(
workspace: Path | None = typer.Option( # noqa: B008
None,
"--workspace",
"-w",
help="Directory the MCP server will use as its working "
"directory. STRATEGY.md / STATE.json will live here. "
"Defaults to ~/mureo.",
),
with_demo: str | None = typer.Option(
None,
"--with-demo",
help="Seed the workspace with a demo scenario "
"(seasonality-trap | halo-effect | hidden-champion | strategy-drift).",
),
force: bool = typer.Option(
False,
"--force",
"-f",
help="Overwrite an existing 'mureo' MCP entry without prompting "
"(a timestamped backup is always saved first).",
),
dry_run: bool = typer.Option(
False,
"--dry-run",
help="Show what would happen without writing anything.",
),
) -> None:
"""Wire mureo into Claude Desktop's MCP config.

Creates the workspace, generates a wrapper shell script, and
merges a 'mureo' entry into ~/Library/Application
Support/Claude/claude_desktop_config.json.
"""
resolved_workspace = workspace if workspace is not None else Path.home() / "mureo"
try:
result = install_desktop(
workspace=resolved_workspace,
with_demo=with_demo,
force=force,
dry_run=dry_run,
)
except DesktopInstallUnsupportedPlatformError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=2) from exc
except DesktopInstallExistsError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=1) from exc
except DesktopConfigCorruptError as exc:
typer.echo(f"Error: {exc}", err=True)
raise typer.Exit(code=1) from exc

if result.dry_run:
typer.echo("Dry-run — no changes written.")
typer.echo(f" Would create workspace: {result.workspace}")
typer.echo(f" Would write wrapper: {result.wrapper_path}")
typer.echo(f" Would update config: {result.config_path}")
return

typer.echo(format_next_steps(result))
2 changes: 2 additions & 0 deletions mureo/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from mureo.cli.auth_cmd import auth_app
from mureo.cli.byod_cmd import byod_app
from mureo.cli.demo_cmd import demo_app
from mureo.cli.install_desktop_cmd import install_desktop_app
from mureo.cli.rollback_cmd import rollback_app
from mureo.cli.setup_cmd import setup_app

Expand All @@ -22,6 +23,7 @@

app.add_typer(auth_app)
app.add_typer(setup_app)
app.add_typer(install_desktop_app)
app.add_typer(rollback_app)
app.add_typer(byod_app)
app.add_typer(demo_app)
Expand Down
Loading
Loading