-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/better autonomous dev #13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: Jules Push Monitor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| on: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| push: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| branches: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - '**' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jobs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── Detect whether any commit in this push is from JulesWyrm ────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| detect-jules: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| outputs: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| jules_pushed: ${{ steps.check.outputs.jules_pushed }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commit_range: ${{ steps.check.outputs.commit_range }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Check commit authors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: check | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| COMMITS: ${{ toJson(github.event.commits) }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEFORE: ${{ github.event.before }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AFTER: ${{ github.event.after }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JULES_FOUND=$(echo "$COMMITS" | python3 -c " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import json, sys | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| commits = json.load(sys.stdin) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| found = any(c.get('author', {}).get('username') == 'JulesWyrm' for c in commits) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| print('true' if found else 'false') | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "jules_pushed=$JULES_FOUND" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| echo "commit_range=${BEFORE}..${AFTER}" >> "$GITHUB_OUTPUT" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # ── Notify via Beeper ───────────────────────────────────────────────────── | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notify: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| needs: detect-jules | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if: needs.detect-jules.outputs.jules_pushed == 'true' | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| runs-on: ubuntu-latest | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| steps: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| - name: Send Beeper notification | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # TODO: Confirm the following before enabling: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 1. Base URL — if this is the Beeper Desktop API (localhost), it is not | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # reachable from GitHub Actions. Confirm whether a cloud endpoint exists, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # or set up a proxy (e.g. a Vercel edge function that forwards to desktop). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 2. Auth header — replace "Bearer" with the correct scheme if different. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 3. accountID — add as a repo secret (BEEPER_ACCOUNT_ID). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # 4. user.id — your own Beeper user ID to start a self-DM (BEEPER_SELF_USER_ID). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # API shape (from https://developers.beeper.com/desktop-api-reference/resources/chats/methods/create): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # POST /v1/chats | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "accountID": "<your account id>", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "mode": "start", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "user": { "id": "<your user id>" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # "messageText": "<notification text>" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| env: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEEPER_API_KEY: ${{ secrets.BEEPER_API_KEY }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEEPER_BASE_URL: ${{ vars.BEEPER_BASE_URL }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEEPER_ACCOUNT_ID: ${{ secrets.BEEPER_ACCOUNT_ID }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BEEPER_SELF_USER_ID: ${{ secrets.BEEPER_SELF_USER_ID }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| BRANCH: ${{ github.ref_name }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| SHA: ${{ github.event.after }} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| run: | | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| MESSAGE="JulesWyrm pushed to ${BRANCH} (${SHA:0:7}). A wiring-review issue has been opened." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curl -sf -X POST "${BEEPER_BASE_URL}/v1/chats" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Authorization: Bearer ${BEEPER_API_KEY}" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+55
to
+65
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -H "Content-Type: application/json" \ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| -d "{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \"accountID\": \"${BEEPER_ACCOUNT_ID}\", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \"mode\": \"start\", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \"user\": { \"id\": \"${BEEPER_SELF_USER_ID}\" }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| \"messageText\": \"${MESSAGE}\" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+64
to
+72
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| curl -sf -X POST "${BEEPER_BASE_URL}/v1/chats" \ | |
| -H "Authorization: Bearer ${BEEPER_API_KEY}" \ | |
| -H "Content-Type: application/json" \ | |
| -d "{ | |
| \"accountID\": \"${BEEPER_ACCOUNT_ID}\", | |
| \"mode\": \"start\", | |
| \"user\": { \"id\": \"${BEEPER_SELF_USER_ID}\" }, | |
| \"messageText\": \"${MESSAGE}\" | |
| }" | |
| PAYLOAD=$(MESSAGE="$MESSAGE" \ | |
| BEEPER_ACCOUNT_ID="$BEEPER_ACCOUNT_ID" \ | |
| BEEPER_SELF_USER_ID="$BEEPER_SELF_USER_ID" \ | |
| python3 -c ' | |
| import json | |
| import os | |
| print(json.dumps({ | |
| "accountID": os.environ["BEEPER_ACCOUNT_ID"], | |
| "mode": "start", | |
| "user": {"id": os.environ["BEEPER_SELF_USER_ID"]}, | |
| "messageText": os.environ["MESSAGE"], | |
| })) | |
| ') | |
| curl -sf -X POST "${BEEPER_BASE_URL}/v1/chats" \ | |
| -H "Authorization: Bearer ${BEEPER_API_KEY}" \ | |
| -H "Content-Type: application/json" \ | |
| -d "$PAYLOAD" |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -152,3 +152,41 @@ Run with `npm test`. 20 tests across 3 suites, all passing. | |||||
| | B001 | Fixed | Circuit suite iframe state loss on tab switch | Fixed by rendering both iframes simultaneously, toggling with CSS display:none | | ||||||
| | B002 | Fixed | Electromagnet coil flat rendering (no depth) | Fixed draw order: back arches → rod (white fill) → front arches | | ||||||
| | B003 | Reverted | Electromagnet integration removed from circuit tools | Symbol style did not match required exam format (hatched steel rod + stacked cell symbols). To be redesigned before re-integrating. | | ||||||
|
|
||||||
| --- | ||||||
|
|
||||||
| ## Planned: Co-contributor Push Automation | ||||||
|
|
||||||
| ### Problem | ||||||
| JulesWyrm (co-contributor) pushes raw HTML tool additions/enhancements directly. These changes need Next.js wiring (new page, `tool-id`, tracker meta, tests) before they are production-ready. Currently this wiring is done manually after noticing her push. | ||||||
|
|
||||||
| ### Goal | ||||||
| Automate detection, notification, and issue creation whenever JulesWyrm pushes, so nothing falls through the cracks. | ||||||
|
|
||||||
| ### Design | ||||||
|
|
||||||
| **Trigger:** GitHub Actions workflow on `push` to any branch. | ||||||
|
|
||||||
| **Job 1 — Notify** | ||||||
| - Check if any commit author in the push is `JulesWyrm` | ||||||
| - If yes, call Beeper API to send a push notification to Kahhow | ||||||
|
|
||||||
| **Job 2 — Auto-issue (runs only on JulesWyrm pushes)** | ||||||
| - Generate a diff of her changes using the full push range (`${before}..${after}`), not just the last commit; if `before` is all zeros (new branch), handle that case separately so the diff still covers everything introduced by the push | ||||||
| - Send the diff to Claude API (Anthropic) with a structured prompt that checks for: | ||||||
| - New HTML files in `public/tools/` → needs a Next.js page + `tool-id` added to `VALID_TOOLS` | ||||||
| - Missing `<meta name="tool-id">` or `<script src="/tracker.js">` in any HTML file | ||||||
| - New tool → needs Jest test suite added under `__tests__/canvas/` | ||||||
| - Changes to existing canvas logic → flag tests may need updating | ||||||
| - Claude returns a structured issue body (markdown) summarising what changed and what wiring is needed | ||||||
| - Open a GitHub Issue via `gh issue create --assignee "@copilot"` so Copilot automatically attempts the wiring and opens a PR | ||||||
|
|
||||||
| **Why Claude for analysis, Copilot for implementation:** | ||||||
| Claude handles open-ended diff analysis and writing the issue spec. Copilot handles the mechanical Next.js wiring (creating page files, adding tool-id, etc.) once given a clear spec. | ||||||
|
|
||||||
| **Secrets required:** | ||||||
| - `ANTHROPIC_API_KEY` — for Claude diff analysis | ||||||
| - `BEEPER_*` — Beeper API auth (TBD once API docs confirmed) | ||||||
| - `GITHUB_TOKEN` — already available in Actions | ||||||
|
|
||||||
| **Status:** Planned — not yet implemented. | ||||||
|
||||||
| **Status:** Planned — not yet implemented. | |
| **Status:** Partially implemented — the GitHub Actions workflow and diff-analysis script are now in the repo. Remaining TODOs: finalize the Beeper endpoint/auth configuration and complete any notification wiring that depends on those credentials. |
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,129 @@ | ||||||
| #!/usr/bin/env node | ||||||
| /** | ||||||
| * analyze-diff.mjs | ||||||
| * | ||||||
| * Reads a git diff from stdin, calls the Anthropic Messages API | ||||||
| * (claude-sonnet-4-6), and writes a markdown GitHub issue body to stdout. | ||||||
| * | ||||||
| * Usage: | ||||||
| * git diff <before>..<after> | node scripts/analyze-diff.mjs | ||||||
| * | ||||||
| * Required env: | ||||||
| * ANTHROPIC_API_KEY | ||||||
| */ | ||||||
|
|
||||||
| // ── 1. Read diff from stdin ─────────────────────────────────────────────────── | ||||||
| const diff = await readStdin() | ||||||
|
|
||||||
| if (!diff.trim()) { | ||||||
| process.stdout.write('<!-- analyze-diff: empty diff, nothing to review -->\n') | ||||||
| process.exit(0) | ||||||
| } | ||||||
|
|
||||||
| // ── 2. Build the prompt ─────────────────────────────────────────────────────── | ||||||
| const SYSTEM_PROMPT = `\ | ||||||
| You are a code-review assistant for a Next.js 14 (App Router) project that hosts | ||||||
| standalone HTML canvas tools in \`public/tools/\`. Your job is to review a git diff | ||||||
| and identify any Next.js wiring work that a developer needs to do next. | ||||||
|
|
||||||
| Project conventions: | ||||||
| - Every HTML tool file in \`public/tools/\` needs: | ||||||
| 1. A \`<meta name="tool-id" content="<id>" />\` tag in \`<head>\` | ||||||
| 2. A \`<script src="/tracker.js"></script>\` tag before \`</body>\` | ||||||
| - Every new tool also needs: | ||||||
| 3. A new route at \`app/tools/<name>/page.tsx\` that renders a full-viewport iframe | ||||||
| 4. A new tool-id string added to the VALID_TOOLS Set in \`app/api/event/route.ts\` | ||||||
| 5. A Jest test suite in \`__tests__/canvas/<name>.test.js\` using | ||||||
| \`loadCircuitScript\` (non-IIFE) or \`loadIifeScript\` (IIFE scripts) | ||||||
| from \`__tests__/canvas/helpers.js\` | ||||||
| - When canvas logic functions change (snap, gcd, componentSize, getComponentNodes, | ||||||
| rotatePoint, getLocalNodes), the corresponding __tests__/canvas/ suite likely | ||||||
| needs updating. | ||||||
| - GRID=28, COMPONENT_SCALE=0.8 for circuit_diagram_creatorv2.html | ||||||
| - GRID=22.4 for circuit_diagram_secjc.html; includes transistor (3-node) and transformer (2-node) | ||||||
| - object_circuitv2.html: COMPONENT_SCALE=0.8 for battery/switch, BULB_SCALE=1 (bulb nodes unscaled) | ||||||
| ` | ||||||
|
|
||||||
| // Truncate at 48 000 chars to avoid runaway costs on huge diffs | ||||||
| const diffSnippet = diff.length > 48_000 | ||||||
| ? diff.slice(0, 48_000) + '\n\n[... diff truncated at 48 000 chars ...]' | ||||||
| : diff | ||||||
|
|
||||||
| const USER_PROMPT = `\ | ||||||
| Below is a git diff from a push by JulesWyrm. Analyse it and produce a GitHub | ||||||
| issue body in Markdown describing the Next.js wiring work needed. | ||||||
|
|
||||||
| Structure your response as: | ||||||
| 1. A short (2-3 sentence) **Summary** of what changed. | ||||||
| 2. A **Checklist** of actionable tasks (GitHub task-list syntax: \`- [ ] ...\`), | ||||||
| covering where relevant: | ||||||
| - New \`public/tools/\` HTML files that need a Next.js page | ||||||
| - Missing or incorrect \`<meta name="tool-id">\` tags | ||||||
| - Missing \`<script src="/tracker.js">\` tags | ||||||
| - New tool-ids to add to VALID_TOOLS in \`app/api/event/route.ts\` | ||||||
| - New Jest test suites to create in \`__tests__/canvas/\` | ||||||
| - Existing canvas test suites that need updating due to logic changes | ||||||
| 3. A **Files changed** section listing every file touched in the diff. | ||||||
|
|
||||||
| If nothing in the diff requires Next.js wiring work, say so clearly and keep the | ||||||
| issue body short. | ||||||
|
|
||||||
| \`\`\`diff | ||||||
| ${diffSnippet} | ||||||
| \`\`\` | ||||||
| ` | ||||||
|
|
||||||
| // ── 3. Call the Anthropic Messages API ─────────────────────────────────────── | ||||||
| const apiKey = process.env.ANTHROPIC_API_KEY | ||||||
| if (!apiKey) { | ||||||
| process.stderr.write('ERROR: ANTHROPIC_API_KEY is not set\n') | ||||||
| process.exit(1) | ||||||
| } | ||||||
|
|
||||||
| const response = await fetch('https://api.anthropic.com/v1/messages', { | ||||||
| method: 'POST', | ||||||
| headers: { | ||||||
| 'x-api-key': apiKey, | ||||||
| 'anthropic-version': '2023-06-01', | ||||||
| 'content-type': 'application/json' | ||||||
| }, | ||||||
| body: JSON.stringify({ | ||||||
| model: 'claude-sonnet-4-6', | ||||||
| max_tokens: 2048, | ||||||
| system: SYSTEM_PROMPT, | ||||||
| messages: [{ role: 'user', content: USER_PROMPT }] | ||||||
| }) | ||||||
| }) | ||||||
|
|
||||||
| if (!response.ok) { | ||||||
| const errText = await response.text() | ||||||
| process.stderr.write(`ERROR: Anthropic API returned ${response.status}: ${errText}\n`) | ||||||
| process.exit(1) | ||||||
| } | ||||||
|
|
||||||
| const data = await response.json() | ||||||
| const issueBody = data?.content?.[0]?.text ?? '' | ||||||
|
|
||||||
| if (!issueBody) { | ||||||
| process.stderr.write('ERROR: Unexpected response shape from Anthropic API\n') | ||||||
| process.stderr.write(JSON.stringify(data, null, 2) + '\n') | ||||||
| process.exit(1) | ||||||
| } | ||||||
|
|
||||||
| // ── 4. Write issue body to stdout ───────────────────────────────────────────── | ||||||
| const footer = [ | ||||||
| '', | ||||||
| '---', | ||||||
| '*Auto-generated by [analyze-diff.mjs](../scripts/analyze-diff.mjs) · model: claude-sonnet-4-6*' | ||||||
|
||||||
| '*Auto-generated by [analyze-diff.mjs](../scripts/analyze-diff.mjs) · model: claude-sonnet-4-6*' | |
| '*Auto-generated by analyze-diff.mjs · model: claude-sonnet-4-6*' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
github.event.commitspayload isn’t guaranteed to include all commits in a push (it can be truncated on larger pushes), so this author check can miss JulesWyrm and skip the workflow. A more reliable approach is tocheckoutand rungit log ${BEFORE}..${AFTER}(or use the GitHub compare API) to scan authors across the full commit range.