feat: add AI worker workflows (Gemini + Jules)

Distributed AI compute using contributor's free tiers:

Gemini 2.0 Flash (ai-worker.yml):
- 1500 req/day free from Google
- Code review, security scan, bug detection
- Contributor sets GEMINI_API_KEY in fork secrets

Jules/Copilot (jules-dispatch.yml):
- Triggered by @jules or /jules comments
- Creates PRs to fix issues automatically
- Uses contributor's Copilot allowance (free for OSS)

Documentation (doc/free-tier-compute.md):
- Setup guide for all free tiers
- Compute distribution model diagram
- Donor fleet instructions

Innocent strategy: Jules commits fixes to contributor's fork 😇

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Snider 2026-01-31 22:30:38 +00:00
parent c03c49a539
commit a78ef46133
3 changed files with 293 additions and 0 deletions

126
.github/workflows/ai-worker.yml vendored Normal file
View file

@ -0,0 +1,126 @@
name: AI Worker (Free Tier)
# Runs on contributor's fork - uses THEIR API allowances
# Gemini 2.0 Flash: 1500 req/day free
# Jules: Available to all GitHub users
on:
pull_request:
types: [opened, synchronize]
issues:
types: [labeled]
permissions:
contents: write
pull-requests: write
issues: write
env:
# Contributors set their own GEMINI_API_KEY in fork secrets
# Free tier: 1500 requests/day, 1M tokens/min
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
jobs:
# Analyze PR with Gemini (contributor's free tier)
gemini-review:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get diff
id: diff
run: |
DIFF=$(git diff origin/${{ github.base_ref }}...HEAD | head -c 50000)
echo "diff<<EOFMARKER" >> $GITHUB_OUTPUT
echo "$DIFF" >> $GITHUB_OUTPUT
echo "EOFMARKER" >> $GITHUB_OUTPUT
- name: Gemini Analysis
if: env.GEMINI_API_KEY != ''
id: gemini
run: |
# Call Gemini 2.0 Flash (free tier)
RESPONSE=$(curl -s "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp:generateContent?key=$GEMINI_API_KEY" \
-H 'Content-Type: application/json' \
-d @- << 'PAYLOAD'
{
"contents": [{
"parts": [{
"text": "Review this code diff. Be concise. List: 1) Security issues 2) Bugs 3) Improvements. If none, say 'LGTM'.\n\nDiff:\n${{ steps.diff.outputs.diff }}"
}]
}],
"generationConfig": {
"temperature": 0.2,
"maxOutputTokens": 1000
}
}
PAYLOAD
)
# Extract text from response
REVIEW=$(echo "$RESPONSE" | jq -r '.candidates[0].content.parts[0].text // "Analysis failed"')
echo "review<<EOFMARKER" >> $GITHUB_OUTPUT
echo "$REVIEW" >> $GITHUB_OUTPUT
echo "EOFMARKER" >> $GITHUB_OUTPUT
- name: Post Gemini review
if: steps.gemini.outputs.review != ''
uses: actions/github-script@v7
with:
script: |
const review = `${{ steps.gemini.outputs.review }}`;
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## 🤖 Gemini Analysis (Free Tier)\n\n${review}\n\n---\n_Powered by contributor's Gemini API allowance (1500 req/day free)_`
});
# Trigger Jules to fix issues (contributor's Copilot allowance)
trigger-jules:
if: github.event_name == 'issues' && github.event.label.name == 'agent:ready'
runs-on: ubuntu-latest
steps:
- name: Check Jules eligibility
id: check
uses: actions/github-script@v7
with:
script: |
// Check if this repo has Copilot coding agent enabled
const issue = context.payload.issue;
console.log(`Issue #${issue.number}: ${issue.title}`);
console.log(`Labels: ${issue.labels.map(l => l.name).join(', ')}`);
// Jules can be assigned to issues with specific labels
const hasAgentic = issue.labels.some(l => l.name === 'agentic');
return { eligible: hasAgentic, issueNumber: issue.number };
- name: Assign to Jules
if: steps.check.outputs.result != ''
uses: actions/github-script@v7
with:
script: |
// Trigger Jules by adding the copilot label
// Jules will pick up the issue and create a PR
try {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
labels: ['copilot'] // This triggers Jules/Copilot coding agent
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.payload.issue.number,
body: `## 🤖 Jules Activated\n\nThis issue has been assigned to GitHub Copilot coding agent (Jules).\n\nJules will:\n1. Analyze the issue\n2. Create a solution\n3. Open a PR with the fix\n\n_Using contributor's Copilot allowance_ 🆓`
});
} catch (e) {
console.log('Could not assign to Jules:', e.message);
}

