affaan-m/everything-claude-code

7 workflows · maturity 67% · 6 patterns · GitHub ↗

Security 17.86/100

Practices

✓ Matrix✓ Permissions○ Security scan○ AI review✓ Cache✓ Concurrency○ Reusable workflows

Detected patterns

Security dimensions

permissions
17.9
security scan
0
supply chain
0
secret handling
0
harden runner
0

Workflows (7)

ci matrix perms .github/workflows/ci.yml
Triggers
push, pull_request
Runs on
${{ matrix.os }}, ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
test, validate, security, lint
Matrix
exclude, exclude.os, exclude.pm, node, os, pm→ 18.x, 20.x, 22.x, bun, macos-latest, npm, pnpm, ubuntu-latest, windows-latest, yarn
Actions
pnpm/action-setup, oven-sh/setup-bun
Commands
  • corepack enable corepack prepare yarn@stable --activate
  • echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
  • echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
  • # Try Yarn Berry first, fall back to Yarn v1 if yarn config get cacheFolder >/dev/null 2>&1; then echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT else echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT fi
  • case "${{ matrix.pm }}" in npm) npm ci ;; pnpm) pnpm install --no-frozen-lockfile ;; # Yarn Berry (v4+) removed --ignore-engines; engine checking is no longer a core feature yarn) yarn install ;; bun) bun install ;; *) echo "Unsupported package manager: ${{ matrix.pm }}" && exit 1 ;; esac
  • node tests/run-all.js
  • npm ci --ignore-scripts
  • node scripts/ci/validate-agents.js
View raw YAML
name: CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Prevent duplicate runs
concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

# Minimal permissions
permissions:
  contents: read

jobs:
  test:
    name: Test (${{ matrix.os }}, Node ${{ matrix.node }}, ${{ matrix.pm }})
    runs-on: ${{ matrix.os }}
    timeout-minutes: 10

    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node: ['18.x', '20.x', '22.x']
        pm: [npm, pnpm, yarn, bun]
        exclude:
          # Bun has limited Windows support
          - os: windows-latest
            pm: bun

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js ${{ matrix.node }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node }}

      # Package manager setup
      - name: Setup pnpm
        if: matrix.pm == 'pnpm'
        uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4
        with:
          version: latest

      - name: Setup Yarn (via Corepack)
        if: matrix.pm == 'yarn'
        shell: bash
        run: |
          corepack enable
          corepack prepare yarn@stable --activate

      - name: Setup Bun
        if: matrix.pm == 'bun'
        uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2

      # Cache configuration
      - name: Get npm cache directory
        if: matrix.pm == 'npm'
        id: npm-cache-dir
        shell: bash
        run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT

      - name: Cache npm
        if: matrix.pm == 'npm'
        uses: actions/cache@v4
        with:
          path: ${{ steps.npm-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ matrix.node }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ matrix.node }}-npm-

      - name: Get pnpm store directory
        if: matrix.pm == 'pnpm'
        id: pnpm-cache-dir
        shell: bash
        run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT

      - name: Cache pnpm
        if: matrix.pm == 'pnpm'
        uses: actions/cache@v4
        with:
          path: ${{ steps.pnpm-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ matrix.node }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ matrix.node }}-pnpm-

      - name: Get yarn cache directory
        if: matrix.pm == 'yarn'
        id: yarn-cache-dir
        shell: bash
        run: |
          # Try Yarn Berry first, fall back to Yarn v1
          if yarn config get cacheFolder >/dev/null 2>&1; then
            echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
          else
            echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
          fi

      - name: Cache yarn
        if: matrix.pm == 'yarn'
        uses: actions/cache@v4
        with:
          path: ${{ steps.yarn-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ matrix.node }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ matrix.node }}-yarn-

      - name: Cache bun
        if: matrix.pm == 'bun'
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      # Install dependencies
      # COREPACK_ENABLE_STRICT=0 allows pnpm to install even though
      # package.json declares "packageManager": "yarn@..."
      - name: Install dependencies
        shell: bash
        env:
          COREPACK_ENABLE_STRICT: '0'
        run: |
          case "${{ matrix.pm }}" in
            npm) npm ci ;;
            pnpm) pnpm install --no-frozen-lockfile ;;
            # Yarn Berry (v4+) removed --ignore-engines; engine checking is no longer a core feature
            yarn) yarn install ;;
            bun) bun install ;;
            *) echo "Unsupported package manager: ${{ matrix.pm }}" && exit 1 ;;
          esac

      # Run tests
      - name: Run tests
        run: node tests/run-all.js
        env:
          CLAUDE_CODE_PACKAGE_MANAGER: ${{ matrix.pm }}

      # Upload test artifacts on failure
      - name: Upload test artifacts
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ matrix.os }}-node${{ matrix.node }}-${{ matrix.pm }}
          path: |
            tests/
            !tests/node_modules/

  validate:
    name: Validate Components
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Install validation dependencies
        run: npm ci --ignore-scripts

      - name: Validate agents
        run: node scripts/ci/validate-agents.js
        continue-on-error: false

      - name: Validate hooks
        run: node scripts/ci/validate-hooks.js
        continue-on-error: false

      - name: Validate commands
        run: node scripts/ci/validate-commands.js
        continue-on-error: false

      - name: Validate skills
        run: node scripts/ci/validate-skills.js
        continue-on-error: false

      - name: Validate install manifests
        run: node scripts/ci/validate-install-manifests.js
        continue-on-error: false

      - name: Validate rules
        run: node scripts/ci/validate-rules.js
        continue-on-error: false

      - name: Validate catalog counts
        run: node scripts/ci/catalog.js --text
        continue-on-error: false

  security:
    name: Security Scan
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Run npm audit
        run: npm audit --audit-level=high
        continue-on-error: true  # Allows PR to proceed, but marks job as failed if vulnerabilities found

  lint:
    name: Lint
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v4

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

      - name: Install dependencies
        run: npm ci

      - name: Run ESLint
        run: npx eslint scripts/**/*.js tests/**/*.js

      - name: Run markdownlint
        run: npx markdownlint "agents/**/*.md" "skills/**/*.md" "commands/**/*.md" "rules/**/*.md"
