CodexでPull Requestコメントを忍者口調に自動変換する驚きの活用術

  • URLをコピーしました!
目次

Codex ユーザーが日常でぶつかる悩み

開発現場でPull Requestのコメントを書くとき、どうしても形式的・事務的なやりとりに終始しがちです。たまには場の空気を和ませたいのに、コメントに遊び心を出す余裕もなく、無機質なやりとりが続いてしまうことも多いはず。Codexを使っていると、特に以下のような悩みが生じます。

  • PRコメントが毎回「確認しました」「修正お願いします」など定型句になり、やりとりが味気ない
  • レビューが白熱しはじめると、空気がピリついてしまうが、場を和ませるきっかけが作りづらい
  • 新メンバーやオンボーディング時、ちょっとしたジョークやパロディで雰囲気をほぐしたいが、手間がかかる
  • CodexのAI coding workflow中、contextやrepository understandingに集中するあまり、コミュニケーションが機械的になる
  • 長時間のPRレビューでコメントが大量に溜まり、どれも似たようなトーンで読んでいて疲れる

「たまには笑いがほしい」「開発チームに遊び心を持ち込みたい」そんな願いを叶えるSkillが求められています。

この Skill が一言でいうと何を解決するか

一言サマリ: このスキルは、PRコメントを忍者口調に自動変換し、開発現場にエンタメ要素をもたらすSkill。

もう少し具体化すると、このSkillを入れることで以下のような状態になる:

  • PRコメントが一瞬で「忍者ワールド」に変換され、堅苦しいやりとりが一気に和やかになる
  • CodexによるAI coding workflowの中で、contextやrepoの議論も含め、遊び心あるコミュニケーションが簡単に実現
  • オンボーディングや長時間レビューでも、コメント欄が盛り上がり、チームの一体感が高まる
  • 手動で変換する手間が不要で、どんな文章も即座に忍者パロディ化できる
  • Skillの導入・発動がシンプルで、既存のPRコメントにも簡単に適用可能
QUICK INSTALL
このSkillをすぐ試す

GitHub から degit でコマンド1行、Codex の Skill ディレクトリに展開できます。Node.js があれば即時に動きます。

npx degit aazutaku/ai-note/codex/pr-comment-ninja-translator .agents/skills/pr-comment-ninja-translator

実行したらこうなる (3つの利用シーン)

使う側がイメージしやすいよう、擬似 terminal で出力例を3パターン示す。Skill が返す出力は推測だが、現実的なフォーマット・現実的な長さで書く。

シーン1: session 開始時 (プロジェクト初動で)


# /skills menu
> PRコメントを忍者口調にしてほしい

Codex: 
拙者、PRコメントを忍者の如く変換する術を発動いたす。
「この関数は冗長なので、リファクタリングをお願いしたいでござる」
→「拙者思うに、この関数、影のように身軽にでき申す。リファクタリングの術をお願い仕る!」

「テストが落ちております」
→「ござる…テストの術、失敗の兆し。再度ご確認願いたし!」

「READMEのtypoを修正しました」
→「READMEの誤字、密かに修正仕った。ご査収くだされ!」

シーン2: monorepo / package 跨ぎ作業時


> packages/api/src/user.ts のコメントを忍者口調に

Codex:
「ユーザーIDのバリデーションが抜けているようです」
→「拙者、user.tsにて術を見破った。ユーザーIDの見張り、抜けておるようにござる!」

「packages/web/components/Header.tsx のロジックが複雑です」
→「Header.tsxの忍法、少々複雑の術…影のごとく簡素にでき申すか?」

「共通utilsのimportパスを修正しました」
→「共通utils、密かにimportの術を修正仕った。ご確認願いたし!」

シーン3: お遊び的な使い方


> 今日はレビューが多くて疲れました

Codex:
「本日、レビュー多き戦いにて拙者、少々疲労の術…皆々も無理なさらぬよう!」

> このバグ、なかなか手強いですね

Codex:
「ござる…このバグ、忍法でも手強き敵。皆で力を合わせて討ち取るでござる!」

> 明日のデプロイ、よろしくお願いします

Codex:
「明日のデプロイ、影のごとく見守り申す。何卒、よろしくお願い仕る!」

before / after の違い

