Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f2110a8ba |
@@ -70,7 +70,7 @@ Choose your agents, review the proposed changes, and approve them. Setup adds
|
||||
REA's MCP server and matching workflow instructions, with backups of existing
|
||||
configuration. Restart your agent afterward.
|
||||
|
||||
Setup supports Claude Code, Codex, Cursor, Gemini CLI and
|
||||
Setup supports Claude Code, Codex, Cursor, Gemini CLI, Grok Build, Grok Bot and
|
||||
[other agents](docs/installation.md#supported-agents). See
|
||||
[installation and setup](docs/installation.md) for provider configuration and
|
||||
manual MCP registration.
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ npx rea-agents setup
|
||||
|
||||
اختر الوكلاء، وراجع التغييرات المقترحة، ثم وافق عليها. يضيف الإعداد خادم MCP الخاص بـ REA وتعليمات سير العمل المطابقة، مع الاحتفاظ بنسخ احتياطية من الإعدادات الموجودة. أعد تشغيل وكيلك بعد ذلك.
|
||||
|
||||
يدعم الإعداد Claude Code وCodex وCursor وGemini CLI و[وكلاء آخرين](docs/installation.md#supported-agents). راجع [التثبيت والإعداد](docs/installation.md) لتهيئة موفّري التحليل وتسجيل MCP يدويًا.
|
||||
يدعم الإعداد Claude Code وCodex وCursor وGemini CLI وGrok Build وGrok Bot و[وكلاء آخرين](docs/installation.md#supported-agents). راجع [التثبيت والإعداد](docs/installation.md) لتهيئة موفّري التحليل وتسجيل MCP يدويًا.
|
||||
|
||||
### اطلب من وكيلك
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ npx rea-agents setup
|
||||
|
||||
使うエージェントを選び、予定されている変更を確認して承認してください。セットアップは REA の MCP サーバーと対応するワークフローの手順を追加し、既存の設定をバックアップします。完了後にエージェントを再起動してください。
|
||||
|
||||
セットアップは Claude Code、Codex、Cursor、Gemini CLI、および[その他のエージェント](docs/installation.md#supported-agents)に対応しています。プロバイダーの設定と MCP の手動登録については、[インストールとセットアップ](docs/installation.md)を参照してください。
|
||||
セットアップは Claude Code、Codex、Cursor、Gemini CLI、Grok Build、Grok Bot、および[その他のエージェント](docs/installation.md#supported-agents)に対応しています。プロバイダーの設定と MCP の手動登録については、[インストールとセットアップ](docs/installation.md)を参照してください。
|
||||
|
||||
### エージェントに依頼する
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ npx rea-agents setup
|
||||
|
||||
에이전트를 선택하고, 예정된 변경 사항을 검토한 뒤 승인하세요. 설정 과정은 기존 설정을 백업하고 REA의 MCP 서버와 버전에 맞는 워크플로 지침을 추가합니다. 완료 후 에이전트를 다시 시작하세요.
|
||||
|
||||
설정은 Claude Code, Codex, Cursor, Gemini CLI 및 [다른 에이전트](docs/installation.md#supported-agents)를 지원합니다. 제공자 설정과 수동 MCP 등록은 [설치 및 설정](docs/installation.md)을 참고하세요.
|
||||
설정은 Claude Code, Codex, Cursor, Gemini CLI, Grok Build, Grok Bot 및 [다른 에이전트](docs/installation.md#supported-agents)를 지원합니다. 제공자 설정과 수동 MCP 등록은 [설치 및 설정](docs/installation.md)을 참고하세요.
|
||||
|
||||
### 에이전트에게 요청
|
||||
|
||||
|
||||
+1
-1
@@ -68,7 +68,7 @@ npx rea-agents setup
|
||||
|
||||
选择智能体,检查计划中的变更并批准。设置流程会添加 REA 的 MCP 服务和匹配的工作流指引,并备份已有配置。完成后重启智能体。
|
||||
|
||||
设置流程支持 Claude Code、Codex、Cursor、Gemini CLI 和[其他智能体](docs/installation.md#supported-agents)。提供方配置和手动注册 MCP 的方法见[安装与设置](docs/installation.md)。
|
||||
设置流程支持 Claude Code、Codex、Cursor、Gemini CLI、Grok Build、Grok Bot 和[其他智能体](docs/installation.md#supported-agents)。提供方配置和手动注册 MCP 的方法见[安装与设置](docs/installation.md)。
|
||||
|
||||
### 询问智能体
|
||||
|
||||
|
||||
+16
-1
@@ -135,12 +135,27 @@ Setup can configure these clients for REA's local MCP server:
|
||||
| GitHub Copilot CLI | `copilot_cli` |
|
||||
| Command Code | `commandcode` |
|
||||
| VS Code | `vscode` |
|
||||
| Grok Build | `grok_build` |
|
||||
| Grok Bot | `grok_bot` |
|
||||
|
||||
For OpenCode, setup writes the V1 `mcp.rea` entry, which OpenCode V1 and V2
|
||||
both load. If the configuration already uses OpenCode V2's native
|
||||
`mcp.servers` table, setup registers REA there instead and replaces any earlier
|
||||
`mcp.rea` entry from REA.
|
||||
|
||||
Grok Build loads `[mcp_servers.rea]` from `$GROK_HOME/config.toml`, or from
|
||||
`~/.grok/config.toml` when `GROK_HOME` is unset. Setup edits that server
|
||||
table, `[mcp_servers.rea.env]`, and a root `disabled_mcp_servers` entry that
|
||||
names `rea`. It sets `startup_timeout_sec = 30` and leaves every other name
|
||||
in that list. The shared skill installed under `~/.agents/skills` is already
|
||||
on Grok Build's skill path.
|
||||
|
||||
Grok Bot is detected from `~/.grokbot`, or from `SAND_DATA_ROOT` when that
|
||||
value is an absolute path. Setup writes `mcpServers.rea` to `mcp.json` in that
|
||||
directory. The desktop app keeps the connectors it actually runs in the
|
||||
signed-in account. That file is the registration to add there; setup does not
|
||||
call the account API.
|
||||
|
||||
## Review setup changes
|
||||
|
||||
`rea setup` first offers the supported agents in a multi-select. Existing REA
|
||||
@@ -209,7 +224,7 @@ rea setup --yes --all-detected --install-hopper --json
|
||||
|
||||
Setup pins package-runner MCP registrations to the exact installed REA version,
|
||||
installs the matching skill and on-demand references in the same plan, and adds
|
||||
`startup_timeout_sec = 30` for Codex. `rea update` installs the exact resolved
|
||||
`startup_timeout_sec = 30` for Codex and Grok Build. `rea update` installs the exact resolved
|
||||
release into the npm prefix that owns the running package, then checks the new
|
||||
executable's version before reporting success. It does not reopen onboarding.
|
||||
Release lookup and installation both use npm's configured registry.
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ REA setup lets you select agent integration and optional Hopper installation.
|
||||
It installs the bundled workflow, configures detected agents, and can save
|
||||
verified paths for an existing Ghidra installation. It configures Claude Code,
|
||||
Claude Desktop, Codex, Cursor, Gemini CLI, Windsurf, Devin, OpenCode, Antigravity,
|
||||
GitHub Copilot CLI, Command Code, and VS Code using each client's configuration format.
|
||||
GitHub Copilot CLI, Command Code, VS Code, Grok Build, and Grok Bot using each client's configuration format.
|
||||
|
||||
Ghidra analysis supports Linux x64 and macOS x64/arm64 with Ghidra 12.1.x and the
|
||||
64-bit full JDK that installation declares. Current 12.1 releases require JDK 21
|
||||
|
||||
@@ -79,6 +79,8 @@ export async function verifyPackageEnvironment({
|
||||
CLAUDE_CONFIG_DIR: home,
|
||||
CODEX_HOME: codexDir,
|
||||
COPILOT_HOME: join(home, ".copilot"),
|
||||
GROK_HOME: join(home, ".grok"),
|
||||
SAND_DATA_ROOT: join(home, ".grokbot"),
|
||||
OPENCODE_CONFIG: undefined,
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
PATH: `${fakeBin}:${process.env.PATH ?? ""}`,
|
||||
|
||||
@@ -101,6 +101,8 @@ export async function verifyPackageUpdate({
|
||||
CLAUDE_CONFIG_DIR: home,
|
||||
CODEX_HOME: join(home, ".codex"),
|
||||
COPILOT_HOME: join(home, ".copilot"),
|
||||
GROK_HOME: join(home, ".grok"),
|
||||
SAND_DATA_ROOT: join(home, ".grokbot"),
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
OPENCODE_CONFIG: join(home, ".config", "opencode", "opencode.json"),
|
||||
npm_config_prefix: prefix,
|
||||
|
||||
@@ -99,6 +99,15 @@ async function verifyPublishedUpdate({ version: targetVersion, canaryRoot }) {
|
||||
const environment = {
|
||||
...process.env,
|
||||
HOME: home,
|
||||
USERPROFILE: home,
|
||||
APPDATA: join(home, "AppData", "Roaming"),
|
||||
CLAUDE_CONFIG_DIR: home,
|
||||
CODEX_HOME: join(home, ".codex"),
|
||||
COPILOT_HOME: join(home, ".copilot"),
|
||||
GROK_HOME: join(home, ".grok"),
|
||||
SAND_DATA_ROOT: join(home, ".grokbot"),
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
OPENCODE_CONFIG: join(home, ".config", "opencode", "opencode.json"),
|
||||
npm_config_cache: join(canaryRoot, "update-cache"),
|
||||
npm_config_prefix: prefix,
|
||||
PATH: `${join(prefix, "bin")}${delimiter}${process.env.PATH ?? ""}`,
|
||||
|
||||
@@ -108,9 +108,13 @@ try {
|
||||
HOME: home,
|
||||
USERPROFILE: home,
|
||||
APPDATA: join(home, "AppData", "Roaming"),
|
||||
CLAUDE_CONFIG_DIR: home,
|
||||
CODEX_HOME: join(home, ".codex"),
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
OPENCODE_CONFIG: join(home, ".config", "opencode", "opencode.jsonc"),
|
||||
COPILOT_HOME: join(home, ".copilot"),
|
||||
GROK_HOME: join(home, ".grok"),
|
||||
SAND_DATA_ROOT: join(home, ".grokbot"),
|
||||
};
|
||||
await mkdir(join(home, ".config", "opencode"), { recursive: true });
|
||||
await writeFile(
|
||||
|
||||
@@ -31,7 +31,7 @@ When tools are absent or registration is stale:
|
||||
`npx -y rea-agents@latest doctor --client codex --json`. Substitute the current
|
||||
supported client: `claude_code`, `claude_desktop`, `codex`, `cursor`,
|
||||
`gemini_cli`, `windsurf`, `devin`, `opencode`, `antigravity`, `copilot_cli`,
|
||||
`commandcode`, or `vscode`. If the client is unknown, use `doctor --json` and
|
||||
`commandcode`, `vscode`, `grok_build`, or `grok_bot`. If the client is unknown, use `doctor --json` and
|
||||
inspect its registration results before choosing a setup scope.
|
||||
2. Distinguish the reason. Missing, malformed, or stale registration needs a
|
||||
scoped configuration repair. An aligned registration with no tools in the
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
effectiveClientServer,
|
||||
grokServerListedDisabled,
|
||||
parseClientConfiguration,
|
||||
} from "./ClientConfigurationDocument.js";
|
||||
import { access, readFile } from "node:fs/promises";
|
||||
@@ -80,7 +81,9 @@ export const readClientRegistrationStatuses = async (
|
||||
CLAUDE_CONFIG_DIR: options.environment.CLAUDE_CONFIG_DIR,
|
||||
CODEX_HOME: options.environment.CODEX_HOME,
|
||||
COPILOT_HOME: options.environment.COPILOT_HOME,
|
||||
GROK_HOME: options.environment.GROK_HOME,
|
||||
OPENCODE_CONFIG: options.environment.OPENCODE_CONFIG,
|
||||
SAND_DATA_ROOT: options.environment.SAND_DATA_ROOT,
|
||||
XDG_CONFIG_HOME: options.environment.XDG_CONFIG_HOME,
|
||||
},
|
||||
)) {
|
||||
@@ -91,10 +94,8 @@ export const readClientRegistrationStatuses = async (
|
||||
continue;
|
||||
try {
|
||||
const content = await readFile(client.configPath, "utf8");
|
||||
const raw = effectiveClientServer(
|
||||
parseClientConfiguration(content, client.format),
|
||||
PRODUCT_IDENTITY.mcpServerKey,
|
||||
);
|
||||
const parsed = parseClientConfiguration(content, client.format);
|
||||
const raw = effectiveClientServer(parsed, PRODUCT_IDENTITY.mcpServerKey);
|
||||
if (raw === undefined) {
|
||||
statuses.push(
|
||||
unavailableStatus(client.name, client.configPath, "missing"),
|
||||
@@ -116,6 +117,13 @@ export const readClientRegistrationStatuses = async (
|
||||
client,
|
||||
currentCommandPath,
|
||||
options.platform ?? process.platform,
|
||||
) &&
|
||||
!(
|
||||
client.format === "grok" &&
|
||||
grokServerListedDisabled(
|
||||
parsed.document,
|
||||
PRODUCT_IDENTITY.mcpServerKey,
|
||||
)
|
||||
)
|
||||
? "aligned"
|
||||
: "stale",
|
||||
@@ -148,7 +156,7 @@ const registrationAligned = (
|
||||
if (!isOwnedClientRegistrationCommand(command, currentCommandPath))
|
||||
return false;
|
||||
if (
|
||||
client.name === "codex" &&
|
||||
(client.name === "codex" || client.name === "grok_build") &&
|
||||
registration.startup_timeout_sec !==
|
||||
MCP_STARTUP_POLICY.codexStartupTimeoutSeconds
|
||||
)
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
clientRegistrationEntry,
|
||||
clientConfigurationValuesEqual,
|
||||
clientServerPath,
|
||||
grokServerListedDisabled,
|
||||
legacyClientServerPath,
|
||||
parseClientConfiguration,
|
||||
serializeClientConfiguration,
|
||||
@@ -58,7 +59,7 @@ const configureClientDocument = async (
|
||||
let parsed: ClientConfigurationDocument;
|
||||
try {
|
||||
parsed = parseClientConfiguration(
|
||||
original ?? (format === "toml" ? "" : "{}"),
|
||||
original ?? (format === "toml" || format === "grok" ? "" : "{}"),
|
||||
format,
|
||||
);
|
||||
} catch (cause: unknown) {
|
||||
@@ -234,7 +235,7 @@ const restoreConfig = async (
|
||||
}
|
||||
};
|
||||
|
||||
/** Whether REA's entry matches and no conflicting legacy entry remains. */
|
||||
/** Whether REA's entry matches, no legacy entry remains, and Grok is not suppressing it. */
|
||||
const registrationCurrent = (
|
||||
parsed: ClientConfigurationDocument,
|
||||
desired: unknown,
|
||||
@@ -242,7 +243,12 @@ const registrationCurrent = (
|
||||
clientConfigurationValuesEqual(
|
||||
parsed.servers[PRODUCT_IDENTITY.mcpServerKey],
|
||||
desired,
|
||||
) && !Object.hasOwn(parsed.legacyServers, PRODUCT_IDENTITY.mcpServerKey);
|
||||
) &&
|
||||
!Object.hasOwn(parsed.legacyServers, PRODUCT_IDENTITY.mcpServerKey) &&
|
||||
!(
|
||||
parsed.dialect === "grok" &&
|
||||
grokServerListedDisabled(parsed.document, PRODUCT_IDENTITY.mcpServerKey)
|
||||
);
|
||||
|
||||
const clientConfigurationDesired = (
|
||||
client: SetupClient,
|
||||
@@ -262,7 +268,7 @@ const clientConfigurationDesired = (
|
||||
);
|
||||
return {
|
||||
...registration,
|
||||
...(client.name === "codex"
|
||||
...(client.name === "codex" || client.name === "grok_build"
|
||||
? {
|
||||
startup_timeout_sec: MCP_STARTUP_POLICY.codexStartupTimeoutSeconds,
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { join } from "node:path";
|
||||
import { isAbsolute, join } from "node:path";
|
||||
import { lstatSync } from "node:fs";
|
||||
|
||||
/** One supported client configuration location. */
|
||||
@@ -14,6 +14,7 @@ export interface SetupClient {
|
||||
| "copilot_cli"
|
||||
| "opencode"
|
||||
| "commandcode"
|
||||
| "grok"
|
||||
| "unsupported";
|
||||
}
|
||||
|
||||
@@ -27,7 +28,9 @@ interface ClientPathContext {
|
||||
readonly CLAUDE_CONFIG_DIR?: string | undefined;
|
||||
readonly CODEX_HOME?: string | undefined;
|
||||
readonly COPILOT_HOME?: string | undefined;
|
||||
readonly GROK_HOME?: string | undefined;
|
||||
readonly OPENCODE_CONFIG?: string | undefined;
|
||||
readonly SAND_DATA_ROOT?: string | undefined;
|
||||
readonly XDG_CONFIG_HOME?: string | undefined;
|
||||
};
|
||||
}
|
||||
@@ -77,6 +80,17 @@ const claudeCodeMarkerDirectory = ({ home, env }: ClientPathContext): string =>
|
||||
const codexDirectory = ({ home, env }: ClientPathContext): string =>
|
||||
env.CODEX_HOME ?? join(home, ".codex");
|
||||
|
||||
const grokDirectory = ({ home, env }: ClientPathContext): string =>
|
||||
env.GROK_HOME ?? join(home, ".grok");
|
||||
|
||||
/** Grok Bot uses an absolute SAND_DATA_ROOT; anything else stays ~/.grokbot. */
|
||||
const grokBotDirectory = ({ home, env }: ClientPathContext): string => {
|
||||
const root = env.SAND_DATA_ROOT;
|
||||
return root !== undefined && root !== "" && isAbsolute(root)
|
||||
? root
|
||||
: join(home, ".grokbot");
|
||||
};
|
||||
|
||||
const copilotDirectory = ({ home, env }: ClientPathContext): string =>
|
||||
env.COPILOT_HOME ?? join(home, ".copilot");
|
||||
|
||||
@@ -212,6 +226,22 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
|
||||
markerPath: vscodeUserDirectory,
|
||||
format: "vscode",
|
||||
},
|
||||
{
|
||||
name: "grok_build",
|
||||
displayName: "Grok Build",
|
||||
configPath: (context: ClientPathContext) =>
|
||||
join(grokDirectory(context), "config.toml"),
|
||||
markerPath: grokDirectory,
|
||||
format: "grok",
|
||||
},
|
||||
{
|
||||
name: "grok_bot",
|
||||
displayName: "Grok Bot",
|
||||
configPath: (context: ClientPathContext) =>
|
||||
join(grokBotDirectory(context), "mcp.json"),
|
||||
markerPath: grokBotDirectory,
|
||||
format: "json",
|
||||
},
|
||||
] as const satisfies readonly ClientDefinition[];
|
||||
|
||||
const resolvePath = (path: ClientPath, context: ClientPathContext): string =>
|
||||
@@ -226,7 +256,9 @@ export const supportedClients = (
|
||||
CLAUDE_CONFIG_DIR: process.env.CLAUDE_CONFIG_DIR,
|
||||
CODEX_HOME: process.env.CODEX_HOME,
|
||||
COPILOT_HOME: process.env.COPILOT_HOME,
|
||||
GROK_HOME: process.env.GROK_HOME,
|
||||
OPENCODE_CONFIG: process.env.OPENCODE_CONFIG,
|
||||
SAND_DATA_ROOT: process.env.SAND_DATA_ROOT,
|
||||
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME,
|
||||
},
|
||||
): readonly SetupClient[] => {
|
||||
|
||||
@@ -152,7 +152,7 @@ const removeClient = async (
|
||||
return item(
|
||||
client.name,
|
||||
"failed",
|
||||
`Configuration is not valid ${client.format === "toml" ? "TOML" : client.format === "opencode" ? "JSONC" : "JSON"} and was not changed. Repair it, then rerun uninstall.`,
|
||||
`Configuration is not valid ${client.format === "toml" || client.format === "grok" ? "TOML" : client.format === "opencode" ? "JSONC" : "JSON"} and was not changed. Repair it, then rerun uninstall.`,
|
||||
);
|
||||
}
|
||||
const name = PRODUCT_IDENTITY.mcpServerKey;
|
||||
|
||||
@@ -25,7 +25,9 @@ beforeEach(() => {
|
||||
"CLAUDE_CONFIG_DIR",
|
||||
"CODEX_HOME",
|
||||
"COPILOT_HOME",
|
||||
"GROK_HOME",
|
||||
"OPENCODE_CONFIG",
|
||||
"SAND_DATA_ROOT",
|
||||
"XDG_CONFIG_HOME",
|
||||
])
|
||||
vi.stubEnv(name, undefined);
|
||||
@@ -398,6 +400,40 @@ describe("platform-aware client config paths", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it("honors Grok Build and Grok Bot configuration roots", () => {
|
||||
expect(
|
||||
supportedClients("/home/a", "linux", {
|
||||
GROK_HOME: "/custom/grok",
|
||||
SAND_DATA_ROOT: "/custom/grokbot",
|
||||
}).filter(({ name }) => name === "grok_build" || name === "grok_bot"),
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
name: "grok_build",
|
||||
displayName: "Grok Build",
|
||||
configPath: "/custom/grok/config.toml",
|
||||
markerPath: "/custom/grok",
|
||||
format: "grok",
|
||||
}),
|
||||
expect.objectContaining({
|
||||
name: "grok_bot",
|
||||
displayName: "Grok Bot",
|
||||
configPath: "/custom/grokbot/mcp.json",
|
||||
markerPath: "/custom/grokbot",
|
||||
format: "json",
|
||||
}),
|
||||
]);
|
||||
expect(
|
||||
supportedClients("/home/a", "linux", {
|
||||
SAND_DATA_ROOT: "relative/sand",
|
||||
}).find(({ name }) => name === "grok_bot")?.configPath,
|
||||
).toBe("/home/a/.grokbot/mcp.json");
|
||||
expect(
|
||||
supportedClients("/home/a", "darwin", {}).find(
|
||||
({ name }) => name === "grok_build",
|
||||
)?.configPath,
|
||||
).toBe("/home/a/.grok/config.toml");
|
||||
});
|
||||
|
||||
it("honors Copilot CLI and Devin platform configuration roots", () => {
|
||||
expect(
|
||||
supportedClients("/home/a", "linux", {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +1,30 @@
|
||||
import { mkdir, writeFile } from "node:fs/promises";
|
||||
import { join, resolve } from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
|
||||
import { readClientRegistrationStatuses } from "../../../src/application/ClientRegistrationStatus.js";
|
||||
import { PRODUCT_IDENTITY } from "../../../src/identity.js";
|
||||
|
||||
beforeEach(() => {
|
||||
for (const name of [
|
||||
"APPDATA",
|
||||
"CLAUDE_CONFIG_DIR",
|
||||
"CODEX_HOME",
|
||||
"COPILOT_HOME",
|
||||
"GROK_HOME",
|
||||
"OPENCODE_CONFIG",
|
||||
"SAND_DATA_ROOT",
|
||||
"XDG_CONFIG_HOME",
|
||||
])
|
||||
vi.stubEnv(name, undefined);
|
||||
});
|
||||
afterEach(() => {
|
||||
vi.unstubAllEnvs();
|
||||
});
|
||||
|
||||
describe("client registration status", () => {
|
||||
it("discovers Claude Code from its config file without a marker directory", async () => {
|
||||
const home = await createTestTempDirectory("rea-claude-config-only-");
|
||||
@@ -206,6 +223,43 @@ describe("Node-wrapped registration policy", () => {
|
||||
},
|
||||
);
|
||||
|
||||
it.each([1, 30])(
|
||||
"checks Grok Build startup timeout %d independently of launcher",
|
||||
async (timeout) => {
|
||||
const home = await createTestTempDirectory("rea-grok-registration-");
|
||||
await mkdir(join(home, ".grok"));
|
||||
const entry = resolve("scripts/rea.mjs");
|
||||
await writeFile(
|
||||
join(home, ".grok/config.toml"),
|
||||
`[mcp_servers.rea]\ncommand = ${JSON.stringify(process.execPath)}\nargs = [${JSON.stringify(entry)}, "mcp"]\nstartup_timeout_sec = ${String(timeout)}\n`,
|
||||
);
|
||||
const statuses = await readClientRegistrationStatuses(home, entry, {
|
||||
environment: {},
|
||||
});
|
||||
expect(statuses).toEqual([
|
||||
expect.objectContaining({
|
||||
client: "grok_build",
|
||||
state: timeout === 30 ? "aligned" : "stale",
|
||||
}),
|
||||
]);
|
||||
},
|
||||
);
|
||||
|
||||
it("reports Grok Build stale when rea is listed in disabled_mcp_servers", async () => {
|
||||
const home = await createTestTempDirectory("rea-grok-disabled-status-");
|
||||
await mkdir(join(home, ".grok"));
|
||||
const entry = resolve("scripts/rea.mjs");
|
||||
await writeFile(
|
||||
join(home, ".grok/config.toml"),
|
||||
`disabled_mcp_servers = ["rea"]\n[mcp_servers.rea]\ncommand = ${JSON.stringify(process.execPath)}\nargs = [${JSON.stringify(entry)}, "mcp"]\nstartup_timeout_sec = 30\n`,
|
||||
);
|
||||
expect(
|
||||
await readClientRegistrationStatuses(home, entry, { environment: {} }),
|
||||
).toEqual([
|
||||
expect.objectContaining({ client: "grok_build", state: "stale" }),
|
||||
]);
|
||||
});
|
||||
|
||||
it("checks a direct-launcher Codex startup timeout without Node wrapping", async () => {
|
||||
const home = await createTestTempDirectory("rea-direct-registration-");
|
||||
await mkdir(join(home, ".codex"));
|
||||
|
||||
@@ -27,6 +27,8 @@ beforeEach(async () => {
|
||||
CLAUDE_CONFIG_DIR: home,
|
||||
CODEX_HOME: join(home, ".codex"),
|
||||
COPILOT_HOME: join(home, ".copilot"),
|
||||
GROK_HOME: join(home, ".grok"),
|
||||
SAND_DATA_ROOT: join(home, ".grokbot"),
|
||||
XDG_CONFIG_HOME: join(home, ".config"),
|
||||
OPENCODE_CONFIG: join(home, ".config", "opencode", "opencode.jsonc"),
|
||||
}))
|
||||
|
||||
@@ -107,7 +107,7 @@
|
||||
Any agent that supports local MCP servers. Setup includes
|
||||
Claude Code, Claude Desktop, Codex, Cursor, Gemini CLI,
|
||||
Windsurf, Devin, OpenCode, Antigravity, GitHub Copilot CLI,
|
||||
Command Code and VS Code.
|
||||
Command Code, Grok Build, Grok Bot and VS Code.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../get-started/#agent">Connect your coding agent</a>.
|
||||
|
||||
@@ -75,8 +75,8 @@
|
||||
<h2>Connect your coding agent</h2>
|
||||
<p>
|
||||
Run setup to connect REA to your coding agent. Client choices
|
||||
include Claude Code, Codex, Cursor, Gemini CLI, Windsurf and other
|
||||
supported MCP clients.
|
||||
include Claude Code, Codex, Cursor, Gemini CLI, Windsurf, Grok
|
||||
Build, Grok Bot and other supported MCP clients.
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
|
||||
Reference in New Issue
Block a user