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@v4 with: version: latest - name: Setup Bun if: matrix.pm == 'bun' uses: oven-sh/setup-bun@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 - name: Install dependencies shell: bash run: | case "${{ matrix.pm }}" in npm) npm ci ;; pnpm) pnpm install ;; # --ignore-engines required for Node 18 compat with some devDependencies (e.g., markdownlint-cli) yarn) yarn install --ignore-engines ;; 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: 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 rules run: node scripts/ci/validate-rules.js 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"