1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Cursor rulesの変更PRが作成されたら Roo Codeのrulesにも反映するPRを作成する

Last updated at Posted at 2025-06-07

はじめに

CursorやClineなどAI開発を前提としたツールが熱いですが、チーム内でCursor派、Cline派など複数の派閥があるとルールの管理が怠かったりします。(自分はRoo Codeを使うことが多いです。)

そこで今回はCursorのルールを管理する.mdcファイルを変更するPRが作成されたら.rooの配下に同じようなルールが作成されるPRを自動生成するGithub Actionsの設定を作ってみました。

設定すると以下の動きをします。

  • 実装者が.cursor/rulesを変更するPRを作成
    image.png

  • GithubActionsで変更された.cursor/rulesから.rooのrulesを修正するPRを作成
    image.png

Github Actionsの設定

以下のスニペットのような.ymlを.github/workflowsに作成します。

  • .cursor/**
name: Sync Cursor Rules to Roo

on:
  pull_request:
    paths:
      - '.cursor/**/*.mdc'
    types: [opened, synchronize]

jobs:
  sync-cursor-rules:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          fetch-depth: 0

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'

      - name: Setup GitHub CLI
        run: |
          type -p curl >/dev/null || (sudo apt update && sudo apt install curl -y)
          curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg | sudo dd of=/usr/share/keyrings/githubcli-archive-keyring.gpg \
          && sudo chmod go+r /usr/share/keyrings/githubcli-archive-keyring.gpg \
          && echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" | sudo tee /etc/apt/sources.list.d/github-cli.list > /dev/null \
          && sudo apt update \
          && sudo apt install gh -y

      - name: Run sync script
        run: node scripts/sync-cursor-to-roo.js

      - name: Check for changes
        id: check-changes
        run: |
          if [ -n "$(git status --porcelain .roo)" ]; then
            echo "changes=true" >> $GITHUB_OUTPUT
            echo "changed_files<<EOF" >> $GITHUB_OUTPUT
            git status --porcelain .roo >> $GITHUB_OUTPUT
            echo "EOF" >> $GITHUB_OUTPUT
          else
            echo "changes=false" >> $GITHUB_OUTPUT
          fi

      - name: Create PR for .roo changes
        if: steps.check-changes.outputs.changes == 'true'
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          # Get current PR info
          PR_NUMBER=${{ github.event.number }}
          PR_BRANCH=${{ github.head_ref }}
          BASE_BRANCH=${{ github.base_ref }}

          # Create a new branch for .roo changes with timestamp to ensure uniqueness
          TIMESTAMP=$(date +%Y%m%d-%H%M%S)
          ROO_BRANCH="sync-roo-from-pr-${PR_NUMBER}-${TIMESTAMP}"

          # Configure git
          git config --local user.email "action@github.com"
          git config --local user.name "GitHub Action"

          # Delete existing branch if it exists (both locally and remotely)
          git branch -D "sync-roo-from-pr-${PR_NUMBER}" 2>/dev/null || true
          git push origin --delete "sync-roo-from-pr-${PR_NUMBER}" 2>/dev/null || true

          # Create and switch to new branch
          git checkout -b "${ROO_BRANCH}"

          # Add and commit .roo changes
          git add .roo
          git commit -m "Auto-sync: Update .roo files from .cursor changes in PR #${PR_NUMBER}

          This PR automatically syncs .cursor rule changes to .roo directory.

          Source PR: #${PR_NUMBER}
          Changed files:
          ${{ steps.check-changes.outputs.changed_files }}"

          # Push the new branch (use force push to handle any conflicts)
          git push --force-with-lease origin "${ROO_BRANCH}"

          # Create PR using GitHub CLI
          gh pr create \
            --title "🔄 Auto-sync .roo from .cursor changes (PR #${PR_NUMBER})" \
            --body "This PR automatically syncs .cursor rule changes to .roo directory.

          ## Source
          - **Source PR**: #${PR_NUMBER}
          - **Source Branch**: \`${PR_BRANCH}\`

          ## Changes
          - Converted .mdc files to .md files
          - Removed frontmatter from rule files
          - Updated .roo directory structure

          ## Files Changed
          \`\`\`
          ${{ steps.check-changes.outputs.changed_files }}
          \`\`\`

          ## Auto-generated
          This PR was automatically created by GitHub Actions when .cursor files were modified." \
            --base "${PR_BRANCH}" \
            --head "${ROO_BRANCH}" \
            --label "auto-sync" \
            --label "roo-sync"

流れとしては以下のような感じです。

  • トリガー

    • 対象ファイル:.cursor 配下の .mdc ファイル
    • 対象イベント:Pull Requestの作成 (opened) または更新 (synchronize)
  • 環境の設定

    • Node.jsのセットアップ
    • 対象リポジトリをクローン
    • GitHub CLI のインストール
  • RooCode用のPR作成

    • .cursor → .roo への同期スクリプト実行(詳細は後述)
    • .roo ディレクトリに差分があるか確認
    • 差分があればPRを自動作成

.cursor → .roo のルールファイル作成スクリプト

Github Actionsで実行されるコードは以下になります。

#!/usr/bin/env node

const fs = require('fs');
const path = require('path');

function processFile(filePath) {
  console.log(`Processing: ${filePath}`);

  // Read the .mdc file
  const content = fs.readFileSync(filePath, 'utf8');

  // Remove frontmatter (from first --- to second ---)
  const lines = content.split('\n');
  let startIndex = -1;
  let endIndex = -1;

  for (let i = 0; i < lines.length; i++) {
    if (lines[i].trim() === '---') {
      if (startIndex === -1) {
        startIndex = i;
      } else {
        endIndex = i;
        break;
      }
    }
  }

  let processedContent;
  if (startIndex !== -1 && endIndex !== -1) {
    // Remove frontmatter
    processedContent = lines.slice(endIndex + 1).join('\n');
  } else {
    // No frontmatter found, use original content
    processedContent = content;
  }

  // Determine output path
  const relativePath = path.relative('.cursor', filePath);

  // Convert 'rules' directory to 'rules-code' if the path starts with 'rules/'
  let outputRelativePath = relativePath;
  if (outputRelativePath.startsWith('rules/')) {
    outputRelativePath = 'rules-code/' + outputRelativePath.substring('rules/'.length);
  }

  const outputPath = path.join('.roo', outputRelativePath.replace(/\.mdc$/, '.md'));

  // Create output directory if it doesn't exist
  const outputDir = path.dirname(outputPath);
  fs.mkdirSync(outputDir, { recursive: true });

  // Write processed content
  fs.writeFileSync(outputPath, processedContent);
  console.log(`Created: ${outputPath}`);
}

function findMdcFiles(dir) {
  const files = [];
  const items = fs.readdirSync(dir);

  for (const item of items) {
    const fullPath = path.join(dir, item);
    const stat = fs.statSync(fullPath);

    if (stat.isDirectory()) {
      files.push(...findMdcFiles(fullPath));
    } else if (item.endsWith('.mdc')) {
      files.push(fullPath);
    }
  }

  return files;
}

// Main execution
console.log('Starting sync from .cursor to .roo...');

if (fs.existsSync('.cursor')) {
  const mdcFiles = findMdcFiles('.cursor');
  console.log(`Found ${mdcFiles.length} .mdc files`);

  for (const file of mdcFiles) {
    processFile(file);
  }

  console.log('Sync completed successfully');
} else {
  console.log('No .cursor directory found');
}

流れとしては以下のような感じです。

  • .mdc → .md に変換
  • frontmatter(YAML形式のメタデータ)を削除
  • rules/ → rules-code/ にディレクトリ名を変換
  • 変換後のファイルを .roo/ に保存

おわりに

一旦作ってRoo Codeのルールファイルも自動でメンテナンスされるようになりました。
とはいえ、すべての変更をRoo CodeのCodeモードに対応しているrules-codeディレクトリに突っ込んでしまっているなど、コードの改善の余地はあるので、今後運用しながらCursorを使う人も、Cline(Roo Cline)を使う人も常に快適にAI活用できるようブラッシュアップしていきたいと思います。

また今回のyml, jsファイルはどちらも完全にAIが作成してくれていて、作成時間は30m未満でした。「自動化系の実装も速攻で実現できるようになって楽だな〜」と思ったので、フットワーク軽く改善~検証をしていきたいですね。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?