From 1dc38a1dd30f3e4b4bfa1a9b2324ad3a999a6a78 Mon Sep 17 00:00:00 2001 From: admin-raintree <277948009+admin-raintree@users.noreply.github.com> Date: Thu, 11 Jun 2026 23:34:21 -0700 Subject: [PATCH 1/6] chore: align with Raintree standard --- .github/workflows/ci.yml | 122 +------ .github/workflows/drift-check.yml | 28 ++ .npmrc | 1 + .nvmrc | 1 + README.md | 51 ++- bench/generate-workloads.mjs | 7 +- bench/run.mjs | 9 +- bin/cli.js | 23 +- biome.base.jsonc | 32 ++ biome.jsonc | 36 ++ package-lock.json | 164 +++++++++ package.json | 2 + renovate.json | 3 + site/.npmrc | 1 + site/README.md | 33 +- site/app/.well-known/security.txt/route.ts | 10 +- site/app/layout.tsx | 34 +- site/app/llms.txt/route.ts | 8 +- site/app/manifest.ts | 6 +- site/app/not-found.tsx | 14 +- site/app/opengraph-image.tsx | 142 ++++---- site/app/page.tsx | 370 +++++++++++++-------- site/app/robots.ts | 8 +- site/app/sitemap.ts | 6 +- site/biome.jsonc | 52 +++ site/bun.lock | 27 +- site/components/github-stats.tsx | 72 ++-- site/components/theme-provider.tsx | 13 +- site/components/theme-toggle.tsx | 34 +- site/components/ui/button.tsx | 29 +- site/lib/site.ts | 12 +- site/lib/utils.ts | 2 +- site/next.config.ts | 6 +- site/package.json | 13 +- site/postcss.config.mjs | 4 +- site/proxy.ts | 48 ++- site/tailwind.config.ts | 239 ++++++------- site/tsconfig.base.json | 15 + site/tsconfig.json | 1 + src/commands/add.js | 16 +- src/commands/init.js | 123 ++++--- src/commands/status.js | 37 ++- src/commands/sync.js | 33 +- src/index.js | 7 +- src/manifest.js | 31 +- src/mcps.js | 8 +- src/profiles.js | 21 +- src/utils/copy.js | 259 ++++++++------- src/utils/managed-block.js | 16 +- src/utils/security.js | 12 +- src/utils/toon.js | 4 +- test/install-regression.test.js | 92 +++-- test/manifest-sync.test.js | 61 +++- test/security-hardening.test.js | 12 +- test/skill-quality.test.js | 57 ++-- test/template-settings.test.js | 7 +- 56 files changed, 1512 insertions(+), 962 deletions(-) create mode 100644 .github/workflows/drift-check.yml create mode 100644 .npmrc create mode 100644 .nvmrc create mode 100644 biome.base.jsonc create mode 100644 biome.jsonc create mode 100644 renovate.json create mode 100644 site/.npmrc create mode 100644 site/biome.jsonc create mode 100644 site/tsconfig.base.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5186d62..8d4e4bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,108 +10,20 @@ permissions: contents: read jobs: - root-checks: - name: Root checks - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - - - name: Setup Node - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e - with: - node-version: 20.19.5 - cache: npm - - - name: Install dependencies - run: npm ci - - - name: Lint - run: npm run lint - - - name: Test - run: npm test - - - name: Audit dependencies - run: npm audit --audit-level=moderate - - - name: Package dry-run - run: npm pack --dry-run - - site-checks: - name: Site checks - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - - - name: Setup Bun - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 - with: - bun-version: 1.3.11 - - - name: Install dependencies - working-directory: site - run: bun install --frozen-lockfile - - - name: Audit dependencies - working-directory: site - run: bun audit - - - name: Lint - working-directory: site - run: bun run lint - - - name: Typecheck - working-directory: site - run: bun run typecheck - - - name: Build - working-directory: site - run: bun run build - - secret-scan: - name: Secret scan - runs-on: ubuntu-24.04 - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - with: - fetch-depth: 0 - - - name: Install Gitleaks - env: - GITLEAKS_VERSION: 8.30.1 - run: | - set -euo pipefail - archive="gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" - curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/${archive}" -o "$RUNNER_TEMP/${archive}" - curl -sSfL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_checksums.txt" -o "$RUNNER_TEMP/gitleaks_checksums.txt" - grep " ${archive}$" "$RUNNER_TEMP/gitleaks_checksums.txt" > "$RUNNER_TEMP/gitleaks.sha256" - (cd "$RUNNER_TEMP" && sha256sum -c gitleaks.sha256) - mkdir -p "$RUNNER_TEMP/gitleaks" - tar -xzf "$RUNNER_TEMP/${archive}" -C "$RUNNER_TEMP/gitleaks" - - - name: Gitleaks - run: "$RUNNER_TEMP/gitleaks/gitleaks detect --source . --redact --verbose" - - codeql: - name: CodeQL - runs-on: ubuntu-24.04 - permissions: - security-events: write - packages: read - actions: read - contents: read - steps: - - name: Checkout - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 - - - name: Initialize CodeQL - uses: github/codeql-action/init@fee9466b8957867761f2d78f922ab084e3e2dd17 - with: - languages: javascript-typescript - build-mode: none - - - name: Analyze - uses: github/codeql-action/analyze@fee9466b8957867761f2d78f922ab084e3e2dd17 + root: + name: Root package + uses: raintree-technology/.github/.github/workflows/ci.yml@326f815e920df16bafa8234b236e2de4b78ee660 + with: + package-manager: npm + node-version: "22" + working-directory: "." + secrets: inherit + + site: + name: Marketing site + uses: raintree-technology/.github/.github/workflows/ci.yml@326f815e920df16bafa8234b236e2de4b78ee660 + with: + package-manager: bun + bun-version: "1.3.11" + working-directory: site + secrets: inherit diff --git a/.github/workflows/drift-check.yml b/.github/workflows/drift-check.yml new file mode 100644 index 0000000..d4658b7 --- /dev/null +++ b/.github/workflows/drift-check.yml @@ -0,0 +1,28 @@ +name: Drift Check + +on: + workflow_dispatch: + schedule: + - cron: "17 6 * * 1" + pull_request: + paths: + - ".github/workflows/**" + - ".npmrc" + - ".nvmrc" + - "biome*.json*" + - "package.json" + - "package-lock.json" + - "renovate.json" + - "site/biome*.json*" + - "site/package.json" + - "site/bun.lock" + - "site/tsconfig.json" + - "site/tsconfig.base.json" + +permissions: + contents: read + +jobs: + drift: + name: Standard drift + uses: raintree-technology/.github/.github/workflows/drift-check.yml@326f815e920df16bafa8234b236e2de4b78ee660 diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/README.md b/README.md index efb6302..1cac928 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,61 @@ # agent-starter -An opinionated multi-agent skill pack for Claude Code, Codex, and Cursor. Deep, handwritten skills for HCI usability modeling, Apple HIG Doctor guidance, copywriting, code cleanup, and TOON token savings. +Raintree Technology's opinionated multi-agent skill pack for Claude Code, Codex, and Cursor. Deep, handwritten skills for HCI usability modeling, Apple HIG Doctor guidance, copywriting, code cleanup, and TOON token savings. No orchestration framework. No aspirational YAML. Just agent-native project files generated from one shared skill source. +[![status: live](https://img.shields.io/badge/status-live-brightgreen.svg)](https://github.com/raintree-technology/agent-starter) +[![CI](https://github.com/raintree-technology/agent-starter/actions/workflows/ci.yml/badge.svg)](https://github.com/raintree-technology/agent-starter/actions/workflows/ci.yml) [![npm version](https://img.shields.io/npm/v/create-agent-starter.svg)](https://www.npmjs.com/package/create-agent-starter) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +## Status + +This repository is live. It contains the published `create-agent-starter` npm package and the `claude.raintree.technology` marketing site. + +## Stack + +- Root package: Node.js ESM CLI/library, npm, `node:test`, ESLint, Biome. +- Site: Next.js 16 App Router, React 19, TypeScript, Bun, Tailwind CSS, shadcn/ui primitives. +- CI: Raintree Technology reusable CI and drift-check workflows pinned to the org standard commit. + +## Setup + +```bash +npm ci +cd site +bun install --frozen-lockfile +``` + +Use Node.js 22 for repository development (`.nvmrc`), while the published root package keeps its existing `>=18.0.0` runtime contract. + +## Scripts + +Root package: + +```bash +npm run check +npm run lint +npm test +npm pack --dry-run +``` + +Marketing site: + +```bash +cd site +bun run check +bun run lint +bun run typecheck +bun run build +``` + +## Environment And Deploy + +The root package does not require runtime environment variables. The site documents its public local URL in `site/.env.example`. + +The npm package is published from the root package. The site is intended for Vercel deployment at `claude.raintree.technology`. + ## What you get **29 shipped skills**: diff --git a/bench/generate-workloads.mjs b/bench/generate-workloads.mjs index 5f570d1..de99287 100644 --- a/bench/generate-workloads.mjs +++ b/bench/generate-workloads.mjs @@ -2,7 +2,7 @@ // Generate reproducible sample workloads for TOON vs JSON benchmarking. // Deterministic output — seeded PRNG — so measurements are stable across runs. -import { writeFileSync, mkdirSync } from 'node:fs'; +import { mkdirSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; @@ -11,7 +11,10 @@ const OUT = join(__dirname, 'workloads'); mkdirSync(OUT, { recursive: true }); let seed = 42; -const rand = () => ((seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff); +const rand = () => { + seed = (seed * 1103515245 + 12345) & 0x7fffffff; + return seed / 0x7fffffff; +}; const pick = (arr) => arr[Math.floor(rand() * arr.length)]; const int = (lo, hi) => Math.floor(lo + rand() * (hi - lo)); diff --git a/bench/run.mjs b/bench/run.mjs index d09cece..e0bd3e1 100644 --- a/bench/run.mjs +++ b/bench/run.mjs @@ -3,7 +3,7 @@ // Uses gpt-tokenizer (OpenAI BPE) as a proxy for Claude's tokenizer. // Writes RESULTS.md with a markdown table. -import { readFileSync, writeFileSync, readdirSync } from 'node:fs'; +import { readdirSync, readFileSync, writeFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { encode as toonEncode } from '@toon-format/toon'; @@ -15,7 +15,9 @@ const RESULTS = join(__dirname, 'RESULTS.md'); const countTokens = (text) => gptEncode(text).length; -const files = readdirSync(WORKLOADS).filter((f) => f.endsWith('.json')).sort(); +const files = readdirSync(WORKLOADS) + .filter((f) => f.endsWith('.json')) + .sort(); const rows = files.map((f) => { const path = join(WORKLOADS, f); @@ -24,7 +26,6 @@ const rows = files.map((f) => { let toonStr; let toonTokens; - let savedPct; let encodeError = null; try { toonStr = toonEncode(data); @@ -37,7 +38,7 @@ const rows = files.map((f) => { const jsonTokens = countTokens(raw); const delta = toonTokens !== null ? jsonTokens - toonTokens : null; - savedPct = delta !== null && jsonTokens ? (delta / jsonTokens) * 100 : null; + const savedPct = delta !== null && jsonTokens ? (delta / jsonTokens) * 100 : null; return { file: f, diff --git a/bin/cli.js b/bin/cli.js index 6e672a9..638e729 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,13 +1,13 @@ #!/usr/bin/env node +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; +import { fileURLToPath } from 'node:url'; import { program } from 'commander'; +import { addMcp, addSkill } from '../src/commands/add.js'; import { init } from '../src/commands/init.js'; -import { sync } from '../src/commands/sync.js'; import { status } from '../src/commands/status.js'; -import { addMcp, addSkill } from '../src/commands/add.js'; -import { readFileSync } from 'fs'; -import { fileURLToPath } from 'url'; -import { dirname, join } from 'path'; +import { sync } from '../src/commands/sync.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); @@ -24,14 +24,19 @@ program .option('-y, --yes', 'Skip confirmation prompts') .option('-f, --force', 'Overwrite existing files') .option('--agent ', 'Agent target(s): claude, codex, cursor, or all', 'claude') - .option('--profile ', 'Use preset profile (all, apple-hig, design-hci, minimal, next-saas, next, node, base)') + .option( + '--profile ', + 'Use preset profile (all, apple-hig, design-hci, minimal, next-saas, next, node, base)', + ) .option('--skills ', 'Comma-separated skills to install') .option('--no-toon', 'Skip TOON utilities') .action(init); program .command('sync [dir]') - .description('Write native agent config (skills, MCP servers) for every target declared in agent.json') + .description( + 'Write native agent config (skills, MCP servers) for every target declared in agent.json', + ) .action(sync); program @@ -39,9 +44,7 @@ program .description('Diff agent.json against the native config of each target; exits 1 on drift') .action(status); -const add = program - .command('add') - .description('Add an entry to agent.json and re-sync'); +const add = program.command('add').description('Add an entry to agent.json and re-sync'); add .command('mcp ') diff --git a/biome.base.jsonc b/biome.base.jsonc new file mode 100644 index 0000000..313fad1 --- /dev/null +++ b/biome.base.jsonc @@ -0,0 +1,32 @@ +// Raintree Technology — canonical Biome base. +// Vendored into each repo as biome.base.jsonc; the repo's biome.json extends it: +// { "extends": ["./biome.base.jsonc"], ... } +// This base enforces org invariants (VCS integration, recommended lint rules, +// organized imports). Formatter style (tabs/spaces, quotes, line width) is a +// per-repo choice and intentionally not set here. +{ + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "ignoreUnknown": true + }, + "formatter": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "assist": { + "actions": { + "source": { + "organizeImports": "on" + } + } + } +} diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 0000000..6700881 --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,36 @@ +{ + "$schema": "https://biomejs.dev/schemas/2.4.16/schema.json", + "extends": ["./biome.base.jsonc"], + "files": { + "includes": [ + ".github/**/*.yml", + "bench/**/*.mjs", + "bin/**/*.js", + "package-lock.json", + "package.json", + "src/**/*.js", + "templates/**/*.js", + "templates/**/*.json", + "test/**/*.js" + ] + }, + "formatter": { + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100 + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "semicolons": "always", + "trailingCommas": "all" + } + }, + "linter": { + "rules": { + "suspicious": { + "noTemplateCurlyInString": "off" + } + } + } +} diff --git a/package-lock.json b/package-lock.json index acea95c..05cf618 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22,6 +22,7 @@ "create-claude-starter": "bin/cli.js" }, "devDependencies": { + "@biomejs/biome": "2.4.16", "@eslint/js": "10.0.1", "@toon-format/toon": "2.1.0", "eslint": "10.4.1", @@ -31,6 +32,169 @@ "node": ">=18.0.0" } }, + "node_modules/@biomejs/biome": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.4.16.tgz", + "integrity": "sha512-x9ajFh1zChVybCiM3TN6OD4phAqLgtPZjFrZF+aTMYCPjwBO+k529TX7PPsAqtGNLeV4UgzwQnowEgS7bGmzcA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.16", + "@biomejs/cli-darwin-x64": "2.4.16", + "@biomejs/cli-linux-arm64": "2.4.16", + "@biomejs/cli-linux-arm64-musl": "2.4.16", + "@biomejs/cli-linux-x64": "2.4.16", + "@biomejs/cli-linux-x64-musl": "2.4.16", + "@biomejs/cli-win32-arm64": "2.4.16", + "@biomejs/cli-win32-x64": "2.4.16" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.4.16.tgz", + "integrity": "sha512-wxPvu4XOA85YJk9ixSWUmq/QBHbid85BISbOAqqBM/5xQpPk9ayjk5375tOlSC0BeCwNSbPFafQBm+vBumXq0A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.4.16.tgz", + "integrity": "sha512-xFCqGPwYusQJp4N4NJLi1XJiZqjwFdjhT+KqtNy+Ug3qgfczqnTa6MSDvxJF6TkuDLoYJItMapz6tAf7kCekFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.4.16.tgz", + "integrity": "sha512-2kFb4//jxfZaP6D+Rj5VkHkxgyD9EoRAVBEQb8PKRv+s4NO2zYNJKXFaJmK1CmhufJOWEfpHKaRbOja7qjmdhQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.4.16.tgz", + "integrity": "sha512-oYxnW0ARfJkr72ezzF2OR8N/rtkgLUQeYtF8cFhVswbknHxtTcmzSsanVJP8yQKnGpGpc2ck6c5zLvHahL6Cbg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.4.16.tgz", + "integrity": "sha512-NbcBbi/nJqn5baae6wqRXdS7Gadf2uRpehSh6vMSYpG8OhkXl/Xg8aorWrJ+9VWqAT5ml90alLvorkpMW0nBwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.4.16.tgz", + "integrity": "sha512-iHDS+MCM65DPqWGu+ECC3uoALyj2H7F4nVUPxIPjz/PIl94EUu+EDfGZDzFP+NY1EOPVt9NQvwFqq7HdMmowdg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.4.16.tgz", + "integrity": "sha512-0rgImMsNb5v/chhkIFe3wu7PEFClS6RBAYUijGL9UsYN3PanSaoK24HSSuSJb1pYbYYVjzAyZTl3gtjJ84BM8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.4.16", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.4.16.tgz", + "integrity": "sha512-Kp85jgoBHa05gix6UIRjfCDiUV3w/8VIdZ247VyyO2gEjaw12WEVhdIjlxp/AMzXxqxQwbxNTDVZ3Mwd2RG5rw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", diff --git a/package.json b/package.json index 46cee6c..f8dc9ec 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "LICENSE" ], "scripts": { + "check": "biome ci .", "test": "node --test", "test:security": "node --test test/*.test.js", "lint": "eslint src/ test/ bin/ bench/ templates/.claude/utils/", @@ -64,6 +65,7 @@ "ora": "9.4.0" }, "devDependencies": { + "@biomejs/biome": "2.4.16", "@eslint/js": "10.0.1", "@toon-format/toon": "2.1.0", "eslint": "10.4.1", diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..9989071 --- /dev/null +++ b/renovate.json @@ -0,0 +1,3 @@ +{ + "extends": ["github>raintree-technology/.github//configs/renovate-base.json"] +} diff --git a/site/.npmrc b/site/.npmrc new file mode 100644 index 0000000..cffe8cd --- /dev/null +++ b/site/.npmrc @@ -0,0 +1 @@ +save-exact=true diff --git a/site/README.md b/site/README.md index 4e9e915..eaea299 100644 --- a/site/README.md +++ b/site/README.md @@ -1,23 +1,42 @@ # claude.raintree.technology -Landing page for [agent-starter](https://github.com/raintree-technology/claude-starter). +Raintree Technology landing page for [agent-starter](https://github.com/raintree-technology/agent-starter). + +## Stack + +- Next.js 16 App Router +- React 19 + TypeScript +- Bun package manager +- Tailwind CSS + shadcn/ui primitives +- next-themes for dark mode +- lucide-react icons ## Develop ```bash -bun install +bun install --frozen-lockfile bun run dev ``` Open http://localhost:3000. +## Scripts + +```bash +bun run check +bun run lint +bun run typecheck +bun run build +``` + +## Environment + +Copy `site/.env.example` for local development when needed. It only contains `NEXT_PUBLIC_APP_URL`. + ## Deploy Deploy to Vercel and point `claude.raintree.technology` at the project via CNAME. -## Stack +## License -- Next.js 16 (App Router, Turbopack) -- Tailwind CSS + shadcn/ui primitives -- next-themes for dark mode -- lucide-react icons +The site is part of the MIT-licensed `agent-starter` repository. diff --git a/site/app/.well-known/security.txt/route.ts b/site/app/.well-known/security.txt/route.ts index eaae948..41b919d 100644 --- a/site/app/.well-known/security.txt/route.ts +++ b/site/app/.well-known/security.txt/route.ts @@ -1,19 +1,19 @@ -import { siteConfig } from "@/lib/site"; +import { siteConfig } from "@/lib/site" -export const dynamic = "force-static"; +export const dynamic = "force-static" export function GET() { - const expires = new Date("2027-06-03T00:00:00.000Z").toISOString(); + const expires = new Date("2027-06-03T00:00:00.000Z").toISOString() const body = `Contact: mailto:${siteConfig.securityContact} Preferred-Languages: en Policy: ${siteConfig.repoUrl}/blob/main/SECURITY.md Expires: ${expires} -`; +` return new Response(body, { headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "public, max-age=3600, s-maxage=86400", }, - }); + }) } diff --git a/site/app/layout.tsx b/site/app/layout.tsx index befd1cd..c0f8a1b 100644 --- a/site/app/layout.tsx +++ b/site/app/layout.tsx @@ -1,19 +1,19 @@ -import type { Metadata, Viewport } from "next"; -import { headers } from "next/headers"; -import { Inter, JetBrains_Mono } from "next/font/google"; -import "./globals.css"; -import { ThemeProvider } from "@/components/theme-provider"; -import { absoluteUrl, siteConfig } from "@/lib/site"; +import type { Metadata, Viewport } from "next" +import { Inter, JetBrains_Mono } from "next/font/google" +import { headers } from "next/headers" +import "./globals.css" +import { ThemeProvider } from "@/components/theme-provider" +import { absoluteUrl, siteConfig } from "@/lib/site" -const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); -const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-mono" }); +const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }) +const mono = JetBrains_Mono({ subsets: ["latin"], variable: "--font-mono" }) export const viewport: Viewport = { width: "device-width", initialScale: 1, colorScheme: "light dark", themeColor: siteConfig.themeColor, -}; +} export const metadata: Metadata = { metadataBase: new URL(siteConfig.url), @@ -70,27 +70,21 @@ export const metadata: Metadata = { "msapplication-TileColor": "#0a0a0a", "og:see_also": absoluteUrl("/sitemap.xml"), }, -}; +} export default async function RootLayout({ children, }: Readonly<{ - children: React.ReactNode; + children: React.ReactNode }>) { - const nonce = (await headers()).get("x-nonce") ?? undefined; + const nonce = (await headers()).get("x-nonce") ?? undefined return ( - + {children} - ); + ) } diff --git a/site/app/llms.txt/route.ts b/site/app/llms.txt/route.ts index 0bb9982..ad15a71 100644 --- a/site/app/llms.txt/route.ts +++ b/site/app/llms.txt/route.ts @@ -1,6 +1,6 @@ -import { absoluteUrl, siteConfig } from "@/lib/site"; +import { absoluteUrl, siteConfig } from "@/lib/site" -export const dynamic = "force-static"; +export const dynamic = "force-static" export function GET() { const body = `# ${siteConfig.name} @@ -55,12 +55,12 @@ npx create-agent-starter@latest --agent codex,cursor --skills copywriting-framew ## Licensing Code is MIT licensed. The package is not affiliated with Anthropic, Apple, OpenAI, Cursor, or @toon-format/toon. -`; +` return new Response(body, { headers: { "Content-Type": "text/plain; charset=utf-8", "Cache-Control": "public, max-age=3600, s-maxage=86400", }, - }); + }) } diff --git a/site/app/manifest.ts b/site/app/manifest.ts index 711a391..c6daa8e 100644 --- a/site/app/manifest.ts +++ b/site/app/manifest.ts @@ -1,5 +1,5 @@ -import type { MetadataRoute } from "next"; -import { siteConfig } from "@/lib/site"; +import type { MetadataRoute } from "next" +import { siteConfig } from "@/lib/site" export default function manifest(): MetadataRoute.Manifest { return { @@ -26,5 +26,5 @@ export default function manifest(): MetadataRoute.Manifest { purpose: "maskable", }, ], - }; + } } diff --git a/site/app/not-found.tsx b/site/app/not-found.tsx index 7beda0e..aa4ac6b 100644 --- a/site/app/not-found.tsx +++ b/site/app/not-found.tsx @@ -1,6 +1,6 @@ -import type { Metadata } from "next"; -import Link from "next/link"; -import { siteConfig } from "@/lib/site"; +import type { Metadata } from "next" +import Link from "next/link" +import { siteConfig } from "@/lib/site" export const metadata: Metadata = { title: `Not found - ${siteConfig.name}`, @@ -8,7 +8,7 @@ export const metadata: Metadata = { index: false, follow: false, }, -}; +} export default function NotFound() { return ( @@ -16,9 +16,7 @@ export default function NotFound() {

