Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Configure Framer CI/CD integration with GitHub Actions and testing. Use when setting up automated testing, configuring CI pipelines, or integrating Framer tests into your build process. Trigger with phrases like "framer CI", "framer GitHub Actions", "framer automated tests", "CI framer".
.claude/skills/jeremylongshore-framer-ci-integration/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | -21% | 0% |
| case-03 | ✗→✓ | ▲ Improved | 29% | 0% |
| case-06 | ✗→✓ | ▲ Improved | -38% | 0% |
| case-07 | ✗→✓ | ▲ Improved | 26% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 41% | 0% |
Set up CI/CD for Framer plugins and Server API integrations. Plugin builds are tested with Vite + vitest. Server API CMS sync can be triggered from CI for automated content publishing.
yamlname: Framer Plugin CI on: push: branches: [main] pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20', cache: 'npm' } - run: npm ci - run: npm run build - run: npm test cms-sync: if: github.ref == 'refs/heads/main' needs: build runs-on: ubuntu-latest env: FRAMER_API_KEY: ${{ secrets.FRAMER_API_KEY }} FRAMER_SITE_ID: ${{ secrets.FRAMER_SITE_ID }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: { node-version: '20', cache: 'npm' } - run: npm ci - name: Sync CMS and publish run: node scripts/sync-and-publish.js
typescript// scripts/sync-and-publish.ts import { framer } from 'framer-api'; async function main() { const client = await framer.connect({ apiKey: process.env.FRAMER_API_KEY!, siteId: process.env.FRAMER_SITE_ID!, }); // Fetch content from your CMS/API const posts = await fetch('https://your-api.com/posts').then(r => r.json()); // Sync to Framer CMS const collections = await client.getCollections(); const blogCollection = collections.find(c => c.name === 'Blog Posts'); if (blogCollection) { await blogCollection.setItems(posts.map(p => ({ fieldData: { title: p.title, body: p.content, slug: p.slug } }))); console.log(`Synced ${posts.length} posts`); } // Publish site await client.publish(); console.log('Site published'); } main().catch(e => { console.error(e); process.exit(1); });
bashgh secret set FRAMER_API_KEY --body "framer_sk_..." gh secret set FRAMER_SITE_ID --body "abc123"
For deployment, see framer-deploy-integration.
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 19,053 | 14,056 | -26% | 1 | 1 | 0% | 2,932 | 2,755 | -6% | 0 | 0 | — |
case-02 | fail→pass | 16,242 | 9,670 | -40% | 1 | 1 | 0% | 3,495 | 2,768 | -21% | 0 | 0 | — |
case-03 | fail→pass | 17,428 | 16,248 | -7% | 1 | 1 | 0% | 2,416 | 3,122 | +29% | 0 | 0 | — |
case-04 | pass→pass | 16,091 | 7,966 | -50% | 1 | 1 | 0% | 2,030 | 1,290 | -36% | 0 | 0 | — |
case-05 | pass→pass | 9,381 | 7,559 | -19% | 1 | 1 | 0% | 1,689 | 2,218 | +31% | 0 | 0 | — |
case-06 | fail→pass | 16,918 | 7,788 | -54% | 1 | 1 | 0% | 2,069 | 1,293 | -38% | 0 | 0 | — |
case-07 | fail→pass | 13,823 | 11,669 | -16% | 1 | 1 | 0% | 1,626 | 2,049 | +26% | 0 | 0 | — |
case-08 | fail→pass | 10,758 | 10,526 | -2% | 1 | 1 | 0% | 1,893 | 2,673 | +41% | 0 | 0 | — |
case-09 | pass→pass | 11,761 | 3,501 | -70% | 1 | 1 | 0% | 1,280 | 1,383 | +8% | 0 | 0 | — |
case-10 | pass→pass | 5,067 | 2,737 | -46% | 1 | 1 | 0% | 882 | 1,236 | +40% | 0 | 0 | — |
case-11 | pass→pass | 3,127 | 1,570 | -50% | 1 | 1 | 0% | 569 | 992 | +74% | 0 | 0 | — |
case-12 | fail→pass | 10,375 | 14,317 | +38% | 1 | 1 | 0% | 1,825 | 2,336 | +28% | 0 | 0 | — |
case-13 | fail→fail | 14,858 | 9,231 | -38% | 1 | 1 | 0% | 2,259 | 2,609 | +15% | 0 | 0 | — |
case-14 | pass→pass | 10,690 | 7,841 | -27% | 1 | 1 | 0% | 1,971 | 1,267 | -36% | 0 | 0 | — |
case-15 | fail→pass | 12,430 | 2,147 | -83% | 1 | 1 | 0% | 1,219 | 1,134 | -7% | 0 | 0 | — |
case-16 | fail→pass | 16,177 | 6,412 | -60% | 1 | 1 | 0% | 1,867 | 948 | -49% | 0 | 0 | — |
case-17 | fail→pass | 10,888 | 2,052 | -81% | 1 | 1 | 0% | 969 | 1,098 | +13% | 0 | 0 | — |
case-18 | pass→fail | 3,033 | 1,682 | -45% | 1 | 1 | 0% | 382 | 1,020 | +167% | 0 | 0 | — |
case-19 | pass→fail | 4,452 | 6,512 | +46% | 1 | 1 | 0% | 771 | 1,015 | +32% | 0 | 0 | — |
case-20 | pass→pass | 16,197 | 16,430 | +1% | 1 | 1 | 0% | 2,230 | 3,183 | +43% | 0 | 0 | — |
case-21 | pass→pass | 15,779 | 11,731 | -26% | 1 | 1 | 0% | 3,527 | 3,204 | -9% | 0 | 0 | — |
case-22 | pass→pass | 13,961 | 8,170 | -41% | 1 | 1 | 0% | 1,967 | 2,537 | +29% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases. 2 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.