64
.github/workflows/jules-dispatch.yml vendored Normal file
View file

@ -0,0 +1,64 @@
name: Jules Task Dispatch
# Dispatch tasks to Jules (Copilot coding agent)
# Jules works on the contributor's fork using their Copilot Pro allowance
on:
issue_comment:
types: [created]
jobs:
dispatch-to-jules:
# Trigger when someone comments "@jules" or "/jules"
if: contains(github.event.comment.body, '@jules') || contains(github.event.comment.body, '/jules')
runs-on: ubuntu-latest
permissions:
issues: write
pull-requests: write
steps:
- name: Parse Jules command
id: parse
uses: actions/github-script@v7
with:
script: |
const comment = context.payload.comment.body;
// Extract task from comment
// Format: @jules <task description>
// or: /jules fix the bug in auth.php
const match = comment.match(/(?:@jules|\/jules)\s+(.+)/i);
const task = match ? match[1].trim() : null;
return { task, commenter: context.payload.comment.user.login };
- name: Create Jules task
uses: actions/github-script@v7
with:
script: |
const result = ${{ steps.parse.outputs.result }};
if (!result.task) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `@${result.commenter} Please specify a task for Jules:\n\n\`@jules fix the authentication bug\`\n\`@jules add unit tests for User model\`\n\`@jules refactor this to use async/await\``
});
return;
}
// Add copilot label to trigger Jules
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: ['copilot', 'agent:wip']
});
// Create a sub-issue or task for Jules
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `## 🤖 Jules Task Queued\n\n**Task:** ${result.task}\n**Requested by:** @${result.commenter}\n\nJules (Copilot coding agent) will work on this using the repository owner's Copilot allowance.\n\n---\n_Tip: Fork this repo to use your own Copilot Pro allowance for faster processing._`
});

103
doc/free-tier-compute.md Normal file
View file

@ -0,0 +1,103 @@
# Free Tier Compute Guide
This project uses distributed compute from contributors' free tier allowances. Here's how to maximize it.
## GitHub Actions (Microsoft)
**Free tier:** 2,000 minutes/month for public repos
When you fork this repo, CI runs on YOUR allowance, not upstream's. This includes:
- Full test suite
- Linting and formatting
- Security scanning
- AI-powered triage
## Gemini API (Google)
**Free tier:** 1,500 requests/day, 1M tokens/minute
### Setup
1. Get API key: https://aistudio.google.com/apikey
2. Add to your fork's secrets:
```bash
gh secret set GEMINI_API_KEY --repo YOUR_USER/YOUR_FORK
```
The `ai-worker.yml` workflow will use your Gemini allowance for:
- Code review
- Bug detection
- Security analysis
## Jules / Copilot (GitHub)
**Free tier:** Available with GitHub Copilot (free for OSS contributors)
### Trigger Jules
Comment on any issue:
```
@jules fix the authentication bug in login.php
@jules add unit tests for the User model
@jules refactor to use async/await
```
Jules will:
1. Analyze the issue
2. Write code to fix it
3. Create a PR on your fork
4. Uses YOUR Copilot allowance
### Get Copilot Free
- Open source contributors: https://github.com/settings/copilot
- Students: GitHub Education pack
- Verified OSS maintainers: Free Copilot Pro
## CodeRabbit
**Free tier:** Unlimited for public repos
Already configured via `.coderabbit.yaml`. Reviews run automatically on PRs.
## Compute Distribution Model
```
┌────────────────────────────────────────────┐
│ CONTRIBUTOR FORK │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ GitHub │ │ Gemini │ │
│ │ Actions │ │ 2.0 Flash │ │
│ │ FREE │ │ FREE │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Jules/ │ │ CodeRabbit │ │
│ │ Copilot │ │ FREE │ │
│ │ FREE* │ │ │ │
│ └─────────────┘ └─────────────┘ │
│ │
│ * With Copilot Pro (free for OSS) │
└────────────────────────────────────────────┘
│ PR (already analyzed)
┌────────────────────────────────────────────┐
│ UPSTREAM REPO │
│ │
│ Just verify fork CI passed ✓ │
│ Cost: ~$0.001 per PR │
└────────────────────────────────────────────┘
```
## For Donor Fleet Members
If you want to contribute compute without coding:
1. Fork the repo
2. Set up your API keys (Gemini, etc.)
3. Keep Actions enabled
4. Pick up `agent:review` tasks to verify others' work
Your free tier becomes part of the distributed compute network!