404

Page not found

-

- The requested page does not exist on {siteConfig.name}. -

+

The requested page does not exist on {siteConfig.name}.

- ); + ) } diff --git a/site/app/opengraph-image.tsx b/site/app/opengraph-image.tsx index 1c5cc86..5e4f82c 100644 --- a/site/app/opengraph-image.tsx +++ b/site/app/opengraph-image.tsx @@ -1,102 +1,100 @@ -import { ImageResponse } from "next/og"; -import { siteConfig } from "@/lib/site"; +import { ImageResponse } from "next/og" +import { siteConfig } from "@/lib/site" -export const alt = "agent-starter — agent.json, skills, and MCPs for Claude Code, Codex, and Cursor"; +export const alt = "agent-starter — agent.json, skills, and MCPs for Claude Code, Codex, and Cursor" export const size = { width: 1200, height: 630, -}; -export const contentType = "image/png"; +} +export const contentType = "image/png" export default function OpenGraphImage() { return new ImageResponse( - ( +
-
- A -
- {siteConfig.name} -
- -
-
- One agent.json. - Three agent targets. -
-
- Skills, MCP servers, and stack profiles, synced to Claude Code, Codex, and Cursor. -
+ A
+ {siteConfig.name} +
+
+ One agent.json. + Three agent targets. +
+
- .claude/ - .codex/ - .cursor/rules/ + Skills, MCP servers, and stack profiles, synced to Claude Code, Codex, and Cursor.
- ), + +
+ .claude/ + .codex/ + .cursor/rules/ +
+
, size, - ); + ) } diff --git a/site/app/page.tsx b/site/app/page.tsx index 93c1549..380f467 100644 --- a/site/app/page.tsx +++ b/site/app/page.tsx @@ -1,17 +1,17 @@ -import Link from "next/link"; -import { headers } from "next/headers"; -import { connection } from "next/server"; -import { Github, ArrowRight } from "lucide-react"; -import { ThemeToggle } from "@/components/theme-toggle"; -import { GitHubStats } from "@/components/github-stats"; -import { absoluteUrl, siteConfig } from "@/lib/site"; +import { ArrowRight, Github } from "lucide-react" +import { headers } from "next/headers" +import Link from "next/link" +import { connection } from "next/server" +import { GitHubStats } from "@/components/github-stats" +import { ThemeToggle } from "@/components/theme-toggle" +import { absoluteUrl, siteConfig } from "@/lib/site" -const REPO = siteConfig.repoUrl; -const DOCPULL = siteConfig.docpullUrl; +const REPO = siteConfig.repoUrl +const DOCPULL = siteConfig.docpullUrl export default async function Home() { - await connection(); - const nonce = (await headers()).get("x-nonce") ?? undefined; + await connection() + const nonce = (await headers()).get("x-nonce") ?? undefined return (
@@ -34,7 +34,7 @@ export default async function Home() {
- ); + ) } /* ---------------- Header ---------------- */ @@ -48,7 +48,10 @@ function Header() { agent-starter
-
- ); + ) } function LogoMark() { @@ -98,7 +101,7 @@ function LogoMark() { - ); + ) } /* ---------------- Hero ---------------- */ @@ -111,23 +114,24 @@ function Hero() { "██╔══██║██║ ██║██╔══╝ ██║╚██╗██║ ██║ ", "██║ ██║╚██████╔╝███████╗██║ ╚████║ ██║ ", "╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═╝ ", - ].join("\n"); + ].join("\n") return (

- One agent.json.
+ One agent.json. +
Three agent targets.

The package.json for agent environments. Declare skills, MCP servers, and a stack profile in{" "} agent.json;{" "} - sync{" "} - writes native config for{" "} + sync writes + native config for{" "} .claude/,{" "} .codex/, and{" "} .cursor/. @@ -150,7 +154,7 @@ function Hero() {

- ); + ) } /* ---------------- agent.json manifest ---------------- */ @@ -167,26 +171,25 @@ function Manifest() { ` { "name": "stripe", "command": "npx", "args": ["-y", "@stripe/mcp"] }`, ` ]`, `}`, - ].join("\n"); + ].join("\n") const commands = [ { code: "npx agent-starter sync", note: "agent.json → native config for every target" }, { code: "npx agent-starter status", note: "diff manifest vs configs; exits 1 on drift" }, { code: "npx agent-starter add mcp neon", note: "catalog: github, neon, stripe, resend, posthog" }, { code: "npx agent-starter add skill cleanup-types", note: "add a shipped skill and re-sync" }, - ]; + ] return (
agent.json

- One declarative manifest for the whole agent environment. Check it into git; every - contributor runs{" "} - npx agent-starter sync{" "} - and gets identical skills and MCP servers in whichever agent they use. Sync is - idempotent — generated sections are fenced with markers, manual edits outside them - survive, and MCP entries agent-starter didn't write are never touched. Secrets stay{" "} - {"${VAR}"}{" "} - references, resolved by each agent at runtime. + One declarative manifest for the whole agent environment. Check it into git; every contributor runs{" "} + npx agent-starter sync and + gets identical skills and MCP servers in whichever agent they use. Sync is idempotent — generated sections are + fenced with markers, manual edits outside them survive, and MCP entries agent-starter didn't write are + never touched. Secrets stay{" "} + {"${VAR}"} references, + resolved by each agent at runtime.

           {manifest}
@@ -204,41 +207,37 @@ function Manifest() {
         
       
- ); + ) } /* ---------------- Agent targets ---------------- */ function Inside() { const rows: Array<{ path: string; note?: string; dim?: boolean }> = [ - { path: "agent.json", note: "the manifest — profile, targets, skills, MCPs" }, - { path: ".claude/ + .mcp.json", note: "Claude Code skills, commands, settings, MCP servers" }, - { path: ".codex/ + AGENTS.md", note: "Codex skill files, config.toml MCPs, project guidance" }, + { path: "agent.json", note: "the manifest — profile, targets, skills, MCPs" }, + { path: ".claude/ + .mcp.json", note: "Claude Code skills, commands, settings, MCP servers" }, + { path: ".codex/ + AGENTS.md", note: "Codex skill files, config.toml MCPs, project guidance" }, { path: ".cursor/rules/ + .cursor/mcp.json", note: "Cursor project rules and MCP servers" }, - { path: "├── agent-starter.mdc", dim: true }, + { path: "├── agent-starter.mdc", dim: true }, { path: "├── hig-foundations.mdc", dim: true }, { path: "├── copywriting-frameworks.mdc", dim: true }, - { path: "└── ...", dim: true }, - ]; + { path: "└── ...", dim: true }, + ] return (
Agent targets
-          {rows.map((r, i) => (
-            
+ {rows.map((r) => ( +
{r.path} - {r.note && ( - - {r.note} - - )} + {r.note && {r.note}}
))}
- ); + ) } /* ---------------- Profiles ---------------- */ @@ -269,20 +268,19 @@ function Profiles() { skills: "cleanup-unused", mcps: "none", }, - ]; + ] return (
Stack profiles

- A stack profile bundles the skills and MCP servers a project type needs, so - setup is one command instead of a scavenger hunt for server packages.{" "} - init{" "} - auto-detects the right one from{" "} - package.json. - Skill-set profiles (all,{" "} - apple-hig, design-hci,{" "} - minimal) still exist for picking skills without MCPs. + A stack profile bundles the skills and MCP servers a project type needs, so setup is one command + instead of a scavenger hunt for server packages.{" "} + init auto-detects the right + one from package.json. + Skill-set profiles (all, apple-hig,{" "} + design-hci, minimal) still exist for + picking skills without MCPs.

@@ -308,98 +306,188 @@ function Profiles() {

- next-saas{" "} - is the flagship. It ships the{" "} - finish-setup{" "} - skill: after scaffolding, open your agent and say “finish setup” — it creates - Stripe products matching your billing plans, verifies database migrations, and walks - email-domain DNS through the wired MCPs. + next-saas is the flagship. It + ships the finish-setup skill: + after scaffolding, open your agent and say “finish setup” — it creates Stripe products matching + your billing plans, verifies database migrations, and walks email-domain DNS through the wired MCPs.

- ); + ) } /* ---------------- Skills ---------------- */ -type Skill = { name: string; blurb: string }; +type Skill = { name: string; blurb: string } function Skills() { const groups: { group: string; items: Skill[] }[] = [ { group: "Design / HCI", items: [ - { name: "human-processor-model", blurb: "Models perceptual, cognitive, motor, and memory costs to estimate task time and reveal usability bottlenecks." }, - { name: "goms-klm-analysis", blurb: "Compares task flows with GOMS/KLM operators, waits, selection rules, and expert-user interaction cost." }, + { + name: "human-processor-model", + blurb: + "Models perceptual, cognitive, motor, and memory costs to estimate task time and reveal usability bottlenecks.", + }, + { + name: "goms-klm-analysis", + blurb: + "Compares task flows with GOMS/KLM operators, waits, selection rules, and expert-user interaction cost.", + }, ], }, { group: "Apple HIG", items: [ - { name: "hig-doctor-audit", blurb: "Runs HIG Doctor audits with npx hig-doctor, exported reports, severity gates, and category-to-skill routing." }, - { name: "hig-project-context", blurb: "Creates shared Apple design context so other HIG skills can tailor guidance without repeated setup questions." }, - { name: "hig-foundations", blurb: "Color, typography, SF Symbols, dark mode, accessibility, layout, materials, motion, privacy, and writing." }, - { name: "hig-platforms", blurb: "Platform-specific design guidance for iOS, iPadOS, macOS, tvOS, visionOS, watchOS, and games." }, - { name: "hig-patterns", blurb: "UX patterns for onboarding, launch, loading, permissions, feedback, undo, settings, sharing, and collaboration." }, - { name: "hig-inputs", blurb: "Gestures, keyboards, pointers, Apple Pencil, Digital Crown, focus, remotes, eye tracking, and spatial input." }, - { name: "hig-technologies", blurb: "Siri, Apple Pay, HealthKit, ARKit, iCloud, Sign in with Apple, SharePlay, Wallet, VoiceOver, and more." }, - { name: "hig-components-content", blurb: "Charts, collections, image views, web views, color wells, image wells, lockups, and share sheets." }, - { name: "hig-components-layout", blurb: "Sidebars, split views, tab bars, scroll views, windows, panels, lists, tables, and ornaments." }, - { name: "hig-components-menus", blurb: "Buttons, context menus, toolbars, the menu bar, pop-up buttons, pull-downs, and disclosure controls." }, - { name: "hig-components-search", blurb: "Search fields, page controls, path controls, search scopes, suggestions, and pagination." }, - { name: "hig-components-dialogs", blurb: "Alerts, action sheets, popovers, sheets, destructive confirmations, and digit entry views." }, - { name: "hig-components-controls", blurb: "Pickers, toggles, sliders, steppers, segmented controls, text fields, labels, and validation." }, - { name: "hig-components-status", blurb: "Progress indicators, loading states, status bars, determinate progress, and activity rings." }, - { name: "hig-components-system", blurb: "Widgets, Live Activities, notifications, complications, App Clips, shortcuts, and watch faces." }, + { + name: "hig-doctor-audit", + blurb: + "Runs HIG Doctor audits with npx hig-doctor, exported reports, severity gates, and category-to-skill routing.", + }, + { + name: "hig-project-context", + blurb: + "Creates shared Apple design context so other HIG skills can tailor guidance without repeated setup questions.", + }, + { + name: "hig-foundations", + blurb: + "Color, typography, SF Symbols, dark mode, accessibility, layout, materials, motion, privacy, and writing.", + }, + { + name: "hig-platforms", + blurb: "Platform-specific design guidance for iOS, iPadOS, macOS, tvOS, visionOS, watchOS, and games.", + }, + { + name: "hig-patterns", + blurb: + "UX patterns for onboarding, launch, loading, permissions, feedback, undo, settings, sharing, and collaboration.", + }, + { + name: "hig-inputs", + blurb: + "Gestures, keyboards, pointers, Apple Pencil, Digital Crown, focus, remotes, eye tracking, and spatial input.", + }, + { + name: "hig-technologies", + blurb: + "Siri, Apple Pay, HealthKit, ARKit, iCloud, Sign in with Apple, SharePlay, Wallet, VoiceOver, and more.", + }, + { + name: "hig-components-content", + blurb: "Charts, collections, image views, web views, color wells, image wells, lockups, and share sheets.", + }, + { + name: "hig-components-layout", + blurb: "Sidebars, split views, tab bars, scroll views, windows, panels, lists, tables, and ornaments.", + }, + { + name: "hig-components-menus", + blurb: "Buttons, context menus, toolbars, the menu bar, pop-up buttons, pull-downs, and disclosure controls.", + }, + { + name: "hig-components-search", + blurb: "Search fields, page controls, path controls, search scopes, suggestions, and pagination.", + }, + { + name: "hig-components-dialogs", + blurb: "Alerts, action sheets, popovers, sheets, destructive confirmations, and digit entry views.", + }, + { + name: "hig-components-controls", + blurb: "Pickers, toggles, sliders, steppers, segmented controls, text fields, labels, and validation.", + }, + { + name: "hig-components-status", + blurb: "Progress indicators, loading states, status bars, determinate progress, and activity rings.", + }, + { + name: "hig-components-system", + blurb: "Widgets, Live Activities, notifications, complications, App Clips, shortcuts, and watch faces.", + }, ], }, { group: "Growth", items: [ - { name: "copywriting-frameworks", blurb: "Direct-response workflows for headlines, ads, landing pages, emails, CTAs, objections, and critiques." }, + { + name: "copywriting-frameworks", + blurb: + "Direct-response workflows for headlines, ads, landing pages, emails, CTAs, objections, and critiques.", + }, ], }, { group: "Workflow", items: [ - { name: "finish-setup", blurb: "Provisions a freshly scaffolded SaaS project through the wired MCPs: Stripe products, database migrations, email DNS, analytics, GitHub." }, + { + name: "finish-setup", + blurb: + "Provisions a freshly scaffolded SaaS project through the wired MCPs: Stripe products, database migrations, email DNS, analytics, GitHub.", + }, ], }, { group: "Utilities", - items: [ - { name: "toon-formatter", blurb: "When to reach for TOON, when not. Wraps @toon-format/toon." }, - ], + items: [{ name: "toon-formatter", blurb: "When to reach for TOON, when not. Wraps @toon-format/toon." }], }, { group: "Cleanup", items: [ - { name: "cleanup-all", blurb: "Runs the ordered cleanup pipeline across unused code, cycles, dedupe, types, defensive code, legacy paths, and comments." }, - { name: "cleanup-unused", blurb: "Finds high-confidence dead code, exports, files, and dependencies before applying verified removals." }, - { name: "cleanup-cycles", blurb: "Finds circular dependencies, traces import paths, and plans low-risk untangling work." }, - { name: "cleanup-dedupe", blurb: "Identifies duplicated logic and extracts shared helpers only when the reuse is clear." }, - { name: "cleanup-types", blurb: "Consolidates duplicated or fragmented type definitions into maintainable shared shapes." }, - { name: "cleanup-weak-types", blurb: "Replaces weak types with stronger inferred, validated, or locally appropriate types." }, - { name: "cleanup-defensive", blurb: "Removes pointless guards and catch blocks that hide errors instead of handling them." }, - { name: "cleanup-legacy", blurb: "Removes zero-caller deprecated paths, fallbacks, and compatibility code after verification." }, - { name: "cleanup-slop", blurb: "Removes AI narration and restated-code comments while preserving useful WHY comments." }, + { + name: "cleanup-all", + blurb: + "Runs the ordered cleanup pipeline across unused code, cycles, dedupe, types, defensive code, legacy paths, and comments.", + }, + { + name: "cleanup-unused", + blurb: "Finds high-confidence dead code, exports, files, and dependencies before applying verified removals.", + }, + { + name: "cleanup-cycles", + blurb: "Finds circular dependencies, traces import paths, and plans low-risk untangling work.", + }, + { + name: "cleanup-dedupe", + blurb: "Identifies duplicated logic and extracts shared helpers only when the reuse is clear.", + }, + { + name: "cleanup-types", + blurb: "Consolidates duplicated or fragmented type definitions into maintainable shared shapes.", + }, + { + name: "cleanup-weak-types", + blurb: "Replaces weak types with stronger inferred, validated, or locally appropriate types.", + }, + { + name: "cleanup-defensive", + blurb: "Removes pointless guards and catch blocks that hide errors instead of handling them.", + }, + { + name: "cleanup-legacy", + blurb: "Removes zero-caller deprecated paths, fallbacks, and compatibility code after verification.", + }, + { + name: "cleanup-slop", + blurb: "Removes AI narration and restated-code comments while preserving useful WHY comments.", + }, ], }, - ]; - const skillCount = groups.reduce((total, group) => total + group.items.length, 0); + ] + const skillCount = groups.reduce((total, group) => total + group.items.length, 0) return (
Skills

- {skillCount}{" "}hand-maintained skills generated into each agent's native project shape. Claude gets skills and slash commands, Codex gets `AGENTS.md` plus local `SKILL.md` files, and Cursor gets `.mdc` project rules. + {skillCount} hand-maintained skills generated into each agent's native project shape. Claude gets skills + and slash commands, Codex gets `AGENTS.md` plus local `SKILL.md` files, and Cursor gets `.mdc` project rules.

{groups.map((g) => (
-

- {g.group} -

+

{g.group}

{g.items.map((it) => (
@@ -416,41 +504,40 @@ function Skills() {
- ); + ) } function SkillLogo({ item }: { item: Skill }) { - const letter = item.name.charAt(0).toUpperCase(); + const letter = item.name.charAt(0).toUpperCase() return ( {letter} - ); + ) } /* ---------------- Benchmarks ---------------- */ -type Row = { workload: string; jsonTokens: number; toonTokens: number; savings: string }; +type Row = { workload: string; jsonTokens: number; toonTokens: number; savings: string } function Benchmarks() { const rows: Row[] = [ - { workload: "API response (50 users)", jsonTokens: 4133, toonTokens: 2128, savings: "48.5%" }, - { workload: "DB transactions (100 rows)", jsonTokens: 5708, toonTokens: 2252, savings: "60.5%" }, - { workload: "Logs (200 events)", jsonTokens: 13052, toonTokens: 6266, savings: "52.0%" }, - { workload: "Metrics (288 points)", jsonTokens: 13537, toonTokens: 4622, savings: "65.9%" }, - { workload: "Irregular nested", jsonTokens: 135, toonTokens: 80, savings: "40.7%" }, - { workload: "Small array (3 items)", jsonTokens: 62, toonTokens: 27, savings: "56.5%" }, - ]; + { workload: "API response (50 users)", jsonTokens: 4133, toonTokens: 2128, savings: "48.5%" }, + { workload: "DB transactions (100 rows)", jsonTokens: 5708, toonTokens: 2252, savings: "60.5%" }, + { workload: "Logs (200 events)", jsonTokens: 13052, toonTokens: 6266, savings: "52.0%" }, + { workload: "Metrics (288 points)", jsonTokens: 13537, toonTokens: 4622, savings: "65.9%" }, + { workload: "Irregular nested", jsonTokens: 135, toonTokens: 80, savings: "40.7%" }, + { workload: "Small array (3 items)", jsonTokens: 62, toonTokens: 27, savings: "56.5%" }, + ] return (
Measured savings

- Real token counts from{" "} - gpt-tokenizer{" "} - (OpenAI BPE — directional proxy for Claude's tokenizer). Run{" "} - node bench/run.mjs{" "} - locally to reproduce. Workloads are seeded and deterministic. + Real token counts from gpt-tokenizer (OpenAI + BPE — directional proxy for Claude's tokenizer). Run{" "} + node bench/run.mjs locally to reproduce. + Workloads are seeded and deterministic.

@@ -483,16 +570,20 @@ function Benchmarks() {

Full methodology + raw data:{" "} - + bench/RESULTS.md . For exact Claude token counts, use Claude's{" "} - /v1/messages/count_tokens{" "} - endpoint. + /v1/messages/count_tokens endpoint.

- ); + ) } /* ---------------- Install ---------------- */ @@ -503,15 +594,27 @@ function Install() {
Install
    - + - - + +
- ); + ) } function Snippet({ step, code, hint }: { step: string; code: string; hint?: string }) { @@ -524,7 +627,7 @@ function Snippet({ step, code, hint }: { step: string; code: string; hint?: stri {hint && {hint}} - ); + ) } /* ---------------- Footer ---------------- */ @@ -551,13 +654,18 @@ function Footer() { security.txt - + Raintree - ); + ) } /* ---------------- primitives ---------------- */ @@ -568,11 +676,11 @@ function SectionLabel({ children }: { children: React.ReactNode }) { {children} - ); + ) } function StructuredData({ nonce }: { nonce?: string }) { - const pageUrl = absoluteUrl("/"); + const pageUrl = absoluteUrl("/") const schema = { "@context": "https://schema.org", "@graph": [ @@ -634,7 +742,7 @@ function StructuredData({ nonce }: { nonce?: string }) { ], }, ], - }; + } return (