Skip to content

Commit 137b43b

Browse files
setup(codex/hermes/opencode): mirror Claude skills so non-Claude agents are not skill-blind
CODA bundled 43 skills but only Claude Code consumed them. Codex, Hermes, and OpenCode were skill-blind — same Databricks knowledge, exposed to one of the five agents. setup_gemini.py already did this for Gemini. This PR factors out the copy logic into a `mirror_claude_skills(target_dir, agent_name)` utility and calls it from setup_codex.py (-> ~/.codex/skills/), setup_hermes.py (-> ~/.hermes/skills/), and setup_opencode.py (-> ~/.local/share/opencode/skills/). Each agent's setup is idempotent across restarts (target dir is replaced, not appended). setup_gemini.py is left alone — its inline implementation already works. A separate cleanup can route Gemini through the same utility for consistency. Closes [Discipline Gaps](https://docs.google.com/spreadsheets/d/1_VBK5twTQmMRDSzwDa7JkCN2Rb6xg5HIM-U4jGC5PKU/edit?gid=1103133504#gid=1103133504) row #17. This PR was prepared by Claude.
1 parent e4a4b21 commit 137b43b

4 files changed

Lines changed: 31 additions & 3 deletions

File tree

setup_codex.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
import subprocess
1414
from pathlib import Path
1515

16-
from utils import adapt_instructions_file, ensure_https, get_gateway_host, get_npm_version
16+
from utils import adapt_instructions_file, ensure_https, get_gateway_host, get_npm_version, mirror_claude_skills
1717

1818
# Set HOME if not properly set
1919
if not os.environ.get("HOME") or os.environ["HOME"] == "/":
@@ -93,6 +93,9 @@
9393
codex_dir = home / ".codex"
9494
codex_dir.mkdir(exist_ok=True)
9595

96+
# Mirror Claude skills into ~/.codex/skills so Codex sees the same skill content
97+
mirror_claude_skills(codex_dir / "skills", "Codex")
98+
9699
# Copy bundled Databricks model catalog into ~/.codex so it can be referenced
97100
# by relative path in config.toml (codex resolves relatives against CODEX_HOME).
98101
catalog_src = Path(__file__).parent / ".codex" / "databricks-models.json"

setup_hermes.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
import subprocess
2626
from pathlib import Path
2727

28-
from utils import adapt_instructions_file, ensure_https, get_gateway_host
28+
from utils import adapt_instructions_file, ensure_https, get_gateway_host, mirror_claude_skills
2929

3030
# Opt-out: allow operators to disable Hermes bundling without removing the file.
3131
if os.environ.get("ENABLE_HERMES", "true").strip().lower() in ("false", "0", "no"):
@@ -58,6 +58,9 @@
5858
local_bin.mkdir(parents=True, exist_ok=True)
5959
hermes_home.mkdir(parents=True, exist_ok=True)
6060

61+
# Mirror Claude skills into ~/.hermes/skills so Hermes sees the same skill content
62+
mirror_claude_skills(hermes_home / "skills", "Hermes")
63+
6164

6265
def _run(cmd, **kwargs):
6366
"""Run a subprocess command and return (rc, stdout, stderr)."""

setup_opencode.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
import subprocess
1212
from pathlib import Path
1313

14-
from utils import ensure_https, get_gateway_host, get_npm_version
14+
from utils import ensure_https, get_gateway_host, get_npm_version, mirror_claude_skills
1515

1616
# content-filter proxy local proxy — sanitizes empty content blocks before reaching Databricks
1717
# (see https://github.com/sst/opencode/issues/5028)
@@ -278,6 +278,9 @@
278278
opencode_data_dir = home / ".local" / "share" / "opencode"
279279
opencode_data_dir.mkdir(parents=True, exist_ok=True)
280280

281+
# Mirror Claude skills into ~/.local/share/opencode/skills so OpenCode sees them
282+
mirror_claude_skills(opencode_data_dir / "skills", "OpenCode")
283+
281284
if gateway_host:
282285
auth_data = {
283286
"databricks": {

utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,29 @@
44

55
import os
66
import re
7+
import shutil
78
import subprocess
89
from pathlib import Path
910

1011

12+
def mirror_claude_skills(target_dir: Path, agent_name: str) -> None:
13+
"""Mirror ~/.claude/skills into ``target_dir`` so non-Claude agents see them.
14+
15+
Replaces ``target_dir`` if it already exists (idempotent across restarts).
16+
Resolves the Claude skills directory via ``HOME``, falling back to a no-op
17+
if nothing is there (the same shape `setup_gemini.py` uses for Gemini).
18+
"""
19+
home = Path(os.environ.get("HOME", "/app/python/source_code"))
20+
claude_skills_dir = home / ".claude" / "skills"
21+
if not claude_skills_dir.exists():
22+
print(f"No Claude skills at {claude_skills_dir}, skipping {agent_name} mirror")
23+
return
24+
if target_dir.exists():
25+
shutil.rmtree(target_dir)
26+
shutil.copytree(claude_skills_dir, target_dir)
27+
print(f"Skills mirrored: {claude_skills_dir} -> {target_dir}")
28+
29+
1130
def get_npm_version(package_name):
1231
"""Resolve the latest stable version of an npm package.
1332

0 commit comments

Comments
 (0)