Fix setup UI and improve CLI workflows
This commit is contained in:
1 parent
7b005601f6
commit
c78c201e94
8 files changed
+302
-54
No files matched your search
@@ -82,26 +82,37 @@ Experience seamless collaboration between **Antigravity** and **ARTEMIS** via na
|
||||
|
||||
Ensure an Android device (with **USB Debugging** enabled) or emulator is connected. The one-click startup script will automatically:
|
||||
- 🛠️ **Install System Toolchains**: Detect and auto-install ADB, scrcpy, FFmpeg, and Python (`uv`) dependencies.
|
||||
- 🔌 **Mount Global MCP Server & AI Agent Rules**: Prompt to automatically install global MCP configurations and the **Artemis Mobile Testing Mindset (`rules.md`)** into your AI IDEs (**Antigravity**, **Cursor**, **Claude Code**, **Windsurf**, **VS Code**, **Cline/Roo**, **OpenClaw**).
|
||||
- 🔌 **Mount Global MCP Server & AI Agent Rules**: Prompt to automatically install global MCP configurations and the **Artemis Mobile Testing Mindset (`rules.md`)** into your AI IDEs (**Antigravity**, **Cursor**, **Claude Code**, **Codex**, **Windsurf**, **VS Code**, **Cline/Roo**, **OpenClaw**).
|
||||
|
||||
### 🍎 macOS and 🐧 Linux
|
||||
|
||||
```bash
|
||||
# 1. Clone repo & navigate to directory
|
||||
git clone https://github.com/google/artemis.git && cd artemis
|
||||
|
||||
# 2. One-click launch
|
||||
# 🍎 macOS & 🐧 Linux
|
||||
./start.sh
|
||||
|
||||
# 🪟 Windows
|
||||
start.bat
|
||||
```
|
||||
|
||||
### 🪟 Windows PowerShell
|
||||
|
||||
```powershell
|
||||
# 1. Clone repo & navigate to directory
|
||||
git clone https://github.com/google/artemis.git
|
||||
cd artemis
|
||||
|
||||
# 2. One-click launch
|
||||
.\start.bat
|
||||
```
|
||||
|
||||
> PowerShell does not search the current directory for executable scripts by default, so use `.\start.bat` without a trailing `\`. In Command Prompt (CMD), use `start.bat` instead.
|
||||
|
||||
> 💡 **Tip**: Opens `http://localhost:8000` in your default browser with a device connection wizard, live screen mirroring, prompt sandbox, and execution replays. You can also run directly from CLI: `uv run artemis run "Open Settings, find Battery and tell me current level" --profile flash`.
|
||||
|
||||
<a id="mcp-setup"></a>
|
||||
<a id="mcp"></a>
|
||||
<details>
|
||||
<summary><b>🔌 MCP Setup for Antigravity / Claude Code / Windsurf (Click to expand)</b></summary>
|
||||
<summary><b>🔌 MCP Setup for Codex / Antigravity / Claude Code / Windsurf (Click to expand)</b></summary>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -109,13 +120,13 @@ ARTEMIS includes a native **Model Context Protocol (MCP)** server. Connect your
|
||||
|
||||
### 1. One-Click Auto Install (Recommended)
|
||||
|
||||
Running `./start.sh` or `start.bat` will prompt you to configure global MCP and testing rules for detected IDEs (or you can install/update anytime later manually using the commands below):
|
||||
Running `./start.sh` (macOS/Linux) or `.\start.bat` (Windows PowerShell) will prompt you to configure global MCP and testing rules for detected IDEs (or you can install/update anytime later manually using the commands below):
|
||||
|
||||
```bash
|
||||
# Auto-install MCP server & global rules for Antigravity / Jetski:
|
||||
uv run artemis mcp --install antigravity
|
||||
|
||||
# Or install for all detected AI IDEs (Antigravity, Cursor, Claude Desktop/Code, OpenClaw):
|
||||
# Or install for all supported AI IDEs (including Codex):
|
||||
uv run artemis mcp --install all
|
||||
```
|
||||
|
||||
@@ -124,7 +135,19 @@ uv run artemis mcp --install all
|
||||
|
||||
### 2. Manual Configuration (Optional)
|
||||
|
||||
If you prefer to configure manually, run `uv run artemis mcp --generate-config antigravity` (or `all`) to output the exact JSON snippet, or paste the following into your IDE configuration (replace `/path/to/artemis` with your actual repo path and point `command` to your `.venv` Python executable):
|
||||
If you prefer to configure manually, run `uv run artemis mcp --generate-config <client>` (for example, `codex` or `antigravity`) to output the appropriate TOML or JSON snippet. Replace `/path/to/artemis` with your actual repo path and point `command` to your `.venv` Python executable:
|
||||
|
||||
* **Codex** (`~/.codex/config.toml`):
|
||||
```toml
|
||||
[mcp_servers.artemis]
|
||||
command = "/path/to/artemis/.venv/bin/python"
|
||||
args = ["-m", "mcp_server"]
|
||||
cwd = "/path/to/artemis"
|
||||
|
||||
[mcp_servers.artemis.env]
|
||||
PYTHONUNBUFFERED = "1"
|
||||
PYTHONPATH = "/path/to/artemis"
|
||||
```
|
||||
|
||||
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
|
||||
```json
|
||||
@@ -169,12 +192,13 @@ You can mount or copy [`mcp_server/rules.md`](./mcp_server/rules.md) into your A
|
||||
* **Antigravity**: Add the contents of `rules.md` to your Workspace Rules, Global Rules settings, or agent instructions.
|
||||
* **Claude Code**: Copy or include the contents of `rules.md` in your project's `CLAUDE.md` file.
|
||||
* **Cursor**: Copy the contents into `.cursorrules` or create a rule file at `.cursor/rules/artemis.mdc`.
|
||||
* **Codex**: Add the contents to `~/.codex/AGENTS.md` (or the active `AGENTS.override.md`).
|
||||
* **Windsurf / OpenClaw**: Add the rules to your workspace rules or global system prompts.
|
||||
|
||||
> 💡 For more details on the testing mindset and MCP architecture, see the [MCP Server README](./mcp_server/README.md).
|
||||
|
||||
### 4. Prompt Your Phone in the IDE Chat
|
||||
In Antigravity or Claude Code, simply prompt:
|
||||
In Codex, Antigravity, or Claude Code, simply prompt:
|
||||
> 💬 *"Build the latest changes into an APK, install it on the connected device, open the login screen with a test account, verify if there are any unexpected popups after login, and return screenshots of the final page."*
|
||||
|
||||
</details>
|
||||
|
||||
+34
-10
@@ -84,26 +84,37 @@
|
||||
|
||||
确保电脑已连接 Android 实体机(已开启 **USB 调试**)或 Android 模拟器。一键启动脚本将会自动完成以下配置:
|
||||
- 🛠️ **安装系统环境依赖**:自动检测并安装 ADB、scrcpy、FFmpeg 与 Python(`uv`)运行时及项目依赖。
|
||||
- 🔌 **全局挂载 MCP 服务与测试准则 (Rules)**:主动引导并自动将全局 MCP 服务与 **Artemis 移动端测试思维准则 (`rules.md`)** 挂载至你使用的 AI IDE(支持 **Antigravity**、**Cursor**、**Claude Code**、**Windsurf**、**VS Code**、**Cline/Roo**、**OpenClaw**)。
|
||||
- 🔌 **全局挂载 MCP 服务与测试准则 (Rules)**:主动引导并自动将全局 MCP 服务与 **Artemis 移动端测试思维准则 (`rules.md`)** 挂载至你使用的 AI IDE(支持 **Antigravity**、**Cursor**、**Claude Code**、**Codex**、**Windsurf**、**VS Code**、**Cline/Roo**、**OpenClaw**)。
|
||||
|
||||
### 🍎 macOS 与 🐧 Linux
|
||||
|
||||
```bash
|
||||
# 1. 克隆代码仓库并进入目录
|
||||
git clone https://github.com/google/artemis.git && cd artemis
|
||||
|
||||
# 2. 一键启动
|
||||
# 🍎 macOS & 🐧 Linux
|
||||
./start.sh
|
||||
|
||||
# 🪟 Windows
|
||||
start.bat
|
||||
```
|
||||
|
||||
### 🪟 Windows PowerShell
|
||||
|
||||
```powershell
|
||||
# 1. 克隆代码仓库并进入目录
|
||||
git clone https://github.com/google/artemis.git
|
||||
cd artemis
|
||||
|
||||
# 2. 一键启动
|
||||
.\start.bat
|
||||
```
|
||||
|
||||
> PowerShell 默认不会从当前目录查找可执行脚本,因此必须使用 `.\start.bat`,且命令末尾不要添加 `\`。如果使用传统命令提示符(CMD),则运行 `start.bat`。
|
||||
|
||||
> 💡 **提示**:启动后将自动在默认浏览器中打开 Web 控制台(`http://localhost:8000`),提供设备连接向导、实时投屏、任务演练与状态回放面板。你也可以通过命令行直接运行:`uv run artemis run "打开系统设置,找到电池选项并告诉我当前电量" --profile flash`。
|
||||
|
||||
<a id="mcp-setup"></a>
|
||||
<a id="mcp"></a>
|
||||
<details>
|
||||
<summary><b>🔌 接入 Antigravity / Claude Code / Windsurf (MCP)(点击展开)</b></summary>
|
||||
<summary><b>🔌 接入 Codex / Antigravity / Claude Code / Windsurf (MCP)(点击展开)</b></summary>
|
||||
|
||||
<br>
|
||||
|
||||
@@ -111,13 +122,13 @@ ARTEMIS 内置原生 **Model Context Protocol (MCP)** 服务。只需将以下
|
||||
|
||||
### 1. 一键自动安装到 IDE(推荐)
|
||||
|
||||
运行 `./start.sh` 或 `start.bat` 启动脚本时会主动询问是否自动挂载全局 MCP 与测试行为准则(支持跳过并在之后随时手动执行以下命令挂载):
|
||||
运行 `./start.sh`(macOS/Linux)或 `.\start.bat`(Windows PowerShell)启动脚本时,会主动询问是否自动挂载全局 MCP 与测试行为准则(支持跳过并在之后随时手动执行以下命令挂载):
|
||||
|
||||
```bash
|
||||
# 一键安装全局 MCP 服务与 Rules 到 Antigravity / Jetski:
|
||||
uv run artemis mcp --install antigravity
|
||||
|
||||
# 或一键安装到所有检测到的 AI IDE(Antigravity、Cursor、Claude Desktop/Code、OpenClaw):
|
||||
# 或一键安装到所有支持的 AI IDE(包括 Codex):
|
||||
uv run artemis mcp --install all
|
||||
```
|
||||
|
||||
@@ -126,7 +137,19 @@ uv run artemis mcp --install all
|
||||
|
||||
### 2. 手动配置(可选)
|
||||
|
||||
如果你习惯手动复制配置,可运行 `uv run artemis mcp --generate-config antigravity`(或 `all`)获取自动计算好路径的 JSON 代码段,也可以参考以下模板将 `command` 填写为项目下 `.venv` 虚拟环境中的 Python 绝对路径,并将 `/path/to/artemis` 替换为项目实际路径:
|
||||
如果你习惯手动复制配置,可运行 `uv run artemis mcp --generate-config <client>`(例如 `codex` 或 `antigravity`)获取对应的 TOML 或 JSON 配置。请将 `command` 填写为项目下 `.venv` 虚拟环境中的 Python 绝对路径,并将 `/path/to/artemis` 替换为项目实际路径:
|
||||
|
||||
* **Codex** (`~/.codex/config.toml`):
|
||||
```toml
|
||||
[mcp_servers.artemis]
|
||||
command = "/path/to/artemis/.venv/bin/python"
|
||||
args = ["-m", "mcp_server"]
|
||||
cwd = "/path/to/artemis"
|
||||
|
||||
[mcp_servers.artemis.env]
|
||||
PYTHONUNBUFFERED = "1"
|
||||
PYTHONPATH = "/path/to/artemis"
|
||||
```
|
||||
|
||||
* **Antigravity** (`~/.gemini/jetski/mcp_config.json`):
|
||||
```json
|
||||
@@ -171,12 +194,13 @@ uv run artemis mcp --install all
|
||||
* **Antigravity**:将 `rules.md` 内容添加至工作区规则(Workspace Rules)或全局规则设置中。
|
||||
* **Claude Code**:将 `rules.md` 内容复制或引入至项目根目录的 `CLAUDE.md` 文件中。
|
||||
* **Cursor**:将内容复制到 `.cursorrules` 文件或在 `.cursor/rules/artemis.mdc` 中创建新规则。
|
||||
* **Codex**:将内容添加至 `~/.codex/AGENTS.md`(或当前生效的 `AGENTS.override.md`)。
|
||||
* **Windsurf / OpenClaw**:将内容添加到工作区规则或全局 System Prompt 中。
|
||||
|
||||
> 💡 更多规范设计细节与 MCP 架构说明,请参阅 [MCP Server 文档](./mcp_server/README.md)。
|
||||
|
||||
### 4. 在 IDE 中体验真机协同
|
||||
在 Antigravity / Claude Code 对话框中直接输入:
|
||||
在 Codex / Antigravity / Claude Code 对话框中直接输入:
|
||||
> 💬 *"请帮我把刚刚修改的代码编译成 APK 并安装到手机上,打开登录页面输入测试账号,验证登录后是否有异常弹窗,并把最终页面截图回传。"*
|
||||
|
||||
</details>
|
||||
|
||||
@@ -54,6 +54,51 @@ $text-secondary: #475569;
|
||||
$text-muted: #64748b;
|
||||
$text-light: #94a3b8;
|
||||
|
||||
// Shared OS switcher. It is rendered in both the dependency installer and
|
||||
// the empty-AVD guide, so the styling cannot be scoped to either card.
|
||||
.os-selector-pills {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px;
|
||||
gap: 2px;
|
||||
background: rgba(255, 255, 255, 0.65);
|
||||
border: 1px solid rgba(226, 232, 240, 0.85);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 1px 3px rgba(15, 23, 42, 0.04), inset 0 1px 1px rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(12px) saturate(180%);
|
||||
-webkit-backdrop-filter: blur(12px) saturate(180%);
|
||||
|
||||
.os-pill {
|
||||
padding: 3px 9px;
|
||||
border: 0;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
color: $text-secondary;
|
||||
font-family: inherit;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1.35;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s ease, background-color 0.15s ease, box-shadow 0.15s ease;
|
||||
|
||||
&:hover {
|
||||
color: $text-main;
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: #ffffff;
|
||||
background: $primary;
|
||||
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.12);
|
||||
}
|
||||
|
||||
&:focus-visible {
|
||||
outline: 2px solid rgba($primary, 0.35);
|
||||
outline-offset: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// FROSTED GLASS (GLASSMORPHISM) MIXINS
|
||||
// =============================================================================
|
||||
|
||||
@@ -141,19 +141,21 @@ def init_command() -> None:
|
||||
console.print(" [3] Claude Desktop / Claude Code")
|
||||
console.print(" [4] Windsurf")
|
||||
console.print(" [5] VS Code / Cline / Roo Code")
|
||||
console.print(" [6] All supported IDEs")
|
||||
console.print(" [7] Skip for now")
|
||||
console.print(" [6] Codex")
|
||||
console.print(" [7] All supported IDEs")
|
||||
console.print(" [8] Skip for now")
|
||||
mcp_choice = Prompt.ask(
|
||||
"Select IDE for MCP auto-install", choices=["1", "2", "3", "4", "5", "6", "7"], default="1"
|
||||
"Select IDE for MCP auto-install", choices=["1", "2", "3", "4", "5", "6", "7", "8"], default="1"
|
||||
)
|
||||
if mcp_choice != "7":
|
||||
if mcp_choice != "8":
|
||||
target_map = {
|
||||
"1": "antigravity",
|
||||
"2": "cursor",
|
||||
"3": "claude",
|
||||
"4": "windsurf",
|
||||
"5": "vscode",
|
||||
"6": "all",
|
||||
"6": "codex",
|
||||
"7": "all",
|
||||
}
|
||||
target = target_map.get(mcp_choice, "antigravity")
|
||||
try:
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
import tomllib
|
||||
from typing import Annotated
|
||||
|
||||
from mcp_server.base import mcp as agent_mcp
|
||||
@@ -72,6 +74,8 @@ def _get_config_snippet(client: str, python_exe: str, project_root: str) -> dict
|
||||
}
|
||||
elif client == "openclaw":
|
||||
return {"plugins": {"artemis_mcp": {"enabled": True, "type": "mcp", "server": config_body}}}
|
||||
elif client == "codex":
|
||||
return {"mcp_servers": {"artemis": config_body}}
|
||||
else: # cursor, windsurf, claude, vscode, cline, roo, generic
|
||||
return {"mcpServers": {"artemis": config_body}}
|
||||
|
||||
@@ -83,7 +87,6 @@ def _parse_json_lenient(text: str) -> dict:
|
||||
return data if isinstance(data, dict) else {}
|
||||
except Exception:
|
||||
pass
|
||||
import re
|
||||
# Remove // single-line comments
|
||||
cleaned = re.sub(r"//.*", "", text)
|
||||
# Remove /* ... */ multi-line comments
|
||||
@@ -125,6 +128,76 @@ def _merge_json_file(file_path: Path, server_name: str, server_config: dict, key
|
||||
return False
|
||||
|
||||
|
||||
def _codex_toml_block(server_config: dict) -> str:
|
||||
"""Renders a managed Codex MCP server block using TOML-compatible JSON strings."""
|
||||
command = json.dumps(str(server_config["command"]), ensure_ascii=False)
|
||||
args = json.dumps(server_config.get("args", []), ensure_ascii=False)
|
||||
cwd = json.dumps(str(server_config["cwd"]), ensure_ascii=False)
|
||||
env = server_config.get("env", {})
|
||||
lines = [
|
||||
"# BEGIN ARTEMIS MCP CONFIG",
|
||||
"[mcp_servers.artemis]",
|
||||
f"command = {command}",
|
||||
f"args = {args}",
|
||||
f"cwd = {cwd}",
|
||||
]
|
||||
if env:
|
||||
lines.extend(["", "[mcp_servers.artemis.env]"])
|
||||
for key, value in env.items():
|
||||
lines.append(f"{key} = {json.dumps(str(value), ensure_ascii=False)}")
|
||||
lines.append("# END ARTEMIS MCP CONFIG")
|
||||
return "\n".join(lines) + "\n"
|
||||
|
||||
|
||||
def _merge_codex_toml(file_path: Path, server_config: dict) -> bool:
|
||||
"""Adds or updates Artemis in Codex config.toml while preserving unrelated settings."""
|
||||
try:
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
existing = file_path.read_text(encoding="utf-8") if file_path.exists() else ""
|
||||
managed_block = _codex_toml_block(server_config)
|
||||
begin_marker = "# BEGIN ARTEMIS MCP CONFIG"
|
||||
end_marker = "# END ARTEMIS MCP CONFIG"
|
||||
|
||||
if begin_marker in existing and end_marker in existing:
|
||||
managed_pattern = re.compile(
|
||||
rf"(?ms)^{re.escape(begin_marker)}\n.*?^{re.escape(end_marker)}\n?"
|
||||
)
|
||||
updated = managed_pattern.sub(managed_block, existing, count=1)
|
||||
else:
|
||||
# Remove an older, unmanaged Artemis table before adding the managed block.
|
||||
kept_lines: list[str] = []
|
||||
skipping_artemis = False
|
||||
header_pattern = re.compile(r"^\s*\[{1,2}\s*(.+?)\s*\]{1,2}\s*(?:#.*)?$")
|
||||
for line in existing.splitlines(keepends=True):
|
||||
header_match = header_pattern.match(line.rstrip("\r\n"))
|
||||
if header_match:
|
||||
normalized = re.sub(r"[\s\"']", "", header_match.group(1)).lower()
|
||||
skipping_artemis = normalized == "mcp_servers.artemis" or normalized.startswith(
|
||||
"mcp_servers.artemis."
|
||||
)
|
||||
if not skipping_artemis:
|
||||
kept_lines.append(line)
|
||||
preserved = "".join(kept_lines).rstrip()
|
||||
updated = f"{preserved}\n\n{managed_block}" if preserved else managed_block
|
||||
|
||||
# Never replace a user's config with invalid TOML.
|
||||
tomllib.loads(updated)
|
||||
if updated != existing:
|
||||
file_path.write_text(updated, encoding="utf-8")
|
||||
return True
|
||||
except Exception as e:
|
||||
if file_path.exists():
|
||||
try:
|
||||
backup_path = file_path.with_suffix(file_path.suffix + ".bak")
|
||||
backup_path.write_text(file_path.read_text(encoding="utf-8"), encoding="utf-8")
|
||||
logger.warning(f"Could not update {file_path}; backup created at {backup_path}: {e}")
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
logger.warning(f"Could not create Codex MCP config file {file_path}: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def _inject_rules_block(file_path: Path, content: str) -> bool:
|
||||
"""Injects or updates a managed ARTEMIS block in a global rules file without overwriting user content."""
|
||||
try:
|
||||
@@ -219,7 +292,7 @@ def install_rules(client: str, project_root: str) -> list[str]:
|
||||
return []
|
||||
|
||||
targets = (
|
||||
["antigravity", "cursor", "claude", "windsurf", "vscode", "cline", "roo", "openclaw"]
|
||||
["antigravity", "cursor", "claude", "windsurf", "vscode", "cline", "roo", "openclaw", "codex"]
|
||||
if client == "all"
|
||||
else [client]
|
||||
)
|
||||
@@ -281,6 +354,16 @@ def install_rules(client: str, project_root: str) -> list[str]:
|
||||
installed_paths.append(str(openclaw_md))
|
||||
if _write_rule_file(global_rule, raw_rules):
|
||||
installed_paths.append(str(global_rule))
|
||||
elif target == "codex":
|
||||
codex_home = Path(os.environ.get("CODEX_HOME") or (Path.home() / ".codex"))
|
||||
override_file = codex_home / "AGENTS.override.md"
|
||||
agents_file = (
|
||||
override_file
|
||||
if override_file.exists() and override_file.read_text(encoding="utf-8").strip()
|
||||
else codex_home / "AGENTS.md"
|
||||
)
|
||||
if _inject_rules_block(agents_file, raw_rules):
|
||||
installed_paths.append(str(agents_file))
|
||||
|
||||
return installed_paths
|
||||
|
||||
@@ -289,7 +372,7 @@ def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[
|
||||
"""Auto-installs/merges ARTEMIS MCP configuration and testing rules into IDE config files across any OS."""
|
||||
installed_paths: list[str] = []
|
||||
targets = (
|
||||
["antigravity", "cursor", "claude", "windsurf", "vscode", "cline", "roo", "openclaw"]
|
||||
["antigravity", "cursor", "claude", "windsurf", "vscode", "cline", "roo", "openclaw", "codex"]
|
||||
if client == "all"
|
||||
else [client]
|
||||
)
|
||||
@@ -350,6 +433,12 @@ def install_mcp_config(client: str, python_exe: str, project_root: str) -> list[
|
||||
openclaw_path = Path.home() / ".openclaw" / "openclaw.json"
|
||||
if _merge_json_file(openclaw_path, "artemis_mcp", plugin_cfg, key_name="plugins"):
|
||||
installed_paths.append(str(openclaw_path))
|
||||
elif target == "codex":
|
||||
server_cfg = snippet["mcp_servers"]["artemis"]
|
||||
codex_home = Path(os.environ.get("CODEX_HOME") or (Path.home() / ".codex"))
|
||||
codex_path = codex_home / "config.toml"
|
||||
if _merge_codex_toml(codex_path, server_cfg):
|
||||
installed_paths.append(str(codex_path))
|
||||
|
||||
rules_paths = install_rules(client, project_root)
|
||||
installed_paths.extend(rules_paths)
|
||||
@@ -397,7 +486,7 @@ def mcp_command(
|
||||
typer.Option(
|
||||
"--install",
|
||||
"-i",
|
||||
help="Auto-install and merge ARTEMIS MCP configuration and testing rules into 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.",
|
||||
help="Auto-install and merge ARTEMIS MCP configuration and testing rules into 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', 'codex', or 'all'.",
|
||||
),
|
||||
] = None,
|
||||
generate_config: Annotated[
|
||||
@@ -405,7 +494,7 @@ def mcp_command(
|
||||
typer.Option(
|
||||
"--generate-config",
|
||||
"-g",
|
||||
help="Output ready-to-use MCP configuration JSON for 'antigravity', 'cursor', 'claude', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.",
|
||||
help="Output ready-to-use MCP configuration for 'antigravity', 'cursor', 'claude', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', 'codex', or 'all'.",
|
||||
),
|
||||
] = None,
|
||||
) -> None:
|
||||
@@ -428,10 +517,11 @@ def mcp_command(
|
||||
"roo",
|
||||
"roo_code",
|
||||
"openclaw",
|
||||
"codex",
|
||||
"all",
|
||||
):
|
||||
console.print(
|
||||
f"[bold red]Unsupported install target: '{install_config}'. Use 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', or 'all'.[/bold red]"
|
||||
f"[bold red]Unsupported install target: '{install_config}'. Use 'antigravity', 'claude', 'cursor', 'windsurf', 'vscode', 'cline', 'roo', 'openclaw', 'codex', or 'all'.[/bold red]"
|
||||
)
|
||||
raise typer.Exit(1)
|
||||
installed_paths = install_mcp_config(client, python_exe, project_root)
|
||||
@@ -469,13 +559,22 @@ def mcp_command(
|
||||
"openclaw (openclaw.json)": _get_config_snippet(
|
||||
"openclaw", python_exe, project_root
|
||||
),
|
||||
"codex (~/.codex/config.toml)": _get_config_snippet(
|
||||
"codex", python_exe, project_root
|
||||
),
|
||||
}
|
||||
json_str = json.dumps(all_configs, indent=2)
|
||||
syntax_language = "json"
|
||||
elif client == "codex":
|
||||
server_cfg = _get_config_snippet("codex", python_exe, project_root)["mcp_servers"]["artemis"]
|
||||
json_str = _codex_toml_block(server_cfg)
|
||||
syntax_language = "toml"
|
||||
else:
|
||||
snippet = _get_config_snippet(client, python_exe, project_root)
|
||||
json_str = json.dumps(snippet, indent=2)
|
||||
syntax_language = "json"
|
||||
|
||||
syntax = Syntax(json_str, "json", theme="monokai", line_numbers=False)
|
||||
syntax = Syntax(json_str, syntax_language, theme="monokai", line_numbers=False)
|
||||
console.print(f"[bold cyan]MCP Configuration for {client.upper()}:[/bold cyan]")
|
||||
console.print(syntax)
|
||||
raise typer.Exit(0)
|
||||
|
||||
+22
-17
@@ -26,7 +26,7 @@ $RootDir = Split-Path -Parent $ScriptDir
|
||||
Set-Location $RootDir
|
||||
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host " ✨ Artemis Autonomous Mobile Agent UI " -ForegroundColor Cyan
|
||||
Write-Host " Artemis Autonomous Mobile Agent UI " -ForegroundColor Cyan
|
||||
Write-Host "======================================================" -ForegroundColor Cyan
|
||||
Write-Host ""
|
||||
|
||||
@@ -59,7 +59,7 @@ Update-EnvironmentPath
|
||||
|
||||
# 2. Check or install uv
|
||||
if (-not (Test-CommandExists "uv")) {
|
||||
Write-Host "⚡ uv not found. Installing Astral uv..." -ForegroundColor Yellow
|
||||
Write-Host "[INFO] uv not found. Installing Astral uv..." -ForegroundColor Yellow
|
||||
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
|
||||
Update-EnvironmentPath
|
||||
}
|
||||
@@ -72,7 +72,7 @@ if (-not (Test-CommandExists "scrcpy")) { $missingCore += "scrcpy" }
|
||||
|
||||
if ($missingCore.Count -gt 0) {
|
||||
if (Test-CommandExists "winget") {
|
||||
Write-Host "⚡ Auto-installing missing components ($($missingCore -join ', '))..." -ForegroundColor DarkYellow
|
||||
Write-Host "[INFO] Auto-installing missing components ($($missingCore -join ', '))..." -ForegroundColor DarkYellow
|
||||
if ($missingCore -contains "adb") {
|
||||
winget install --id Google.PlatformTools -e --accept-source-agreements --accept-package-agreements --silent 2>$null | Out-Null
|
||||
}
|
||||
@@ -90,14 +90,14 @@ if ($missingCore.Count -gt 0) {
|
||||
if (-not (Test-Path ".env")) {
|
||||
if (Test-Path ".env.example") {
|
||||
Copy-Item ".env.example" ".env"
|
||||
Write-Host " ✔ Initialized .env configuration file." -ForegroundColor Green
|
||||
Write-Host " [OK] Initialized .env configuration file." -ForegroundColor Green
|
||||
} else {
|
||||
New-Item -ItemType File -Path ".env" | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
# 5. Synchronize dependencies
|
||||
Write-Host " 📦 Synchronizing Python runtime and project dependencies..." -ForegroundColor Cyan
|
||||
Write-Host " [INFO] Synchronizing Python runtime and project dependencies..." -ForegroundColor Cyan
|
||||
uv sync --quiet
|
||||
|
||||
# 6. Check and build Showcase UI if not already compiled
|
||||
@@ -107,33 +107,33 @@ $ShowcaseIndexAlt2 = "$RootDir\apps\showcase_ui\dist\index.html"
|
||||
if ((-not (Test-Path $ShowcaseIndex)) -and (-not (Test-Path $ShowcaseIndexAlt1)) -and (-not (Test-Path $ShowcaseIndexAlt2))) {
|
||||
if (-not (Test-CommandExists "npm")) {
|
||||
if (Test-CommandExists "winget") {
|
||||
Write-Host " ⚡ Node.js/npm not found. Installing Node.js LTS via WinGet..." -ForegroundColor Yellow
|
||||
Write-Host " [INFO] Node.js/npm not found. Installing Node.js LTS via WinGet..." -ForegroundColor Yellow
|
||||
winget install --id OpenJS.NodeJS.LTS -e --accept-source-agreements --accept-package-agreements --silent 2>$null | Out-Null
|
||||
Update-EnvironmentPath
|
||||
}
|
||||
}
|
||||
|
||||
if (Test-CommandExists "npm") {
|
||||
Write-Host " 🎨 Showcase UI build not found. Compiling Angular Showcase UI..." -ForegroundColor Yellow
|
||||
Write-Host " [INFO] Showcase UI build not found. Compiling Angular Showcase UI..." -ForegroundColor Yellow
|
||||
Push-Location "$RootDir\apps\showcase_ui"
|
||||
try {
|
||||
npm install --silent
|
||||
npm run build
|
||||
Write-Host " ✔ Showcase UI compiled successfully." -ForegroundColor Green
|
||||
Write-Host " [OK] Showcase UI compiled successfully." -ForegroundColor Green
|
||||
} catch {
|
||||
Write-Host " ⚠ Failed to build Showcase UI: $_" -ForegroundColor DarkYellow
|
||||
Write-Host " [WARN] Failed to build Showcase UI: $_" -ForegroundColor DarkYellow
|
||||
} finally {
|
||||
Pop-Location
|
||||
}
|
||||
} else {
|
||||
Write-Host " ⚠ Node.js/npm not found. Please install Node.js (>= 18) to compile Showcase UI." -ForegroundColor DarkYellow
|
||||
Write-Host " [WARN] Node.js/npm not found. Please install Node.js (>= 18) to compile Showcase UI." -ForegroundColor DarkYellow
|
||||
}
|
||||
}
|
||||
|
||||
# 7. Optionally configure MCP for detected AI IDEs
|
||||
Write-Host ""
|
||||
Write-Host " 🔌 Would you like to configure ARTEMIS MCP & testing rules for your AI IDEs?" -ForegroundColor Cyan
|
||||
Write-Host " (Supported: Antigravity, Cursor, Claude Code/Desktop, OpenClaw, Windsurf)"
|
||||
Write-Host " [INFO] Would you like to configure ARTEMIS MCP & testing rules for your AI IDEs?" -ForegroundColor Cyan
|
||||
Write-Host " (Supported: Antigravity, Cursor, Claude Code/Desktop, Codex, OpenClaw, Windsurf)"
|
||||
$installMcp = "Y"
|
||||
if ([Console]::IsInputRedirected -eq $false) {
|
||||
$response = Read-Host " Install MCP configuration & rules now? [Y/n]"
|
||||
@@ -145,16 +145,21 @@ if ([Console]::IsInputRedirected -eq $false) {
|
||||
}
|
||||
|
||||
if ($installMcp -match "^[Yy]") {
|
||||
Write-Host " ✔ Installing MCP server configuration & testing rules..." -ForegroundColor Green
|
||||
uv run artemis mcp --install all 2>$null | Out-Null
|
||||
Write-Host " 💡 Tip: You can update or re-install anytime with: uv run artemis mcp --install all" -ForegroundColor Cyan
|
||||
Write-Host " Installing MCP server configuration & testing rules..." -ForegroundColor Cyan
|
||||
uv run artemis mcp --install all
|
||||
if ($LASTEXITCODE -eq 0) {
|
||||
Write-Host " [OK] MCP configuration and rules installed successfully." -ForegroundColor Green
|
||||
Write-Host " [TIP] You can update or re-install anytime with: uv run artemis mcp --install all" -ForegroundColor Cyan
|
||||
} else {
|
||||
Write-Host " [WARN] MCP installation failed. Review the error above and retry with: uv run artemis mcp --install all" -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host " ⏭️ Skipped. You can install MCP anytime later with: uv run artemis mcp --install all" -ForegroundColor Yellow
|
||||
Write-Host " [SKIP] You can install MCP anytime later with: uv run artemis mcp --install all" -ForegroundColor Yellow
|
||||
}
|
||||
Write-Host ""
|
||||
|
||||
# 8. Launch unified Showcase UI & open browser
|
||||
Write-Host " 🚀 Launching Artemis Showcase UI & Admin Console..." -ForegroundColor Green
|
||||
Write-Host " [INFO] Launching Artemis Showcase UI & Admin Console..." -ForegroundColor Green
|
||||
if ($NoOpen) {
|
||||
uv run artemis ui --port $Port --no-open
|
||||
} else {
|
||||
|
||||
@@ -156,7 +156,7 @@ fi
|
||||
# 7. Optionally configure MCP for detected AI IDEs
|
||||
echo ""
|
||||
echo -e " ${CYAN}🔌 Would you like to configure ARTEMIS MCP & testing rules for your AI IDEs?${NC}"
|
||||
echo -e " (Supported: Antigravity, Cursor, Claude Code/Desktop, OpenClaw, Windsurf)"
|
||||
echo -e " (Supported: Antigravity, Cursor, Claude Code/Desktop, Codex, OpenClaw, Windsurf)"
|
||||
if [ -t 0 ]; then
|
||||
read -r -p " Install MCP configuration & rules now? [Y/n]: " INSTALL_MCP
|
||||
INSTALL_MCP=${INSTALL_MCP:-Y}
|
||||
@@ -165,9 +165,13 @@ else
|
||||
fi
|
||||
|
||||
if [[ "${INSTALL_MCP}" =~ ^[Yy]$ ]]; then
|
||||
echo -e " ${GREEN}✔ Installing MCP server configuration & testing rules...${NC}"
|
||||
uv run artemis mcp --install all >/dev/null 2>&1 || true
|
||||
echo -e " ${CYAN}💡 Tip: You can update or re-install anytime with: ${BOLD}uv run artemis mcp --install all${NC}"
|
||||
echo -e " ${CYAN}Installing MCP server configuration & testing rules...${NC}"
|
||||
if uv run artemis mcp --install all; then
|
||||
echo -e " ${GREEN}✔ MCP configuration and rules installed successfully.${NC}"
|
||||
echo -e " ${CYAN}💡 Tip: You can update or re-install anytime with: ${BOLD}uv run artemis mcp --install all${NC}"
|
||||
else
|
||||
echo -e " ${YELLOW}⚠ MCP installation failed. Review the error above and retry with: ${BOLD}uv run artemis mcp --install all${NC}"
|
||||
fi
|
||||
else
|
||||
echo -e " ${YELLOW}⏭️ Skipped. You can install MCP anytime later with: ${BOLD}uv run artemis mcp --install all${NC}"
|
||||
fi
|
||||
|
||||
+46
-1
@@ -90,7 +90,7 @@ def test_cli_mcp_generate_config_antigravity():
|
||||
|
||||
|
||||
def test_cli_mcp_generate_config_all():
|
||||
"""Verify 'artemis mcp --generate-config all' includes antigravity, cursor, windsurf, claude, vscode, cline, and roo."""
|
||||
"""Verify 'artemis mcp --generate-config all' includes every supported client."""
|
||||
result = runner.invoke(app, ["mcp", "--generate-config", "all"])
|
||||
assert result.exit_code == 0
|
||||
assert "antigravity" in result.output
|
||||
@@ -100,6 +100,15 @@ def test_cli_mcp_generate_config_all():
|
||||
assert "vscode" in result.output
|
||||
assert "cline" in result.output
|
||||
assert "roo" in result.output
|
||||
assert "codex" in result.output
|
||||
|
||||
|
||||
def test_cli_mcp_generate_config_codex():
|
||||
"""Verify Codex generation produces a ready-to-use TOML server block."""
|
||||
result = runner.invoke(app, ["mcp", "--generate-config", "codex"])
|
||||
assert result.exit_code == 0
|
||||
assert "[mcp_servers.artemis]" in result.output
|
||||
assert "[mcp_servers.artemis.env]" in result.output
|
||||
|
||||
|
||||
def test_cli_mcp_install_antigravity(tmp_path, monkeypatch):
|
||||
@@ -137,6 +146,7 @@ def test_cli_mcp_install_all(tmp_path, monkeypatch):
|
||||
assert (tmp_path / ".codeium" / "windsurf" / "mcp_config.json").exists()
|
||||
assert (tmp_path / ".openclaw" / "openclaw.json").exists()
|
||||
assert (tmp_path / ".claude.json").exists()
|
||||
assert (tmp_path / ".codex" / "config.toml").exists()
|
||||
|
||||
# Verify global rule files installed across IDEs
|
||||
assert (tmp_path / ".gemini" / "GEMINI.md").exists()
|
||||
@@ -154,6 +164,41 @@ def test_cli_mcp_install_all(tmp_path, monkeypatch):
|
||||
assert (tmp_path / ".roo" / "rules" / "artemis.md").exists()
|
||||
assert (tmp_path / ".openclaw" / "OPENCLAW.md").exists()
|
||||
assert (tmp_path / ".openclaw" / "rules" / "artemis.md").exists()
|
||||
assert (tmp_path / ".codex" / "AGENTS.md").exists()
|
||||
|
||||
|
||||
def test_cli_mcp_install_codex_preserves_config_and_is_idempotent(tmp_path, monkeypatch):
|
||||
"""Verify Codex TOML merging preserves other servers and updates only Artemis."""
|
||||
monkeypatch.setattr("pathlib.Path.home", lambda: tmp_path)
|
||||
monkeypatch.setattr("mcp_server.utils.env_utils.get_project_root", lambda: str(tmp_path))
|
||||
codex_dir = tmp_path / ".codex"
|
||||
codex_dir.mkdir(parents=True)
|
||||
config_file = codex_dir / "config.toml"
|
||||
config_file.write_text(
|
||||
'model = "test-model"\n\n'
|
||||
'[mcp_servers.existing]\ncommand = "echo"\n\n'
|
||||
'[mcp_servers.artemis]\ncommand = "old-command"\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
result = runner.invoke(app, ["mcp", "--install", "codex"])
|
||||
assert result.exit_code == 0
|
||||
result2 = runner.invoke(app, ["mcp", "--install", "codex"])
|
||||
assert result2.exit_code == 0
|
||||
|
||||
import tomllib
|
||||
config_text = config_file.read_text(encoding="utf-8")
|
||||
data = tomllib.loads(config_text)
|
||||
assert data["model"] == "test-model"
|
||||
assert data["mcp_servers"]["existing"]["command"] == "echo"
|
||||
assert data["mcp_servers"]["artemis"]["args"] == ["-m", "mcp_server"]
|
||||
assert data["mcp_servers"]["artemis"]["env"]["PYTHONPATH"] == str(tmp_path)
|
||||
assert config_text.count("# BEGIN ARTEMIS MCP CONFIG") == 1
|
||||
|
||||
agents_file = codex_dir / "AGENTS.md"
|
||||
agents_text = agents_file.read_text(encoding="utf-8")
|
||||
assert "Mobile Testing Mindset" in agents_text
|
||||
assert agents_text.count("<!-- BEGIN ARTEMIS MOBILE TESTING RULES -->") == 1
|
||||
|
||||
|
||||
def test_cli_mcp_install_jsonc_and_backup(tmp_path, monkeypatch):
|
||||
|
||||
Reference in new issue
Block a user