name: Check Plugin Structure

on:
  pull_request:
    branches: [main]
    paths:
      - "plugins/**"

permissions:
  contents: read
  pull-requests: write

jobs:
  check-materialized-files:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1

      - name: Check for materialized files in plugin directories
        uses: actions/github-script@f28e40c7f34bde8b3046d885e986cb6290c5673b # v7.1.0
        with:
          script: |
            const fs = require('fs');
            const path = require('path');

            const pluginsDir = 'plugins';
            const errors = [];

            function findSymlinks(rootDir) {
              const symlinks = [];
              const dirsToScan = [rootDir];

              while (dirsToScan.length > 0) {
                const currentDir = dirsToScan.pop();
                let entries;

                try {
                  entries = fs.readdirSync(currentDir, { withFileTypes: true });
                } catch (error) {
                  throw new Error(`Failed to read directory "${currentDir}": ${error.message}`);
                }

                for (const entry of entries) {
                  const entryPath = path.join(currentDir, entry.name);
                  let stat;

                  try {
                    stat = fs.lstatSync(entryPath);
                  } catch (error) {
                    throw new Error(`Failed to inspect "${entryPath}": ${error.message}`);
                  }

                  if (stat.isSymbolicLink()) {
                    symlinks.push(entryPath);
                    continue;
                  }

                  if (stat.isDirectory()) {
                    dirsToScan.push(entryPath);
                  }
                }
              }

              return symlinks;
            }

            if (!fs.existsSync(pluginsDir)) {
              console.log('No plugins directory found');
              return;
            }

            const pluginDirs = fs.readdirSync(pluginsDir, { withFileTypes: true })
              .filter(d => d.isDirectory())
              .map(d => d.name);

            for (const plugin of pluginDirs) {
              const pluginPath = path.join(pluginsDir, plugin);

              // Check for materialized agent/command/skill files
              for (const subdir of ['agents', 'commands', 'skills']) {
                const subdirPath = path.join(pluginPath, subdir);
                if (!fs.existsSync(subdirPath)) continue;

                const stat = fs.lstatSync(subdirPath);
                if (stat.isSymbolicLink()) {
                  errors.push(`${pluginPath}/${subdir} is a symlink — symlinks should not exist in plugin directories`);
                  continue;
                }

                if (stat.isDirectory()) {
                  const files = fs.readdirSync(subdirPath);
                  if (files.length > 0) {
                    errors.push(
                      `${pluginPath}/${subdir}/ contains ${files.length} file(s): ${files.join(', ')}. ` +
                      `Plugin directories on main should only contain .github/plugin/plugin.json and README.md. ` +
                      `Agent, command, and skill files are materialized automatically during publish to marketplace.`
                    );
                  }
                }
              }

              // Check for symlinks anywhere in the plugin directory without invoking a shell
              try {
                const symlinkPaths = findSymlinks(pluginPath);
                if (symlinkPaths.length > 0) {
                  const formattedPaths = symlinkPaths.map(filePath => `\`${filePath}\``).join(', ');
                  errors.push(`${pluginPath} contains symlinks: ${formattedPaths}`);
                }
              } catch (error) {
                errors.push(`Failed to inspect ${pluginPath} for symlinks: ${error.message}`);
              }
            }

            if (errors.length > 0) {
              const prBranch = context.payload.pull_request.head.ref;
              const prRepo = context.payload.pull_request.head.repo.full_name;
              const isFork = context.payload.pull_request.head.repo.fork;

              const body = [
                '⚠️ **Materialized files or symlinks detected in plugin directories**',
                '',
                'Plugin directories on the `main` branch should only contain:',
                '- `.github/plugin/plugin.json` (metadata)',
                '- `README.md`',
                '',
                'Agent, command, and skill files are copied in automatically when publishing to `marketplace`.',
                '',
                '**Issues found:**',
                ...errors.map(e => `- ${e}`),
                '',
                '---',
                '',
                '### How to fix',
                '',
                'It looks like your branch may include materialized plugin files that should not be on `main`. Here are two options:',
                '',
                '**Option 1: Rebase to drop materialized files** (recommended if you have few commits)',
                '```bash',
                `git fetch origin main`,
                `git rebase --onto origin/main origin/main ${prBranch}`,
                `git push --force-with-lease`,
                '```',
                '',
                '**Option 2: Remove the extra files manually**',
                '```bash',
                '# Remove materialized files from plugin directories',
                'find plugins/ -mindepth 2 -maxdepth 2 -type d \\( -name agents -o -name commands -o -name skills \\) -exec rm -rf {} +',
                '# Remove any symlinks',
                'find plugins/ -type l -delete',
                'git add -A && git commit -m "fix: remove materialized plugin files"',
                'git push',
                '```',
              ].join('\n');

              let reviewPosted = false;

              if (!isFork) {
                try {
                  await github.rest.pulls.createReview({
                    owner: context.repo.owner,
                    repo: context.repo.repo,
                    pull_number: context.issue.number,
                    event: 'REQUEST_CHANGES',
                    body
                  });
                  reviewPosted = true;
                } catch (error) {
                  core.warning(
                    `Could not create PR review (continuing with failure report): ${error.message}`
                  );
                }
              } else {
                core.warning('PR is from a fork; skipping createReview to avoid permission errors.');
              }

              if (!reviewPosted) {
                core.warning('Materialized plugin issues detected. Full details:');
                core.warning(body);
              }

              core.setFailed('Plugin directories contain materialized files or symlinks that should not be on main');
            } else {
              console.log('✅ All plugin directories are clean');
            }