maintenance perms .github/workflows/maintenance.yml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-latest, ubuntu-latest, ubuntu-latest
Jobs
dependency-check, security-audit, stale
Actions
actions/stale
Commands
  • npm outdated || true
  • if [ -f package-lock.json ]; then npm ci npm audit --audit-level=high else echo "No package-lock.json found; skipping npm audit" fi
View raw YAML
name: Scheduled Maintenance

on:
  schedule:
    - cron: '0 9 * * 1'  # Weekly Monday 9am UTC
  workflow_dispatch:

permissions:
  contents: read
  issues: write
  pull-requests: write

jobs:
  dependency-check:
    name: Check Dependencies
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      - name: Check for outdated packages
        run: npm outdated || true

  security-audit:
    name: Security Audit
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20.x'
      - name: Run security audit
        run: |
          if [ -f package-lock.json ]; then
            npm ci
            npm audit --audit-level=high
          else
            echo "No package-lock.json found; skipping npm audit"
          fi

  stale:
    name: Stale Issues/PRs
    runs-on: ubuntu-latest
    steps:
      - uses: actions/stale@v9
        with:
          stale-issue-message: 'This issue is stale due to inactivity.'
          stale-pr-message: 'This PR is stale due to inactivity.'
          days-before-stale: 30
          days-before-close: 7
monthly-metrics perms .github/workflows/monthly-metrics.yml
Triggers
schedule, workflow_dispatch
Runs on
ubuntu-latest
Jobs
snapshot
View raw YAML
name: Monthly Metrics Snapshot

on:
  schedule:
    - cron: '0 14 1 * *' # Monthly on the 1st at 14:00 UTC
  workflow_dispatch:

permissions:
  contents: read
  issues: write

