diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b18c62..37bca53 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,27 +10,14 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - - name: Set up pnpm - uses: pnpm/action-setup@v4 + - name: Set up bun + uses: oven-sh/setup-bun@v2 with: - version: 11.5.0 - - name: Set up Node.js - id: setup-node-act - uses: actions/setup-node@v6 - with: - cache: pnpm - cache-dependency-path: pnpm-lock.yaml - node-version: 25 + bun-version: latest - name: Run CI - env: - SETUP_NODE_VERSION: ${{ steps.setup-node-act.outputs.node-version }} run: | set -euo pipefail - node_version="${SETUP_NODE_VERSION#v}" - export PATH="/opt/hostedtoolcache/node/${node_version}/x64/bin:$PATH" - node --version - npm --version - npm install --global pnpm@11.5.0 - pnpm install --frozen-lockfile - pnpm typecheck - pnpm build \ No newline at end of file + bun install --frozen-lockfile + bun run test + bun run typecheck + bun run build diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 42eeb5f..987fdb0 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -29,25 +29,18 @@ jobs: token: ${{ secrets.TOKEN }} fetch-depth: 0 - - name: Set up pnpm - uses: pnpm/action-setup@v4 + - name: Set up bun + uses: oven-sh/setup-bun@v2 with: - version: 11.5.0 - - - name: Set up Node.js - uses: actions/setup-node@v6 - with: - node-version: 25 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml + bun-version: latest - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Run tests with coverage # Writes coverage/cobertura-coverage.xml and coverage/coverage-summary.json # in addition to the existing text + lcov reporters. - run: pnpm test:coverage + run: bun run test:coverage - name: Code Coverage Summary Report uses: irongut/CodeCoverageSummary@v1.3.0 @@ -63,7 +56,7 @@ jobs: thresholds: '50 75' - name: Update README badge + summary - run: node scripts/update-coverage-badge.mjs --from-markdown code-coverage-results.md + run: bun scripts/update-coverage-badge.mjs --from-markdown code-coverage-results.md - name: Commit updated README uses: stefanzweifel/git-auto-commit-action@v6 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index aa11eb5..460ef9a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,30 +14,24 @@ jobs: with: persist-credentials: false - - uses: pnpm/action-setup@v4 + - uses: oven-sh/setup-bun@v2 with: - version: 11.5.0 - - - uses: actions/setup-node@v6 - with: - node-version: 25 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml + bun-version: latest - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Type check - run: pnpm typecheck + run: bun run typecheck - name: Build & ZIP Chrome - run: pnpm zip:chrome + run: bun run zip:chrome - name: Build & ZIP Firefox - run: pnpm zip:firefox + run: bun run zip:firefox - name: Build & ZIP Edge - run: pnpm zip:edge + run: bun run zip:edge - name: Upload release artifacts uses: actions/upload-artifact@v4 @@ -83,18 +77,12 @@ jobs: with: persist-credentials: false - - uses: pnpm/action-setup@v4 - with: - version: 11.5.0 - - - uses: actions/setup-node@v6 + - uses: oven-sh/setup-bun@v2 with: - node-version: 25 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml + bun-version: latest - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Download store submission artifacts uses: actions/download-artifact@v8 @@ -104,7 +92,7 @@ jobs: - name: Submit to Chrome Web Store run: | - pnpm wxt submit \ + bunx wxt submit \ --chrome-zip dist/*-chrome.zip env: CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }} @@ -122,18 +110,12 @@ jobs: with: persist-credentials: false - - uses: pnpm/action-setup@v4 - with: - version: 11.5.0 - - - uses: actions/setup-node@v6 + - uses: oven-sh/setup-bun@v2 with: - node-version: 25 - cache: pnpm - cache-dependency-path: pnpm-lock.yaml + bun-version: latest - name: Install dependencies - run: pnpm install --frozen-lockfile + run: bun install --frozen-lockfile - name: Download store submission artifacts uses: actions/download-artifact@v8 @@ -143,7 +125,7 @@ jobs: - name: Submit to Firefox Add-ons run: | - pnpm wxt submit \ + bunx wxt submit \ --firefox-zip dist/*-firefox.zip \ --firefox-sources-zip dist/*-sources.zip env: diff --git a/AGENTS.md b/AGENTS.md index 90b8803..37f27ea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,23 +1,396 @@ + + + + + # AGENTS.md -## Cursor Cloud specific instructions +Centralised AI agent instructions. Add coding guidelines, style guides, and project context here. + +Ruler concatenates all .md files in this directory (and subdirectories), starting with AGENTS.md (if present), then remaining files in sorted order. + + + + + +# Karpathy behavioral guidelines + +Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed. + +**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment. + +## 1. Think Before Coding + +**Don't assume. Don't hide confusion. Surface tradeoffs.** + +Before implementing: +- State your assumptions explicitly. If uncertain, ask. +- If multiple interpretations exist, present them - don't pick silently. +- If a simpler approach exists, say so. Push back when warranted. +- If something is unclear, stop. Name what's confusing. Ask. + +## 2. Simplicity First + +**Minimum code that solves the problem. Nothing speculative.** + +- No features beyond what was asked. +- No abstractions for single-use code. +- No "flexibility" or "configurability" that wasn't requested. +- No error handling for impossible scenarios. +- If you write 200 lines and it could be 50, rewrite it. + +Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify. + +## 3. Surgical Changes + +**Touch only what you must. Clean up only your own mess.** + +When editing existing code: +- Don't "improve" adjacent code, comments, or formatting. +- Don't refactor things that aren't broken. +- Match existing style, even if you'd do it differently. +- If you notice unrelated dead code, mention it - don't delete it. + +When your changes create orphans: +- Remove imports/variables/functions that YOUR changes made unused. +- Don't remove pre-existing dead code unless asked. + +The test: Every changed line should trace directly to the user's request. + +## 4. Goal-Driven Execution + +**Define success criteria. Loop until verified.** + +Transform tasks into verifiable goals: +- "Add validation" → "Write tests for invalid inputs, then make them pass" +- "Fix the bug" → "Write a test that reproduces it, then make it pass" +- "Refactor X" → "Ensure tests pass before and after" + +For multi-step tasks, state a brief plan: +``` +1. [Step] → verify: [check] +2. [Step] → verify: [check] +3. [Step] → verify: [check] +``` + +Strong success criteria let you loop independently. Weak criteria ("make it work") require constant clarification. + +--- + +**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes. + +- In plan mode, always use the /ask skill to read the code and the original transcript that generated it. Understanding intent will help you write a better plan. + + + + + +# AGENTS.md + +## Project Overview +This repository contains "Refined GitHub Projects", a browser extension built with the [WXT framework](https://wxt.dev/), React, and TypeScript. It interacts with the GitHub Projects V2 GraphQL API to bulk-edit project items. + +## Setup & Dev Commands +- Install dependencies: `bun install` +- Start dev server: `bun run dev` +- Build for production: `bun run build` +- Test: `bun test` +- Typecheck: `bun run typecheck` + +## Tech Stack & Architecture +- **Framework:** [WXT](https://wxt.dev/) (Manifest V3) +- **UI:** React 19 + `@primer/react` v38. **No `sx` prop, no `Box` component** — v38 removed both. Style with plain CSS classes and Primer CSS custom properties (`var(--bgColor-default)`, `var(--borderColor-default)`, etc.). +- **Fetching:** `@tanstack/react-query` in the content script — its declarative `retry`/`retryDelay` absorb rate limits and `Retry-After`. +- **Mutation queue:** `@tanstack/pacer`'s `AsyncQueuer` — paces writes only, `concurrency: 1`, `wait: 500`. It does not inspect HTTP errors; the query client's retry policy already did. +- **Storage:** `@tanstack/db` custom `SyncConfig` collections over `browser.storage.local` (`src/lib/field-cache.ts`), never `localStorage`. +- **State:** in-memory module-level pub/sub stores in `src/lib/*-store.ts`, read via `useSyncExternalStore`. +- **API:** plain `async`/`await` over native `fetch`, background-only, in `src/lib/gql.ts` — no retry loop there. +- **Messaging:** `@webext-core/messaging` with a hand-written `ProtocolMap` in `src/lib/messages.ts`. +- **Injection:** light DOM only. No shadow roots, no `ShadowThemeProvider`, no `createShadowRootUi`. +- **Background:** WXT Background Service Worker owns the PAT and every GitHub `fetch`. +- **There is no Effect-TS.** Do not reintroduce `effect`, `@effect/platform`, `Schema`, `ManagedRuntime`, `Layer`, `Context.Tag`, or fiber-based concurrency. Plain TypeScript is the house style here. + +### Architecture + +Three layers, each with exactly one job: + +``` + ┌────────────────────┐ ┌──────────────────────────┐ ┌────────────────────┐ + │ Content Script │ │ Background SW │ │ Popup │ + │ React, light DOM │ wxt │ entries/background.ts │ │ React │ + │ TanStack Query │ msgs │ owns PAT + fetch() │ │ token setup │ + │ + Pacer + DB │ ───▶ │ gql.ts: no retry │ │ │ + │ module stores │ ◀─── │ │ └────────────────────┘ + └────────────────────┘ └──────────────────────────┘ +``` + +- The **background** owns the PAT and every `fetch` — `gql()` injects the token, classifies the HTTP status, and returns a discriminated `GqlResult`, with no retry loop. +- The **content script** owns TanStack Query (`src/lib/query-client.ts`), whose `retry`/`retryDelay` absorb `Retry-After`, and TanStack DB (`src/lib/field-cache.ts`), which persists project field metadata to `browser.storage.local`. +- **Pacer** (`src/lib/write-queue.ts`) sits between the UI and the mutation dispatcher doing nothing but pacing. + +Errors cross the background↔content message boundary **as data, not as thrown `Error`s** — extension messaging does not reliably preserve custom fields like `_tag` and `retryAfter`, which the retry policy needs. `src/lib/gql.ts` serializes; `src/lib/query-client.ts` (`rehydrate`) reconstructs the real `GithubError` before `request()` throws it, so `shouldRetry`/`retryDelayMs` can `instanceof` it. + +Cross-cutting modules: +- **Errors** (`src/lib/errors.ts`): tagged ADT of plain `Error` subclasses, each with a literal `_tag` and its `name` set — `GithubRateLimitError`, `GithubServerError`, `GithubClientError`, `GithubGraphQLError`, `GithubNetworkError`, `GithubDecodeError`. An HTTP status is mapped by `classifyHttpError(...)`; callers narrow with a `switch` on `_tag` (`instanceof` also works). +- **Stores** (`src/lib/selection-store.ts`, `src/lib/write-queue.ts`): each holds a module-local `current`/`state`, a `Set` of listeners, and a `subscribe(fn)` returning an unsubscribe, exposed to React via `useSyncExternalStore`. +- **Field cache** (`src/lib/field-cache.ts`): a TanStack DB collection with a custom `SyncConfig` — the `browser.storage.onChanged` listener is registered **before** the first `browser.storage.local.get()` read, so writes landing mid-fetch aren't lost. + +**Where to add new code:** +- New GitHub API call → add the query/mutation string to `src/lib/github.ts` and call it through `request()` from `src/lib/query-client.ts`. Keep the query literal and greppable. +- New WXT message → add the entry to the `ProtocolMap` in `src/lib/messages.ts`, add `onMessage('name', …)` in `src/entries/background.ts`. +- New shared in-memory state → a new `src/lib/*-store.ts` following the existing module-level pattern. Consumers wire it with `useSyncExternalStore`. +- **Never `fetch` GitHub from a content script.** Send a message to the background worker, which owns PAT retrieval and all network calls. + +## ⚠️ Critical Boundaries & Rules (MUST READ) + +1. **Anti-Abuse API Rate Limiting (Strictly Enforced):** + - NEVER use `Promise.all()` to execute multiple GraphQL mutations concurrently. GitHub will instantly ban the user's PAT with a 403 Abuse alert. + - All bulk operations MUST be processed sequentially through the Pacer `AsyncQueuer` in `src/lib/write-queue.ts` (`concurrency: 1`, `wait: 500`). + - Every fetch in `src/lib/gql.ts` classifies `403 Forbidden` and `429 Too Many Requests` via `classifyHttpError`; the retry policy in `src/lib/query-client.ts` honors `Retry-After` exactly before retrying. + +2. **Security & Data Flow:** + - NEVER execute `fetch` calls to GitHub directly from Content Scripts. + - The UI (React components) must use WXT Messaging (`sendMessage`) to reach the Background Service Worker. + - The Background Service Worker handles all PAT retrieval from storage and executes the GraphQL requests. The PAT never enters a TanStack DB collection, never enters `localStorage`, and never crosses into page context. + +3. **DOM Stability:** + - GitHub's DOM updates frequently and is virtualised. When anchoring Content Script UI, prefer `[data-hovercard-subject-tag]` / ARIA roles over volatile CSS class names (see `src/lib/project-table-dom.ts`), and re-mount idempotently via a `MutationObserver` keyed on a `data-rgp` mark. + +4. **UI/UX (Strictly Enforced):** + - **Primer React is first-class** for real controls: `Button`, `FormControl`, `TextInput`, `ActionList`, `ActionMenu`, `Flash`, `ThemeProvider`, `BaseStyles`. + - **No `sx`, no `Box`.** Style with plain CSS files (see `src/entries/content/style.css`) using Primer functional CSS custom properties, never raw hex. + - **All light DOM.** No shadow roots anywhere in this codebase. + - **`@media (prefers-reduced-motion: reduce)` guards must cover every transitioned property on the element**, not just one. + - **No undo, no presets, no dry-run.** Confirmation only where an action is destructive. + +## @wxt-agent + +- **Description:** Lead Fullstack Extension Engineer. +- **Persona:** You are an expert in Manifest V3 browser extensions, the WXT framework, and complex GraphQL orchestration. You prioritize security, rate-limit safety, and resilient DOM manipulation. +- **Responsibilities:** + - Scaffold WXT Background Service Workers and message handlers. + - Build the paced queue manager for API requests (Pacer, never `Promise.all()`). + - Write robust DOM `MutationObserver` hooks to detect when a user selects items in the GitHub Projects Table view. + + + + + +# RTK (Rust Token Killer) - Token-Optimized Commands + +## Golden Rule + +**Always prefix commands with `rtk`**. If RTK has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always safe to use. + +**Important**: Even in command chains with `&&`, use `rtk`: +```bash +# ❌ Wrong +git add . && git commit -m "msg" && git push + +# ✅ Correct +rtk git add . && rtk git commit -m "msg" && rtk git push +``` + +## RTK Commands by Workflow + +### Build & Compile (80-90% savings) +```bash +rtk cargo build # Cargo build output +rtk cargo check # Cargo check output +rtk cargo clippy # Clippy warnings grouped by file (80%) +rtk tsc # TypeScript errors grouped by file/code (83%) +rtk lint # ESLint/Biome violations grouped (84%) +rtk prettier --check # Files needing format only (70%) +rtk next build # Next.js build with route metrics (87%) +``` + +### Test (90-99% savings) +```bash +rtk cargo test # Cargo test failures only (90%) +rtk vitest run # Vitest failures only (99.5%) +rtk playwright test # Playwright failures only (94%) +rtk test # Generic test wrapper - failures only +``` + +### Git (59-80% savings) +```bash +rtk git status # Compact status +rtk git log # Compact log (works with all git flags) +rtk git diff # Compact diff (80%) +rtk git show # Compact show (80%) +rtk git add # Ultra-compact confirmations (59%) +rtk git commit # Ultra-compact confirmations (59%) +rtk git push # Ultra-compact confirmations +rtk git pull # Ultra-compact confirmations +rtk git branch # Compact branch list +rtk git fetch # Compact fetch +rtk git stash # Compact stash +rtk git worktree # Compact worktree +``` + +Note: Git passthrough works for ALL subcommands, even those not explicitly listed. + +### GitHub (26-87% savings) +```bash +rtk gh pr view # Compact PR view (87%) +rtk gh pr checks # Compact PR checks (79%) +rtk gh run list # Compact workflow runs (82%) +rtk gh issue list # Compact issue list (80%) +rtk gh api # Compact API responses (26%) +``` + +### JavaScript/TypeScript Tooling (70-90% savings) +```bash +rtk pnpm list # Compact dependency tree (70%) +rtk pnpm outdated # Compact outdated packages (80%) +rtk pnpm install # Compact install output (90%) +rtk npm run