Skip to content

Commit 7faf656

Browse files
authored
Merge pull request #13 from agentsdance/codex
feat(codex): add support for Codex CLI agent
2 parents 0ee4e7b + e434686 commit 7faf656

13 files changed

Lines changed: 454 additions & 54 deletions

File tree

CLAUDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,15 @@ make clean
3232

3333
## Architecture
3434

35-
AgentX is a CLI tool for managing MCP servers, skills, and plugins across multiple AI coding agents (Claude Code, Cursor, Gemini CLI, OpenCode).
35+
AgentX is a CLI tool for managing MCP servers, skills, and plugins across multiple AI coding agents (Claude Code, Codex, Cursor, Gemini CLI, OpenCode).
3636

3737
### Core Layers
3838

3939
**CLI Layer** (`cmd/`): Cobra-based commands. `root.go` launches the TUI when run without arguments, or delegates to subcommands (`install`, `check`, `list`, `remove`, `skills`, `plugins`).
4040

41-
**Agent Abstraction** (`internal/agent/`): The `Agent` interface defines operations for all supported agents. Each agent (Claude, Cursor, Gemini, OpenCode) implements this interface with its own config file location and format:
41+
**Agent Abstraction** (`internal/agent/`): The `Agent` interface defines operations for all supported agents. Each agent (Claude, Codex, Cursor, Gemini, OpenCode) implements this interface with its own config file location and format:
4242
- Claude Code: `~/.claude.json`
43+
- Codex: `~/.codex/config.toml`
4344
- Cursor: `~/.cursor/mcp.json`
4445
- Gemini CLI: `~/.gemini/settings.json`
4546
- OpenCode: `~/.opencode/config.json`

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ AgentX stands for Agent eXtension: A unified CLI tool for managing **MCP (Model
77
AgentX simplifies the installation, management, and monitoring of MCP servers and skills across popular AI coding tools:
88

99
- **Claude Code**
10+
- **Codex**
1011
- **Cursor**
1112
- **Gemini CLI**
1213
- **OpenCode**
@@ -23,9 +24,9 @@ It provides both a command-line interface and an interactive terminal UI (TUI) f
2324
- **Playwright** - Browser automation capabilities
2425
- **Context7** - Library documentation access
2526

26-
### Claude Code Skills Management
27+
### Claude Code & Codex Skills Management
2728
- Install skills from local paths or Git repositories
28-
- Support for skill directories (with `SKILL.md`) and command files (`.md`)
29+
- Support for skill directories (with `SKILL.md`); Claude Code also supports command files (`.md`)
2930
- Install from GitHub URLs with tree fragments
3031
- Personal and project scope management
3132
- Skills health checking and validation
@@ -105,24 +106,34 @@ The tool responds to: `agentx`, `agents`, or `ax`
105106
| Agent | Config Path |
106107
|-------|-------------|
107108
| Claude Code | `~/.claude.json` |
109+
| Codex | `~/.codex/config.toml` |
108110
| Cursor | `~/.cursor/mcp.json` |
109111
| Gemini CLI | `~/.gemini/settings.json` |
110112
| OpenCode | `~/.opencode/config.json` |
111113

112114
### Skills Storage
113115

116+
Claude Code:
117+
114118
| Scope | Skills Directory | Commands Directory |
115119
|-------|------------------|-------------------|
116120
| Personal | `~/.claude/skills/` | `~/.claude/commands/` |
117121
| Project | `.claude/skills/` | `.claude/commands/` |
118122

123+
Codex:
124+
125+
| Scope | Skills Directory |
126+
|-------|------------------|
127+
| Personal | `$CODEX_HOME/skills/` (default `~/.codex/skills/`) |
128+
| Project | `.codex/skills/` |
129+
119130
## Project Structure
120131

121132
```
122133
agentx/
123134
├── cmd/ # CLI commands
124135
├── internal/
125-
│ ├── agent/ # Agent implementations (Claude, Cursor, Gemini, OpenCode)
136+
│ ├── agent/ # Agent implementations (Claude, Codex, Cursor, Gemini, OpenCode)
126137
│ ├── config/ # Configuration management
127138
│ ├── skills/ # Skills management
128139
│ ├── mcp/ # MCP-specific logic

cmd/install.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package cmd
33
import (
44
"fmt"
55

6-
"github.com/spf13/cobra"
76
"github.com/agentsdance/agentx/internal/agent"
7+
"github.com/spf13/cobra"
88
)
99

1010
var agentFlag string
@@ -54,5 +54,5 @@ var installCmd = &cobra.Command{
5454
}
5555

5656
func init() {
57-
installCmd.Flags().StringVarP(&agentFlag, "agent", "a", "", "Target agent (claude, gemini, opencode)")
57+
installCmd.Flags().StringVarP(&agentFlag, "agent", "a", "", "Target agent (claude, codex, cursor, gemini, opencode)")
5858
}

cmd/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,17 @@ import (
44
"fmt"
55
"os"
66

7-
"github.com/spf13/cobra"
87
"github.com/agentsdance/agentx/internal/version"
98
"github.com/agentsdance/agentx/ui"
9+
"github.com/spf13/cobra"
1010
)
1111

1212
var rootCmd = &cobra.Command{
1313
Use: "agentx",
1414
Aliases: []string{"agents", "ax"},
1515
Short: "Unified MCP Servers & Agent Skills Manager for AI coding agents",
1616
Long: `agentx is a CLI tool for managing MCP servers and skills across AI coding agents
17-
(Claude Code, Cursor, Gemini cli, opencode).
17+
(Claude Code, Codex, Cursor, Gemini cli, opencode).
1818
1919
Run without arguments to launch the TUI interface.
2020

cmd/skills.go

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,33 +3,45 @@ package cmd
33
import (
44
"fmt"
55
"os"
6+
"strings"
67
"text/tabwriter"
78

8-
"github.com/spf13/cobra"
99
"github.com/agentsdance/agentx/internal/skills"
10+
"github.com/spf13/cobra"
1011
)
1112

1213
var skillsScope string
14+
var skillsAgent string
1315

1416
var skillsCmd = &cobra.Command{
1517
Use: "skills",
16-
Short: "Manage Claude Code skills",
17-
Long: `Manage Claude Code skills and slash commands.
18+
Short: "Manage Claude Code and Codex skills",
19+
Long: `Manage Claude Code and Codex skills and slash commands.
20+
21+
Use --agent to switch between agents (default: Claude Code). Codex does not
22+
support command files.
1823
1924
Skills are stored in:
2025
Personal: ~/.claude/skills/ and ~/.claude/commands/
21-
Project: .claude/skills/ and .claude/commands/`,
26+
Project: .claude/skills/ and .claude/commands/
27+
28+
Codex skills are stored in:
29+
Personal: $CODEX_HOME/skills/ (default ~/.codex/skills/)
30+
Project: .codex/skills/`,
2231
}
2332

2433
var skillsListCmd = &cobra.Command{
2534
Use: "list",
2635
Short: "List installed skills",
2736
Long: `List all installed skills and commands.`,
2837
Run: func(cmd *cobra.Command, args []string) {
29-
mgr := skills.NewSkillManager()
38+
mgr, err := resolveSkillsManager()
39+
if err != nil {
40+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
41+
os.Exit(1)
42+
}
3043

3144
var skillList []skills.Skill
32-
var err error
3345

3446
if skillsScope != "" {
3547
scope := skills.SkillScope(skillsScope)
@@ -90,7 +102,11 @@ The source can be:
90102
scope = skills.ScopeProject
91103
}
92104

93-
mgr := skills.NewSkillManager()
105+
mgr, err := resolveSkillsManager()
106+
if err != nil {
107+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
108+
os.Exit(1)
109+
}
94110
skill, err := mgr.Install(source, scope)
95111
if err != nil {
96112
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -117,7 +133,11 @@ var skillsRemoveCmd = &cobra.Command{
117133
scope = skills.ScopeProject
118134
}
119135

120-
mgr := skills.NewSkillManager()
136+
mgr, err := resolveSkillsManager()
137+
if err != nil {
138+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
139+
os.Exit(1)
140+
}
121141
if err := mgr.Remove(name, scope); err != nil {
122142
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
123143
os.Exit(1)
@@ -132,7 +152,11 @@ var skillsCheckCmd = &cobra.Command{
132152
Short: "Check skills installation status",
133153
Long: `Verify that all installed skills are valid and properly configured.`,
134154
Run: func(cmd *cobra.Command, args []string) {
135-
mgr := skills.NewSkillManager()
155+
mgr, err := resolveSkillsManager()
156+
if err != nil {
157+
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
158+
os.Exit(1)
159+
}
136160
statuses, err := mgr.Check()
137161
if err != nil {
138162
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
@@ -186,9 +210,30 @@ var skillsCheckCmd = &cobra.Command{
186210
func init() {
187211
skillsCmd.PersistentFlags().StringVarP(&skillsScope, "scope", "s", "",
188212
"Scope for the operation (personal, project)")
213+
skillsCmd.PersistentFlags().StringVarP(&skillsAgent, "agent", "a", "claude",
214+
"Target agent for skills (claude, codex)")
189215

190216
skillsCmd.AddCommand(skillsListCmd)
191217
skillsCmd.AddCommand(skillsInstallCmd)
192218
skillsCmd.AddCommand(skillsRemoveCmd)
193219
skillsCmd.AddCommand(skillsCheckCmd)
194220
}
221+
222+
func resolveSkillsManager() (*skills.DefaultSkillManager, error) {
223+
switch normalizeSkillsAgent(skillsAgent) {
224+
case "", "claude", "claudecode":
225+
return skills.NewSkillManager(), nil
226+
case "codex":
227+
return skills.NewCodexSkillManager(), nil
228+
default:
229+
return nil, fmt.Errorf("unknown agent: %s (use 'claude' or 'codex')", skillsAgent)
230+
}
231+
}
232+
233+
func normalizeSkillsAgent(name string) string {
234+
normalized := strings.ToLower(strings.TrimSpace(name))
235+
normalized = strings.ReplaceAll(normalized, "-", "")
236+
normalized = strings.ReplaceAll(normalized, "_", "")
237+
normalized = strings.ReplaceAll(normalized, " ", "")
238+
return normalized
239+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.25.1
55
require (
66
github.com/charmbracelet/bubbletea v1.3.10
77
github.com/charmbracelet/lipgloss v1.1.0
8+
github.com/pelletier/go-toml/v2 v2.2.4
89
github.com/spf13/cobra v1.10.2
910
gopkg.in/yaml.v3 v3.0.1
1011
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELU
3131
github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
3232
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
3333
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
34+
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
35+
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
3436
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
3537
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
3638
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=

internal/agent/agent.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ type Agent interface {
4242
func GetAllAgents() []Agent {
4343
return []Agent{
4444
NewClaudeAgent(),
45+
NewCodexAgent(),
4546
NewCursorAgent(),
4647
NewGeminiAgent(),
4748
NewOpenCodeAgent(),
@@ -64,6 +65,8 @@ func matchAgentName(agentName, input string) bool {
6465
switch input {
6566
case "claude", "claudecode", "claude-code", "claude_code":
6667
return agentName == "Claude Code"
68+
case "codex", "codexcli", "codex-cli", "codex_cli":
69+
return agentName == "Codex"
6770
case "cursor":
6871
return agentName == "Cursor"
6972
case "gemini", "geminicli", "gemini-cli", "gemini_cli":

0 commit comments

Comments
 (0)