Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,15 @@ A game built entirely by Claude Code using these agents, skills, and pipelines.

## Installation

Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed and working (`claude --version` should print a version number). Codex CLI is also supported: the installer mirrors toolkit skills into `~/.codex/skills` so Codex can use the same skill library (`codex --version` should print a version number if you want Codex support too).
Requires [Claude Code](https://docs.anthropic.com/en/docs/claude-code) installed and working (`claude --version` should print a version number). Codex CLI is also supported: the installer mirrors toolkit skills into `~/.codex/skills` and toolkit agents into `~/.codex/agents` so Codex can use the same skill and agent library (`codex --version` should print a version number if you want Codex support too).

```bash
git clone https://github.com/notque/claude-code-toolkit.git ~/claude-code-toolkit
cd ~/claude-code-toolkit
./install.sh --symlink
```

The installer links agents, skills, hooks, commands, and scripts into `~/.claude/`, where Claude Code loads extensions from. It also mirrors skills into `~/.codex/skills` for Codex. Use `--symlink` to get updates via `git pull`, or run without it for a stable copy.
The installer links agents, skills, hooks, commands, and scripts into `~/.claude/`, where Claude Code loads extensions from. It also mirrors skills into `~/.codex/skills` and agents into `~/.codex/agents` for Codex. Use `--symlink` to get updates via `git pull`, or run without it for a stable copy.

Verify the install with:

Expand Down
4 changes: 2 additions & 2 deletions docs/QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ cd ~/claude-code-toolkit
./install.sh
```

Claude Code is the primary runtime. If you also use Codex CLI, the same install mirrors toolkit skills into `~/.codex/skills`.
Claude Code is the primary runtime. If you also use Codex CLI, the same install mirrors toolkit skills into `~/.codex/skills` and toolkit agents into `~/.codex/agents`.

Command entry points:
- Claude Code: `/do`
Expand All @@ -35,7 +35,7 @@ python3 ~/.claude/scripts/install-doctor.py check
python3 ~/.claude/scripts/install-doctor.py inventory
```

If Codex should pick up newly added skills after a `git pull`, rerun `./install.sh --symlink`.
If Codex should pick up newly added skills or agents after a `git pull`, rerun `./install.sh --symlink`.

---

Expand Down
6 changes: 3 additions & 3 deletions docs/start-here.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ claude --version

If that prints a version number, you're good. If not, install Claude Code first and come back.

Optional: if you also use Codex CLI, run `codex --version`. The toolkit mirrors its skills into `~/.codex/skills`, but Claude Code is still the full runtime for hooks, agents, commands, and scripts.
Optional: if you also use Codex CLI, run `codex --version`. The toolkit mirrors its skills into `~/.codex/skills` and its agents into `~/.codex/agents`, so Codex sessions can Read the same domain expertise Claude Code dispatches. Claude Code is still the full runtime for hooks, commands, and scripts.

Command entry points:
- Claude Code: `/do`
Expand All @@ -36,7 +36,7 @@ cd claude-code-toolkit

The installer asks one question -- symlink or copy -- then sets everything up. Pick symlink if you want updates via `git pull`, copy if you want a stable snapshot. Either works fine.

What just happened: the installer linked agents, skills, hooks, commands, and scripts into `~/.claude/`, which is where Claude Code looks for extensions. It also mirrored skills into `~/.codex/skills` for Codex and configured hooks in your settings so they activate automatically.
What just happened: the installer linked agents, skills, hooks, commands, and scripts into `~/.claude/`, which is where Claude Code looks for extensions. It also mirrored skills into `~/.codex/skills` and agents into `~/.codex/agents` for Codex, and configured hooks in your settings so they activate automatically.

## Verify It

Expand All @@ -47,7 +47,7 @@ python3 ~/.claude/scripts/install-doctor.py check
python3 ~/.claude/scripts/install-doctor.py inventory
```

`check` verifies the install layout, settings, hook paths, learning DB access, and Codex skill mirror. `inventory` shows what Claude and Codex can currently see. If you pull new toolkit changes later and want Codex to pick up new skills, rerun `./install.sh`.
`check` verifies the install layout, settings, hook paths, learning DB access, and both Codex skill and agent mirrors. `inventory` shows what Claude and Codex can currently see. If you pull new toolkit changes later and want Codex to pick up new skills or agents, rerun `./install.sh`.

## Your First Commands

Expand Down
39 changes: 37 additions & 2 deletions hooks/sync-to-user-claude.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -502,8 +502,10 @@ def main():
if voice_count > 0:
synced.append(f"private-voices({voice_count})")

# Sync skills to ~/.codex/skills/ for OpenAI Codex CLI.
# Codex only supports skills (no agents, hooks, or scripts).
# Sync skills and agents to ~/.codex/ for OpenAI Codex CLI.
# Codex natively supports skills; agents are mirrored as reference
# material so Codex sessions can Read the same domain expertise that
# Claude Code sessions dispatch via subagent_type.
codex_skills_dst = Path.home() / ".codex" / "skills"
codex_sources = [("skills", repo_root / "skills")]
codex_count = 0
Expand Down Expand Up @@ -555,6 +557,39 @@ def main():
total = sum(1 for _ in codex_skills_dst.rglob("*") if _.is_file())
synced.append(f".codex/skills({total} current)")

# Sync agents to ~/.codex/agents/ — parallel mirror to skills.
# Agents carry domain expertise (Go, Python, K8s, TypeScript, etc.)
# and their reference subdirectories. Codex can Read them even though
# it has no native subagent_type dispatch.
codex_agents_dst = Path.home() / ".codex" / "agents"
codex_agent_sources = [("agents", repo_root / "agents")]
private_agents_dir = repo_root / "private-agents"
if private_agents_dir.is_dir():
codex_agent_sources.append(("private-agents", private_agents_dir))
codex_agent_count = 0
for label, src in codex_agent_sources:
if not src.is_dir():
continue
try:
codex_agents_dst.mkdir(parents=True, exist_ok=True)
for item in src.rglob("*"):
if item.is_file():
rel = item.relative_to(src)
target = codex_agents_dst / rel
target.parent.mkdir(parents=True, exist_ok=True)
if target.exists() and filecmp.cmp(item, target, shallow=False):
continue
shutil.copy2(item, target)
codex_agent_count += 1
except Exception as e:
errors.append(f"codex-{label}: {e}")
# No stale cleanup for Codex agents — additive only, same rationale as skills.
if codex_agent_count > 0:
synced.append(f".codex/agents({codex_agent_count} updated)")
elif codex_agents_dst.is_dir():
total = sum(1 for _ in codex_agents_dst.rglob("*") if _.is_file())
synced.append(f".codex/agents({total} current)")

# Output for hook feedback
if synced:
print(f"[sync] Updated ~/.claude: {', '.join(synced)}")
Expand Down
66 changes: 66 additions & 0 deletions install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLAUDE_DIR="${HOME}/.claude"
CODEX_DIR="${HOME}/.codex"
CODEX_SKILLS_DIR="${CODEX_DIR}/skills"
CODEX_AGENTS_DIR="${CODEX_DIR}/agents"

echo -e "${BLUE}╔════════════════════════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ Claude Code Toolkit - Installation Script ║${NC}"
Expand Down Expand Up @@ -282,6 +283,42 @@ os.rename(tmp, dst)
echo " No ~/.codex/skills mirror found. Nothing to clean."
fi

echo ""
echo -e "${YELLOW}Cleaning Codex agents mirror...${NC}"
if [ -d "$CODEX_AGENTS_DIR" ]; then
for item in "${SCRIPT_DIR}/agents/"*; do
[ -e "$item" ] || continue
target="${CODEX_AGENTS_DIR}/$(basename "$item")"
if [ -L "$target" ] || [ -e "$target" ]; then
if [ "$DRY_RUN" = true ]; then
echo -e "${BLUE} Would remove Codex entry: ${target}${NC}"
else
rm -rf "$target"
echo -e "${GREEN} ✓ Removed Codex entry: ${target}${NC}"
fi
REMOVED+=("Codex agent $(basename "$item")")
fi
done

if [ -d "${SCRIPT_DIR}/private-agents" ]; then
for item in "${SCRIPT_DIR}/private-agents/"*; do
[ -e "$item" ] || continue
target="${CODEX_AGENTS_DIR}/$(basename "$item")"
if [ -L "$target" ] || [ -e "$target" ]; then
if [ "$DRY_RUN" = true ]; then
echo -e "${BLUE} Would remove Codex entry: ${target}${NC}"
else
rm -rf "$target"
echo -e "${GREEN} ✓ Removed Codex entry: ${target}${NC}"
fi
REMOVED+=("Codex agent $(basename "$item")")
fi
done
fi
else
echo " No ~/.codex/agents mirror found. Nothing to clean."
fi

# Phase 4: Remove install manifest
echo ""
echo -e "${YELLOW}Cleaning up manifest...${NC}"
Expand Down Expand Up @@ -408,6 +445,15 @@ else
fi
echo -e "${GREEN}✓ ${CODEX_SKILLS_DIR} ready${NC}"

echo ""
echo -e "${YELLOW}Setting up ~/.codex agents directory...${NC}"
if [ "$DRY_RUN" = true ]; then
echo -e "${BLUE} Would create: ${CODEX_AGENTS_DIR}${NC}"
else
mkdir -p "${CODEX_AGENTS_DIR}"
fi
echo -e "${GREEN}✓ ${CODEX_AGENTS_DIR} ready${NC}"

# Install components
echo ""
echo -e "${YELLOW}Installing components (mode: ${MODE})...${NC}"
Expand Down Expand Up @@ -572,6 +618,25 @@ if [ -d "${SCRIPT_DIR}/private-skills" ]; then
done
fi

echo ""
echo -e "${YELLOW}Syncing Codex agents mirror...${NC}"
CODEX_AGENT_COUNT=0
for item in "${SCRIPT_DIR}/agents/"*; do
[ -e "$item" ] || continue
target="${CODEX_AGENTS_DIR}/$(basename "$item")"
sync_codex_entry "$item" "$target"
CODEX_AGENT_COUNT=$((CODEX_AGENT_COUNT + 1))
done

if [ -d "${SCRIPT_DIR}/private-agents" ]; then
for item in "${SCRIPT_DIR}/private-agents/"*; do
[ -e "$item" ] || continue
target="${CODEX_AGENTS_DIR}/$(basename "$item")"
sync_codex_entry "$item" "$target"
CODEX_AGENT_COUNT=$((CODEX_AGENT_COUNT + 1))
done
fi

# Set up local overlay
echo ""
echo -e "${YELLOW}Setting up local overlay...${NC}"
Expand Down Expand Up @@ -751,6 +816,7 @@ echo "Installed components:"
echo " • Agents: ${AGENT_COUNT} specialized domain experts"
echo " • Skills: ${SKILL_COUNT} workflow methodologies (${INVOCABLE_COUNT} user-invocable)"
echo " • Codex skills: ${CODEX_ENTRY_COUNT} mirrored entries in ~/.codex/skills"
echo " • Codex agents: ${CODEX_AGENT_COUNT} mirrored entries in ~/.codex/agents"
echo " • Hooks: ${HOOK_COUNT} automation hooks"
echo " • Commands: ${COMMAND_COUNT} slash commands"
echo " • Scripts: ${SCRIPT_COUNT} utility scripts"
Expand Down
61 changes: 60 additions & 1 deletion scripts/install-doctor.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,55 @@ def check_codex_skills() -> dict:
}


def check_codex_agents() -> dict:
"""Check that toolkit agents are mirrored into ~/.codex/agents."""
codex_agents_dir = CODEX_DIR / "agents"
repo_root = get_toolkit_repo_root()

if repo_root is None:
return {
"name": "codex_agents",
"label": "~/.codex/agents mirror",
"passed": codex_agents_dir.is_dir(),
"detail": str(codex_agents_dir)
if codex_agents_dir.is_dir()
else "Codex agents mirror not found. Run install.sh from the toolkit repo.",
}

expected_entries = [item.name for item in sorted((repo_root / "agents").iterdir())]

private_agents_dir = repo_root / "private-agents"
if private_agents_dir.is_dir():
for agent_item in sorted(private_agents_dir.iterdir()):
expected_entries.append(agent_item.name)

expected_entries = list(dict.fromkeys(expected_entries))

if not codex_agents_dir.is_dir():
return {
"name": "codex_agents",
"label": "~/.codex/agents mirror",
"passed": False,
"detail": "Directory not found. Run install.sh to mirror toolkit agents for Codex.",
}

missing = [entry for entry in expected_entries if not (codex_agents_dir / entry).exists()]
if missing:
return {
"name": "codex_agents",
"label": "~/.codex/agents mirror",
"passed": False,
"detail": f"{len(expected_entries) - len(missing)}/{len(expected_entries)} entries present; missing: {', '.join(missing[:5])}",
}

return {
"name": "codex_agents",
"label": "~/.codex/agents mirror",
"passed": True,
"detail": f"All {len(expected_entries)} toolkit entries mirrored",
}


def check_hook_files() -> list[dict]:
"""Check that hooks referenced in settings.json actually exist."""
settings_file = CLAUDE_DIR / "settings.json"
Expand Down Expand Up @@ -567,6 +616,12 @@ def inventory() -> dict:
else:
counts["codex_skills"] = 0

codex_agents_dir = CODEX_DIR / "agents"
if codex_agents_dir.is_dir():
counts["codex_agents"] = len([f for f in codex_agents_dir.glob("*.md") if f.name != "README.md"])
else:
counts["codex_agents"] = 0

# Count MCP servers from registry
mcp_results = check_mcp_servers()
mcp_total = sum(1 for r in mcp_results if r["name"].startswith("mcp_") and r["name"] != "mcp_registry")
Expand All @@ -585,6 +640,7 @@ def run_all_checks() -> list[dict]:
results.append(check_claude_dir())
results.extend(check_components_installed())
results.append(check_codex_skills())
results.append(check_codex_agents())
results.append(check_settings_json())
results.extend(check_hook_files())
results.append(check_python_version())
Expand Down Expand Up @@ -638,7 +694,10 @@ def main():
print("\n Installed Components:\n")
print(f" Agents: {counts.get('agents', 0)}")
print(f" Skills: {counts.get('skills', 0)} ({counts.get('skills_invocable', 0)} user-invocable)")
print(f" Codex: {counts.get('codex_skills', 0)} skills available")
print(
f" Codex: {counts.get('codex_skills', 0)} skills available, "
f"{counts.get('codex_agents', 0)} agents available"
)
print(f" Hooks: {counts.get('hooks', 0)}")
print(f" Commands: {counts.get('commands', 0)}")
print(f" Scripts: {counts.get('scripts', 0)}")
Expand Down
Loading