Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Commands

```bash
# Development
pnpm dev # Start dev server with Turbopack
pnpm build # Build all packages
pnpm type-check # Run TypeScript checks

# Run commands for specific app
pnpm --filter @onruntime/web dev
pnpm --filter @onruntime/web build
```

## Architecture

### Monorepo Structure (Turborepo + pnpm workspaces)

```
/
├── apps/
│ └── web/ # Next.js 16 website (@onruntime/web)
├── packages/ # Shared packages (future)
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation states that there is a packages/ directory for shared packages with the comment "(future)", but this directory does not currently exist in the repository. This could be misleading to someone using this documentation. Consider either removing this line entirely or making it clearer that this is a planned directory structure that doesn't exist yet (e.g., "packages/ (planned)" or removing it until the directory is actually created).

Suggested change
├── packages/ # Shared packages (future)
├── packages/ # Shared packages (planned, directory not yet created)

Copilot uses AI. Check for mistakes.
├── turbo.json # Turborepo configuration
└── pnpm-workspace.yaml # pnpm workspace config
```
Comment on lines +22 to +29
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language specifier to the directory tree code block.

The fenced code block showing the monorepo structure is missing a language identifier, which markdown linters require for best practices.

🔎 Proposed fix
-```
+```text
 /
 ├── apps/
 │   └── web/              # Next.js 16 website (@onruntime/web)
 ├── packages/             # Shared packages (future)
 ├── turbo.json            # Turborepo configuration
 └── pnpm-workspace.yaml   # pnpm workspace config
-```
+```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
/
├── apps/
│ └── web/ # Next.js 16 website (@onruntime/web)
├── packages/ # Shared packages (future)
├── turbo.json # Turborepo configuration
└── pnpm-workspace.yaml # pnpm workspace config
```
🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

22-22: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
In CLAUDE.md around lines 22 to 29 the fenced code block showing the directory
tree lacks a language specifier; update the opening fence to include a language
tag such as "text" (i.e. change ``` to ```text) so markdown linters recognize
it, leaving the block content unchanged and keeping the closing ``` as is.


### Web App Structure (`apps/web/src/`)

- **app/**: Next.js App Router pages and API routes
- **components/**: React components (ui/, layout/, marketing/)
- **services/**: External API clients with lazy initialization
- **constants/**: Static data (projects, agencies, services, team members)
- **content/**: MDX content (glossary, legal pages)
- **lib/**: Utilities and helpers
- **types/**: TypeScript type definitions
Comment on lines +33 to +39
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Web App Structure section lists several directories but omits some that exist in the actual codebase (such as hooks/, logos/, screens/, and styles/). While not every directory needs to be documented, consider whether hooks/ and screens/ should be included for completeness, as they may contain important patterns for developers to understand.

Copilot uses AI. Check for mistakes.

### Key Patterns

**Services**: Use lazy initialization for external API clients to avoid build failures when env vars are missing in CI:
```typescript
// services/email.ts - Proxy pattern for lazy init
export const resend = new Proxy({} as Resend, {
get(_, prop) {
if (!instance) {
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code example shows new Resend(env.RESEND_API_KEY) but the actual implementation in apps/web/src/services/email.ts includes additional error handling that checks if the API key exists before creating the Resend instance. The example would be more accurate if it included the error handling or added a comment noting that error handling is omitted for brevity.

Suggested change
if (!instance) {
if (!instance) {
// Note: The real implementation checks that env.RESEND_API_KEY is defined
// before creating the Resend instance; this example omits that error handling
// for brevity.

Copilot uses AI. Check for mistakes.
instance = new Resend(env.RESEND_API_KEY);
}
return instance[prop as keyof Resend];
},
});

// services/join.ts - Client pattern
export const joinClient = {
async jobs() { ... },
async job(id) { ... },
};
```

**Environment Variables**: Managed via `@t3-oss/env-nextjs` in `env.ts`. Validation is skipped in CI (`skipValidation: !!process.env.CI`).

**API Routes**: Use `unstable_cache` from Next.js for caching external API responses.

## Environment Variables

Required for runtime (optional for build):
- `RESEND_API_KEY`: Email sending via Resend
- `JOIN_API_KEY`: Job listings from join.com API
File renamed without changes.
44 changes: 0 additions & 44 deletions apps/web/next.config.mjs

This file was deleted.

44 changes: 44 additions & 0 deletions apps/web/next.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import createMDX from "@next/mdx";
import type { NextConfig } from "next";
import "./env";

const nextConfig: NextConfig = {
pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"],
reactStrictMode: process.env.NODE_ENV === "development",
transpilePackages: ["next-seo"],
env: {
NEXT_PUBLIC_NODE_ENV: process.env.NODE_ENV,
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/i,
use: ["@svgr/webpack"],
});
return config;
},
async redirects() {
return [
{
source: "/projects/instagram-dark/welcome",
destination: "/projects/dark-theme-instagram",
permanent: true,
},
{
source: "/projects/instagram-dark",
destination: "/projects/dark-theme-instagram",
permanent: true,
},
{
source: "/about",
destination: "/npo",
permanent: false,
},
];
},
};

const withMDX = createMDX({
extension: /\.mdx?$/,
});

export default withMDX(nextConfig);
2 changes: 1 addition & 1 deletion apps/web/src/app/careers/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { constructMetadata } from "@/lib/utils/metadata";
import JobDetailPage from "@/screens/marketing/careers/job-details";
import { JobPosting } from "@/types/job";
import { Metadata } from "next";
import { env } from "env.mjs";
import { env } from "env";

async function getJobById(id: string): Promise<JobPosting | null> {
try {
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/services/email.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { env } from "env.mjs";
import { env } from "env";
import { Resend } from "resend";

let instance: Resend | null = null;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/services/join.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from "zod";
import { env } from "env.mjs";
import { env } from "env";

const JOIN_API_BASE_URL = "https://api.join.com/v2";

Expand Down