diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..df07a85 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,140 @@ +# AGENTS.md +Guide for agentic coding assistants working in `openusage-mono`. + +## 1) Repo Overview +- Package manager: `bun` +- Monorepo runner: `turbo` +- Web app: `apps/web` (TanStack Router + Vite) +- Backend: `packages/backend` (Convex) +- Desktop: `packages/tauri-src` (Tauri 2 + React + Rust) +- Shared config/env: `packages/config`, `packages/env` + +## 2) Setup +- Run from repo root: `bun install --frozen-lockfile` +- Configure Convex once: `bun run dev:setup` + +## 3) Build / Typecheck / Test Commands +### Root commands +- `bun run dev` +- `bun run build` +- `bun run check-types` +- `bun run dev:web` +- `bun run dev:tauri-src` +- `bun run dev:server` + +### Web (`apps/web`) +- `bun run --cwd apps/web dev` +- `bun run --cwd apps/web build` +- `bun run --cwd apps/web check-types` + +### Desktop (`packages/tauri-src`) +- `bun run --cwd packages/tauri-src dev` +- `bun run --cwd packages/tauri-src build` +- `bun run --cwd packages/tauri-src check-types` +- `bun run --cwd packages/tauri-src desktop:dev` +- `bun run --cwd packages/tauri-src desktop:build` + +### Tests (`packages/tauri-src`) +- Full: `bun run --cwd packages/tauri-src test` +- Watch: `bun run --cwd packages/tauri-src test:watch` +- Coverage: `bun run --cwd packages/tauri-src test:coverage` + +### Single test commands (important) +- `bun run --cwd packages/tauri-src test -- src/lib/color.test.ts` +- `bun run --cwd packages/tauri-src test -- src/App.test.tsx` +- `bun run --cwd packages/tauri-src test -- plugins/codex/plugin.test.js` +- `bun run --cwd packages/tauri-src test -- plugins/codex/plugin.test.js -t "throws when auth missing"` + +### Lint status +- `turbo.json` includes a `lint` pipeline key. +- There is no active root lint script/config yet. +- Do not add new lint tooling unless explicitly requested. + +## 4) CI/CD Reality +- CI workflow: `.github/workflows/ci.yml` + - triggers on `main` and `dev` + - runs `bun run check-types` + - runs `bun run --cwd apps/web build` +- Dev desktop release: `.github/workflows/release-dev.yml` (push to `dev`) +- Stable desktop release: `.github/workflows/release-stable.yml` (tags `v*`) +- Web deployment is via Vercel Git integration. +- Vercel production behavior depends on Vercel "Production Branch": + - If Production Branch is `main` (default), `dev` gets preview deploys and `main` gets live web. + - If Production Branch is `dev`, every push to `dev` updates live web; desktop release flows stay unchanged. + +## 5) Day-to-Day Shipping Flow +1. Branch from `dev` (`feature/...`, `fix/...`). +2. Open PR into `dev`. +3. Validate CI + Vercel preview URL from commit checks. +4. Merge to `dev` when preview is correct. +5. Open PR `dev -> main`. +6. Merge to `main` to deploy production web. +7. Tag from `main` (`vX.Y.Z`) for stable desktop release. + +When Vercel Production Branch is switched to `dev`, step 6 is no longer required for web deploys (but keep `dev -> main` for stable release promotion/tagging discipline). + +## 6) Release Version Sync +- Before stable tagging run: `bun run release:version 0.6.1` +- This updates: + - `packages/tauri-src/package.json` + - `packages/tauri-src/src-tauri/tauri.conf.json` + - `packages/tauri-src/src-tauri/Cargo.toml` + +## 7) Code Style Expectations +### General +- Keep changes minimal and focused. +- Prefer readable, explicit code over clever abstractions. +- Preserve local architecture and naming patterns. +- Avoid unrelated refactors. + +### Imports +- Use ESM imports. +- Group imports: external -> `@/...` aliases -> relative. +- Use `import type` for type-only imports. +- Keep imports deterministic. + +### Formatting +- Follow style in touched files (semicolon usage is mixed across packages). +- Prefer small functions and early returns. +- Favor readability in JSX/TSX and object literals. + +### Types +- TypeScript is strict across the workspace. +- Prefer `unknown` at boundaries and narrow via guards. +- Avoid `any` in production code. +- Avoid non-null assertions unless justified. +- Prefer discriminated unions for stateful flows. +- Parse external input (env, JSON, API) at boundaries. + +### Naming +- Components/types: `PascalCase` +- Functions/vars/hooks: `camelCase` +- Constants: `SCREAMING_SNAKE_CASE` +- UI file names often use kebab-case; match local conventions. + +### Error Handling +- Fail fast at boundaries with actionable messages. +- Catch and degrade gracefully in UI refresh/update paths. +- Log with context (`what failed` + error object). +- Plugin probes should surface useful states (auth missing, token expired, HTTP failure). + +### Testing +- Keep tests near source: `*.test.ts`, `*.test.tsx`, `plugins/**/*.test.js`. +- Prefer behavior-focused tests over implementation details. +- Add regression tests for bug fixes when practical. + +## 8) Important Gotchas +- Tauri updater pubkey must be base64-encoded minisign public key payload in `packages/tauri-src/src-tauri/tauri.conf.json` at `plugins.updater.pubkey`. +- Using raw `RWS...` key text fails release builds (`failed to decode pubkey`). +- Keep `TAURI_SIGNING_PRIVATE_KEY` and `TAURI_SIGNING_PRIVATE_KEY_PASSWORD` in GitHub secrets. +- Landing download CTA logic is in `apps/web/src/routes/index.tsx`; prefer direct installer links over generic releases pages. + +## 9) Cursor/Copilot Rules +- No `.cursor/rules/`, `.cursorrules`, or `.github/copilot-instructions.md` exist currently. +- If these files are added later, treat them as authoritative and update this file. + +## 10) Agent Completion Checklist +- Run relevant build/typecheck/test commands for touched areas. +- Prefer single-test runs first, then broader suites as needed. +- Confirm branch/release assumptions (`dev` vs `main`) before workflow edits. +- Call out release-impacting changes in PR descriptions. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..5fea229 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,157 @@ +# Contributing to openusage-mono + +Thanks for contributing. + +This repository is a Bun + Turbo monorepo with three primary surfaces: + +- `apps/web`: marketing/download website (TanStack Router + Vite) +- `packages/backend`: Convex backend +- `packages/tauri-src`: desktop app (Tauri 2 + React + Rust) + +Please read this guide before opening a PR. + +## Prerequisites + +- Bun installed (`packageManager` is pinned in `package.json`) +- Node.js available for tooling scripts used in CI +- Rust toolchain for desktop builds (`packages/tauri-src/src-tauri`) +- Convex account/project for backend-connected local dev + +## Local Setup + +From repo root: + +```bash +bun install --frozen-lockfile +bun run dev:setup +``` + +Then run the workspace: + +```bash +bun run dev +``` + +Useful scoped commands: + +```bash +bun run dev:web +bun run dev:tauri-src +bun run dev:server +``` + +## Build and Typecheck + +From repo root: + +```bash +bun run check-types +bun run build +``` + +Web only: + +```bash +bun run --cwd apps/web check-types +bun run --cwd apps/web build +``` + +Desktop only: + +```bash +bun run --cwd packages/tauri-src check-types +bun run --cwd packages/tauri-src build +bun run --cwd packages/tauri-src desktop:build +``` + +## Tests + +Tests are currently centered in `packages/tauri-src`: + +```bash +bun run --cwd packages/tauri-src test +bun run --cwd packages/tauri-src test:watch +bun run --cwd packages/tauri-src test:coverage +``` + +Single-file test examples: + +```bash +bun run --cwd packages/tauri-src test -- src/lib/color.test.ts +bun run --cwd packages/tauri-src test -- plugins/codex/plugin.test.js +``` + +Single named test case: + +```bash +bun run --cwd packages/tauri-src test -- plugins/codex/plugin.test.js -t "throws when auth missing" +``` + +## Branching and Release Flow + +Primary branches: + +- `dev`: integration branch and prerelease desktop channel +- `main`: production branch and stable release source + +Day-to-day shipping flow: + +1. Branch off `dev` (`feature/...`). +2. Open PR into `dev`. +3. Validate on PR preview deployment (Vercel URL from the commit checks), plus CI. +4. Merge to `dev` when preview looks right. +5. For release, open PR `dev -> main`. +6. Merge to `main` to deploy production web. +7. When ready for desktop stable, tag from `main` (`v0.6.x`) to trigger `release-stable`. + +## CI and Deploy Notes + +- CI workflow: `.github/workflows/ci.yml` + - checks `bun run check-types` + - builds web with `bun run --cwd apps/web build` +- Dev desktop release workflow: `.github/workflows/release-dev.yml` +- Stable desktop release workflow: `.github/workflows/release-stable.yml` +- Web deployment is via Vercel Git integration. + +Important: the production Vercel alias follows the configured production branch (typically `main`), while `dev` updates preview deployments. + +## Versioning and Tagging + +Before creating a stable tag, sync version numbers: + +```bash +bun run release:version 0.6.1 +``` + +This updates: + +- `packages/tauri-src/package.json` +- `packages/tauri-src/src-tauri/tauri.conf.json` +- `packages/tauri-src/src-tauri/Cargo.toml` + +## Code Quality Expectations + +- Keep changes focused and minimal. +- Avoid unrelated refactors in feature/fix PRs. +- Follow local file style (semicolon use varies by package). +- Use strict TypeScript patterns (`unknown` + narrowing at boundaries). +- Prefer `import type` for type-only imports. +- Add or update tests for behavior changes and regressions. + +## Updater Signing Gotcha + +For Tauri updater configuration, `plugins.updater.pubkey` in `packages/tauri-src/src-tauri/tauri.conf.json` must be the base64-encoded minisign public key payload. + +Do not paste only the raw `RWS...` line, or release builds will fail to decode the pubkey. + +## Pull Request Checklist + +Before requesting review: + +- Run relevant typecheck/build/test commands locally. +- Ensure CI is green. +- Validate Vercel preview for web-facing changes. +- Include clear PR notes for behavior changes and release-impacting changes. +- Keep PR scope single-purpose. + +Thanks again for helping improve OpenUsage. diff --git a/apps/web/.env.example b/apps/web/.env.example index 7f3bc9c..fa46f3b 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -1,2 +1,2 @@ -VITE_RELEASE_REPOSITORY=Noisemaker111/opencode-mono +VITE_RELEASE_REPOSITORY=Noisemaker111/openusage-mono VITE_RELEASE_CHANNEL=stable diff --git a/apps/web/src/routes/index.tsx b/apps/web/src/routes/index.tsx index 7a113d9..0cb2e35 100644 --- a/apps/web/src/routes/index.tsx +++ b/apps/web/src/routes/index.tsx @@ -12,7 +12,7 @@ const PRODUCTION_BODY_CLASS = "geistsans_d5a4f12f-module__Ur3q_a__variable geistpixelcircle_7ee616e3-module__hUl13q__variable antialiased"; const PRODUCTION_STYLESHEET_URL = "https://www.openusage.ai/_next/static/chunks/18141af1dfe18c48.css?dpl=dpl_FEcNUMfudsUjMFbSH2z2Gkhx1iG7"; -const DEFAULT_RELEASE_REPOSITORY = "Noisemaker111/opencode-mono"; +const DEFAULT_RELEASE_REPOSITORY = "Noisemaker111/openusage-mono"; const RELEASE_REPOSITORY = import.meta.env.VITE_RELEASE_REPOSITORY && import.meta.env.VITE_RELEASE_REPOSITORY.trim().length > 0 ? import.meta.env.VITE_RELEASE_REPOSITORY.trim() @@ -505,9 +505,9 @@ function getPrimaryDownloadOption( }; } -function getDownloadTrackLabel(option: DownloadOption): string { +function getDownloadTrackLabel(option: DownloadOption): string | null { if (option.releaseTrack === "stable") { - return "Stable"; + return null; } if (option.comingSoon) { @@ -667,14 +667,15 @@ function updatePrimaryDownloadCtas(primaryOption: DownloadOption, platformLabel: }); const primaryTrackLabel = getDownloadTrackLabel(primaryOption); - const primaryLabel = `Download for ${platformLabel} (${primaryTrackLabel})`; + const primaryLabel = + primaryTrackLabel === null ? `Download for ${platformLabel}` : `Download for ${platformLabel} (${primaryTrackLabel})`; for (const anchor of anchors.slice(0, 2)) { anchor.textContent = primaryLabel; anchor.setAttribute("href", primaryOption.href); if (primaryOption.available) { - anchor.setAttribute("target", "_blank"); - anchor.setAttribute("rel", "noopener noreferrer"); + anchor.removeAttribute("target"); + anchor.removeAttribute("rel"); anchor.onclick = null; continue; } @@ -699,7 +700,10 @@ function updatePrimaryDownloadCtas(primaryOption: DownloadOption, platformLabel: }); if (ctaParagraph !== undefined) { - ctaParagraph.textContent = `Download OpenUsage for ${platformLabel} (${primaryTrackLabel}). It is free, and you will never have to guess your limits again.`; + ctaParagraph.textContent = + primaryTrackLabel === null + ? `Download OpenUsage for ${platformLabel}. It is free, and you will never have to guess your limits again.` + : `Download OpenUsage for ${platformLabel} (${primaryTrackLabel}). It is free, and you will never have to guess your limits again.`; } const ctaFootnote = Array.from(document.querySelectorAll("p")).find((paragraph) => { @@ -708,7 +712,7 @@ function updatePrimaryDownloadCtas(primaryOption: DownloadOption, platformLabel: }); if (ctaFootnote !== undefined) { - ctaFootnote.textContent = "macOS Stable - Windows Beta - Linux Beta soon - MIT License"; + ctaFootnote.textContent = "macOS - Windows Beta - Linux Beta soon - MIT License"; } ensureHeroMoreDownloadsAnchor(); @@ -750,7 +754,7 @@ function renderMoreDownloadsSection(options: ReadonlyArray, rele description.className = "text-sm lg:text-base mt-2"; description.style.color = "var(--page-fg-muted)"; const releaseSummary = releaseTag === null ? "Latest release" : `Latest release ${releaseTag}`; - description.textContent = `${releaseSummary}. macOS is Stable, Windows is Beta, Linux is Beta soon.`; + description.textContent = `${releaseSummary}. macOS is available, Windows is Beta, Linux is Beta soon.`; header.append(title, description); @@ -761,8 +765,8 @@ function renderMoreDownloadsSection(options: ReadonlyArray, rele const card = document.createElement("a"); card.href = option.href; if (option.available) { - card.target = "_blank"; - card.rel = "noopener noreferrer"; + card.removeAttribute("target"); + card.removeAttribute("rel"); } else { card.removeAttribute("target"); card.removeAttribute("rel"); @@ -790,8 +794,13 @@ function renderMoreDownloadsSection(options: ReadonlyArray, rele stageBadge.style.border = "1px solid var(--page-border)"; stageBadge.style.backgroundColor = "rgba(255,255,255,0.08)"; stageBadge.style.color = "var(--page-fg-subtle)"; - stageBadge.textContent = getDownloadTrackLabel(option); - cardHeader.append(cardTitle, stageBadge); + const trackLabel = getDownloadTrackLabel(option); + if (trackLabel !== null) { + stageBadge.textContent = trackLabel; + cardHeader.append(cardTitle, stageBadge); + } else { + cardHeader.append(cardTitle); + } const cardSubtitle = document.createElement("p"); cardSubtitle.className = "text-xs mt-1"; @@ -801,7 +810,11 @@ function renderMoreDownloadsSection(options: ReadonlyArray, rele const cardAction = document.createElement("p"); cardAction.className = "text-xs mt-3"; cardAction.style.color = option.available ? "var(--page-accent)" : "var(--page-fg-muted)"; - cardAction.textContent = option.available ? `Download ${getDownloadTrackLabel(option).toLowerCase()}` : option.comingSoon ? "Coming soon" : "Unavailable"; + if (option.available) { + cardAction.textContent = trackLabel === null ? "Download" : `Download ${trackLabel.toLowerCase()}`; + } else { + cardAction.textContent = option.comingSoon ? "Coming soon" : "Unavailable"; + } card.append(cardHeader, cardSubtitle, cardAction); grid.append(card); diff --git a/docs/release-flow.md b/docs/release-flow.md index 029869e..d8daa86 100644 --- a/docs/release-flow.md +++ b/docs/release-flow.md @@ -59,7 +59,7 @@ The website download logic reads release metadata from GitHub and supports chann Configure via env vars at build time: -- `VITE_RELEASE_REPOSITORY` (default: `Noisemaker111/opencode-mono`) +- `VITE_RELEASE_REPOSITORY` (default: `Noisemaker111/openusage-mono`) - `VITE_RELEASE_CHANNEL` (`stable` or `dev`) When deployed on Vercel Git integration: diff --git a/packages/tauri-src/README.md b/packages/tauri-src/README.md index 91ed59d..6b5644a 100644 --- a/packages/tauri-src/README.md +++ b/packages/tauri-src/README.md @@ -6,7 +6,7 @@ Amp, Cursor, Claude, Codex, and more coming. See your usage at a glance from you ## Download -[**Download the latest release**](https://github.com/Noisemaker111/opencode-mono/releases/latest) (macOS, Apple Silicon & Intel, Windows x64) +[**Download the latest release**](https://github.com/Noisemaker111/openusage-mono/releases/latest) (macOS, Apple Silicon & Intel, Windows x64) The app auto-updates. Install once and you're set. diff --git a/packages/tauri-src/src-tauri/tauri.conf.json b/packages/tauri-src/src-tauri/tauri.conf.json index e1df560..3c3d232 100644 --- a/packages/tauri-src/src-tauri/tauri.conf.json +++ b/packages/tauri-src/src-tauri/tauri.conf.json @@ -66,7 +66,7 @@ "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDM4ODZBQUU4MTUyODU5NDkKUldSSldTZ1Y2S3FHT09sam9jVXRKSTFFNjIrRkJjdDRUc0xGNDRnbU5HNG9pZ1YvVS9ZdndMMDQK", "endpoints": [ - "https://github.com/Noisemaker111/opencode-mono/releases/latest/download/latest.json" + "https://github.com/Noisemaker111/openusage-mono/releases/latest/download/latest.json" ] } }