CodexとClaude Codeを行き来していると、同じ作業手順をそれぞれのスキルに書くのが少し気になります。片方だけ直して、もう片方が古いままになるのも避けたいところです。
どちらもSKILL.mdを読めるなら、ファイルを1つにしてしまえばよいのでは。今回は共通のCSV整形スキルを作り、読み込みから生成ファイルまで比べました。
共通にするのは、説明文と整形スクリプト
スキルは、作業の指示を書いたSKILL.mdと、必要なスクリプトなどをまとめたディレクトリです。今回はCSVの整形ルールをPythonに持たせ、スキルにはその呼び方を書きました。
文章だけで整形を頼むと、列の順番や改行までモデルの判断に任せることになります。まずは処理そのものを固定して、両ツールが同じ処理にたどり着けるか確認することにしました。
Codexの公式ドキュメントでは、プロジェクトのスキルを.agents/skillsから読み込み、シンボリックリンクにも対応すると説明されています。Claude Codeの公式ドキュメントでは、プロジェクト用は.claude/skillsです。こちらもリンクしたスキルディレクトリを読み込めます。
そこで、実体をshared/に置き、両方の探索先から同じディレクトリを参照させました。
project/
├── .agents/skills/normalize-csv -> ../../shared/normalize-csv
├── .claude/skills/normalize-csv -> ../../shared/normalize-csv
├── shared/normalize-csv/
│ ├── SKILL.md
│ └── scripts/normalize_csv.py
├── data/input.csv
└── nested/リンクを作るコマンドは次のとおりです。shared/normalize-csvは事前に作成しておきます。
mkdir -p .agents/skills .claude/skills
ln -s ../../shared/normalize-csv .agents/skills/normalize-csv
ln -s ../../shared/normalize-csv .claude/skills/normalize-csvコピーを2つ置く方法でも内容は揃えられますが、今回は更新漏れを避けたかったので、実体を1つにしました。リンク先のSKILL.mdが同じファイルであることも確認しています。
スクリプトの相対パスは、少し気を付ける
スキルにpython3 scripts/normalize_csv.pyとだけ書くと、シェルの作業ディレクトリから探してしまいます。SKILL.mdの隣にあるからといって、そこへ自動的に移動するわけではありません。
比較用に、プロジェクトのルートからその相対パスを直接実行しました。予想どおり、終了コードは2でした。これは両ツールに失敗させた結果ではなく、パスの前提を確認するために別途実行した対照ケースです。
can't open file '<project>/scripts/normalize_csv.py':
[Errno 2] No such file or directoryClaude Codeには${CLAUDE_SKILL_DIR}という置換変数があります。ただ、今回の共通スキルでは製品固有の変数を使わず、読み込んだSKILL.mdの場所からスクリプトを解決する指示にしました。
実際に使ったSKILL.mdは次の内容です。
---
name: normalize-csv
description: Normalize a CSV with id,name,amount columns into deterministic UTF-8 output using the bundled Python script. Use when asked to normalize this CSV format; not for spreadsheets or arbitrary schemas.
---
Run the bundled `scripts/normalize_csv.py` with Python 3. Resolve this script relative to the directory containing this SKILL.md, never relative to the shell working directory. Use the absolute script path.
The user supplies absolute input and output paths. Pass them with `--input` and `--output`. Do not change the shell working directory. Do not rewrite the formatter or the CSV manually. The script also writes `<output>.audit.json` with its actual working directory and file hashes.
Report the script exit status, output SHA-256, row count and recorded working directory. On failure, report the error without editing the skill or input.入力と出力も絶対パスで渡しています。これで、起動位置によって別のCSVを読んだり、別の場所へ保存したりする条件を外しました。相対パスの入力を自然に扱えるかどうかは、今回の対象にしていません。
CSVの内容と、出力のルールを固定する
入力は4レコードだけですが、日本語、カンマを含む名前、セル内改行、先頭ゼロ付きIDを混ぜました。元ファイルはBOM付きUTF-8で、レコードの区切りはCRLFです。
整形では名前の前後の空白を取り除き、IDを数値として並べ替えます。ただし、001のようなID表記はそのまま残します。金額はDecimalで扱って小数2桁に揃え、出力はBOMなしUTF-8、レコードの区切りをLFに固定しました。
処理の中心はこの部分です。実際のスクリプトには列名・重複ID・金額の検査と監査JSONの保存も入れています。
rows.sort(key=lambda row: (int(row[0]), row[0]))
buf = io.StringIO(newline='')
writer = csv.writer(buf, lineterminator='\n')
writer.writerow(['id', 'name', 'amount'])
writer.writerows(rows)
dest.write_bytes(buf.getvalue().encode('utf-8'))金額を勝手に丸める処理は入れませんでした。小数3桁の1.234やNaNはエラーにします。別に用意した期待バイト列との比較に加え、金額・重複ID・列名・入力への上書きの拒否も確認しました。
ルートと子ディレクトリから、両方のCLIを起動する
検証は2026年10月16日のmacOS環境で行いました。Codex CLIは0.156.1、モデル指定はgpt-6-astra、Claude Codeは2.1.295、実行ログ上のモデルはclaude-opus-5-5です。整形に使われたPythonは、4条件とも3.13.5でした。
一時ディレクトリにGitリポジトリを作り、ルートとnested/から、それぞれ新しいセッションを起動しました。スキル名はCodexで$normalize-csv、Claude Codeで/normalize-csvと明示しています。説明文から自動的に選ばれるかの比較ではありません。
依頼内容は共通で、入力パスは同じ、出力ファイル名だけを条件ごとに変えています。
$normalize-csv を使ってCSVを整形してください。
入力: <project>/data/input.csv
出力: <project>/output/codex-root.csv
スキルが認識されていない場合は探索や代替実装をせず、
その旨だけ報告してください。スクリプト自身にPath.cwd()、入力・スクリプト・スキル・出力のSHA-256を記録させました。モデルが最後に返す説明だけでは、実行場所や生成ファイルの一致を判断しないためです。
実行環境側の制約で起動できなかった試行と、Claude CodeのOAuth期限切れもありました。認証を更新して再実行できたので、これらはスキルの互換性とは分け、失敗ログも残しています。
生成された86バイトは、4条件で完全に一致
Claude Code側は初期ログのskills一覧と、スキル名を明示した後の同梱スクリプト実行を確認しました。内部で展開されたプロンプト全体を比較したわけではありません。
成功した4条件では、入力・SKILL.md・Pythonスクリプトのハッシュが揃っていました。出力CSVのハッシュも同じです。
| 条件 | スキルの読み込み確認 | スクリプトの作業ディレクトリ | 出力CSV |
|---|---|---|---|
| Codex・ルート | SKILL.mdの読み取りログ | プロジェクトのルート | 86バイト・一致 |
| Codex・子ディレクトリ | SKILL.mdの読み取りログ | nested/ | 86バイト・一致 |
| Claude Code・ルート | 初期スキル一覧と同梱スクリプト実行 | プロジェクトのルート | 86バイト・一致 |
| Claude Code・子ディレクトリ | 初期スキル一覧と同梱スクリプト実行 | nested/ | 86バイト・一致 |