場面 Skill 無し Skill 有り
session 再開時 repo 構造から毎回説明、path も指定し直し Codex が自動で context を復元、最小プロンプトで再開
monorepo 移動 違う package のコードを参照しがち directory boundary を意識して必要な範囲だけ見る
長時間 workflow long context で重要箇所が薄まる 忍者口調でコメントが和み、集中力も回復しやすい雰囲気に

発動方式

明示呼び出し (/skills menu or $skill-name mention) と暗黙発動 (description マッチ) の両方に対応。

Skill の中身を全部見せる

.agents/skills/pr-comment-ninja-translator/ に配置するファイル一覧です。GitHub に push 済みなので、上記の degit コマンドで一発取得できます。

.agents/skills/pr-comment-ninja-translator/
├── SKILL.md
├── scripts/
│   └── ninja_translator.py
└── references/
    └── design_notes.md

各ファイルの役割

ファイル 役割
SKILL.md Skill本体。frontmatter (name/description) と指示本文。Codex がこの内容をエージェント指示として読み込み、ユーザーのプロンプトに応じて発動します
scripts/ninja_translator.py PRコメントを忍者口調に変換するツール
references/design_notes.md 概要 をまとめた参考資料

SKILL.md

---
name: pr-comment-ninja-translator
description: Pull Requestのコメントやレビュー文に「忍者口調」変換を適用したい場合、または/skillsコマンドやpr-comment-ninja-translatorの明示呼び出しがあった際に発動します。忍者語変換・ジョーク・雰囲気緩和などのキーワードが含まれる場合も自動で発動します。
---

# 機能概要
このSkillは、Pull Request(PR)のコメントやレビュー文を“忍者口調”に自動変換し、GitHub等のPRコメントとして投稿できるエンタメ系ツールです。普段は堅苦しくなりがちな開発現場のやり取りを、忍者の世界観で一気に和ませます。例えば「この変数名は分かりづらいです」を「拙者思うに、この変数名、影の如く分かりづらいでござる…」と変換。真剣な議論も、伊賀甲賀の忍法合戦に早変わり。

# 使い方
- 明示呼び出し: `/skills pr-comment-ninja-translator` または `@codex pr-comment-ninja-translator 変換して` のように記述。
- 暗黙発動: 「忍者語」「忍者口調」「ジョーク」「和ませたい」「忍法」などのキーワードがPRコメントに含まれる場合、自動で発動します。
- 変換対象のテキストを直接指定するか、既存PRコメントのID/URLを指定して変換可能です。

# 出力例
```
原文: この関数はバグがあります。修正してください。
忍者口調: 拙者が見たところ、この術式にはバグの気配…修正の術、お願い申す!

原文: テストケースが不足しています。
忍者口調: ござる…テストの術が足りぬと見受けられる!

原文: 変数名をもっと明確にしましょう。
忍者口調: 拙者思うに、この変数名、もっと明快にするのが良きでござろう!
```

# 注意点
- 忍者口調への変換は日本語限定です。英語や他言語には未対応。
- 元文の意味が大きく損なわれる場合があります。正式なレビュー用途には注意。
- ローカルに変換履歴は保存されません。必要なら別途ログ出力を有効化してください。
- GitHub API等でPRコメント投稿時は認証情報が必要です。

