Install an integration into an existing project.
(
key: str = typer.Argument(help="Integration key to install (e.g. claude, copilot)"),
script: str | None = typer.Option(None, "--script", help="Script type: sh or ps (default: from init-options.json or platform default)"),
force: bool = typer.Option(False, "--force", help="Allow multi-install when integrations are not declared safe"),
integration_options: str | None = typer.Option(None, "--integration-options", help='Options for the integration (e.g. --integration-options="--commands-dir .myagent/cmds")'),
)
| 37 | |
| 38 | @integration_app.command("install") |
| 39 | def integration_install( |
| 40 | key: str = typer.Argument(help="Integration key to install (e.g. claude, copilot)"), |
| 41 | script: str | None = typer.Option(None, "--script", help="Script type: sh or ps (default: from init-options.json or platform default)"), |
| 42 | force: bool = typer.Option(False, "--force", help="Allow multi-install when integrations are not declared safe"), |
| 43 | integration_options: str | None = typer.Option(None, "--integration-options", help='Options for the integration (e.g. --integration-options="--commands-dir .myagent/cmds")'), |
| 44 | ): |
| 45 | """Install an integration into an existing project.""" |
| 46 | from . import INTEGRATION_REGISTRY, get_integration |
| 47 | from .manifest import IntegrationManifest |
| 48 | from .. import _require_specify_project, _install_shared_infra_or_exit |
| 49 | |
| 50 | project_root = _require_specify_project() |
| 51 | integration = get_integration(key) |
| 52 | if integration is None: |
| 53 | console.print(f"[red]Error:[/red] Unknown integration '{key}'") |
| 54 | available = ", ".join(sorted(INTEGRATION_REGISTRY.keys())) |
| 55 | console.print(f"Available integrations: {available}") |
| 56 | raise typer.Exit(1) |
| 57 | |
| 58 | current = _read_integration_json(project_root) |
| 59 | default_key = _default_integration_key(current) |
| 60 | installed_keys = _installed_integration_keys(current) |
| 61 | |
| 62 | if key in installed_keys: |
| 63 | console.print(f"[yellow]Integration '{key}' is already installed.[/yellow]") |
| 64 | if default_key == key: |
| 65 | console.print("It is already the default integration.") |
| 66 | else: |
| 67 | console.print( |
| 68 | f"To make it the default integration, run " |
| 69 | f"[cyan]specify integration use {key}[/cyan]." |
| 70 | ) |
| 71 | console.print( |
| 72 | f"To refresh its managed files or options, run " |
| 73 | f"[cyan]specify integration upgrade {key}[/cyan]." |
| 74 | ) |
| 75 | console.print("No files were changed.") |
| 76 | raise typer.Exit(0) |
| 77 | |
| 78 | if installed_keys and not force: |
| 79 | unsafe_keys = [] |
| 80 | for installed_key in installed_keys: |
| 81 | installed_integration = get_integration(installed_key) |
| 82 | if not installed_integration or not getattr(installed_integration, "multi_install_safe", False): |
| 83 | unsafe_keys.append(installed_key) |
| 84 | if unsafe_keys or not getattr(integration, "multi_install_safe", False): |
| 85 | console.print( |
| 86 | f"[red]Error:[/red] Installed integrations: {', '.join(installed_keys)}." |
| 87 | ) |
| 88 | if default_key: |
| 89 | console.print(f"Default integration: [cyan]{default_key}[/cyan].") |
| 90 | console.print( |
| 91 | "Installing multiple integrations is only automatic when all involved " |
| 92 | "integrations are declared multi-install safe." |
| 93 | ) |
| 94 | console.print( |
| 95 | f"To replace the default integration, run " |
| 96 | f"[cyan]specify integration switch {key}[/cyan]." |
nothing calls this directly
no test coverage detected