Skip to content

Commit 8be3559

Browse files
committed
feat: add codex agent to cli
1 parent 7ca9fd0 commit 8be3559

File tree

10 files changed

+52
-9
lines changed

10 files changed

+52
-9
lines changed

packages/cli/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
### Patch Changes
66

77
- fix: ux nits
8+
- feat: add Codex agent integration to CLI
89

910
## 0.0.70
1011

packages/cli/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ npx @react-grab/cli -p pnpm -a claude-code -y
3838
| `--framework` | `-f` | Framework to configure | `next`, `vite`, `webpack` |
3939
| `--package-manager` | `-p` | Package manager to use | `npm`, `yarn`, `pnpm`, `bun` |
4040
| `--router` | `-r` | Next.js router type | `app`, `pages` |
41-
| `--agent` | `-a` | Agent integration to add | `claude-code`, `cursor`, `opencode`, `none` |
41+
| `--agent` | `-a` | Agent integration to add | `claude-code`, `cursor`, `codex`, `opencode`, `none` |
4242
| `--yes` | `-y` | Skip all confirmation prompts | - |
4343
| `--skip-install` | - | Skip package installation (only modify files) | - |
4444
| `--help` | `-h` | Show help | - |
@@ -56,6 +56,9 @@ npx @react-grab/cli -y
5656
# Next.js App Router with Cursor agent
5757
npx @react-grab/cli -f next -r app -a cursor -y
5858

59+
# Next.js App Router with Codex agent
60+
npx @react-grab/cli -f next -r app -a codex -y
61+
5962
# Vite with Claude Code agent using pnpm
6063
npx @react-grab/cli -f vite -p pnpm -a claude-code -y
6164

@@ -81,6 +84,7 @@ The CLI can optionally set up agent integrations for:
8184

8285
- **Claude Code** (`-a claude-code`) - Send selected elements to Claude Code
8386
- **Cursor** (`-a cursor`) - Send selected elements to Cursor
87+
- **Codex** (`-a codex`) - Send selected elements to Codex
8488
- **Opencode** (`-a opencode`) - Send selected elements to Opencode
8589

8690
## Manual Installation