jobs:
  snapshot:
    name: Update metrics issue
    runs-on: ubuntu-latest
    steps:
      - name: Update monthly metrics issue
        uses: actions/github-script@v7
        with:
          script: |
            const owner = context.repo.owner;
            const repo = context.repo.repo;
            const title = "Monthly Metrics Snapshot";
            const label = "metrics-snapshot";
            const monthKey = new Date().toISOString().slice(0, 7);

            function parseLastPage(linkHeader) {
              if (!linkHeader) return null;
              const match = linkHeader.match(/&page=(\d+)>; rel="last"/);
              return match ? Number(match[1]) : null;
            }

            function fmt(value) {
              if (value === null || value === undefined) return "n/a";
              return Number(value).toLocaleString("en-US");
            }

            async function getNpmDownloads(range, pkg) {
              try {
                const res = await fetch(`https://api.npmjs.org/downloads/point/${range}/${pkg}`);
                if (!res.ok) return null;
                const data = await res.json();
                return data.downloads ?? null;
              } catch {
                return null;
              }
            }

            async function getContributorsCount() {
              try {
                const resp = await github.rest.repos.listContributors({
                  owner,
                  repo,
                  per_page: 1,
                  anon: "false"
                });
                return parseLastPage(resp.headers.link) ?? resp.data.length;
              } catch {
                return null;
              }
            }

            async function getReleasesCount() {
              try {
                const resp = await github.rest.repos.listReleases({
                  owner,
                  repo,
                  per_page: 1
                });
                return parseLastPage(resp.headers.link) ?? resp.data.length;
              } catch {
                return null;
              }
            }

            async function getTraffic(metric) {
              try {
                const route = metric === "clones"
                  ? "GET /repos/{owner}/{repo}/traffic/clones"
                  : "GET /repos/{owner}/{repo}/traffic/views";
                const resp = await github.request(route, { owner, repo });
                return resp.data?.count ?? null;
              } catch {
                return null;
              }
            }

            const [
              mainWeek,
              shieldWeek,
              mainMonth,
              shieldMonth,
              repoData,
              contributors,
              releases,
              views14d,
              clones14d
            ] = await Promise.all([
              getNpmDownloads("last-week", "ecc-universal"),
              getNpmDownloads("last-week", "ecc-agentshield"),
              getNpmDownloads("last-month", "ecc-universal"),
              getNpmDownloads("last-month", "ecc-agentshield"),
              github.rest.repos.get({ owner, repo }),
              getContributorsCount(),
              getReleasesCount(),
              getTraffic("views"),
              getTraffic("clones")
            ]);

            const stars = repoData.data.stargazers_count;
            const forks = repoData.data.forks_count;

            const tableHeader = [
              "| Month (UTC) | ecc-universal (week) | ecc-agentshield (week) | ecc-universal (30d) | ecc-agentshield (30d) | Stars | Forks | Contributors | GitHub App installs (manual) | Views (14d) | Clones (14d) | Releases |",
              "|---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|"
            ].join("\n");

            const row = `| ${monthKey} | ${fmt(mainWeek)} | ${fmt(shieldWeek)} | ${fmt(mainMonth)} | ${fmt(shieldMonth)} | ${fmt(stars)} | ${fmt(forks)} | ${fmt(contributors)} | n/a | ${fmt(views14d)} | ${fmt(clones14d)} | ${fmt(releases)} |`;

            const intro = [
              "# Monthly Metrics Snapshot",
              "",
              "Automated monthly snapshot for sponsor/partner reporting.",
              "",
              "- `GitHub App installs (manual)` is intentionally manual until a stable public API path is available.",
              "- Traffic metrics are 14-day rolling windows from the GitHub traffic API and can show `n/a` if unavailable.",
              "",
              tableHeader
            ].join("\n");

            try {
              await github.rest.issues.getLabel({ owner, repo, name: label });
            } catch (error) {
              if (error.status === 404) {
                await github.rest.issues.createLabel({
                  owner,
                  repo,
                  name: label,
                  color: "0e8a16",
                  description: "Automated monthly project metrics snapshots"
                });
              } else {
                throw error;
              }
            }

            const issuesResp = await github.rest.issues.listForRepo({
              owner,
              repo,
              state: "open",
              labels: label,
              per_page: 100
            });

            let issue = issuesResp.data.find((item) => item.title === title);

            if (!issue) {
              const created = await github.rest.issues.create({
                owner,
                repo,
                title,
                labels: [label],
                body: `${intro}\n${row}\n`
              });
              console.log(`Created issue #${created.data.number}`);
              return;
            }

            const currentBody = issue.body || "";
            if (currentBody.includes(`| ${monthKey} |`)) {
              console.log(`Issue #${issue.number} already has snapshot row for ${monthKey}`);
              return;
            }

            const body = currentBody.includes("| Month (UTC) |")
              ? `${currentBody.trimEnd()}\n${row}\n`
              : `${intro}\n${row}\n`;

            await github.rest.issues.update({
              owner,
              repo,
              issue_number: issue.number,
              body
            });
            console.log(`Updated issue #${issue.number}`);