共通のSHA-256は次の値です。ハッシュだけでなく、保存した4ファイルのバイト列も直接比較しました。別に定義した期待バイト列とも一致しています。
559b8ed2e96d7a02cf2d4ddfcfa23844b5543169907843a0e365d830f53abaa9出力内容は次のとおりです。改行とありの間はセル内改行なので、見た目の行数は増えていますが、データは4レコードです。
id,name,amount
001,佐藤,0.00
2,"A, B",3.00
3,"改行
あり",-2.50
010,山田,12.50監査JSONそのものは一致しません。作業ディレクトリや出力先のパスが違うためです。今回比較した成果物はCSV本体で、監査JSONは差を確認するための記録として扱っています。
途中のコマンドまでは揃わなかった
ここは少し想定と違いました。Codexは、リンク先の実体であるshared/normalize-csv/scripts/normalize_csv.pyを実行しました。Claude Codeは、.claude/skills/normalize-csv/scripts/normalize_csv.pyというリンク経由のパスを使っています。どちらも同じファイルを指しています。
さらにClaude Codeは、最初のコマンドに; echo "EXIT=$?"を追加しました。終了コードを報告する指示に対応するためだと思います。ただ、今回の実験では対象のPythonコマンドだけを許可していたため、この複合コマンドは拒否されました。
Claude Codeは追加部分を外して再実行し、成功しています。ルートと子ディレクトリの両方で同じ流れになり、permission_denialsがそれぞれ1件残りました。

同じスキルを渡しても、コマンドの組み立てや確認の仕方まで固定されるわけではないようです。今回は最終的に同じCSVになりましたが、非対話で止めずに動かしたい処理なら、この違いも見ておきたいと感じました。
ただし、両CLIの権限設定は同一ではありません。Codexはworkspace-write、Claude Codeは実験用の--allowedTools指定を使っています。この1件を、製品全体の成功率や優劣として読むことはできません。
共通化できた範囲を、次のスキルにも使いたい
今回揃ったのは、共通のスキルが案内する同じPythonスクリプトに、同じ入力を渡した結果です。自然言語の回答や、モデルがその場で生成するコードの一致まで確認できたわけではありません。
成功実行は各条件1回ずつです。大量データ、Windows、クラウド実行、暗黙のスキル選択、フックなどの製品固有設定は試していません。別の端末ではPythonのバージョンも揃えて確認したいところです。
それでも、同じ処理を二重管理せずに呼び出せることは確認できました。私はまず、CSV整形のように結果を機械的に比較できる処理から共通化していこうと思います。次は入力パスも相対指定にして、どこまで指示を減らしても迷わず実行できるか試したいです。
ここまで読んでいただき、ありがとうございます。もしこの記事の技術や考え方に少しでも興味を持っていただけたら、ネクストのエンジニアと気軽に話してみませんか。
- 選考ではありません
- 履歴書不要
- 技術の話が中心
- 所要時間30分程度
- オンラインOK