packages/cli/src/cli.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ const PACKAGE_MANAGER_NAMES: Record<PackageManager, string> = {
7373
const AGENT_NAMES: Record<string, string> = {
7474
"claude-code": "Claude Code",
7575
cursor: "Cursor",
76+
codex: "Codex",
7677
opencode: "Opencode",
7778
};
7879

@@ -258,7 +259,7 @@ const parseArgs = async (): Promise<CliArgs> => {
258259
.option("agent", {
259260
alias: "a",
260261
type: "string",
261-
choices: ["claude-code", "cursor", "opencode", "none"] as const,
262+
choices: ["claude-code", "cursor", "opencode", "codex", "none"] as const,
262263
description:
263264
"Agent integration to automatically forward selected elements to agent instead of copying to clipboard",
264265
})
@@ -284,6 +285,10 @@ const parseArgs = async (): Promise<CliArgs> => {
284285
"$0 -a cursor -y",
285286
"Add Cursor agent integration non-interactively",
286287
)
288+
.example(
289+
"$0 -a codex -y",
290+
"Add Codex agent integration non-interactively",
291+
)
287292
.example("$0 -p pnpm -a claude-code", "Use pnpm and add Claude Code agent")
288293
.example(
289294
"$0 --skip-install",
@@ -293,6 +298,7 @@ const parseArgs = async (): Promise<CliArgs> => {
293298
`${pc.bold("Agent Integrations:")}\n` +
294299
` ${pc.cyan("claude-code")} Connect React Grab to Claude Code\n` +
295300
` ${pc.cyan("cursor")} Connect React Grab to Cursor IDE\n` +
301+
` ${pc.cyan("codex")} Connect React Grab to Codex CLI\n` +
296302
` ${pc.cyan("opencode")} Connect React Grab to Opencode\n\n` +
297303
`${pc.bold("Supported Frameworks:")}\n` +
298304
` ${pc.cyan("next")} Next.js (App Router & Pages Router)\n` +
@@ -496,6 +502,7 @@ const main = async () => {
496502
const availableAgents = [
497503
{ name: "Claude Code", value: "claude-code" as const },
498504
{ name: "Cursor", value: "cursor" as const },
505+
{ name: "Codex", value: "codex" as const },
499506
{ name: "Opencode", value: "opencode" as const },
500507
].filter((agent) => !projectInfo.installedAgents.includes(agent.value));
501508

@@ -511,7 +518,7 @@ const main = async () => {
511518
} else {
512519
const wantAgentIntegration = await confirm({
513520
message:
514-
"Do you want to add an agent integration (Claude Code, Cursor, or Opencode)?",
521+
"Do you want to add an agent integration (Claude Code, Cursor, Codex, or Opencode)?",
515522
default: false,
516523
});
517524

packages/cli/src/detect.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ export const detectReactGrab = (projectRoot: string): boolean => {
291291
return filesToCheck.some(hasReactGrabInFile);
292292
};
293293

294-
const AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode"];
294+
const AGENT_PACKAGES = ["@react-grab/claude-code", "@react-grab/cursor", "@react-grab/opencode", "@react-grab/codex"];
295295

296296
export const detectUnsupportedFramework = (projectRoot: string): UnsupportedFramework => {
297297
const packageJsonPath = join(projectRoot, "package.json");

packages/cli/src/templates.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export type AgentIntegration = "claude-code" | "cursor" | "opencode" | "none";
1+
export type AgentIntegration = "claude-code" | "cursor" | "opencode" | "codex" | "none";
22

33
export const NEXT_APP_ROUTER_SCRIPT = `{process.env.NODE_ENV === "development" && (
44
<Script

packages/cli/src/transform.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ export const transformProject = (
597597
const AGENT_PREFIXES: Record<string, string> = {
598598
"claude-code": "npx @react-grab/claude-code@latest &&",
599599
cursor: "npx @react-grab/cursor@latest &&",
600+
codex: "npx @react-grab/codex@latest &&",
600601
opencode: "npx @react-grab/opencode@latest &&",
601602
};
602603

packages/cli/test/detect.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,13 +224,15 @@ describe("detectInstalledAgents", () => {
224224
devDependencies: {
225225
"@react-grab/cursor": "1.0.0",
226226
"@react-grab/claude-code": "1.0.0",
227+
"@react-grab/codex": "1.0.0",
227228
},
228229
})
229230
);
230231

231232
const agents = detectInstalledAgents("/test");
232233
expect(agents).toContain("cursor");
233234
expect(agents).toContain("claude-code");
235+
expect(agents).toContain("codex");
234236
expect(agents).not.toContain("opencode");
235237
});
236238

packages/cli/test/install.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe("getPackagesToInstall", () => {
2727
});
2828

2929
it("should handle all agent types", () => {
30-
const agents = ["claude-code", "cursor", "opencode"] as const;
30+
const agents = ["claude-code", "cursor", "opencode", "codex"] as const;
3131

3232
for (const agent of agents) {
3333
const packages = getPackagesToInstall(agent, false);

packages/cli/test/templates.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ describe("Next.js App Router templates", () => {
3232
});
3333

3434
it("should include all agent types correctly", () => {
35-
const agents = ["claude-code", "cursor", "opencode"] as const;
35+
const agents = ["claude-code", "cursor", "opencode", "codex"] as const;
3636

3737
for (const agent of agents) {
3838
const script = NEXT_APP_ROUTER_SCRIPT_WITH_AGENT(agent);
@@ -54,6 +54,13 @@ describe("Vite templates", () => {
5454
expect(script).toContain("@react-grab/opencode");
5555
});
5656

57+
it("should generate script with codex agent", () => {
58+
const script = VITE_SCRIPT_WITH_AGENT("codex");
59+
60+
expect(script).toContain("react-grab");
61+
expect(script).toContain("@react-grab/codex");
62+
});
63+
5764
it("should return basic script when agent is none", () => {
5865
const script = VITE_SCRIPT_WITH_AGENT("none");
5966

packages/cli/test/transform.test.ts

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,17 @@ describe("previewTransform - Vite", () => {
162162
expect(result.newContent).toContain("@react-grab/opencode");
163163
});
164164

165+
it("should add React Grab with codex agent to index.html", () => {
166+
mockExistsSync.mockImplementation((path) => String(path).endsWith("index.html"));
167+
mockReadFileSync.mockReturnValue(indexContent);
168+
169+
const result = previewTransform("/test", "vite", "unknown", "codex", false);
170+
171+
expect(result.success).toBe(true);
172+
expect(result.newContent).toContain("react-grab");
173+
expect(result.newContent).toContain("@react-grab/codex");
174+
});
175+
165176
it("should add agent to existing React Grab installation", () => {
166177
const indexWithReactGrab = `<!doctype html>
167178
<html lang="en">
@@ -500,6 +511,16 @@ describe("previewPackageJsonTransform", () => {
500511
expect(result.newContent).toContain("npx @react-grab/opencode@latest &&");
501512
});
502513

514+
it("should add codex prefix to dev script", () => {
515+
mockExistsSync.mockReturnValue(true);
516+
mockReadFileSync.mockReturnValue(packageJsonContent);
517+
518+
const result = previewPackageJsonTransform("/test", "codex", []);
519+
520+
expect(result.success).toBe(true);
521+
expect(result.newContent).toContain("npx @react-grab/codex@latest &&");
522+
});
523+
503524
it("should skip when agent is none", () => {
504525
const result = previewPackageJsonTransform("/test", "none", []);
505526

@@ -533,7 +554,7 @@ describe("previewPackageJsonTransform", () => {
533554
{
534555
name: "my-app",
535556
scripts: {
536-
dev: "npx @react-grab/claude-code@latest && next dev",
557+
dev: "npx @react-grab/codex@latest && next dev",
537558
},
538559
},
539560
null,
@@ -543,7 +564,7 @@ describe("previewPackageJsonTransform", () => {
543564
mockExistsSync.mockReturnValue(true);
544565
mockReadFileSync.mockReturnValue(packageJsonWithAgent);
545566

546-
const result = previewPackageJsonTransform("/test", "cursor", ["claude-code"]);
567+
const result = previewPackageJsonTransform("/test", "cursor", ["codex"]);
547568

548569
expect(result.success).toBe(true);
549570
expect(result.noChanges).toBe(true);

0 commit comments

Comments
 (0)