release perms .github/workflows/release.yml
Triggers
push
Runs on
ubuntu-latest
Jobs
release
Actions
softprops/action-gh-release
Commands
  • if ! [[ "${REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Invalid version tag format. Expected vX.Y.Z" exit 1 fi
  • TAG_VERSION="${TAG_NAME#v}" PLUGIN_VERSION=$(grep -oE '"version": *"[^"]*"' .claude-plugin/plugin.json | grep -oE '[0-9]+\.[0-9]+\.[0-9]+') if [ "$TAG_VERSION" != "$PLUGIN_VERSION" ]; then echo "::error::Tag version ($TAG_VERSION) does not match plugin.json version ($PLUGIN_VERSION)" echo "Run: ./scripts/release.sh $TAG_VERSION" exit 1 fi
  • TAG_VERSION="${TAG_NAME#v}" cat > release_body.md <<EOF ## ECC ${TAG_VERSION} ### What This Release Focuses On - Harness reliability and hook stability across Claude Code, Cursor, OpenCode, and Codex - Stronger eval-driven workflows and quality gates - Better operator UX for autonomous loop execution ### Notable Changes - Session persistence and hook lifecycle fixes - Expanded skills and command coverage for harness performance work - Improved release-note generation and changelog hygiene ### Notes - For migration tips and compatibility notes, see README and CHANGELOG. EOF
View raw YAML
name: Release

on:
  push:
    tags: ['v*']

permissions:
  contents: write

jobs:
  release:
    name: Create Release
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Validate version tag
        run: |
          if ! [[ "${REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "Invalid version tag format. Expected vX.Y.Z"
            exit 1
          fi

        env:
          REF_NAME: ${{ github.ref_name }}
      - name: Verify plugin.json version matches tag
        env:
          TAG_NAME: ${{ github.ref_name }}
        run: |
          TAG_VERSION="${TAG_NAME#v}"
          PLUGIN_VERSION=$(grep -oE '"version": *"[^"]*"' .claude-plugin/plugin.json | grep -oE '[0-9]+\.[0-9]+\.[0-9]+')
          if [ "$TAG_VERSION" != "$PLUGIN_VERSION" ]; then
            echo "::error::Tag version ($TAG_VERSION) does not match plugin.json version ($PLUGIN_VERSION)"
            echo "Run: ./scripts/release.sh $TAG_VERSION"
            exit 1
          fi

      - name: Generate release highlights
        id: highlights
        env:
          TAG_NAME: ${{ github.ref_name }}
        run: |
          TAG_VERSION="${TAG_NAME#v}"
          cat > release_body.md <<EOF
          ## ECC ${TAG_VERSION}

          ### What This Release Focuses On
          - Harness reliability and hook stability across Claude Code, Cursor, OpenCode, and Codex
          - Stronger eval-driven workflows and quality gates
          - Better operator UX for autonomous loop execution

          ### Notable Changes
          - Session persistence and hook lifecycle fixes
          - Expanded skills and command coverage for harness performance work
          - Improved release-note generation and changelog hygiene

          ### Notes
          - For migration tips and compatibility notes, see README and CHANGELOG.
          EOF

      - name: Create GitHub Release
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
        with:
          body_path: release_body.md
          generate_release_notes: true
reusable-release perms .github/workflows/reusable-release.yml
Triggers
workflow_call
Runs on
ubuntu-latest
Jobs
release
Actions
softprops/action-gh-release
Commands
  • if ! [[ "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then echo "Invalid version tag format. Expected vX.Y.Z" exit 1 fi
  • TAG_VERSION="${TAG_NAME#v}" cat > release_body.md <<EOF ## ECC ${TAG_VERSION} ### What This Release Focuses On - Harness reliability and cross-platform compatibility - Eval-driven quality improvements - Better workflow and operator ergonomics EOF
View raw YAML
name: Reusable Release Workflow

on:
  workflow_call:
    inputs:
      tag:
        description: 'Version tag (e.g., v1.0.0)'
        required: true
        type: string
      generate-notes:
        description: 'Auto-generate release notes'
        required: false
        type: boolean
        default: true

permissions:
  contents: write

jobs:
  release:
    name: Create Release
    runs-on: ubuntu-latest

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Validate version tag
        run: |
          if ! [[ "${{ inputs.tag }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
            echo "Invalid version tag format. Expected vX.Y.Z"
            exit 1
          fi

      - name: Generate release highlights
        env:
          TAG_NAME: ${{ inputs.tag }}
        run: |
          TAG_VERSION="${TAG_NAME#v}"
          cat > release_body.md <<EOF
          ## ECC ${TAG_VERSION}

          ### What This Release Focuses On
          - Harness reliability and cross-platform compatibility
          - Eval-driven quality improvements
          - Better workflow and operator ergonomics
          EOF

      - name: Create GitHub Release
        uses: softprops/action-gh-release@153bb8e04406b158c6c84fc1615b65b24149a1fe # v2
        with:
          tag_name: ${{ inputs.tag }}
          body_path: release_body.md
          generate_release_notes: ${{ inputs.generate-notes }}
reusable-test .github/workflows/reusable-test.yml
Triggers
workflow_call
Runs on
${{ inputs.os }}
Jobs
test
Actions
pnpm/action-setup, oven-sh/setup-bun
Commands
  • corepack enable corepack prepare yarn@stable --activate
  • echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT
  • echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT
  • # Try Yarn Berry first, fall back to Yarn v1 if yarn config get cacheFolder >/dev/null 2>&1; then echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT else echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT fi
  • case "${{ inputs.package-manager }}" in npm) npm ci ;; pnpm) pnpm install --no-frozen-lockfile ;; # Yarn Berry (v4+) removed --ignore-engines; engine checking is no longer a core feature yarn) yarn install ;; bun) bun install ;; *) echo "Unsupported package manager: ${{ inputs.package-manager }}" && exit 1 ;; esac
  • node tests/run-all.js
View raw YAML
name: Reusable Test Workflow

on:
  workflow_call:
    inputs:
      os:
        description: 'Operating system'
        required: false
        type: string
        default: 'ubuntu-latest'
      node-version:
        description: 'Node.js version'
        required: false
        type: string
        default: '20.x'
      package-manager:
        description: 'Package manager to use'
        required: false
        type: string
        default: 'npm'

jobs:
  test:
    name: Test
    runs-on: ${{ inputs.os }}
    timeout-minutes: 10

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}

      - name: Setup pnpm
        if: inputs.package-manager == 'pnpm'
        uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4
        with:
          version: latest

      - name: Setup Yarn (via Corepack)
        if: inputs.package-manager == 'yarn'
        shell: bash
        run: |
          corepack enable
          corepack prepare yarn@stable --activate

      - name: Setup Bun
        if: inputs.package-manager == 'bun'
        uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2

      - name: Get npm cache directory
        if: inputs.package-manager == 'npm'
        id: npm-cache-dir
        shell: bash
        run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT

      - name: Cache npm
        if: inputs.package-manager == 'npm'
        uses: actions/cache@v4
        with:
          path: ${{ steps.npm-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ inputs.node-version }}-npm-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ inputs.node-version }}-npm-

      - name: Get pnpm store directory
        if: inputs.package-manager == 'pnpm'
        id: pnpm-cache-dir
        shell: bash
        run: echo "dir=$(pnpm store path)" >> $GITHUB_OUTPUT

      - name: Cache pnpm
        if: inputs.package-manager == 'pnpm'
        uses: actions/cache@v4
        with:
          path: ${{ steps.pnpm-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ inputs.node-version }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ inputs.node-version }}-pnpm-

      - name: Get yarn cache directory
        if: inputs.package-manager == 'yarn'
        id: yarn-cache-dir
        shell: bash
        run: |
          # Try Yarn Berry first, fall back to Yarn v1
          if yarn config get cacheFolder >/dev/null 2>&1; then
            echo "dir=$(yarn config get cacheFolder)" >> $GITHUB_OUTPUT
          else
            echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
          fi

      - name: Cache yarn
        if: inputs.package-manager == 'yarn'
        uses: actions/cache@v4
        with:
          path: ${{ steps.yarn-cache-dir.outputs.dir }}
          key: ${{ runner.os }}-node-${{ inputs.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ inputs.node-version }}-yarn-

      - name: Cache bun
        if: inputs.package-manager == 'bun'
        uses: actions/cache@v4
        with:
          path: ~/.bun/install/cache
          key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }}
          restore-keys: |
            ${{ runner.os }}-bun-

      # COREPACK_ENABLE_STRICT=0 allows pnpm to install even though
      # package.json declares "packageManager": "yarn@..."
      - name: Install dependencies
        shell: bash
        env:
          COREPACK_ENABLE_STRICT: '0'
        run: |
          case "${{ inputs.package-manager }}" in
            npm) npm ci ;;
            pnpm) pnpm install --no-frozen-lockfile ;;
            # Yarn Berry (v4+) removed --ignore-engines; engine checking is no longer a core feature
            yarn) yarn install ;;
            bun) bun install ;;
            *) echo "Unsupported package manager: ${{ inputs.package-manager }}" && exit 1 ;;
          esac

      - name: Run tests
        run: node tests/run-all.js
        env:
          CLAUDE_CODE_PACKAGE_MANAGER: ${{ inputs.package-manager }}

      - name: Upload test artifacts
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: test-results-${{ inputs.os }}-node${{ inputs.node-version }}-${{ inputs.package-manager }}
          path: |
            tests/
            !tests/node_modules/
