Skip to content

Commit d6d1923

Browse files
committed
feat: jules planning enhancements and fixes
1 parent 878b604 commit d6d1923

1,018 files changed

Lines changed: 71948 additions & 6662 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Rule: AI Provider Standards (Antigravity)
2+
3+
## 1. Zod Supremacy
4+
- Every structured AI call (`generateStructuredResponse`, `generateStructuredWithTools`) MUST accept a `z.ZodType<T>` payload.
5+
- Weak interfaces (`any` or `Record<string, unknown>`) are forbidden for output generation.
6+
- Use `zod-to-json-schema` to natively pipe the schema into the payload's `response_format` for supported compat endpoints.
7+
- Always call `schema.parse(rawParsed)` on the final JSON result to guarantee structural integrity.
8+
9+
## 2. Universal File Context
10+
- Any method ending in `*FromFiles` must handle payloads consisting of `FileInput` objects (`{ name, type, data, isBase64 }`).
11+
- Providers with native file processing (e.g., Gemini's `inlineData`) should construct standard multi-part arrays.
12+
- Providers without native large-file limits (e.g., Worker AI) MUST use the transparent Vectorize RAG chunking algorithm if the total string length exceeds 6,000 characters to prevent hitting input limits.
13+
14+
## 3. Jules SDK Integration
15+
- Since Jules operates via long-running chat streams, large structured responses should be handled by getting broad context from Jules, and piping its output into `worker-ai` `generateStructuredResponse` to enforce strict formatting.
16+
- Explicit reasoning and agent orchestration features (`analyzeRepo`, `completeTask`, `createPlan`) should exclusively utilize Jules.
17+
18+
## 4. No Vercel AI SDK
19+
- Under no circumstances will you import `ai` or `@ai-sdk/`. It has been banned due to edge runtime parsing inconsistencies on Workers.
20+
- Use the native `fetch` API against the Cloudflare AI Gateway instead.
Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,17 @@
11
---
22
description: Implement Universal AI Gateway REST Proxy
33
---
4+
# Implement Universal AI Gateway REST Proxy
45

5-
# Workflow: Implement Universal AI Gateway REST Proxy
6+
This workflow outlines the standard operating procedure for upgrading the AI Provider abstraction layer to support unified routing through Cloudflare's AI Gateway.
67

7-
**Objective:** Expose Cloudflare AI Gateway endpoints through the Hono API so that external GitHub Action python scripts can securely interface with Universal Routing.
8+
## Pre-Requisites
9+
1. Ensure the project's `wrangler.jsonc` contains the appropriate `AI_GATEWAY_NAME` and `FILE_EMBEDDINGS` Vectorize binding.
10+
2. Confirm `@google/jules-sdk`, `zod`, and `zod-to-json-schema` are installed in the `backend/` workspace.
811

9-
1. **File Creation:** Created the modular `backend/src/routes/api/ai/gateway.ts`, `backend/src/routes/api/ai/utils/format-model-id.ts`, and `backend/src/routes/api/ai/schemas/gateway.schema.ts` files.
10-
2. **Code Ingestion:** Built full TypeScript end-to-end functionality into each module.
11-
3. **Module Wiring:**
12-
- Navigated to the main routing registry (`backend/src/index.ts`).
13-
- Imported `gatewayApp` from the new module: `import aiGatewayApi from '@/routes/api/ai/gateway';`
14-
- Mounted the route: `app.route('/api/ai/gateway', aiGatewayApi);`
15-
4. **Environment Setup:** Update `.dev.vars` (and `wrangler.toml`/`wrangler.jsonc` `[vars]` block if typed validation exists) to ensure `CLOUDFLARE_ACCOUNT_ID` and `CLOUDFLARE_API_TOKEN` are active. Optionally add `GITHUB_ACTION_API_KEY` for dedicated auth isolation.
16-
5. **Validation:**
17-
- Run tests/CURL locally against `http://localhost:8787/api/ai/gateway/chat/completions`.
18-
- Send a generic `Authorization: Bearer <token>` and payload `{"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "ping"}]}` to confirm universal routing passthrough and Zod schema compliance.
12+
## Steps
13+
1. Route all provider calls (Worker AI, Gemini) through the Cloudflare AI Gateway compat or standard endpoint using native `fetch`. Do not use provider-specific SDKs.
14+
2. Enforce Zod schemas strictly. Convert `z.ZodType<T>` to JSON schema using `zodToJsonSchema` and utilize the provider's `response_format` JSON schema mode.
15+
3. For large file contexts (> 6000 chars), transparently chunk and vectorize the text using `@cf/baai/bge-large-en-v1.5` and Vectorize RAG. Query the prompt embedding against the index, and append the top matches to the final prompt.
16+
4. Integrate the `@google/jules-sdk` and polyfill missing features like structured output by piping the text response into a smaller, strict formatting model like `worker-ai`.
17+
5. Run `pnpm run check` to ensure strict TypeScript compilation completes successfully.

.agents/llms-txt/cloudflare-llms.txt

Lines changed: 0 additions & 157 deletions
This file was deleted.

.github/workflows/skill-sync.yml

Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
##
2+
# .github/workflows/skill-sync.yml
3+
#
4+
# Skill Sync — checks configured skill repos for new or modified SKILL.md files
5+
# and copies them to .agent/skills/ in this repo. Optionally dispatches a Jules
6+
# session to wire updated skills into fetchRemoteSkill agent arrays.
7+
#
8+
# TRIGGERS: Pull Request, Manual (workflow_dispatch)
9+
#
10+
# REQUIRED SECRETS:
11+
# GH_TOKEN — Fine-grained PAT with read access to skill repos and write to this repo
12+
# JULES_API_KEY — Jules SDK API key for the optional agent-wiring step
13+
#
14+
# SKILL_REPOS (env var) — space-separated list of "owner/repo" to scan for skills.
15+
##
16+
17+
name: "🔧 Skill Sync"
18+
19+
on:
20+
pull_request:
21+
branches: [main]
22+
paths:
23+
- ".agent/skills/**"
24+
- ".github/workflows/skill-sync.yml"
25+
workflow_dispatch:
26+
inputs:
27+
skill_repos:
28+
description: "Space-separated list of 'owner/repo' to scan (overrides env default)"
29+
required: false
30+
dispatch_jules:
31+
description: "Dispatch a Jules session to update agent fetchRemoteSkill arrays"
32+
type: boolean
33+
default: true
34+
35+
concurrency:
36+
group: skill-sync-${{ github.ref }}
37+
cancel-in-progress: true
38+
39+
env:
40+
# Default skill source repos — override via workflow_dispatch input or repo variable
41+
DEFAULT_SKILL_REPOS: "jmbish04/core-github-standardization cloudflare/skills"
42+
TARGET_SKILLS_DIR: ".agent/skills"
43+
44+
jobs:
45+
detect-changes:
46+
name: "📡 Detect Skill Changes"
47+
runs-on: ubuntu-latest
48+
outputs:
49+
changed_skills: ${{ steps.scan.outputs.changed_skills }}
50+
has_changes: ${{ steps.scan.outputs.has_changes }}
51+
steps:
52+
- name: Checkout this repo
53+
uses: actions/checkout@v4
54+
with:
55+
fetch-depth: 0
56+
57+
- name: Set up Node.js
58+
uses: actions/setup-node@v4
59+
with:
60+
node-version: "20"
61+
62+
- name: Scan skill repos for changes
63+
id: scan
64+
env:
65+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
66+
SKILL_REPOS: ${{ github.event.inputs.skill_repos || env.DEFAULT_SKILL_REPOS }}
67+
run: |
68+
set -euo pipefail
69+
70+
node - <<'SCAN_SCRIPT'
71+
const { execSync } = require('child_process');
72+
const repos = process.env.SKILL_REPOS.trim().split(/\s+/);
73+
const token = process.env.GH_TOKEN;
74+
const changes = [];
75+
76+
for (const repo of repos) {
77+
const [owner, repoName] = repo.split('/');
78+
if (!owner || !repoName) continue;
79+
80+
try {
81+
// List all SKILL.md files in the repo via GitHub API
82+
const apiUrl = `https://api.github.com/repos/${owner}/${repoName}/git/trees/HEAD?recursive=1`;
83+
const response = execSync(
84+
`curl -sf -H "Authorization: Bearer ${token}" -H "Accept: application/vnd.github+json" "${apiUrl}"`
85+
);
86+
const tree = JSON.parse(response.toString());
87+
const skillFiles = (tree.tree || [])
88+
.map(e => e.path)
89+
.filter(p => p && p.match(/SKILL\.md$/i));
90+
91+
for (const filePath of skillFiles) {
92+
// Extract skill name from path (e.g. skills/agents-sdk/SKILL.md -> agents-sdk)
93+
const parts = filePath.split('/');
94+
const skillName = parts.length >= 2 ? parts[parts.length - 2] : null;
95+
if (!skillName) continue;
96+
97+
const localPath = `.agent/skills/${skillName}/SKILL.md`;
98+
99+
// Check if local copy exists and compare content hash
100+
let localContent = '';
101+
try {
102+
localContent = require('fs').readFileSync(localPath, 'utf8');
103+
} catch { /* file doesn't exist yet */ }
104+
105+
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repoName}/HEAD/${filePath}`;
106+
const remoteContent = execSync(
107+
`curl -sf -H "Authorization: Bearer ${token}" "${rawUrl}"`
108+
).toString();
109+
110+
if (remoteContent !== localContent) {
111+
changes.push({ repo, filePath, skillName, rawUrl });
112+
}
113+
}
114+
} catch (err) {
115+
console.error(`Failed to scan ${repo}:`, err.message);
116+
}
117+
}
118+
119+
require('fs').writeFileSync('/tmp/skill_changes.json', JSON.stringify(changes, null, 2));
120+
console.log(`Found ${changes.length} skill changes`);
121+
const core = { setOutput: (k, v) => process.stdout.write(`::set-output name=${k}::${v}\n`) };
122+
core.setOutput('has_changes', changes.length > 0 ? 'true' : 'false');
123+
core.setOutput('changed_skills', JSON.stringify(changes.map(c => c.skillName)));
124+
SCAN_SCRIPT
125+
126+
- name: Upload changes manifest
127+
uses: actions/upload-artifact@v4
128+
with:
129+
name: skill-changes
130+
path: /tmp/skill_changes.json
131+
132+
copy-skills:
133+
name: "📋 Copy Updated Skills"
134+
needs: detect-changes
135+
if: needs.detect-changes.outputs.has_changes == 'true'
136+
runs-on: ubuntu-latest
137+
permissions:
138+
contents: write
139+
pull-requests: write
140+
steps:
141+
- name: Checkout this repo
142+
uses: actions/checkout@v4
143+
with:
144+
token: ${{ secrets.GH_TOKEN }}
145+
146+
- name: Set up Node.js
147+
uses: actions/setup-node@v4
148+
with:
149+
node-version: "20"
150+
151+
- name: Download changes manifest
152+
uses: actions/download-artifact@v4
153+
with:
154+
name: skill-changes
155+
path: /tmp
156+
157+
- name: Copy changed skills to .agent/skills/
158+
env:
159+
GH_TOKEN: ${{ secrets.GH_TOKEN }}
160+
run: |
161+
set -euo pipefail
162+
163+
node - <<'COPY_SCRIPT'
164+
const { execSync } = require('child_process');
165+
const fs = require('fs');
166+
const path = require('path');
167+
const token = process.env.GH_TOKEN;
168+
const changes = JSON.parse(fs.readFileSync('/tmp/skill_changes.json', 'utf8'));
169+
170+
for (const { rawUrl, skillName } of changes) {
171+
const targetDir = `.agent/skills/${skillName}`;
172+
const targetFile = `${targetDir}/SKILL.md`;
173+
fs.mkdirSync(targetDir, { recursive: true });
174+
const content = execSync(`curl -sf -H "Authorization: Bearer ${token}" "${rawUrl}"`).toString();
175+
fs.writeFileSync(targetFile, content);
176+
console.log(`✅ Updated: ${targetFile}`);
177+
}
178+
COPY_SCRIPT
179+
180+
- name: Commit and push updated skills
181+
run: |
182+
git config user.name "github-actions[bot]"
183+
git config user.email "github-actions[bot]@users.noreply.github.com"
184+
git add "${{ env.TARGET_SKILLS_DIR }}/"
185+
if git diff --cached --quiet; then
186+
echo "No changes to commit."
187+
exit 0
188+
fi
189+
git commit -m "chore(skills): sync updated skills from remote repos [skill-sync-bot]"
190+
git push origin HEAD
191+
192+
dispatch-jules:
193+
name: "🤖 Jules: Wire New Skills into Agents"
194+
needs: [detect-changes, copy-skills]
195+
if: |
196+
needs.detect-changes.outputs.has_changes == 'true' &&
197+
(github.event_name == 'workflow_dispatch' && github.event.inputs.dispatch_jules == 'true' ||
198+
github.event_name == 'pull_request')
199+
runs-on: ubuntu-latest
200+
steps:
201+
- name: Set up Node.js
202+
uses: actions/setup-node@v4
203+
with:
204+
node-version: "20"
205+
206+
- name: Install Jules SDK
207+
run: npm install -g @google/jules-sdk
208+
209+
- name: Download changes manifest
210+
uses: actions/download-artifact@v4
211+
with:
212+
name: skill-changes
213+
path: /tmp
214+
215+
- name: Dispatch Jules to update fetchRemoteSkill arrays
216+
env:
217+
JULES_API_KEY: ${{ secrets.JULES_API_KEY }}
218+
GITHUB_REPOSITORY: ${{ github.repository }}
219+
CHANGED_SKILLS: ${{ needs.detect-changes.outputs.changed_skills }}
220+
run: |
221+
node - <<'JULES_SCRIPT'
222+
const { jules } = require('@google/jules-sdk');
223+
224+
const changedSkills = JSON.parse(process.env.CHANGED_SKILLS || '[]');
225+
if (changedSkills.length === 0) { console.log('No skills to wire.'); process.exit(0); }
226+
227+
const client = jules.with({ apiKey: process.env.JULES_API_KEY });
228+
const repo = process.env.GITHUB_REPOSITORY;
229+
230+
const prompt = `
231+
You are a Cloudflare Workers expert working on the repository \`${repo}\`.
232+
233+
The following skill files have been added or updated in \`.agent/skills/\`:
234+
${changedSkills.map(s => `- \`.agent/skills/${s}/SKILL.md\``).join('\n')}
235+
236+
Your task:
237+
1. Read each new/updated SKILL.md to understand what skill it provides.
238+
2. Open \`src/backend/src/services/octokit/skill-fetcher.ts\` and update the \`AGENT_SKILL_PATHS\` registry to include appropriate skill paths for each agent where the skill is relevant.
239+
3. Skill paths follow the format: \`skills/{skill-name}/SKILL.md\`.
240+
4. Do NOT remove existing skill paths — only add new ones.
241+
5. Run \`cd src/backend && pnpm run build\` to verify no TypeScript errors.
242+
6. Submit a PR titled: "feat(skills): integrate new remote skills into agent fetchRemoteSkill arrays"
243+
244+
Rules:
245+
- Only modify \`src/backend/src/services/octokit/skill-fetcher.ts\`
246+
- Keep all existing entries intact
247+
- Use TypeScript strict mode
248+
`.trim();
249+
250+
(async () => {
251+
const session = await client.session({
252+
prompt,
253+
source: { github: repo, baseBranch: 'main' },
254+
autoPr: true,
255+
});
256+
console.log('Jules session created:', session.id);
257+
258+
for await (const activity of session.stream()) {
259+
if (activity.type === 'agentMessaged') {
260+
console.log('Jules:', activity.message.slice(0, 120));
261+
}
262+
if (activity.type === 'progressUpdated') {
263+
console.log('Progress:', activity.title);
264+
}
265+
}
266+
267+
const result = await session.result();
268+
console.log('Session completed:', result.state);
269+
})().catch(err => {
270+
console.error('Jules dispatch failed:', err.message);
271+
process.exit(1);
272+
});
273+
JULES_SCRIPT

0 commit comments

Comments
 (0)