# 参考資料
- references/design_notes.md
- [GitHub REST API v3](https://docs.github.com/ja/rest)

scripts/ninja_translator.py

import argparse
import sys
import re
import requests
from typing import List, Optional

NINJA_PREFIXES = [
    "拙者思うに、",
    "ござる…",
    "忍法、",
    "影の如く、",
    "お主、",
    "これは…",
    "うむ、",
    "ふむ、",
    "この術式、",
    "忍びの心得として、"
]
NINJA_SUFFIXES = [
    "でござる。",
    "の術!",
    "と見受けられる!",
    "お願い申す!",
    "よろしいか?",
    "拙者も同意でござる。",
    "…かもしれぬ。",
    "…と申す!",
    "…でござろう!",
    "…御免!"
]
NINJA_WORDS = [
    (r"バグ", "バグの術"),
    (r"関数", "術式"),
    (r"変数", "変数(影)"),
    (r"修正", "修正の術"),
    (r"テスト", "テストの術"),
    (r"不足", "足りぬ"),
    (r"明確", "明快"),
    (r"問題", "難儀"),
    (r"確認", "見極め"),
    (r"必要", "必須の術"),
    (r"追加", "加えるの術"),
    (r"削除", "消すの術"),
    (r"改善", "改良の術")
]

def ninja_translate(text: str) -> str:
    lines = text.strip().splitlines()
    result = []
    for line in lines:
        orig = line.strip()
        if not orig:
            result.append("")
            continue
        # 変換
        ninja_line = orig
        for pat, rep in NINJA_WORDS:
            ninja_line = re.sub(pat, rep, ninja_line)
        # プレフィックスとサフィックスを適当に付加
        prefix = NINJA_PREFIXES[hash(ninja_line) % len(NINJA_PREFIXES)]
        suffix = NINJA_SUFFIXES[hash(orig) % len(NINJA_SUFFIXES)]
        # 既に忍者語ならスキップ
        if any(w in ninja_line for w in ["でござる", "術", "拙者", "ござる…"]):
            result.append(ninja_line)
        else:
            # 文章の長さや終止形に応じて調整
            if ninja_line.endswith("。"):
                ninja_line = ninja_line[:-1]
            ninja_line = f"{prefix}{ninja_line}{suffix}"
            result.append(ninja_line)
    return "\n".join(result)

def fetch_github_comment(token: str, repo: str, comment_id: int) -> Optional[str]:
    url = f"https://api.github.com/repos/{repo}/pulls/comments/{comment_id}"
    headers = {"Authorization": f"token {token}"}
    resp = requests.get(url, headers=headers)
    if resp.status_code == 200:
        return resp.json().get("body", "")
    else:
        print(f"GitHub API error: {resp.status_code} {resp.text}", file=sys.stderr)
        return None

def post_github_comment(token: str, repo: str, pr_number: int, body: str):
    url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments"
    headers = {"Authorization": f"token {token}", "Accept": "application/vnd.github+json"}
    resp = requests.post(url, headers=headers, json={"body": body})
    if resp.status_code == 201:
        print("コメント投稿成功!")
    else:
        print(f"投稿失敗: {resp.status_code} {resp.text}", file=sys.stderr)

def main():
    parser = argparse.ArgumentParser(description="PRコメントを忍者口調に変換するツール")
    subparsers = parser.add_subparsers(dest="command")

    # translateコマンド
    p_trans = subparsers.add_parser("translate", help="テキストを忍者口調に変換")
    p_trans.add_argument("--text", type=str, help="変換するテキスト")
    p_trans.add_argument("--infile", type=str, help="テキストファイルから入力")
    p_trans.add_argument("--outfile", type=str, help="変換結果をファイル出力")

    # github-fetchコマンド
    p_fetch = subparsers.add_parser("github-fetch", help="GitHub PRコメントを取得して変換")
    p_fetch.add_argument("--token", type=str, required=True, help="GitHubアクセストークン")
    p_fetch.add_argument("--repo", type=str, required=True, help="リポジトリ名 (user/repo)")
    p_fetch.add_argument("--comment-id", type=int, required=True, help="PRコメントID")
    p_fetch.add_argument("--post", action="store_true", help="変換後にPRへコメント投稿")
    p_fetch.add_argument("--pr-number", type=int, help="コメント投稿先PR番号")

    args = parser.parse_args()
    if args.command == "translate":
        if args.text:
            input_text = args.text
        elif args.infile:
            try:
                with open(args.infile, encoding="utf-8") as f:
                    input_text = f.read()
            except Exception as e:
                print(f"ファイル読み込み失敗: {e}", file=sys.stderr)
                sys.exit(1)
        else:
            print("--text か --infile を指定してください", file=sys.stderr)
            sys.exit(1)
        result = ninja_translate(input_text)
        if args.outfile:
            with open(args.outfile, "w", encoding="utf-8") as f:
                f.write(result)
        else:
            print(result)
    elif args.command == "github-fetch":
        comment = fetch_github_comment(args.token, args.repo, args.comment_id)
        if comment is None:
            sys.exit(1)
        result = ninja_translate(comment)
        print("---- 変換結果 ----")
        print(result)
        if args.post:
            if not args.pr_number:
                print("--pr-number を指定してください", file=sys.stderr)
                sys.exit(1)
            post_github_comment(args.token, args.repo, args.pr_number, result)
    else:
        parser.print_help()

if __name__ == "__main__":
    main()

references/design_notes.md

# 概要
このSkillは、PRコメントやレビュー文を忍者口調に変換し、開発現場の雰囲気を和ませるためのジョーク系ツールです。GitHub APIを用いて既存コメントの取得・投稿にも対応しています。

# 公式ドキュメント抜粋
- [GitHub REST API v3](https://docs.github.com/ja/rest) の `/pulls/comments/{comment_id}` および `/issues/{issue_number}/comments` を利用。

# 利用例
- PRで「この関数はバグがあります」と書かれたコメントを忍者口調に変換し、再投稿。
- チームの雰囲気を和ませたい時に/skillsコマンドで明示呼び出し。

# 注意点
- 日本語以外の文には未対応。
- 意味が大きく変わる場合や、正式なレビューには利用を控えること。
- GitHub API利用時はアクセストークン管理に注意。

# 設計方針
- 変換は単純な置換+忍者語テンプレートで構成。
- コマンドラインからの柔軟な利用を想定し、ファイル入出力やAPI連携も可能とした。

導入手順

このSkillは GitHub で管理されているので、degit を使えば必要なフォルダだけを1コマンドで取得できます。Codex はファイル配置後に再起動するだけで自動認識します。

1. 前提

  • Node.js v16 以上 (degit 実行に必要)
  • Codex がローカルで動いていること

2. degit でフォルダ取得

プロジェクトのルートで以下のコマンドを実行します。

npx degit aazutaku/ai-note/codex/pr-comment-ninja-translator .agents/skills/pr-comment-ninja-translator

.agents/skills/pr-comment-ninja-translator の中に SKILL.md / scripts/ / references/ / README.md が展開されます。

3. ファイル配置確認

ls .agents/skills/pr-comment-ninja-translator
# SKILL.md, scripts/, references/, README.md があればOK

4. Codex を再起動 (or Skill 自動検出を待つ)

新しいSkillが自動で認識されます。リスト確認したい場合は /skills menu or $skill-name mention と Skill 名で出てきます。

5. 動作確認

/skills menu or $skill-name mention で呼び出すか、自然言語で発動条件にマッチする指示を出すと Skill が動きます。期待される出力イメージは「実行したらこうなる」セクションを参照してください。

こんな瞬間に便利

  • session 開始時: 前回までの repo 把握を Codex に一発で復元させたい
  • monorepo 移動時: packages を跨いだ瞬間に context を切り替えたい
  • onboarding 時: 新しい repo を Codex に把握させ、こちらが path を全部指定する手間を省きたい
  • session 再開時: long context が切れた後でも、必要な path と directory 構造だけ素早く戻したい
  • package 跨ぎ作業時: directory boundary を Skill 側で管理して、irrelevant な path 混入を防ぎたい
  • long-running workflow 前: long context で重要箇所が薄まる前に snapshot を取りたい

気になるポイント (壊れそうな箇所)

実運用に乗せる前に頭に入れておきたい懸念。後で検証する観点でもある:

  • stale context 問題: 長時間 workflow で Skill 出力が古くなり、現状と乖離する可能性
  • directory 増えすぎ問題: 大規模 repo で全 directory を網羅すると出力が肥大化して context window を圧迫
  • monorepo 肥大化: packages が多い構成では出力が雑になり、結局 path 指定し直しになる懸念
  • irrelevant path 混入: node_modules / build 成果物 / generated コードを拾ってしまう可能性
  • Codex 固有の引っかかり: description のセマンティックマッチ精度が要件次第
  • 発動しないケース: description が漠然 / 他の Skill が優先 / git管理外 directory

試す前に確かめたいこと

この Skill を実運用に投入する前に確かめたい問いを並べる:

  • 実 repo での token 消費は許容範囲か?
  • monorepo (packages 多数) で安定して動くか?
  • stale context にならず、長時間 workflow でも有効か?
  • AGENTS.md との連携設計はどうあるべきか?
  • どんな文章も忍者口調へ面白く変換されるか?
  • 既存PRコメントへの適用が簡単か?
  • 原文の意味が極端に失われないか?

実際に Codex で試した検証ログは Codexで忍者口調に変換してPRコメントを盛り上げてみた! にまとめる予定 (公開準備中の場合あり)。
あわせて Codex 公式ドキュメント と、本シリーズ「Codexを使いこなすSkillアイデア」の他記事も参照のこと。

関連タグで他のSkill記事を探す

本記事に付いているタグから、気になるテーマの記事を探せます。タグページで関連記事をまとめて読めるので、ぜひチェックしてみてください!

この記事が気に入ったら
フォローしてね!

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
目次