reusable-validate .github/workflows/reusable-validate.yml
Triggers
workflow_call
Runs on
ubuntu-latest
Jobs
validate
Commands
  • npm ci --ignore-scripts
  • node scripts/ci/validate-agents.js
  • node scripts/ci/validate-hooks.js
  • node scripts/ci/validate-commands.js
  • node scripts/ci/validate-skills.js
  • node scripts/ci/validate-install-manifests.js
  • node scripts/ci/validate-rules.js
View raw YAML
name: Reusable Validation Workflow

on:
  workflow_call:
    inputs:
      node-version:
        description: 'Node.js version'
        required: false
        type: string
        default: '20.x'

jobs:
  validate:
    name: Validate Components
    runs-on: ubuntu-latest
    timeout-minutes: 5

    steps:
      - name: Checkout
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ inputs.node-version }}

      - name: Install validation dependencies
        run: npm ci --ignore-scripts

      - name: Validate agents
        run: node scripts/ci/validate-agents.js

      - name: Validate hooks
        run: node scripts/ci/validate-hooks.js

      - name: Validate commands
        run: node scripts/ci/validate-commands.js

      - name: Validate skills
        run: node scripts/ci/validate-skills.js

      - name: Validate install manifests
        run: node scripts/ci/validate-install-manifests.js

      - name: Validate rules
        run: node scripts/ci/validate-rules.js