mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-24 17:03:55 +10:00
Squashed commit of the following:
commit b2b0470a1d9267d042ec0ac66523c6635bf5b199
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 13:13:38 2026 +0200
chore: update .gitignore to include .vite-hooks and modify pnpm-lock.yaml for dependencies
commit d28fadb5cd8706c874e616102878b4a394ec84c1
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 13:08:04 2026 +0200
fix: remove timestamp conflict guard
commit c6998d9dbab19d09d3c8054feef1d2e4117555eb
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 12:11:51 2026 +0200
chore(release): v5.1.5
commit f33d168711804880e1f12e88d24290aae16cc258
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 11:58:35 2026 +0200
revert: compose.yml
commit d961e6535811a10c335525fb33a08d03e737278d
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 11:58:08 2026 +0200
refactor(agent): replace 'revert' terminology with 'restore' for clarity, resolves #3086
commit 17f351171be218e33f01c469d95e4164d4c8dc57
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 11:10:41 2026 +0200
refactor(pdf): simplify sidebar section filtering and update summary feature logic
commit d55179b9d76879e3204de185e8b53fadd0a107ed
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 09:53:37 2026 +0200
chore: update pnpm-lock.yaml and turbo.json
commit 7cade6980e1a04352536bd44ef773f338c4ef599
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 09:38:30 2026 +0200
fix(polyfill): add tested polyfill for Map Upsert methods
commit 26d175bb9c53d93225d1e907678445252c13d660
Merge: 1cf33dc6c 5b1297fa2
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 09:23:29 2026 +0200
Merge remote-tracking branch 'origin/main' into feat/explore-hono-orpc-migration
# Conflicts:
# packages/api/src/services/agent-url.ts
# packages/runtime-externals/package.json
commit 1cf33dc6c9d81735730ad656e16dab6501c6d6a1
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Tue May 19 09:22:12 2026 +0200
chore: preserve branch changes before main sync
commit b380a4b00fdbcdd81ff4f8ef72b330fd027ccda5
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Mon May 18 07:50:28 2026 +0200
chore: lot of fixes for monorepo migration
commit 8fcf0ec64e1c29572ebaff494338368bfcf75760
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 13:57:17 2026 +0200
chore: update knip version and refine web app routing with new SEO endpoints
commit 234e68086ff15610a93877354c98e2c020364533
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 12:10:06 2026 +0200
refactor(auth): update OAuth routes to include API prefix and remove unused schema endpoint
commit 91c84b9a8496b0ce21d71cae9f8b2a027638c9ac
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:54:29 2026 +0200
chore: update dependencies and enhance PWA metadata in web app
commit 150117d4a5a9dd6cd92c64891aad8cae90f6a7af
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:12:35 2026 +0200
docs: revise manifest-only pwa testing scope
commit 6b939a55661aec9dd8122b184e4b60a5c7325fb5
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:11:33 2026 +0200
docs: add manifest-only pwa design
commit 1422e1fc96c400948b273210a1067251087d15d4
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:05:04 2026 +0200
chore(dev): simplify server proxy config
commit bc2ff5a9f6fda41e6c40333c8f163aa23a6c5e48
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:04:50 2026 +0200
docs: add unsafe oauth redirect plan
commit 445359ebe9b96c1515bf1c4c3f73ba8a8448ec12
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 11:04:34 2026 +0200
feat(auth): add unsafe oauth redirect flag
commit 73fffdd24598e56b2793f7657919bc794835892e
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 10:55:02 2026 +0200
docs: design unsafe oauth redirect flag
commit c0066aa19c15fc8a4c8e5179ed49889c117519f4
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 10:22:04 2026 +0200
chore: update translation source paths
commit 9033da082418d252aafd6c2eed72f71f014be3d9
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 10:09:25 2026 +0200
refactor(arch): react spa + hono migration
commit 6f27936c11bda895977dc63ee550c3346d4ce24b
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Fri May 15 01:10:47 2026 +0200
docs: add docker nightly tagging design
commit ecc1fd9a88a0ee1dca2f1977dfc17f74527fe1da
Author: Amruth Pillai <im.amruth@gmail.com>
Date: Thu May 14 20:05:44 2026 +0200
feat: migrate to hono spa server
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
# ADR 0001: Workspace Boundaries
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
Reactive Resume had package and app code arranged mostly by technical layer. Some server runtime code imported files from the web app source tree, resume-domain behavior lived in generic utilities, API implementation was split across package-root routers/services/helpers, and browser PDF preview code sat near React PDF generation code.
|
||||
|
||||
That made debugging harder because a feature's route, service, domain behavior, tests, and runtime adapter could be spread across unrelated folders. It also made package boundaries implicit, so regressions such as app-to-app source imports were easy to reintroduce.
|
||||
|
||||
## Decision
|
||||
|
||||
Use a domain-first monorepo structure with executable boundaries.
|
||||
|
||||
- Keep deployable apps in `apps/web` and `apps/server`.
|
||||
- Keep runtime and domain capabilities in focused internal packages.
|
||||
- Source-consume internal packages through package export maps.
|
||||
- Forbid cross-workspace private `src` imports and repository-path imports.
|
||||
- Use `packages/api/src/features/*` for API features instead of root technical-layer folders.
|
||||
- Use explicit browser/server package subpaths for runtime-specific code.
|
||||
- Enforce package direction with `turbo boundaries`.
|
||||
- Enforce source-path import rules with Biome `noRestrictedImports` and the local GritQL plugin in `tooling/grit/no-cross-workspace-src-imports.grit`.
|
||||
|
||||
## Consequences
|
||||
|
||||
New code needs an owner before it gets a folder. That adds a little up-front friction, but it makes debugging paths predictable.
|
||||
|
||||
Feature-owned API modules can still share code, but shared code needs a named capability and an intentional package export.
|
||||
|
||||
The Turbo tag set is coarse by design. It blocks the current high-risk edges first: package-to-app imports, server-to-browser runtime imports, and universal/domain packages depending on server/app layers. More granular rules can be added as package roles settle.
|
||||
|
||||
The root shared Vitest config remains an intentionally ignored boundary edge for now. Moving it behind a package export is a separate test-infrastructure cleanup.
|
||||
|
||||
## Rejected Alternatives
|
||||
|
||||
Keep the old technical-layer API layout: rejected because it kept feature behavior split across routers, services, and helpers.
|
||||
|
||||
Move every web feature into packages: rejected because route-owned UI and browser-only behavior are easier to evolve inside the web app until they are genuinely reusable.
|
||||
|
||||
Put PDF.js viewer code in `packages/pdf`: rejected because `packages/pdf` owns React PDF generation, while PDF.js viewer/canvas behavior is browser UI.
|
||||
|
||||
Use documentation-only boundaries: rejected because the previous issue was not lack of intent; it was lack of executable enforcement.
|
||||
@@ -4,6 +4,71 @@ description: "List of all notable changes and updates to Reactive Resume"
|
||||
rss: true
|
||||
---
|
||||
|
||||
<Update label="v5.1.5" description="19th May 2026">
|
||||
<Note>
|
||||
**Self-hosters, please review your environment before upgrading.** The production image now runs a dedicated Hono
|
||||
server from `apps/server/dist/index.mjs`. Remove `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`,
|
||||
`CLOUDFLARE_ACCOUNT_ID`, and `CLOUDFLARE_API_TOKEN` from your environment if they are still set.
|
||||
|
||||
Local development now uses `PORT=3000` for Vite and `SERVER_PORT=3001` for the Hono server. This should not affect
|
||||
production deployments.
|
||||
</Note>
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Dedicated Hono server runtime.** Reactive Resume now builds a separate `apps/server` app that mounts auth, RPC, MCP, OpenAPI, uploads, schema JSON, SEO endpoints, health checks, and the built web app from one Node.js process. [ecc1fd9a8](https://github.com/amruthpillai/reactive-resume/commit/ecc1fd9a8), [9033da082](https://github.com/amruthpillai/reactive-resume/commit/9033da082)
|
||||
- **Clearer self-hosting runtime model.** The Docker image now builds both `web` and `server`, runs `node apps/server/dist/index.mjs`, and keeps `/api/health` pointed at the production server port. [ecc1fd9a8](https://github.com/amruthpillai/reactive-resume/commit/ecc1fd9a8)
|
||||
- **Safer Agent restore behavior.** Agent edits now store a resume snapshot before applying a patch, so restoring an action can roll the draft back to the exact prior state and mark later agent patches as rolled back. [d961e6535](https://github.com/amruthpillai/reactive-resume/commit/d961e6535)
|
||||
|
||||
## Self-Hosting & Environment
|
||||
|
||||
- Added `SERVER_PORT` for local development. Vite serves the web app on `PORT` and proxies API, MCP, upload, well-known, and schema routes to the Hono server on `SERVER_PORT`.
|
||||
- Updated the production Dockerfile to copy `apps/web/dist`, `apps/server/dist`, server package dependencies, and migrations into the runtime image. The production start command is now `node apps/server/dist/index.mjs`.
|
||||
- Updated `compose.yml` to use the published image by default and load app configuration through `.env` instead of embedding the main app environment block inline.
|
||||
- Updated `compose.dev.yml` to expose both `3000` and `3001`, add an app profile, and health-check the Hono server port.
|
||||
- Startup checks now run from the server process, including database migrations and local storage writability validation when S3-compatible storage is not configured.
|
||||
- Removed `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`. Dynamic OAuth client registration now allows the app origin and loopback callbacks by default.
|
||||
- Added `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` for trusted self-hosted deployments that intentionally need arbitrary redirect URIs, including custom schemes, private hosts, or non-loopback `http://` callbacks. Keep this disabled on public or multi-tenant instances. [445359ebe](https://github.com/amruthpillai/reactive-resume/commit/445359ebe)
|
||||
- Removed the documented `BETTER_AUTH_URL` and `BETTER_AUTH_SECRET` override path. Auth metadata, JWKS, and OAuth callback URLs are now derived from `APP_URL` and `AUTH_SECRET`.
|
||||
- Removed Cloudflare URL extraction environment variables. Live Agent web research now depends on the selected AI provider and model supporting native web search.
|
||||
- Renamed the Crowdin token example from `CROWDIN_PERSONAL_TOKEN` to `CROWDIN_API_TOKEN`.
|
||||
|
||||
## App Runtime & Architecture
|
||||
|
||||
- Moved API/auth/MCP/OpenAPI/static route ownership out of the web app and into `apps/server`.
|
||||
- Changed the web app build to a Vite/TanStack Router SPA output under `apps/web/dist`, with the Hono server serving the built app and static fallback responses.
|
||||
- Added `robots.txt`, `sitemap.xml`, `llms.txt`, structured data helpers, and server-owned SEO responses. [8fcf0ec64](https://github.com/amruthpillai/reactive-resume/commit/8fcf0ec64)
|
||||
- Added package-boundary rules to Turborepo and per-workspace `turbo.json` files to enforce browser, server, domain, adapter, and infra ownership.
|
||||
- Split focused domains into new packages: `@reactive-resume/docx`, `@reactive-resume/mcp`, and `@reactive-resume/resume`.
|
||||
- Moved development-only scripts from `packages/scripts` to `tooling` so workspace packages contain app/runtime code rather than private repo tooling.
|
||||
- Reorganized API implementation into feature-owned modules under `packages/api/src/features/*`.
|
||||
|
||||
## AI & Agent Workflows
|
||||
|
||||
- Replaced stored inverse JSON patches with `snapshot_data` on agent actions. Legacy actions without snapshots remain non-restorable.
|
||||
- Added a migration that adds `agent_actions.snapshot_data` and drops `agent_actions.inverse_operations`.
|
||||
- Updated Agent UI and docs from "Revert" language to "Restore" language to clarify that restoring an older action rolls back that action and later applied agent patches.
|
||||
- Updated Agent tool documentation to describe provider-native `web_search` behavior instead of app-owned URL fetching.
|
||||
- Kept unsafe/private AI provider base URLs behind `FLAG_ALLOW_UNSAFE_AI_BASE_URL`, with public HTTPS provider URLs remaining the default safe path.
|
||||
|
||||
## Resume Rendering & Exports
|
||||
|
||||
- Moved browser PDF preview code into `apps/web/src/features/resume/preview` and public resume viewer code into `apps/web/src/features/resume/public`.
|
||||
- Added direct PDF.js canvas preview and thumbnail rendering through legacy PDF.js entrypoints, with tests that prevent browser preview code from importing the modern PDF.js runtime. [7cade6980](https://github.com/amruthpillai/reactive-resume/commit/7cade6980)
|
||||
- Added explicit `@reactive-resume/pdf/browser` and `@reactive-resume/pdf/server` generation adapters.
|
||||
- Simplified shared sidebar summary handling for PDF templates and added focused coverage for featured summary behavior. [17f351171](https://github.com/amruthpillai/reactive-resume/commit/17f351171)
|
||||
|
||||
## Docs & Maintenance
|
||||
|
||||
- Added new use-case docs for free, open-source, self-hosted, privacy-focused, export/share, AI, and API/MCP resume workflows.
|
||||
- Rewrote contributor architecture docs around the new monorepo runtime, package ownership model, and boundary checks.
|
||||
- Updated self-hosting Docker and SSO docs for the Hono runtime, removed environment variables, OAuth redirect safety, provider-native Agent web research, and local development ports.
|
||||
- Added and updated architecture notes, plans, and specs for the Hono migration, monorepo reorganization, Docker tagging, manifest-only PWA behavior, unsafe OAuth redirect policy, and Agent snapshot restore design.
|
||||
- Updated Knip configuration so server runtime dependencies that are imported by the built server bundle are treated as intentional dependencies.
|
||||
|
||||
**Full Changelog**: [v5.1.4...v5.1.5](https://github.com/amruthpillai/reactive-resume/compare/v5.1.4...v5.1.5)
|
||||
</Update>
|
||||
|
||||
<Update label="v5.1.4" description="14th May 2026">
|
||||
<Note>
|
||||
**Self-hosters using AI features:** saved AI providers now require `ENCRYPTION_SECRET`, and the new AI Agent
|
||||
@@ -26,7 +91,7 @@ rss: true
|
||||
## AI & Agent Workflows
|
||||
|
||||
- Added server-side AI provider management with encrypted credentials, provider testing, and provider/model capability checks. This replaces the old local AI store and keeps AI configuration centralized. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
- Added Agent tools for reading resume drafts, fetching public URLs, reading supported attachments, asking follow-up questions, and applying JSON Patch updates to the AI draft. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
- Added Agent tools for reading resume drafts, using provider-native web search when supported, reading supported attachments, asking follow-up questions, and applying JSON Patch updates to the AI draft. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
- Added archive and delete actions for Agent threads, including read-only archived states, in-flight run cleanup when archiving, and ownership checks before destructive deletion. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
- Added attachment upload rate limits, private S3 ACLs for Agent attachments, runtime validation for streamed messages, transactional patch/action writes, and a unique message sequence index for safer Agent runs. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
- Added `FLAG_ALLOW_UNSAFE_AI_BASE_URL` for trusted self-hosted deployments that need private or local AI provider URLs, while still restricting provider URLs to `http` or `https`. Thanks to [@SirSKillz](https://github.com/SirSKillz). [#3059](https://github.com/amruthpillai/reactive-resume/pull/3059)
|
||||
@@ -40,7 +105,7 @@ rss: true
|
||||
## Self-Hosting, Docs & Maintenance
|
||||
|
||||
- Added a development Dockerfile plus improved Compose development services and health checks for running Reactive Resume with local dependencies. [1294d3354](https://github.com/amruthpillai/reactive-resume/commit/1294d3354)
|
||||
- Updated self-hosting documentation for Redis, encrypted AI provider credentials, optional Cloudflare URL extraction, private Agent attachments, S3 path-style storage, and unsafe AI base URL behavior. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062), [#3059](https://github.com/amruthpillai/reactive-resume/pull/3059)
|
||||
- Updated self-hosting documentation for Redis, encrypted AI provider credentials, provider-native web research, private Agent attachments, S3 path-style storage, and unsafe AI base URL behavior. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062), [#3059](https://github.com/amruthpillai/reactive-resume/pull/3059)
|
||||
- Added new and refreshed guides for the AI Agent workspace, Agent tools, AI setup, builder dock, dashboard management, importing, exporting, public sharing, and private notes. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062), [affa1d664](https://github.com/amruthpillai/reactive-resume/commit/affa1d664)
|
||||
- Removed a stale Custom CSS documentation link now that custom CSS is no longer part of the v5.1 renderer flow. [#3056](https://github.com/amruthpillai/reactive-resume/pull/3056)
|
||||
- Added a Reactive Resume design system reference and updated dependencies across the workspace. [#3062](https://github.com/amruthpillai/reactive-resume/pull/3062)
|
||||
|
||||
+100
-278
@@ -1,321 +1,143 @@
|
||||
---
|
||||
title: "Project Architecture"
|
||||
description: "Understand the architecture and codebase structure of Reactive Resume"
|
||||
description: "Understand the Reactive Resume monorepo, runtime boundaries, and package ownership model"
|
||||
---
|
||||
|
||||
This guide provides a comprehensive overview of Reactive Resume's architecture and codebase structure, helping you understand how different parts of the application work together.
|
||||
Reactive Resume is a pnpm/Turborepo monorepo. The product runs as one deployed Node.js process, while the source is split into a full-stack web app, a server adapter, and focused internal packages.
|
||||
|
||||
Internal packages are source-consumed through their `package.json` export maps. Import package subpaths, not another workspace's private `src` files.
|
||||
|
||||
---
|
||||
|
||||
## Tech Stack Overview
|
||||
|
||||
Reactive Resume is built with a modern, type-safe stack:
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Frontend" icon="browser">
|
||||
- **React 19** with TanStack Start - **TypeScript** for type safety - **Tailwind CSS** for styling - **Radix UI**
|
||||
for accessible components
|
||||
</Card>
|
||||
<Card title="Backend" icon="server">
|
||||
- **ORPC** for type-safe RPC - **Drizzle ORM** with PostgreSQL - **Better Auth** for authentication - **Sharp** for
|
||||
image processing
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
---
|
||||
|
||||
## Application Architecture
|
||||
## Runtime Shape
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
%% Client Side
|
||||
subgraph Client ["Client"]
|
||||
Router["TanStack Router"]
|
||||
Query["TanStack Query"]
|
||||
ORPCClient["oRPC Client"]
|
||||
PDF["@react-pdf/renderer (client-side PDF export)"]
|
||||
Browser["Browser"] --> WebRoutes["apps/web routes"]
|
||||
WebRoutes --> ORPCClient["oRPC client"]
|
||||
ORPCClient --> RPC["/api/rpc"]
|
||||
|
||||
subgraph NodeProcess["Node process"]
|
||||
Server["apps/server Hono adapter"]
|
||||
API["packages/api feature routers"]
|
||||
Auth["packages/auth"]
|
||||
MCP["packages/mcp"]
|
||||
PDFServer["@reactive-resume/pdf/server"]
|
||||
end
|
||||
|
||||
%% Server Side
|
||||
subgraph Server ["Server"]
|
||||
ORPCRouter["oRPC Router"]
|
||||
Auth["Better Auth"]
|
||||
Services["Services Layer"]
|
||||
Drizzle["Drizzle ORM"]
|
||||
end
|
||||
|
||||
Database["PostgreSQL"]
|
||||
Storage["File System OR S3-Compatible Storage"]
|
||||
|
||||
Router --> ORPCClient
|
||||
Query --> ORPCClient
|
||||
ORPCClient -- calls --> ORPCRouter
|
||||
ORPCRouter --> Services
|
||||
Auth --> Services
|
||||
Services --> Drizzle
|
||||
Drizzle --> Database
|
||||
Services --> Storage
|
||||
Router --> PDF
|
||||
Server --> RPC
|
||||
RPC --> API
|
||||
Server --> Auth
|
||||
Server --> MCP
|
||||
API --> PDFServer
|
||||
API --> DB["packages/db"]
|
||||
API --> Storage["File system or S3-compatible storage"]
|
||||
DB --> Postgres["PostgreSQL"]
|
||||
```
|
||||
|
||||
**Diagram:** This flow shows data and control flow between the main architectural layers of Reactive Resume.
|
||||
`apps/web` owns the TanStack Start experience. `apps/server` owns the production Hono process and mounts RPC, auth, OpenAPI, MCP, static uploads, schema JSON, and the built web app.
|
||||
|
||||
---
|
||||
|
||||
## Directory Structure
|
||||
## Workspace Map
|
||||
|
||||
### Root Level
|
||||
|
||||
| Directory | Purpose |
|
||||
| ------------- | ----------------------------------- |
|
||||
| `src/` | Main application source code |
|
||||
| `public/` | Static assets served directly |
|
||||
| `locales/` | Translation files (.po format) |
|
||||
| `migrations/` | Database migration files |
|
||||
| `docs/` | Mintlify documentation |
|
||||
| `data/` | Local data storage (fonts, uploads) |
|
||||
| `scripts/` | Utility scripts |
|
||||
|
||||
### Source Code (`src/`)
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="components/" icon="cube">
|
||||
Reusable React components organized by category: - **`ui/`** — Base UI components (Button, Card, Dialog, etc.) -
|
||||
**`resume/`** — Resume-specific components (sections, templates) - **`input/`** — Form input components
|
||||
(ColorPicker, RichInput) - **`layout/`** — Layout components (Sidebar, LoadingScreen) - **`animation/`** — Animation
|
||||
components (Spotlight, TextMask) - **`theme/`** — Theme management components - **`typography/`** — Font management
|
||||
components
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="routes/" icon="route">
|
||||
File-based routing using TanStack Router: - **`__root.tsx`** — Root layout with providers - **`_home/`** — Public
|
||||
home page routes - **`auth/`** — Authentication routes (login, register, etc.) - **`dashboard/`** — User dashboard
|
||||
routes - **`builder/`** — Resume builder routes (the main editor) - **`api/`** — API routes
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="integrations/" icon="plug">
|
||||
Third-party service integrations: - **`auth/`** — Better Auth client configuration - **`drizzle/`** — Database
|
||||
schema and utilities - **`orpc/`** — API router, client, and services - **`ai/`** — AI service integrations -
|
||||
**`import/`** — Resume import utilities
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="dialogs/" icon="window-maximize">
|
||||
Modal dialog components: - **`auth/`** — Authentication dialogs - **`resume/`** — Resume management dialogs -
|
||||
**`api-key/`** — API key management dialogs - **`manager.tsx`** — Dialog manager component - **`store.ts`** — Dialog
|
||||
state management (Zustand)
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="schema/" icon="file-code">
|
||||
Zod schemas for validation: - **`resume/`** — Resume data schemas - **`icons.ts`** — Icon definitions -
|
||||
**`templates.ts`** — Template definitions
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="hooks/" icon="hook">
|
||||
Custom React hooks: - `use-confirm.tsx` — Confirmation dialog hook - `use-prompt.tsx` — Prompt dialog hook -
|
||||
`use-mobile.tsx` — Mobile detection hook - `use-safe-context.tsx` — Safe context consumption
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="utils/" icon="wrench">
|
||||
Utility functions: - `env.ts` — Environment variable validation - `locale.ts` — Locale utilities - `theme.ts` —
|
||||
Theme utilities - `string.ts` — String manipulation - `file.ts` — File handling utilities
|
||||
</Accordion>
|
||||
|
||||
</AccordionGroup>
|
||||
| Workspace | Ownership |
|
||||
| --- | --- |
|
||||
| `apps/web` | TanStack Start routes, web features, browser PDF.js preview/viewer code, PWA setup, oRPC browser client |
|
||||
| `apps/server` | Hono route composition, production HTTP adapters, MCP transport, OpenAPI/well-known handlers, static file serving, startup checks |
|
||||
| `packages/api` | oRPC procedures and feature-owned business behavior under `src/features/*` |
|
||||
| `packages/auth` | Better Auth config, auth helpers, and exported auth types |
|
||||
| `packages/db` | Drizzle client and schema; root `migrations/` stores generated migrations |
|
||||
| `packages/env` | Server environment validation and root `.env` loading |
|
||||
| `packages/schema` | Zod schemas and typed resume/page/template models |
|
||||
| `packages/resume` | Pure resume-domain helpers, including JSON Patch behavior and network icon mapping |
|
||||
| `packages/pdf` | React PDF document, template primitives, templates, font registration, and browser/server generation adapters |
|
||||
| `packages/docx` | DOCX export generation |
|
||||
| `packages/mcp` | MCP tools, prompts, resources, server card, and tool metadata |
|
||||
| `packages/ui` | Shared Base UI/shadcn-style primitives and hooks |
|
||||
| `packages/ai` | AI provider types, prompts, resume parsing/sanitization helpers, and model-facing tool contracts |
|
||||
| `packages/import` | Resume importers |
|
||||
| `packages/fonts` | Font metadata |
|
||||
| `packages/email` | Email transport and templates |
|
||||
| `packages/utils` | Narrow cross-cutting utilities with explicit export subpaths |
|
||||
| `packages/config` | Shared development configuration |
|
||||
| `tooling` | Development-only scripts and repo tooling |
|
||||
|
||||
---
|
||||
|
||||
## Key Concepts
|
||||
## Boundary Rules
|
||||
|
||||
### File-Based Routing
|
||||
- Use `@reactive-resume/*` package exports for cross-workspace imports.
|
||||
- Do not import another workspace through `apps/**`, `packages/**`, `@reactive-resume/*/src/**`, or a TypeScript path alias to another workspace's `src`.
|
||||
- Keep browser-only code in web features or explicit browser subpaths.
|
||||
- Keep server-only code in server packages or explicit server subpaths.
|
||||
- Keep environment-neutral domain packages free of DB, HTTP, DOM, and app imports.
|
||||
- Add public package exports deliberately. Wildcard exports are reserved for leaf-style public surfaces such as UI components/hooks and schema resume files.
|
||||
|
||||
Routes are automatically generated from the file structure in `src/routes/`. TanStack Router conventions:
|
||||
The checks are executable:
|
||||
|
||||
| Pattern | Description | Example |
|
||||
| ------------ | --------------------- | -------------------- |
|
||||
| `index.tsx` | Index route | `/dashboard` |
|
||||
| `$param.tsx` | Dynamic parameter | `/builder/$resumeId` |
|
||||
| `_layout/` | Layout group (prefix) | `_home/` |
|
||||
| `__root.tsx` | Root layout | Wraps all routes |
|
||||
|
||||
<Warning>Never edit `src/routeTree.gen.ts` manually — it's auto-generated when you run the dev server.</Warning>
|
||||
|
||||
### API Layer (ORPC)
|
||||
|
||||
ORPC provides end-to-end type safety for API calls:
|
||||
|
||||
```
|
||||
src/integrations/orpc/
|
||||
├── client.ts # Client-side ORPC setup
|
||||
├── router/ # API route definitions
|
||||
│ ├── auth.ts # Authentication endpoints
|
||||
│ ├── resume.ts # Resume CRUD operations
|
||||
│ └── storage.ts # File storage operations
|
||||
├── services/ # Business logic layer
|
||||
└── helpers/ # Utility functions
|
||||
```
|
||||
|
||||
**Using the API client:**
|
||||
|
||||
```tsx
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { orpc } from "@/integrations/orpc/client";
|
||||
|
||||
// Type-safe API calls with TanStack Query
|
||||
const { data } = useQuery(orpc.resume.findMany.queryOptions());
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
Reactive Resume uses a hybrid approach:
|
||||
|
||||
| Type | Tool | Use Case |
|
||||
| ------------ | --------------- | ------------------------------ |
|
||||
| Server State | TanStack Query | API data, caching, sync |
|
||||
| Client State | Zustand | UI state, dialogs, preferences |
|
||||
| Form State | React Hook Form | Form inputs and validation |
|
||||
|
||||
### Database Schema
|
||||
|
||||
The database schema is defined using Drizzle ORM in `src/integrations/drizzle/schema.ts`:
|
||||
|
||||
```tsx
|
||||
import { pgTable, text, timestamp, uuid } from "drizzle-orm/pg-core";
|
||||
|
||||
export const resume = pgTable("resume", {
|
||||
id: text("id").primaryKey().defaultRandom(),
|
||||
title: text("title").notNull(),
|
||||
slug: text("slug").notNull(),
|
||||
// ... more fields
|
||||
});
|
||||
```bash
|
||||
pnpm exec turbo boundaries
|
||||
pnpm exec biome check biome.json turbo.json tooling/grit/no-cross-workspace-src-imports.grit apps/web/tsconfig.json apps/*/turbo.json packages/*/turbo.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Development Patterns
|
||||
## Feature Placement
|
||||
|
||||
### Adding a New Component
|
||||
When adding code, choose the owner by behavior:
|
||||
|
||||
1. Create the component in the appropriate `src/components/` subdirectory
|
||||
2. Export it from the directory's index file (if applicable)
|
||||
3. Use TypeScript props interfaces for type safety
|
||||
4. Follow existing patterns for consistency
|
||||
|
||||
```tsx
|
||||
// src/components/ui/my-component.tsx
|
||||
import { cn } from "@/utils/style";
|
||||
|
||||
interface MyComponentProps {
|
||||
title: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const MyComponent = ({ title, className }: MyComponentProps) => {
|
||||
return <div className={cn("p-4", className)}>{title}</div>;
|
||||
};
|
||||
```
|
||||
|
||||
### Adding a New Route
|
||||
|
||||
1. Create a new file in `src/routes/` following TanStack Router conventions
|
||||
2. The route tree auto-generates when you save
|
||||
3. Use `createFileRoute` for type-safe routes
|
||||
|
||||
```tsx
|
||||
// src/routes/my-page.tsx
|
||||
import { createFileRoute } from "@tanstack/react-router";
|
||||
|
||||
export const Route = createFileRoute("/my-page")({
|
||||
component: MyPage,
|
||||
});
|
||||
|
||||
function MyPage() {
|
||||
return <div>My Page Content</div>;
|
||||
}
|
||||
```
|
||||
|
||||
### Adding an API Endpoint
|
||||
|
||||
1. Add the route handler in `src/integrations/orpc/router/`
|
||||
2. Create service functions in `src/integrations/orpc/services/` if needed
|
||||
3. The endpoint is automatically typed on the client
|
||||
|
||||
```tsx
|
||||
// In router file
|
||||
import { z } from "zod";
|
||||
import { publicProcedure, router } from "../server";
|
||||
|
||||
export const myRouter = router({
|
||||
hello: publicProcedure.input(z.object({ name: z.string() })).handler(async ({ input }) => {
|
||||
return { message: `Hello, ${input.name}!` };
|
||||
}),
|
||||
});
|
||||
```
|
||||
|
||||
### Adding Translations
|
||||
|
||||
1. Wrap text with `t` macro or `<Trans>` component
|
||||
2. Run `pnpm run lingui:extract` to update locale files
|
||||
3. Edit the `.po` files in `locales/` to add translations
|
||||
|
||||
```tsx
|
||||
import { t } from "@lingui/core/macro";
|
||||
import { Trans } from "@lingui/react/macro";
|
||||
|
||||
// In component
|
||||
const title = t`Welcome`;
|
||||
<Trans>Click here to continue</Trans>;
|
||||
```
|
||||
| Change | Put it here |
|
||||
| --- | --- |
|
||||
| Route, loader, route-level server handler, or web workflow | `apps/web/src/routes` plus `apps/web/src/features/<domain>` |
|
||||
| API procedure or authenticated business behavior | `packages/api/src/features/<domain>` |
|
||||
| Pure resume data logic | `packages/resume` |
|
||||
| Resume schema or template list shape | `packages/schema` |
|
||||
| React PDF template/rendering behavior | `packages/pdf` |
|
||||
| PDF.js canvas/viewer UI | `apps/web/src/features/resume` |
|
||||
| DOCX export behavior | `packages/docx` |
|
||||
| MCP tool/prompt/resource behavior | `packages/mcp` |
|
||||
| Shared UI primitive/hook | `packages/ui` |
|
||||
| Cross-cutting helper | Prefer a domain package first; otherwise add an explicit `packages/utils` export |
|
||||
|
||||
---
|
||||
|
||||
## Configuration Files
|
||||
## Web Layout
|
||||
|
||||
| File | Purpose |
|
||||
| ------------------- | --------------------------------- |
|
||||
| `vite.config.ts` | Vite bundler configuration |
|
||||
| `tsconfig.json` | TypeScript configuration |
|
||||
| `.oxfmtrc.json` | Formatter settings |
|
||||
| `.oxlintrc.json` | Linter settings |
|
||||
| `drizzle.config.ts` | Drizzle ORM configuration |
|
||||
| `lingui.config.ts` | Lingui i18n configuration |
|
||||
| `components.json` | shadcn/ui component configuration |
|
||||
`apps/web/src/routes` stays route-owned. Route files handle URL shape, loaders, redirects, metadata, and SSR flags.
|
||||
|
||||
Domain UI and browser-heavy implementation code lives under `apps/web/src/features`. Current feature areas include resume preview/export/public pages, command palette, auth, settings, theme, locale, and user menu behavior.
|
||||
|
||||
Generic app-local components remain in `apps/web/src/components`; shared reusable primitives live in `packages/ui`.
|
||||
|
||||
Dialog runtime state is centralized in `apps/web/src/dialogs/store.ts`, while dialog schemas and renderers are registered by domain under `apps/web/src/dialogs/{auth,api-key,resume}`.
|
||||
|
||||
---
|
||||
|
||||
## Contributing Guidelines
|
||||
## API Layout
|
||||
|
||||
<Steps>
|
||||
<Step title="Fork & Clone">Fork the repository on GitHub and clone your fork locally.</Step>
|
||||
`packages/api/src/routers/index.ts` exports the top-level oRPC contract. Feature modules under `packages/api/src/features/*` own their procedure modules, services, helpers, tests, and public package exports.
|
||||
|
||||
<Step title="Create a Branch">
|
||||
Create a feature branch from `main`: ```bash git checkout -b feature/my-feature ```
|
||||
</Step>
|
||||
|
||||
<Step title="Make Changes">Implement your changes following the patterns described above.</Step>
|
||||
|
||||
<Step title="Test Locally">
|
||||
Ensure the app works correctly with your changes: ```bash pnpm run dev pnpm run lint pnpm run typecheck ```
|
||||
</Step>
|
||||
|
||||
<Step title="Commit & Push">Write clear commit messages and push to your fork.</Step>
|
||||
|
||||
<Step title="Open a Pull Request">
|
||||
Open a PR against the main repository with a clear description of your changes.
|
||||
</Step>
|
||||
|
||||
</Steps>
|
||||
|
||||
<Note>Make sure to read any `CONTRIBUTING.md` file in the repository for additional guidelines.</Note>
|
||||
Avoid reintroducing technical-layer folders such as `services/` or `helpers/` at the package root. If a helper is used by one feature, keep it in that feature. If it becomes shared, name the shared capability explicitly and export it intentionally.
|
||||
|
||||
---
|
||||
|
||||
## Need Help?
|
||||
## PDF And Export Boundaries
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="GitHub Discussions" icon="comments" href="https://github.com/amruthpillai/reactive-resume/discussions">
|
||||
Ask questions and discuss ideas with the community.
|
||||
</Card>
|
||||
<Card title="GitHub Issues" icon="bug" href="https://github.com/amruthpillai/reactive-resume/issues">
|
||||
Report bugs or request features.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
`packages/pdf` owns React PDF generation:
|
||||
|
||||
- `@reactive-resume/pdf/browser` creates browser PDF blobs.
|
||||
- `@reactive-resume/pdf/server` creates server PDF files.
|
||||
- Template code stays under `packages/pdf/src/templates`.
|
||||
|
||||
Localized section-title resolution stays in the caller because it depends on web/server locale context. PDF.js preview and viewer code stays in `apps/web/src/features/resume`, not in `packages/pdf`.
|
||||
|
||||
DOCX export generation lives in `packages/docx`.
|
||||
|
||||
---
|
||||
|
||||
## MCP Boundary
|
||||
|
||||
MCP implementation lives in `packages/mcp`. It exposes canonical unprefixed tool names such as `list_resumes`, `read_resume`, and `apply_resume_patch`.
|
||||
|
||||
The server process imports MCP from `@reactive-resume/mcp` and injects the in-process oRPC router client. It must not import MCP code from `apps/web/src`.
|
||||
|
||||
@@ -4,8 +4,8 @@ description: "Set up a local development environment for Reactive Resume"
|
||||
---
|
||||
|
||||
<Info>
|
||||
**Prerequisites**: - [Node.js](https://nodejs.org/) v20 or higher - [pnpm](https://pnpm.io/) v10.28.0 or higher
|
||||
(package manager) - [Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/)
|
||||
**Prerequisites**: - [Node.js](https://nodejs.org/) v24 - [pnpm](https://pnpm.io/) v11.1.2 through Corepack -
|
||||
[Docker](https://docs.docker.com/get-docker/) and Docker Compose - [Git](https://git-scm.com/)
|
||||
</Info>
|
||||
|
||||
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
|
||||
@@ -18,16 +18,16 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
<Step title="Clone the Repository">
|
||||
```bash
|
||||
git clone https://github.com/amruthpillai/reactive-resume.git
|
||||
cd Reactive-Resume
|
||||
cd reactive-resume
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Install Dependencies">
|
||||
This project uses [pnpm](https://pnpm.io/) as its package manager for its speed and efficiency.
|
||||
This project uses [pnpm](https://pnpm.io/) through Corepack.
|
||||
|
||||
```bash
|
||||
# Install pnpm if you haven't already
|
||||
npm install -g pnpm
|
||||
# Enable the package manager version from package.json
|
||||
corepack enable
|
||||
|
||||
# Install project dependencies
|
||||
pnpm install
|
||||
@@ -35,23 +35,24 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
</Step>
|
||||
|
||||
<Step title="Start Infrastructure Services">
|
||||
Start the required services using the development-specific Docker Compose file:
|
||||
If you want to run the app directly on your machine with `pnpm dev`, start only the infrastructure services:
|
||||
|
||||
```bash
|
||||
docker compose -f compose.dev.yml up -d
|
||||
docker compose -f compose.dev.yml up -d postgres redis seaweedfs seaweedfs_create_bucket
|
||||
```
|
||||
|
||||
This starts the following infrastructure services:
|
||||
- **PostgreSQL** — Database (port 5432)
|
||||
- **Redis** — AI Agent workspace streams/state (port 6379)
|
||||
- **SeaweedFS** — S3-compatible storage (port 8333)
|
||||
- **Mailpit** — Email testing server (SMTP on port 1025, UI on port 8025)
|
||||
|
||||
<Info>
|
||||
**From v5.1.0 onwards** — PDF generation now runs entirely in the browser via `@react-pdf/renderer`, so no Browserless or Chromium container is required for development.
|
||||
</Info>
|
||||
|
||||
<Tip>
|
||||
Use `compose.dev.yml` instead of `compose.yml` for local development. The development file only includes infrastructure services with ports exposed to your host machine, while the main `compose.yml` includes the full application stack intended for production deployments.
|
||||
`compose.dev.yml` can also run the app in a development container with `docker compose -f compose.dev.yml up -d`.
|
||||
Use the service-filtered command above when you want local editor tooling and `pnpm dev` on the host.
|
||||
</Tip>
|
||||
|
||||
<Tip>
|
||||
@@ -63,7 +64,9 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
Create a `.env` file in the project root:
|
||||
|
||||
```bash
|
||||
# Server
|
||||
# Application
|
||||
PORT=3000
|
||||
SERVER_PORT=3001
|
||||
APP_URL=http://localhost:3000
|
||||
|
||||
# Database
|
||||
@@ -82,6 +85,11 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
# Email (Mailpit for local development)
|
||||
SMTP_HOST=localhost
|
||||
SMTP_PORT=1025
|
||||
SMTP_FROM="Reactive Resume <noreply@rxresu.me>"
|
||||
|
||||
# AI Agent workspace and saved AI providers
|
||||
REDIS_URL=redis://localhost:6379
|
||||
ENCRYPTION_SECRET=change-me-to-a-secure-agent-secret-in-production
|
||||
```
|
||||
|
||||
<Tip>
|
||||
@@ -90,11 +98,12 @@ This guide walks you through setting up Reactive Resume for local development. W
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Run Database Migrations">
|
||||
Apply the database schema:
|
||||
<Step title="Run Database Migrations If Needed">
|
||||
The server startup path runs migrations before serving traffic. To apply migrations manually without starting the app,
|
||||
export `DATABASE_URL` because Drizzle Kit reads directly from `process.env`:
|
||||
|
||||
```bash
|
||||
pnpm run db:migrate
|
||||
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres" pnpm run db:migrate
|
||||
```
|
||||
</Step>
|
||||
|
||||
@@ -115,14 +124,16 @@ Here are the most commonly used scripts during development:
|
||||
|
||||
### Development
|
||||
|
||||
| Command | Description |
|
||||
| -------------------- | -------------------------------------------- |
|
||||
| `pnpm run dev` | Start the development server with hot reload |
|
||||
| `pnpm run build` | Build the application for production |
|
||||
| `pnpm run start` | Start the production server |
|
||||
| `pnpm run lint` | Run Oxlint linter and formatter |
|
||||
| `pnpm run format` | Run Oxfmt formatter |
|
||||
| `pnpm run typecheck` | Run TypeScript type checking |
|
||||
| Command | Description |
|
||||
| ------------------------------ | ----------------------------------------------------------- |
|
||||
| `pnpm dev` | Start the web and server development processes |
|
||||
| `pnpm build` | Build the production web bundle and server bundle |
|
||||
| `pnpm start` | Start the built production server |
|
||||
| `pnpm typecheck` | Run TypeScript type checking |
|
||||
| `pnpm test` | Run Vitest across workspaces |
|
||||
| `pnpm exec biome check .` | Run a non-mutating Biome check |
|
||||
| `pnpm check` | Run Biome with write/fix behavior (`--write --unsafe`) |
|
||||
| `pnpm exec turbo boundaries` | Check workspace/package boundary rules |
|
||||
|
||||
### Database
|
||||
|
||||
@@ -138,44 +149,30 @@ Here are the most commonly used scripts during development:
|
||||
| ------------------------- | -------------------------------------- |
|
||||
| `pnpm run lingui:extract` | Extract translatable strings from code |
|
||||
|
||||
### Documentation
|
||||
|
||||
| Command | Description |
|
||||
| ------------------- | ------------------------------------------ |
|
||||
| `pnpm run docs:dev` | Start the Mintlify docs development server |
|
||||
|
||||
---
|
||||
|
||||
## Understanding the Project Structure
|
||||
|
||||
Understanding the project structure will help you navigate the codebase:
|
||||
|
||||
```
|
||||
reactive-resume/
|
||||
├── src/
|
||||
│ ├── components/ # Reusable React components
|
||||
│ │ ├── ui/ # Base UI components (Button, Card, etc.)
|
||||
│ │ ├── resume/ # Resume-specific components
|
||||
│ │ └── ...
|
||||
│ ├── dialogs/ # Modal dialogs
|
||||
│ ├── hooks/ # Custom React hooks
|
||||
│ ├── integrations/ # Third-party integrations
|
||||
│ │ ├── auth/ # Better Auth integration
|
||||
│ │ ├── drizzle/ # Database schema & utilities
|
||||
│ │ └── orpc/ # API routes & services
|
||||
│ ├── routes/ # File-based routing (TanStack Router)
|
||||
│ │ ├── builder/ # Resume builder pages
|
||||
│ │ ├── dashboard/ # User dashboard
|
||||
│ │ ├── auth/ # Authentication pages
|
||||
│ │ └── ...
|
||||
│ ├── schema/ # Zod schemas for validation
|
||||
│ ├── utils/ # Utility functions
|
||||
│ └── styles/ # Global CSS styles
|
||||
├── public/ # Static assets
|
||||
├── locales/ # Translation files (.po format)
|
||||
├── migrations/ # Database migrations
|
||||
├── docs/ # Mintlify documentation
|
||||
└── data/ # Local data (fonts, uploads)
|
||||
├── apps/
|
||||
│ ├── web/ # TanStack Start routes, web features, and browser UI
|
||||
│ └── server/ # Hono production server, HTTP adapters, static serving
|
||||
├── packages/
|
||||
│ ├── api/ # oRPC features and business behavior
|
||||
│ ├── auth/ # Better Auth configuration and helpers
|
||||
│ ├── db/ # Drizzle client and schema
|
||||
│ ├── docx/ # DOCX export generation
|
||||
│ ├── mcp/ # MCP tools, prompts, resources, and metadata
|
||||
│ ├── pdf/ # React PDF rendering and PDF generation adapters
|
||||
│ ├── resume/ # Pure resume-domain helpers
|
||||
│ ├── schema/ # Zod schemas and typed models
|
||||
│ ├── ui/ # Shared Base UI/shadcn-style primitives
|
||||
│ └── ...
|
||||
├── tooling/ # Development-only scripts and repository tooling
|
||||
├── migrations/ # Generated database migrations
|
||||
├── docs/ # Documentation
|
||||
└── data/ # Local development data and uploads
|
||||
```
|
||||
|
||||
---
|
||||
@@ -194,7 +191,7 @@ This opens a web-based GUI at [https://local.drizzle.studio](https://local.drizz
|
||||
|
||||
### Making Schema Changes
|
||||
|
||||
1. Edit the schema in `src/integrations/drizzle/schema.ts`
|
||||
1. Edit the schema in `packages/db/src/schema/*`
|
||||
2. Generate a migration:
|
||||
```bash
|
||||
pnpm run db:generate
|
||||
@@ -235,7 +232,7 @@ After adding new translatable text, extract them to the locale files:
|
||||
pnpm run lingui:extract
|
||||
```
|
||||
|
||||
Translation files are located in the `locales/` directory in `.po` format.
|
||||
Translation files are located in `apps/web/locales` in `.po` format.
|
||||
|
||||
---
|
||||
|
||||
@@ -243,12 +240,14 @@ Translation files are located in the `locales/` directory in `.po` format.
|
||||
|
||||
### Linting & Formatting
|
||||
|
||||
Uses [Oxlint](https://oxlint.dev/) for linting and [Oxfmt](https://oxfmt.dev/) for formatting:
|
||||
Uses [Biome](https://biomejs.dev/) for linting, formatting, import organization, and Tailwind class sorting:
|
||||
|
||||
```bash
|
||||
# Check and auto-fix issues
|
||||
pnpm run lint:fix
|
||||
pnpm run format:fix
|
||||
# Non-mutating check
|
||||
pnpm exec biome check .
|
||||
|
||||
# Project script with write/fix behavior
|
||||
pnpm check
|
||||
```
|
||||
|
||||
### Type Checking
|
||||
@@ -260,9 +259,8 @@ pnpm run typecheck
|
||||
```
|
||||
|
||||
<Tip>
|
||||
Configure your IDE to use Oxlint for automatic linting and Oxfmt for automatic formatting on save. For VS Code,
|
||||
install the [Oxlint extension](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode) and configure the
|
||||
settings in `.vscode/settings.json`.
|
||||
Configure your IDE to use Biome for formatting and lint diagnostics. The repo uses tabs, double quotes, 120-column
|
||||
lines, and organized import groups.
|
||||
</Tip>
|
||||
|
||||
---
|
||||
@@ -270,9 +268,9 @@ pnpm run typecheck
|
||||
## Troubleshooting
|
||||
|
||||
<AccordionGroup>
|
||||
<Accordion title="Port 3000 is already in use">
|
||||
Another process is using port 3000. Either stop that process or start the dev server on a different port: ```bash
|
||||
PORT=3001 pnpm run dev ```
|
||||
<Accordion title="Port 3000 or 3001 is already in use">
|
||||
The Vite web server uses `PORT` (default `3000`), and the Hono server uses `SERVER_PORT` (default `3001`).
|
||||
Either stop the conflicting process or choose alternate ports: ```bash PORT=3002 SERVER_PORT=3003 pnpm dev ```
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database connection refused">
|
||||
|
||||
+14
-2
@@ -3,9 +3,9 @@
|
||||
"theme": "mint",
|
||||
"name": "Reactive Resume",
|
||||
"favicon": "/favicon.svg",
|
||||
"description": "A one-of-a-kind resume builder that keeps your privacy in mind. Completely secure, customizable, portable, open-source and free forever. Try it out today!",
|
||||
"description": "A privacy-minded resume builder that is customizable, portable, open-source, and free to use.",
|
||||
"seo": {
|
||||
"indexing": "all",
|
||||
"indexing": "navigable",
|
||||
"metatags": {
|
||||
"canonical": "https://docs.rxresu.me",
|
||||
"og:image": "https://rxresu.me/opengraph/banner.jpg"
|
||||
@@ -82,6 +82,18 @@
|
||||
"guides/json-resume-schema"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Use Cases",
|
||||
"pages": [
|
||||
"use-cases/free-resume-builder",
|
||||
"use-cases/open-source-resume-builder",
|
||||
"use-cases/privacy-focused-resume-builder",
|
||||
"use-cases/self-hosted-resume-builder",
|
||||
"use-cases/api-mcp-resume-automation",
|
||||
"use-cases/export-and-share-resumes",
|
||||
"use-cases/ai-resume-builder"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Self-Hosting",
|
||||
"pages": ["self-hosting/docker", "self-hosting/examples", "self-hosting/sso", "self-hosting/migration"]
|
||||
|
||||
@@ -89,7 +89,7 @@ Reactive Resume is built with modern web technologies:
|
||||
| API | ORPC (Type-safe RPC) |
|
||||
| Auth | Better Auth |
|
||||
| Styling | Tailwind CSS |
|
||||
| UI Components | Radix UI |
|
||||
| UI Components | Base UI + shadcn-style package |
|
||||
| State Management | Zustand + TanStack Query |
|
||||
|
||||
## Community & Support
|
||||
|
||||
@@ -73,7 +73,7 @@ Before you begin, ensure you have the following installed:
|
||||
<Step title="Clone the Repository">
|
||||
```bash
|
||||
git clone https://github.com/amruthpillai/reactive-resume.git
|
||||
cd Reactive-Resume
|
||||
cd reactive-resume
|
||||
```
|
||||
</Step>
|
||||
|
||||
@@ -188,11 +188,8 @@ Here's a complete list of environment variables you can configure:
|
||||
| `OAUTH_AUTHORIZATION_URL` | OAuth Authorization URL (manual config) | — |
|
||||
| `OAUTH_TOKEN_URL` | OAuth Token URL (manual config) | — |
|
||||
| `OAUTH_USER_INFO_URL` | OAuth User Info URL (manual config) | — |
|
||||
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Trusted HTTPS hosts/origins for dynamic OAuth redirects | — |
|
||||
| `OAUTH_SCOPES` | OAuth Scopes (space-separated) | `openid profile email` |
|
||||
| `BETTER_AUTH_API_KEY` | Better Auth dashboard API key | — |
|
||||
| `BETTER_AUTH_URL` | Better Auth base URL override (advanced) | `APP_URL` |
|
||||
| `BETTER_AUTH_SECRET` | Better Auth secret override (advanced) | `AUTH_SECRET` |
|
||||
| `SMTP_HOST` | SMTP Server Host (for email features) | — |
|
||||
| `SMTP_PORT` | SMTP Server Port | `587` |
|
||||
| `SMTP_USER` | SMTP Username | — |
|
||||
@@ -207,16 +204,17 @@ Here's a complete list of environment variables you can configure:
|
||||
| `S3_FORCE_PATH_STYLE` | Use path-style URLs for S3 (set `true` for MinIO/SeaweedFS) | `false` |
|
||||
| `REDIS_URL` | Redis connection string for the AI Agent workspace | — |
|
||||
| `ENCRYPTION_SECRET` | Encryption secret for saved AI provider credentials | — |
|
||||
| `CLOUDFLARE_ACCOUNT_ID` | Optional Cloudflare URL extraction fallback account ID | — |
|
||||
| `CLOUDFLARE_API_TOKEN` | Optional Cloudflare URL extraction fallback API token | — |
|
||||
| `FLAG_DISABLE_SIGNUPS` | Disables new user signups | `false` |
|
||||
| `FLAG_DISABLE_EMAIL_AUTH` | Disables email/password login (SSO only) | `false` |
|
||||
| `FLAG_DISABLE_IMAGE_PROCESSING` | Disables image processing | `false` |
|
||||
| `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` | Allows arbitrary dynamic OAuth redirect URIs | `false` |
|
||||
| `FLAG_ALLOW_UNSAFE_AI_BASE_URL` | Allows unsafe/private/non-public AI provider base URLs | `false` |
|
||||
|
||||
> **Note:** Some variables are only required for using related features (OAuth, SMTP, S3, etc.) and can be left unset if unused.
|
||||
|
||||
> **AI features:** Saved AI provider management requires `ENCRYPTION_SECRET`, and the AI Agent workspace requires both `REDIS_URL` and `ENCRYPTION_SECRET`. Cloudflare variables only enable the optional URL extraction fallback and are not required for normal agent operation. Keep `FLAG_ALLOW_UNSAFE_AI_BASE_URL` disabled unless this is a trusted self-hosted deployment; public HTTPS provider URLs are the safe default.
|
||||
> **AI features:** Saved AI provider management requires `ENCRYPTION_SECRET`, and the AI Agent workspace requires both `REDIS_URL` and `ENCRYPTION_SECRET`. Live web research depends on the selected AI provider/model supporting native web search. Keep `FLAG_ALLOW_UNSAFE_AI_BASE_URL` disabled unless this is a trusted self-hosted deployment; public HTTPS provider URLs are the safe default.
|
||||
|
||||
> **OAuth redirect safety:** Keep `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` disabled unless this is a trusted self-hosted deployment. Enabling it allows dynamic OAuth clients to register any parseable redirect URI, including custom schemes, private hosts, and non-loopback `http://` URLs, which can enable phishing or token exfiltration on public or multi-tenant instances.
|
||||
|
||||
> **Health check behavior:** `/api/health` reports status for database and storage. A failure in either dependency returns HTTP `503`.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "AI Agent Tools"
|
||||
description: "Understand the tools available to the AI Agent workspace and how they affect resume drafts."
|
||||
---
|
||||
|
||||
The AI Agent workspace can use a curated set of tools while it chats with you. You do not call these tools directly; the agent chooses them when your request needs resume data, web context, attachments, questions, or a resume patch.
|
||||
The AI Agent workspace can use a curated set of tools while it chats with you. You do not call these tools directly; the agent chooses them when your request needs resume data, supported provider web context, attachments, questions, or a resume patch.
|
||||
|
||||
## Tool activity in chat
|
||||
|
||||
@@ -23,11 +23,10 @@ Applied resume patches are shown as a small inline **Patch applied** item. Open
|
||||
| Tool | What it does | Example request |
|
||||
| --- | --- | --- |
|
||||
| `read_resume` | Reads the current AI draft and gives the agent the resume data it can safely edit. | "What are the weakest parts of this resume?" |
|
||||
| `fetch_url` | Fetches public HTTPS pages, extracts readable content, and returns it as agent input. | "Tailor this resume to this job description: `https://example.com/job`" |
|
||||
| Provider-native search | Uses the selected provider's native web search when that provider/model supports it. | "Research this company and adjust the summary for its product area." |
|
||||
| `web_search` | Uses the selected provider's native web search when that provider/model supports it. | "Research this company and adjust the summary for its product area." |
|
||||
| `read_attachment` | Reads extracted text from attached plain text, Markdown, or JSON files. Other supported attachments, such as images or PDFs, are passed to the model when the selected provider can use them. | "Use the attached notes to update the keywords." |
|
||||
| `ask_user_question` | Shows a question card with answer choices when the agent needs your decision. | "Ask me before changing the career narrative." |
|
||||
| `apply_resume_patch` | Applies a JSON Patch to the AI draft and stores enough history to revert it later. | "Change the visible name to Amruth Pillai." |
|
||||
| `apply_resume_patch` | Applies a JSON Patch to the AI draft and stores a rollback snapshot. | "Change the visible name to Amruth Pillai." |
|
||||
|
||||
## Resume patches
|
||||
|
||||
@@ -37,19 +36,21 @@ When a patch is applied:
|
||||
|
||||
- the AI draft updates immediately;
|
||||
- the raw JSON Patch is available from the **Patch applied** details;
|
||||
- an inverse patch is stored for revert;
|
||||
- a snapshot is stored so the draft can be restored to the state before that patch;
|
||||
- the resume preview refreshes to show the updated draft.
|
||||
|
||||
If the resume changed after the patch was generated, revert or apply can fail with a version conflict. In that case, ask the agent to retry from the latest draft.
|
||||
Restoring an older patch rolls back that patch and any patches applied after it. If the resume changed after the latest agent patch, restore or apply can fail with a version conflict. In that case, ask the agent to retry from the latest draft.
|
||||
|
||||
## Web access
|
||||
|
||||
The agent can fetch public HTTPS URLs so it can read job descriptions, company pages, or other public context you provide.
|
||||
Live web research is handled only by the selected AI provider's native web search tool. When the provider/model supports it, the agent can use `web_search` for current company, industry, role, or URL-based context.
|
||||
|
||||
When the provider/model does not support native web search, the agent still works for normal resume editing. If you ask it to browse, search the web, fetch a URL, or use current online context, it should tell you that live web research is unavailable with the selected provider/model and ask you to paste or attach the relevant content instead.
|
||||
|
||||
For self-hosted deployments:
|
||||
|
||||
- private, loopback, and non-HTTPS URLs are blocked by default;
|
||||
- Cloudflare URL extraction is optional and is used only as a fallback when local readability extraction fails;
|
||||
- app-owned URL crawling is not available;
|
||||
- web access depends on the selected provider/model supporting native web search;
|
||||
- unsafe/private AI provider base URLs require `FLAG_ALLOW_UNSAFE_AI_BASE_URL=true`, which should only be used on trusted self-hosted deployments.
|
||||
|
||||
## Attachments
|
||||
@@ -67,7 +68,7 @@ Self-hosted deployments need S3-compatible storage for private agent attachments
|
||||
|
||||
Use direct prompts that tell the agent what context to use and how cautious to be:
|
||||
|
||||
- "Fetch this role URL, identify the most important keywords, and apply a conservative patch."
|
||||
- "Research this role, identify the most important keywords, and apply a conservative patch."
|
||||
- "Read the attached job description and ask me before changing anything outside the summary."
|
||||
- "Compare my current projects against this company page and suggest only truthful wording."
|
||||
- "Apply a patch for the visible resume name, then show me what changed."
|
||||
@@ -79,5 +80,5 @@ Tool use can be limited by the selected provider, deployment configuration, or t
|
||||
- A deleted provider makes the thread read-only; a disabled or untested provider blocks new agent runs until it is enabled and tested again.
|
||||
- A deleted working resume makes the thread read-only.
|
||||
- Archived threads cannot receive new messages.
|
||||
- URL fetching may fail for private URLs, blocked hosts, non-HTML pages, or pages that cannot be extracted.
|
||||
- Live web research is unavailable when the selected provider/model does not support native web search.
|
||||
- Attachments may fail if private object storage is not configured.
|
||||
|
||||
@@ -3,7 +3,7 @@ title: "Sharing your resume publicly"
|
||||
description: "Learn how to share your resume via a public URL, track public engagement, and optionally protect your resume with a password."
|
||||
---
|
||||
|
||||
Reactive Resume lets you share your resume via a **public URL** that anyone can access. When you make your resume public, it becomes available at a unique link that you can share with recruiters, include in your portfolio, or add to your LinkedIn profile.
|
||||
Reactive Resume lets you share your resume via a **public URL** that anyone can access. When you make your resume public, it becomes available at a unique link that you can share with human recipients such as recruiters, collaborators, or portfolio visitors. Public resume URLs are not search-indexed by default.
|
||||
|
||||
## Key benefits of public sharing
|
||||
|
||||
@@ -12,7 +12,7 @@ Reactive Resume lets you share your resume via a **public URL** that anyone can
|
||||
Viewers always see the latest version of your resume. No need to send new files when you make updates.
|
||||
</Card>
|
||||
<Card title="Track engagement" icon="chart-line">
|
||||
See public engagement counters in the builder.
|
||||
See public view counters in the builder.
|
||||
</Card>
|
||||
<Card title="Password protection" icon="lock">
|
||||
Optionally require a password so only people you trust can access your resume.
|
||||
@@ -66,11 +66,11 @@ When someone visits your public resume URL:
|
||||
1. **They see the live version** - The page renders your current resume data with all your latest changes
|
||||
2. **No account required** - Visitors don't need a Reactive Resume account to view or download your resume
|
||||
3. **They can download a PDF** - Visitors can use the download button on the public page
|
||||
4. **Views are tracked** - Each visit is counted in your resume statistics (see below)
|
||||
4. **Views are tracked** - Visits are counted in your resume statistics (see below)
|
||||
|
||||
<Info>
|
||||
Changes you make in the builder are reflected immediately on the public URL. There's no separate "publish" step—your
|
||||
public resume is always in sync.
|
||||
public resume is always in sync. Treat the link as something you send to people directly, not as a search profile page.
|
||||
</Info>
|
||||
|
||||
## Tracking public engagement
|
||||
@@ -81,14 +81,12 @@ When your resume is public, Reactive Resume tracks public views. This helps you
|
||||
|
||||
In the resume builder, open the **right sidebar** and select **Statistics**.
|
||||
|
||||
You'll see:
|
||||
You'll see public view information such as:
|
||||
|
||||
| Metric | Description |
|
||||
| ------------------- | ---------------------------------------------------------------- |
|
||||
| **Views** | Number of times your public resume page was visited |
|
||||
| **Last viewed** | The date when your resume was last viewed |
|
||||
| **Downloads** | Number of recorded resume downloads |
|
||||
| **Last downloaded** | The date when the last recorded download happened |
|
||||
|
||||
<Info>
|
||||
Statistics are only shown after public sharing is enabled. If you turn off public access, existing stats are
|
||||
@@ -101,11 +99,12 @@ A view is counted each time someone loads your public resume page. This includes
|
||||
|
||||
- Direct visits to your public URL
|
||||
- Clicks from links you've shared
|
||||
- Search engine visits (if your resume is indexed)
|
||||
|
||||
Owner self-visits while you are signed in to your account are not included in the view statistics.
|
||||
|
||||
<Note>
|
||||
Only **you** can see your resume's statistics. Visitors to your public URL cannot see how many views or downloads your
|
||||
resume has.
|
||||
Only **you** can see your resume's view statistics. Visitors to your public URL cannot see how many views your resume
|
||||
has.
|
||||
</Note>
|
||||
|
||||
## Password protecting your resume
|
||||
@@ -166,7 +165,8 @@ Your resume will become accessible to anyone with the public URL.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Job applications" icon="briefcase">
|
||||
Some applications ask for a link to your resume. Your public URL is a professional alternative to file uploads.
|
||||
Some applications accept a link to your resume, while others require a file upload. Use the public URL when a link is
|
||||
accepted, and export a PDF when an upload is required.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Networking events" icon="users">
|
||||
@@ -213,18 +213,17 @@ The builder dock includes a **Copy URL** shortcut. It copies the same public URL
|
||||
Yes! The URL is based on your **username** and the resume's **slug**. You can change the slug in the **Update Resume** dialog. To open it, right-click on your resume card in the dashboard and select "Update". The username is set in your account settings.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Will search engines index my public resume?">
|
||||
Public resumes may be indexed by search engines. If you want to prevent indexing, use password protection or keep your
|
||||
resume private.
|
||||
<Accordion title="Are public resumes meant to be search-indexed pages?">
|
||||
No. By default, public resume URLs are meant for human recipients who receive the link from you, not as search profile
|
||||
pages. If you want tighter access control, use password protection or keep your resume private.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Do views from my own visits count?">
|
||||
Views are only counted when someone visits your public resume URL while not logged in. Visits made while you're logged
|
||||
in to your account are not included in the view statistics.
|
||||
No. If you are signed in as the owner, your own visits are excluded from the view statistics.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Can I see who viewed my resume?">
|
||||
No, Reactive Resume only tracks view and download counts, not the identity of visitors. This protects visitor privacy.
|
||||
No, Reactive Resume tracks public view counts, not the identity of visitors. This protects visitor privacy.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="What happens if I change my username?">
|
||||
|
||||
@@ -82,7 +82,7 @@ You can attach files or images from the composer. The agent can read uploaded at
|
||||
|
||||
When the agent edits the resume, the patch is applied immediately to the AI draft. The chat shows a small **Patch applied** line.
|
||||
|
||||
Open the line to inspect the raw JSON Patch and use **Revert** if you want to undo that patch.
|
||||
Open the line to inspect the raw JSON Patch and use **Restore** if you want to roll the draft back to the state before that patch. Restoring an older patch also rolls back patches applied after it.
|
||||
|
||||
<Warning>
|
||||
AI-generated changes can still be inaccurate. Review the draft in the preview or builder before exporting or sharing it.
|
||||
|
||||
@@ -207,27 +207,27 @@ If you're running a self-hosted Reactive Resume instance, replace `https://rxres
|
||||
|
||||
## Available Tools
|
||||
|
||||
Tool names use a `reactive_resume_` prefix so they stay distinct when multiple MCP servers are enabled in the same client.
|
||||
Tool names use canonical unprefixed `snake_case` names.
|
||||
|
||||
| Tool | Description |
|
||||
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `reactive_resume_list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||||
| `reactive_resume_list_resume_tags` | List every distinct tag in use across your resumes (sorted) |
|
||||
| `reactive_resume_get_resume` | Get the full data of a specific resume by ID |
|
||||
| `reactive_resume_get_resume_analysis` | Get the latest saved AI analysis for a resume (from the web app), if any |
|
||||
| `reactive_resume_create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||||
| `reactive_resume_import_resume` | Create a resume from a full ResumeData JSON export (random name/slug). Large files may exceed client limits |
|
||||
| `reactive_resume_duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||||
| `reactive_resume_patch_resume` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||||
| `reactive_resume_update_resume` | Update metadata only: name, slug, tags, `isPublic`. Returns canonical share URL; passwords are not managed via MCP |
|
||||
| `reactive_resume_delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||||
| `reactive_resume_lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||||
| `reactive_resume_unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||||
| `reactive_resume_get_resume_statistics` | Get view and download statistics for a resume |
|
||||
| Tool | Description |
|
||||
| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `list_resumes` | List all resumes with IDs, names, tags, and status. Supports filtering by tags and sorting by last updated, creation date, or name |
|
||||
| `list_resume_tags` | List every distinct tag in use across your resumes (sorted) |
|
||||
| `read_resume` | Get the full data of a specific resume by ID |
|
||||
| `get_resume_analysis` | Get the latest saved AI analysis for a resume (from the web app), if any |
|
||||
| `create_resume` | Create a new, empty resume with a name and slug. Optionally pre-fill with sample data |
|
||||
| `import_resume` | Create a resume from a full ResumeData JSON export (random name/slug). Large files may exceed client limits |
|
||||
| `duplicate_resume` | Create a copy of an existing resume with a new name and slug |
|
||||
| `apply_resume_patch` | Apply JSON Patch (RFC 6902) operations to modify a resume's data |
|
||||
| `update_resume` | Update metadata only: name, slug, tags, `isPublic`. Returns canonical share URL; passwords are not managed via MCP |
|
||||
| `delete_resume` | Permanently delete a resume and all associated files. **Irreversible** |
|
||||
| `lock_resume` | Lock a resume to prevent edits, patches, and deletion |
|
||||
| `unlock_resume` | Unlock a previously locked resume to re-enable editing |
|
||||
| `get_resume_statistics` | Get view and download statistics for a resume |
|
||||
|
||||
### Breaking change (tool names)
|
||||
|
||||
Older clients may refer to unprefixed names (`list_resumes`, `get_resume`, …) or dot-separated names (`reactive_resume.list_resumes`, …). Those names are no longer used; update automations and saved prompts to the `reactive_resume_*` names above.
|
||||
Older clients may refer to prefixed or dot-separated names. Those names are no longer registered; update automations and saved prompts to the canonical names above.
|
||||
|
||||
## Available Resources
|
||||
|
||||
@@ -236,13 +236,13 @@ Resources follow MCP conventions: **static** items appear in `resources/list`; *
|
||||
| Discovery | What you get |
|
||||
| ------------------------------------- | --------------------------------------------------------------------------------------------- |
|
||||
| `resources/list` | Static resources only — currently **`resume://_meta/schema`** (ResumeData JSON Schema) |
|
||||
| `resources/templates/list` | **`resume://{id}`** — template for reading full resume JSON by ID (not enumerated per resume) |
|
||||
| `reactive_resume_list_resumes` (tool) | **Primary way to discover resume IDs** — resumes are not listed as separate MCP resources |
|
||||
| `resources/templates/list` | **`resume://{id}`** — template for reading full resume JSON by ID (not enumerated per resume) |
|
||||
| `list_resumes` (tool) | **Primary way to discover resume IDs** — resumes are not listed as separate MCP resources |
|
||||
|
||||
| URI | Description |
|
||||
| ----------------------- | ------------------------------------------------------------------------ |
|
||||
| `resume://_meta/schema` | ResumeData JSON Schema — use for valid JSON Patch paths and value types |
|
||||
| `resume://{id}` | Full resume data as JSON — use an ID from `reactive_resume_list_resumes` |
|
||||
| `resume://{id}` | Full resume data as JSON — use an ID from `list_resumes` |
|
||||
|
||||
### Breaking change (schema URI)
|
||||
|
||||
@@ -260,7 +260,6 @@ Prompts are pre-built workflows that provide the AI with structured instructions
|
||||
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `build_resume` | Guide you step-by-step through building a resume from scratch — basics, summary, experience, education, skills, and design |
|
||||
| `improve_resume` | Review your resume and suggest concrete improvements to wording, impact, metrics, and structure |
|
||||
| `tailor_resume` | Adapt your resume to match a specific job description with keyword optimization and ATS targeting. Requires the job description as input |
|
||||
| `review_resume` | Get a structured, professional critique with a scorecard (1–10 across seven dimensions) and prioritized recommendations. **Read-only** — no changes are made |
|
||||
|
||||
## Usage Examples
|
||||
@@ -303,12 +302,11 @@ Once your MCP client is connected, you can use natural language to interact with
|
||||
- "Help me build my resume from scratch" (uses `build_resume`)
|
||||
- "Review my resume and give me a score" (uses `review_resume`)
|
||||
- "Improve the wording on my resume" (uses `improve_resume`)
|
||||
- "Tailor my resume for this job description: ..." (uses `tailor_resume`)
|
||||
|
||||
<Tip>
|
||||
The AI will use `reactive_resume_get_resume` to inspect your current resume before making changes with
|
||||
`reactive_resume_patch_resume`. This ensures the correct JSON paths are used. Use `reactive_resume_update_resume` for
|
||||
name, slug, tags, and public visibility (not for section content).
|
||||
The AI will use `read_resume` to inspect your current resume before making changes with `apply_resume_patch`. This
|
||||
ensures the correct JSON paths are used. Use `update_resume` for name, slug, tags, and public visibility (not for
|
||||
section content).
|
||||
</Tip>
|
||||
|
||||
## Troubleshooting
|
||||
@@ -318,7 +316,7 @@ Once your MCP client is connected, you can use natural language to interact with
|
||||
| "Unauthorized" with no login prompt | Your client may not support MCP OAuth discovery. Use API key mode (`x-api-key`) |
|
||||
| OAuth login opens but fails redirect/callback | Confirm your client's MCP OAuth callback settings and retry the connection |
|
||||
| "API error (401)" | Your API key is invalid or expired. Create a new one in **Settings → API Keys** |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `reactive_resume_list_resumes` to find valid IDs |
|
||||
| "API error (404)" | The resume ID doesn't exist. Use `list_resumes` to find valid IDs |
|
||||
| "API error (403)" | The resume is locked. Unlock it in the Reactive Resume dashboard |
|
||||
| Connection refused | Check that the URL is correct and the instance is running |
|
||||
| "ReferenceError: File is not defined" when using `mcp-remote` | You're running Node.js 18. `mcp-remote` requires **Node.js 20 or later** — upgrade with `nvm use 20` or `nvm alias default 20` |
|
||||
|
||||
@@ -82,14 +82,9 @@ OAUTH_DISCOVERY_URL=""
|
||||
OAUTH_AUTHORIZATION_URL=""
|
||||
OAUTH_TOKEN_URL=""
|
||||
OAUTH_USER_INFO_URL=""
|
||||
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS=""
|
||||
# Custom scopes (space-separated, defaults to "openid profile email")
|
||||
OAUTH_SCOPES=""
|
||||
|
||||
# Optional Better Auth runtime overrides for advanced deployments:
|
||||
# BETTER_AUTH_URL="https://auth.example.com"
|
||||
# BETTER_AUTH_SECRET=""
|
||||
|
||||
# --- Email (optional) ---
|
||||
# If all keys are disabled, the app logs the email to be sent to the console instead.
|
||||
SMTP_HOST=""
|
||||
@@ -117,14 +112,13 @@ S3_FORCE_PATH_STYLE="false"
|
||||
REDIS_URL=""
|
||||
# Generated using `openssl rand -hex 32`
|
||||
ENCRYPTION_SECRET=""
|
||||
# Optional fallback for URL extraction. Not required for normal agent operation.
|
||||
CLOUDFLARE_ACCOUNT_ID=""
|
||||
CLOUDFLARE_API_TOKEN=""
|
||||
|
||||
# --- Feature Flags ---
|
||||
FLAG_DISABLE_SIGNUPS="false"
|
||||
FLAG_DISABLE_EMAIL_AUTH="false"
|
||||
FLAG_DISABLE_IMAGE_PROCESSING="false"
|
||||
# Allows any parseable dynamic OAuth redirect URI. Keep false unless this is a trusted self-hosted deployment.
|
||||
FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI="false"
|
||||
# Allows unsafe/private/non-public AI provider base URLs. Keep false unless this is a trusted self-hosted deployment.
|
||||
FLAG_ALLOW_UNSAFE_AI_BASE_URL="false"
|
||||
```
|
||||
@@ -202,6 +196,12 @@ volumes:
|
||||
Prefer pulling from Docker Hub? Keep <code>amruthpillai/reactive-resume:latest</code>. Prefer GHCR? Swap it to <code>ghcr.io/amruthpillai/reactive-resume:latest</code>.
|
||||
</Tip>
|
||||
|
||||
<Note>
|
||||
In Docker, the Reactive Resume server listens on <code>PORT</code> and serves both the API and the built web app.
|
||||
The default image uses <code>PORT=3000</code>, so the example maps <code>3000:3000</code>. If you change
|
||||
<code>PORT</code>, update the container-side port mapping and health check to match.
|
||||
</Note>
|
||||
|
||||
</Step>
|
||||
|
||||
<Step title="Start the stack">
|
||||
@@ -275,6 +275,8 @@ docker compose logs -f reactive-resume
|
||||
<Accordion title="Server">
|
||||
- **`TZ`**: Sets the container timezone (affects logs and server-side timestamps). Recommended: `Etc/UTC`.
|
||||
- **`APP_URL`**: Canonical/public URL for your instance (used for absolute URLs, redirects, and auth flows). If behind a reverse proxy, set this to your public HTTPS URL (for example, `https://resume.example.com`).
|
||||
- **`PORT`**: Port the production Docker container listens on. Defaults to `3000` in the official image. If you change it, update your Compose port mapping and health check from `3000` to the new container port.
|
||||
- **`SERVER_PORT`**: Used only for local development when the Vite web app and Hono server run as separate processes. It is ignored by the production Docker image.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Database (PostgreSQL)">
|
||||
@@ -305,14 +307,9 @@ openssl rand -hex 32
|
||||
|
||||
**`BETTER_AUTH_API_KEY`** (optional): Enables Better Auth dashboard integrations.
|
||||
|
||||
**`BETTER_AUTH_URL`** (optional, advanced): Overrides auth base URL if it must differ from `APP_URL` (for split-host deployments).
|
||||
|
||||
**`BETTER_AUTH_SECRET`** (optional, advanced): Overrides `AUTH_SECRET` for Better Auth internals.
|
||||
|
||||
**Custom OAuth provider** (optional):
|
||||
- **`OAUTH_PROVIDER_NAME`**: Display name in the UI
|
||||
- **`OAUTH_CLIENT_ID`** / **`OAUTH_CLIENT_SECRET`**: Required for any custom OAuth provider
|
||||
- **`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`**: Comma-separated allowlist for extra dynamic OAuth redirect hosts/origins (HTTPS only, non-private hosts).
|
||||
- **`OAUTH_SCOPES`**: Space-separated scopes (defaults to `openid profile email`)
|
||||
|
||||
Configure endpoints using **one** of these methods:
|
||||
@@ -350,7 +347,7 @@ openssl rand -hex 32
|
||||
|
||||
- **`REDIS_URL`**: Redis connection string used by the AI Agent workspace.
|
||||
- **`ENCRYPTION_SECRET`**: Secret used to encrypt saved AI provider credentials. Generate with `openssl rand -hex 32`.
|
||||
- **`CLOUDFLARE_ACCOUNT_ID`** / **`CLOUDFLARE_API_TOKEN`** (optional): Enables the Cloudflare URL extraction fallback. Cloudflare is not required for normal agent operation.
|
||||
- Live web research depends on the selected AI provider/model supporting native web search. The app does not run its own URL crawler.
|
||||
|
||||
If you use the Postgres-only Compose example above and want the AI Agent workspace, add a Redis service or use managed Redis, then set `REDIS_URL`.
|
||||
</Accordion>
|
||||
@@ -359,6 +356,7 @@ openssl rand -hex 32
|
||||
- **`FLAG_DISABLE_SIGNUPS`**: Disables new signups (web app and server). Useful for private instances.
|
||||
- **`FLAG_DISABLE_EMAIL_AUTH`**: Disables email/password login entirely. Also disables email verification, forgot password, and reset password flows. Users can still sign up via social auth (Google/GitHub/LinkedIn/Custom OAuth), unless FLAG_DISABLE_SIGNUPS is also set to true. Useful when only SSO is required.
|
||||
- **`FLAG_DISABLE_IMAGE_PROCESSING`**: Disables image processing. This is useful if you are using a machine with limited resources, like a Raspberry Pi.
|
||||
- **`FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`**: Allows dynamic OAuth client registration to use any parseable redirect URI, including custom schemes, private hosts, and non-loopback `http://` URLs. **Warning: enabling this on a public or multi-tenant deployment can enable phishing or token exfiltration.** Only enable on trusted, self-hosted deployments.
|
||||
- **`FLAG_ALLOW_UNSAFE_AI_BASE_URL`**: Allows AI providers to be configured with unsafe, private, or non-public base URLs, including `http://` and private/loopback addresses (for example, a local Ollama instance at `http://192.168.1.10:11434`). Public HTTPS provider URLs remain the safe default. **Warning: enabling this on a multi-tenant deployment is an SSRF risk.** Only enable on trusted, self-hosted deployments.
|
||||
</Accordion>
|
||||
</AccordionGroup>
|
||||
@@ -495,8 +493,7 @@ A healthy response returns HTTP 200. Any other response (or a connection failure
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dynamic OAuth redirect URI is rejected">
|
||||
- **Common cause**: redirect host is not trusted for dynamic client registration. - **Fix**: add trusted HTTPS
|
||||
hosts/origins to `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`.
|
||||
- **Common cause**: redirect URI is not the app origin or a local loopback callback. - **Fix**: use an app-origin or loopback redirect URI, or enable `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` only on a trusted self-hosted deployment that needs arbitrary redirect URIs.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="S3 storage error: ENOTFOUND bucket.endpoint">
|
||||
|
||||
@@ -66,9 +66,6 @@ You must configure endpoints using **one** of these two methods:
|
||||
| ------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------- |
|
||||
| `OAUTH_PROVIDER_NAME` | Display name shown on the sign-in button | `Custom OAuth` |
|
||||
| `OAUTH_SCOPES` | Space-separated list of OAuth scopes | `openid profile email` |
|
||||
| `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` | Comma-separated allowlist for dynamic OAuth client redirect hosts/origins (HTTPS only) | _empty_ |
|
||||
| `BETTER_AUTH_URL` | Optional auth base URL override for split-host setups | `APP_URL` |
|
||||
| `BETTER_AUTH_SECRET` | Optional Better Auth secret override | `AUTH_SECRET` |
|
||||
|
||||
## Callback URL
|
||||
|
||||
@@ -97,7 +94,7 @@ https://resume.example.com/api/auth/oauth2/callback/custom
|
||||
## URL and Proxy Requirements
|
||||
|
||||
- Set `APP_URL` to the exact public URL users access (prefer HTTPS in production).
|
||||
- If auth metadata/JWKS must be served from a different public host, set `BETTER_AUTH_URL`.
|
||||
- Auth metadata, JWKS, and OAuth callback URLs are derived from `APP_URL`.
|
||||
- Behind a reverse proxy, forward `Host` and `X-Forwarded-Proto` correctly, or cookie/session behavior may break.
|
||||
- `trustedOrigins` are derived from `APP_URL`, so alternate domains are not automatically trusted.
|
||||
|
||||
@@ -316,8 +313,10 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="Dynamic client redirect URI is rejected">
|
||||
Dynamic OAuth client registration only allows HTTPS redirect URIs on trusted hosts. Add allowed hosts/origins to
|
||||
`OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` (comma-separated).
|
||||
Dynamic OAuth client registration allows the app origin and local loopback callbacks by default. Trusted self-hosted
|
||||
deployments that need arbitrary redirect URIs can enable `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`, which permits any
|
||||
parseable redirect URI including custom schemes, private hosts, and non-loopback `http://` URLs. Do not enable it on
|
||||
public or multi-tenant deployments.
|
||||
</Accordion>
|
||||
|
||||
<Accordion title="User profile data is missing or incorrect">
|
||||
@@ -345,7 +344,7 @@ OAUTH_DISCOVERY_URL="https://auth.company.com/application/o/reactive-resume/.wel
|
||||
Configure your OAuth provider to only allow the exact redirect URI. Avoid wildcards in redirect URI configurations.
|
||||
</Card>
|
||||
<Card title="Protect auth internals" icon="shield">
|
||||
Keep `BETTER_AUTH_SECRET` and `BETTER_AUTH_API_KEY` private. Rotating these values may invalidate active sessions.
|
||||
Keep `AUTH_SECRET` and `BETTER_AUTH_API_KEY` private. Rotating `AUTH_SECRET` may invalidate active sessions.
|
||||
</Card>
|
||||
<Card title="Review scopes" icon="list-check">
|
||||
Only request the scopes you need. The default (`openid profile email`) is sufficient for Reactive Resume.
|
||||
|
||||
@@ -0,0 +1,241 @@
|
||||
# Monorepo Architecture Reorg Handoff
|
||||
|
||||
This handoff is the coordination entrypoint for the Reactive Resume architecture reorganization. It intentionally references the implementation plan instead of duplicating it.
|
||||
|
||||
## Primary Plan
|
||||
|
||||
- Plan: `docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Worklog: `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Branch at start: `feat/explore-hono-orpc-migration`
|
||||
|
||||
## Current Operating Rules
|
||||
|
||||
- Preserve existing unrelated local changes:
|
||||
- `apps/server/package.json`
|
||||
- `package.json`
|
||||
- `packages/api/package.json`
|
||||
- `packages/env/package.json`
|
||||
- `packages/utils/package.json`
|
||||
- `pnpm-lock.yaml`
|
||||
- Do not use `git reset --hard` or destructive checkout commands.
|
||||
- Use green internal commits if committing is requested/appropriate.
|
||||
- Use root `AGENTS.md` as the final normative architecture source of truth.
|
||||
- Do not create local package READMEs for architecture rules unless a package has unavoidable operational constraints.
|
||||
|
||||
## Suggested Skills for Future Agents
|
||||
|
||||
- `subagent-driven-development` for executing one task slice with review gates.
|
||||
- `executing-plans` for sequential execution from the plan.
|
||||
- `documentation-writer` for `AGENTS.md`, ADR, and public architecture docs.
|
||||
- `handoff` when pausing or delegating a task outside Codex.
|
||||
- `turborepo` when editing `turbo.json`, package tags, or task graph rules.
|
||||
- `context7-mcp` if checking current docs for library/framework behavior.
|
||||
|
||||
## Delegation Guidance
|
||||
|
||||
External agents should be handed exactly one task from the plan plus this handoff path. They should report:
|
||||
|
||||
- Status: `DONE`, `DONE_WITH_CONCERNS`, `BLOCKED`, or `NEEDS_CONTEXT`
|
||||
- Files changed
|
||||
- Tests/commands run
|
||||
- Any plan deviations
|
||||
- Follow-up tasks needed
|
||||
|
||||
## Task Ownership Status
|
||||
|
||||
- Task 0 Coordination Artifacts: done
|
||||
- Task 1 Pure Resume Domain Package: done
|
||||
- Task 2 DOCX Package: done
|
||||
- Task 3 PDF Package Browser/Server Generation Surface: done
|
||||
- Task 4 MCP Package and Shared Tool Contracts: done
|
||||
- Task 5 API Feature Reorganization: done
|
||||
- Task 6 Server Adapter Reorganization: done
|
||||
- Task 7 Domain-First Web Reorganization: done
|
||||
- Task 8 Dialog Registry Rework: done
|
||||
- Task 9 Boundary Enforcement: done
|
||||
- Task 10 Documentation and Source of Truth: done
|
||||
- Task 11 Final Validation and Handoff: done
|
||||
|
||||
## Architecture Decisions Already Locked
|
||||
|
||||
- `@reactive-resume/schema` is Zod/types only.
|
||||
- `@reactive-resume/resume` owns pure resume-domain behavior.
|
||||
- `@reactive-resume/ai` owns model contracts, not DB-backed agent runtime.
|
||||
- Agent runtime remains in `packages/api/features/agent`.
|
||||
- MCP uses an in-process oRPC `RouterClient`.
|
||||
- MCP tools move to canonical unprefixed snake_case names, no old aliases.
|
||||
- `@reactive-resume/pdf` owns PDF generation helpers but not PDF.js viewer UI.
|
||||
- `apps/web` uses domain-first features and generic-only `src/components`.
|
||||
- `packages/db` remains centralized.
|
||||
|
||||
## Latest Task 1 Result
|
||||
|
||||
- Created `@reactive-resume/resume` with source-consumed exports:
|
||||
- `@reactive-resume/resume/patch`
|
||||
- `@reactive-resume/resume/icons`
|
||||
- Moved JSON Patch behavior/tests and social network icon mapping/tests out of `@reactive-resume/utils`.
|
||||
- Updated consumers in `packages/ai`, `packages/api`, `packages/db`, `packages/import`, and `apps/web`.
|
||||
- Removed old utils exports for `./resume/patch` and `./network-icons`.
|
||||
- Removed `fast-json-patch` from `@reactive-resume/utils` and `@reactive-resume/ai`; it now belongs to `@reactive-resume/resume`.
|
||||
- Task 2 later removed `@reactive-resume/schema` from `@reactive-resume/utils` after moving DOCX code into `@reactive-resume/docx`.
|
||||
|
||||
## Latest Task 2 Result
|
||||
|
||||
- Created `@reactive-resume/docx` with source-consumed root export `@reactive-resume/docx`.
|
||||
- Moved the DOCX builder, HTML conversion, link utilities, section renderers, and colocated tests from `packages/utils/src/resume/docx` to `packages/docx/src`.
|
||||
- Updated web DOCX export callers and the export-section test mock to import from `@reactive-resume/docx`.
|
||||
- Removed the old `@reactive-resume/utils/resume/docx` export and removed `docx` plus `@reactive-resume/schema` from `@reactive-resume/utils`.
|
||||
- Validation passed for `@reactive-resume/docx` tests/typecheck, `@reactive-resume/utils` typecheck, `web` typecheck, focused web export test command, and focused Biome check.
|
||||
|
||||
## Latest Task 3 Notes
|
||||
|
||||
- Added `@reactive-resume/pdf/browser` and `@reactive-resume/pdf/server` exports.
|
||||
- `createResumePdfBlob({ data, template, resolveSectionTitle })` delegates to `pdf(<ResumeDocument ... />).toBlob()`.
|
||||
- `createResumePdfFile({ data, filename, template, resolveSectionTitle })` delegates to `renderToBuffer(<ResumeDocument ... />)` and preserves the existing `File` response body shape.
|
||||
- Lingui locale loading stays in the web-local wrappers under `apps/web/src/features/resume/export`; those wrappers resolve section titles and pass `resolveSectionTitle` into the package helpers.
|
||||
- The PDF.js viewer/canvas components remain in web. `apps/web/src/features/resume/preview/preview.browser.tsx` now calls the web-local blob wrapper instead of importing `@react-pdf/renderer` directly.
|
||||
- The authenticated PDF export path now reaches `@reactive-resume/pdf/server` through `packages/api/src/features/resume/export.ts`; `apps/server` consumes the explicit API feature export instead of owning PDF rendering logic directly.
|
||||
- Validation passed for `@reactive-resume/pdf` tests/typecheck, `web` typecheck, `server` typecheck, focused web PDF export/viewer tests, and focused Biome check.
|
||||
|
||||
## Latest Task 4 Notes
|
||||
|
||||
- Created `@reactive-resume/mcp` with source-consumed exports for the compact public surface plus direct subpaths for server card, tool names, tools, prompts, and resources.
|
||||
- Moved MCP tools, prompts, resources, server-card generation, tool annotations, and colocated tests from `apps/web/src/routes/mcp/-helpers` to `packages/mcp/src`.
|
||||
- `apps/server/src/mcp/handler.ts` and `apps/server/src/openapi/metadata.ts` now import from `@reactive-resume/mcp`; server-side MCP execution still uses the injected in-process oRPC `RouterClient`.
|
||||
- Canonical MCP tool names are unprefixed snake_case. Key renames: `read_resume` replaces the old get-resume tool name, and `apply_resume_patch` replaces the old patch tool name. No `reactive_resume_*` aliases remain.
|
||||
- Added `@reactive-resume/ai/tools/resume-tool-contracts` and reused its JSON Patch operations schema from MCP while keeping MCP-specific `id` context in the MCP input schema.
|
||||
- Validation passed for `@reactive-resume/mcp` tests/typecheck, `server` typecheck, `@reactive-resume/ai` typecheck, focused Biome check, and the app-to-app import scan.
|
||||
|
||||
## Latest Task 5 Notes
|
||||
|
||||
- `packages/api/src` is now organized by feature/capability under `packages/api/src/features`.
|
||||
- The root router still exports the same top-level API contract from `packages/api/src/routers/index.ts`, but it imports feature routers from `features/*/router`.
|
||||
- Agent modules now live under `features/agent`, with separate procedure modules for threads, messages, attachments, and actions; run-state lives in `runs.ts`, tool construction in `tools.ts`, and the remaining shared orchestration stays in `service.ts`.
|
||||
- Resume modules now live under `features/resume`, with capability procedure modules for CRUD, tags, statistics, analysis, events, sharing, and export. Access helpers and resume update events are feature-owned.
|
||||
- The authenticated PDF download procedure moved to `packages/api/src/features/resume/export.ts` and calls `@reactive-resume/pdf/server`.
|
||||
- `@reactive-resume/api` no longer exports `./services/*` or `./helpers/*`; explicit exports now cover routers, context, flags type, resume runtime/export, and storage runtime.
|
||||
- `apps/server` imports storage and the PDF procedure from explicit API feature exports. `apps/web` API imports remain type-only.
|
||||
- Follow-up risk: `features/agent/service.ts` and `features/resume/service.ts` are still large DB-backed facades. They are feature-owned now, but further splitting should be handled as behavior-preserving follow-up work with targeted tests around run lifecycle, patch transactions, notifications, and storage cleanup.
|
||||
- Validation passed for API tests/typecheck, server typecheck, web typecheck, MCP typecheck, focused Biome, and old service/helper import/export scans.
|
||||
|
||||
## Latest Task 6 Notes
|
||||
|
||||
- `apps/server/src` now reads as a runtime adapter app:
|
||||
- `http`: route composition, auth/health HTTP handlers, common header/cookie helpers
|
||||
- `rpc`: oRPC fetch handler and request-locale extraction
|
||||
- `mcp`: MCP auth, per-request server setup, and streamable HTTP transport handler
|
||||
- `openapi`: OpenAPI handler plus OAuth/OpenID/MCP well-known metadata
|
||||
- `static`: upload serving, `/schema.json`, and `apps/web/dist` static/SPA fallback serving
|
||||
- `startup`: database migrations and local-storage path checks
|
||||
- `apps/server/src/index.ts` now only re-exports `createApp`, runs startup checks, computes the port, and starts the Hono server.
|
||||
- The route order and public paths from the previous `index.ts` were preserved, including `/api/rpc`, `/api/openapi`, `/api/auth/*`, `/api/health`, `/uploads/*`, `/schema.json`, `/auth/oauth`, `/mcp`, `/.well-known/*`, and the web-dist fallback.
|
||||
- Server-side API imports remain on explicit runtime exports only; no `@reactive-resume/api/services/*` or `apps/web/src` imports were introduced.
|
||||
- Validation passed for server test/typecheck during implementation. Final Task 6 validation commands should still be listed in the worklog/final response after the executing agent's last run.
|
||||
|
||||
## Latest Task 7 Slice 1 Notes
|
||||
|
||||
- Resume-owned web code now starts under `apps/web/src/features/resume`:
|
||||
- `builder/draft.ts` owns the builder resume draft store/hooks.
|
||||
- `preview/*` owns the builder preview shell, PDF.js canvas renderer, shared preview helpers/tests, and dashboard thumbnail PDF rendering helpers.
|
||||
- `export/pdf-document*.tsx` owns web-local PDF document/blob/file wrappers around `@reactive-resume/pdf`.
|
||||
- `public/*` owns the public resume view, public PDF.js viewer, CSS, and tests.
|
||||
- The old generic resume component folder and public-route private component folder were removed after their files moved.
|
||||
- Direct `pdfjs-dist` imports are expected only under `apps/web/src/features/resume`; do not move them into `packages/pdf`.
|
||||
- The public resume route remains responsible for loader/error/head composition, keeps `ssr: "data-only"`, and lazy-loads `@/features/resume/public/public-resume`.
|
||||
- The builder preview route keeps `ssr: false` and lazy-loads the preview page composition.
|
||||
- Remaining Task 7 work should continue moving non-resume web domains out of generic `components`/routes. Task 8 still owns dialog registry decomposition beyond import updates caused by the draft-store move.
|
||||
|
||||
## Latest Task 7 Slice 2 Notes
|
||||
|
||||
- App-shell code now starts under feature folders:
|
||||
- `apps/web/src/features/command-palette` owns the command palette implementation and tests.
|
||||
- `apps/web/src/features/theme` owns the theme provider, combobox, toggle button, and tests.
|
||||
- `apps/web/src/features/locale` owns the locale combobox and tests.
|
||||
- `apps/web/src/features/user` owns the user dropdown.
|
||||
- Auth route UI now lives under `apps/web/src/features/auth`; route files under `apps/web/src/routes/auth` keep redirects/search validation and compose the feature pages.
|
||||
- Settings page UI now lives under `apps/web/src/features/settings`; route files under `apps/web/src/routes/dashboard/settings` keep dashboard header composition and redirects. `job-search.tsx` remains route-only because it is already just a redirect shim.
|
||||
- The old `@/components/{command-palette,theme,locale,user}` import paths should not be reintroduced; current consumers import from `@/features/*`.
|
||||
- `apps/web/src/components` now contains remaining generic primitive/screen folders only.
|
||||
- Task 8 still owns dialog registry decomposition; this slice did not decompose dialog definitions.
|
||||
|
||||
## Latest Task 8 Notes
|
||||
|
||||
- `apps/web/src/dialogs/store.ts` remains the single global dialog runtime and still exports `useDialogStore` plus `DialogProps<T>` for existing callers.
|
||||
- Dialog schemas now compose through `apps/web/src/dialogs/schemas.ts` from domain-owned schema entries in `dialogs/auth/schema.ts`, `dialogs/api-key/schema.ts`, and `dialogs/resume/schema.ts`.
|
||||
- Dialog rendering now composes through `apps/web/src/dialogs/renderers.tsx` from domain-owned renderer registries in `dialogs/auth/registry.tsx`, `dialogs/api-key/registry.tsx`, and `dialogs/resume/registry.tsx`.
|
||||
- `apps/web/src/dialogs/manager.tsx` only imports the dialog shell, `renderDialog`, and the global store; it no longer imports every dialog implementation directly.
|
||||
- No settings/auth-specific dialog tests existed beyond the shared dialog store and resume template tests.
|
||||
|
||||
## Latest Task 9 Notes
|
||||
|
||||
- `turbo.json` now has executable `boundaries` config:
|
||||
- Global dependencies deny `web` and `server`, preventing package-to-app and app-to-app dependency edges.
|
||||
- Root test tools are explicit `implicitDependencies`: `vitest`, `@testing-library/jest-dom`, `@testing-library/react`, and `@testing-library/user-event`.
|
||||
- Tag rules cover app, server, browser, universal, domain, and UI layers.
|
||||
- Workspace `turbo.json` files now exist for both apps and every package. Each extends root config with `extends: ["//"]` and declares tags used by the root boundaries.
|
||||
- Every workspace `vitest.config.ts` keeps its legitimate root shared config import with `// @boundaries-ignore root shared Vitest config` immediately above `import { createVitestProjectConfig } from "../../vitest.shared";`.
|
||||
- `biome.json` now enables `style.noRestrictedImports` for forbidden cross-workspace source/path imports:
|
||||
- `@reactive-resume/*/src/**`
|
||||
- `apps/**`
|
||||
- `packages/**`
|
||||
- `biome.json` also registers `tooling/grit/no-cross-workspace-src-imports.grit`, which flags import/export/dynamic import sources that reach into another workspace's `src` tree.
|
||||
- `apps/web/tsconfig.json` no longer maps `@reactive-resume/ui/*` directly to `../../packages/ui/src/*`; the web app uses `@reactive-resume/ui` package exports instead.
|
||||
- Files changed by Task 9:
|
||||
- `turbo.json`
|
||||
- `apps/*/turbo.json`
|
||||
- `packages/*/turbo.json`
|
||||
- `biome.json`
|
||||
- `tooling/grit/no-cross-workspace-src-imports.grit`
|
||||
- `apps/web/tsconfig.json`
|
||||
- `apps/server/vitest.config.ts`
|
||||
- `apps/web/vitest.config.ts`
|
||||
- `packages/*/vitest.config.ts`
|
||||
- `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Validation passed:
|
||||
- `pnpm exec turbo boundaries`
|
||||
- `pnpm exec biome check biome.json turbo.json tooling/grit/no-cross-workspace-src-imports.grit apps/web/tsconfig.json apps/server/turbo.json apps/web/turbo.json packages/*/turbo.json`
|
||||
- `pnpm --filter web typecheck`
|
||||
- `pnpm --filter @reactive-resume/api typecheck`
|
||||
- `pnpm --filter server typecheck`
|
||||
- Remaining risks:
|
||||
- The Turbo tag set is intentionally coarse and may need finer per-package rules as future browser/server package classes settle.
|
||||
- The GritQL plugin only inspects code import/export sources; keep the package export-map and tsconfig scans in the final validation list.
|
||||
- The root shared Vitest config remains an intentional boundary ignore; moving it behind a package export would be a separate test-infra cleanup.
|
||||
|
||||
## Latest Task 10 Notes
|
||||
|
||||
- `AGENTS.md` is now the operational source of truth for package roles, runtime tags, import rules, placement decisions, and validation commands.
|
||||
- `docs/contributing/architecture.mdx` is now a public contributor overview of the current monorepo layout instead of the removed single-`src` architecture.
|
||||
- `docs/adr/0001-workspace-boundaries.md` records the rationale for domain-first packages, explicit export maps, `turbo boundaries`, and Biome/Grit import enforcement.
|
||||
- MCP user docs already list canonical unprefixed tool names from Task 4. AI Agent tool docs already list `read_resume` and `apply_resume_patch`; no Task 10 changes were needed there.
|
||||
- Follow-up documentation audit on 2026-05-15 reconciled AGENTS and public docs with the final `apps/server` Hono adapter shape, current Node/pnpm prerequisites, Base UI wording, provider-native web research behavior, and the current env schema.
|
||||
- Docs validation available in-repo is limited:
|
||||
- Stale-text scan passed except expected `ORPCClient` diagram labels.
|
||||
- Biome ignored Markdown/MDX and reported that no files were processed for the docs paths.
|
||||
- No dedicated docs build/check script exists in `package.json`.
|
||||
|
||||
## Latest Task 11 Notes
|
||||
|
||||
- Final cleanup after `pnpm knip`:
|
||||
- Removed stale app dependencies from `apps/server/package.json` and `apps/web/package.json`.
|
||||
- Deleted the unused web server PDF wrapper at `apps/web/src/features/resume/export/pdf-document.server.tsx`.
|
||||
- Removed unnecessary exported markers from internal helper functions/constants.
|
||||
- Added `pdfExportRateLimit` to `packages/api/src/features/resume/export.ts`.
|
||||
- Removed unused jobs rate-limit middleware entries that no current router uses.
|
||||
- Final validation commands passed:
|
||||
- `pnpm install --lockfile-only`
|
||||
- `pnpm install`
|
||||
- `pnpm exec biome check .`
|
||||
- `pnpm exec turbo boundaries`
|
||||
- `pnpm knip`
|
||||
- `pnpm typecheck`
|
||||
- `pnpm test`
|
||||
- `pnpm build`
|
||||
- `pnpm knip` still prints a non-failing configuration hint: `src/server.ts apps/web knip.json Refine entry pattern (no matches)`.
|
||||
- Useful review anchors:
|
||||
- Architecture rules: `AGENTS.md`, `docs/contributing/architecture.mdx`, `docs/adr/0001-workspace-boundaries.md`.
|
||||
- Boundary enforcement: `turbo.json`, workspace `turbo.json` files, `biome.json`, `tooling/grit/no-cross-workspace-src-imports.grit`.
|
||||
- API feature tree: `packages/api/src/features`.
|
||||
- Server adapter tree: `apps/server/src/{http,rpc,mcp,openapi,static,startup}`.
|
||||
- Web feature tree: `apps/web/src/features`.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,277 @@
|
||||
# Monorepo Architecture Reorganization Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use `subagent-driven-development` or `executing-plans` to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
> **Status note:** This implementation plan has been executed and may contain historical intermediate paths. Use `AGENTS.md`, `docs/contributing/architecture.mdx`, `docs/adr/0001-workspace-boundaries.md`, and `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md` for current architecture guidance.
|
||||
|
||||
**Goal:** Reorganize Reactive Resume into clear domain/package boundaries so server, web, API, MCP, PDF, resume-domain logic, and documentation are easier to debug and enforce.
|
||||
|
||||
**Architecture:** This is one PR with green internal commits. Packages expose role-based explicit public surfaces, web routes become thin shells over domain features, API code is colocated by feature/capability, and boundary rules prevent app-to-app source imports and private package source imports.
|
||||
|
||||
**Tech Stack:** pnpm 11, Turborepo 2, TypeScript/tsgo, Vite, React 19, TanStack Router, oRPC, Drizzle, React PDF, PDF.js, MCP SDK, Biome/GritQL, Vitest.
|
||||
|
||||
---
|
||||
|
||||
## Non-Negotiable Decisions
|
||||
|
||||
- Root `AGENTS.md` is the single normative architecture source of truth.
|
||||
- `docs/adr/0001-workspace-boundaries.md` records rationale only; it must not duplicate the full operational rule set.
|
||||
- `docs/contributing/architecture.mdx` is a descriptive overview, not a second source of truth.
|
||||
- Keep one PR and green internal commits; no compatibility wrappers or old import-path shims.
|
||||
- Preserve existing unrelated user changes in manifests and lockfile. Do not reset or revert them.
|
||||
- Keep `apps/server` and `apps/web` split. `apps/server` may serve `apps/web/dist`; app-to-app `src` imports are banned.
|
||||
- `apps/web` may import `packages/api` types only. Runtime imports from `packages/api` in web are banned.
|
||||
- `apps/server` may import explicit runtime exports from `packages/api`.
|
||||
- Tests remain colocated with moved code.
|
||||
|
||||
## Task 0: Coordination Artifacts
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Modify: `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Modify: `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
|
||||
- [ ] Keep this plan updated when implementation discoveries force refinements.
|
||||
- [ ] Keep the handoff file focused on current task ownership and next-agent startup context.
|
||||
- [ ] Keep the worklog append-only with commands, validation results, blockers, and changed ownership decisions.
|
||||
|
||||
## Task 1: Pure Resume Domain Package
|
||||
|
||||
**Goal:** Create `@reactive-resume/resume` for pure resume-domain behavior, keeping `@reactive-resume/schema` validation-only and `@reactive-resume/utils` generic.
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/resume/package.json`
|
||||
- Create: `packages/resume/tsconfig.json`
|
||||
- Create: `packages/resume/vitest.config.ts`
|
||||
- Move/create: `packages/resume/src/patch.ts`
|
||||
- Move/create: `packages/resume/src/icons.ts`
|
||||
- Move tests from `packages/utils/src/resume/patch.test.ts`
|
||||
- Move tests for `packages/utils/src/network-icons.test.ts`
|
||||
- Modify importers/callers currently using `@reactive-resume/utils/resume/patch`
|
||||
- Modify importers/callers currently using `@reactive-resume/utils/network-icons`
|
||||
- Modify `packages/utils/package.json`
|
||||
- Modify root workspace manifests/lockfile as needed
|
||||
|
||||
- [x] Create the new package using the repo's source-consumed package pattern.
|
||||
- [x] Move JSON Patch schema/types/application/comparison/error into `@reactive-resume/resume/patch`.
|
||||
- [x] Move social network to icon-name mapping into `@reactive-resume/resume/icons`.
|
||||
- [x] Update all runtime and test imports.
|
||||
- [x] Remove `@reactive-resume/schema` dependency from `@reactive-resume/utils`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/resume test`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/resume typecheck`.
|
||||
- [x] Run targeted typechecks for known consumers: `@reactive-resume/api`, `@reactive-resume/ai`, `web`.
|
||||
|
||||
## Task 2: DOCX Package
|
||||
|
||||
**Goal:** Create `@reactive-resume/docx` as the dedicated DOCX export package.
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/docx/package.json`
|
||||
- Create: `packages/docx/tsconfig.json`
|
||||
- Create: `packages/docx/vitest.config.ts`
|
||||
- Move: `packages/utils/src/resume/docx/*` to `packages/docx/src/*`
|
||||
- Modify web export callers currently using `@reactive-resume/utils/resume/docx`
|
||||
- Modify package dependencies/lockfile
|
||||
|
||||
- [x] Create `@reactive-resume/docx` with explicit export `"."` or `"./builder"` as appropriate.
|
||||
- [x] Move DOCX implementation and tests unchanged except import paths.
|
||||
- [x] Update web callers to import DOCX export from `@reactive-resume/docx`.
|
||||
- [x] Remove DOCX dependencies from `@reactive-resume/utils` if no longer used there.
|
||||
- [x] Run `pnpm --filter @reactive-resume/docx test`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/docx typecheck`.
|
||||
- [x] Run focused web export tests/typecheck.
|
||||
|
||||
## Task 3: PDF Package Browser/Server Generation Surface
|
||||
|
||||
**Goal:** Keep `@reactive-resume/pdf` focused on document/template/font rendering and pure generation adapters. Do not move PDF.js viewer UI into the PDF package.
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/pdf/src/browser.tsx`
|
||||
- Create: `packages/pdf/src/server.tsx`
|
||||
- Modify: `packages/pdf/package.json`
|
||||
- Modify: web PDF generation callers currently using `apps/web/src/libs/resume/pdf-document.tsx`
|
||||
- Modify: `apps/server`/API PDF download code after Task 5
|
||||
|
||||
- [x] Add data-plus-options generation APIs:
|
||||
- `createResumePdfBlob({ data, template, resolveSectionTitle })`
|
||||
- `createResumePdfFile({ data, filename, template, resolveSectionTitle })`
|
||||
- [x] Keep Lingui locale loading in `apps/web`; pass `resolveSectionTitle` into PDF helpers.
|
||||
- [x] Keep `ResumeDocument` as the underlying render surface.
|
||||
- [x] Add/update tests for the browser/server helpers where practical.
|
||||
- [x] Run `pnpm --filter @reactive-resume/pdf test`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/pdf typecheck`.
|
||||
|
||||
## Task 4: MCP Package and Shared Tool Contracts
|
||||
|
||||
**Goal:** Extract MCP implementation from web route helpers into `@reactive-resume/mcp`; share model-facing tool contracts from `@reactive-resume/ai`.
|
||||
|
||||
**Files:**
|
||||
- Create: `packages/mcp/package.json`
|
||||
- Create: `packages/mcp/tsconfig.json`
|
||||
- Create: `packages/mcp/vitest.config.ts`
|
||||
- Move: `apps/web/src/routes/mcp/-helpers/*` to `packages/mcp/src/*`
|
||||
- Create/modify: `packages/ai/src/tools/resume-tool-contracts.ts`
|
||||
- Modify: `packages/ai/package.json`
|
||||
- Modify: `apps/server/src/handlers/mcp.ts`
|
||||
- Modify: `apps/server/src/handlers/metadata.ts`
|
||||
- Remove old web helper imports
|
||||
|
||||
- [x] Move MCP tools/prompts/resources/metadata card generation and tests into `@reactive-resume/mcp`.
|
||||
- [x] Keep MCP execution through an injected/in-process oRPC `RouterClient`.
|
||||
- [x] Rename MCP tools to canonical unprefixed snake_case names such as `list_resumes`, `read_resume`, `apply_resume_patch`.
|
||||
- [x] Do not keep old `reactive_resume_*` aliases.
|
||||
- [x] Use shared base tool contracts from `@reactive-resume/ai`, with MCP-specific schema extensions for explicit context fields such as `resumeId`.
|
||||
- [ ] Update MCP docs/card version and cache-refresh guidance later in documentation tasks.
|
||||
- [x] Run `pnpm --filter @reactive-resume/mcp test`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/mcp typecheck`.
|
||||
- [x] Run `pnpm --filter server typecheck`.
|
||||
|
||||
## Task 5: API Feature Reorganization
|
||||
|
||||
**Goal:** Move `packages/api/src` from technical layers into feature/capability modules with explicit public exports.
|
||||
|
||||
**Files:**
|
||||
- Reorganize under `packages/api/src/features/*`
|
||||
- Modify: `packages/api/src/routers/index.ts`
|
||||
- Modify: `packages/api/package.json`
|
||||
- Modify consumers in `apps/server`, `apps/web` type imports, and packages
|
||||
|
||||
- [x] Split `features/agent` into `threads`, `messages`, `attachments`, `actions`, `runs`, and `tools`.
|
||||
- [x] Split `features/resume` by capability: `crud`, `tags`, `statistics`, `analysis`, `access`, `events`, `sharing`, `export`.
|
||||
- [x] Move authenticated PDF download procedure into `features/resume/export`; it calls `@reactive-resume/pdf/server`.
|
||||
- [x] Keep Drizzle schema centralized in `packages/db`; API features consume schema but do not own table definitions.
|
||||
- [x] Remove `./services/*` and `./helpers/*` wildcard exports.
|
||||
- [x] Add explicit runtime exports required by `apps/server`.
|
||||
- [x] Preserve `apps/web` API imports as type-only.
|
||||
- [x] Run `pnpm --filter @reactive-resume/api test`.
|
||||
- [x] Run `pnpm --filter @reactive-resume/api typecheck`.
|
||||
- [x] Run `pnpm --filter server typecheck`.
|
||||
- [x] Run `pnpm --filter web typecheck`.
|
||||
|
||||
## Task 6: Server Adapter Reorganization
|
||||
|
||||
**Goal:** Make `apps/server` read as a runtime adapter app.
|
||||
|
||||
**Files:**
|
||||
- Reorganize `apps/server/src` into `http`, `rpc`, `mcp`, `openapi`, `static`, `startup`
|
||||
- Modify: `apps/server/src/index.ts`
|
||||
- Modify: `apps/server/package.json` if imports/dependencies change
|
||||
|
||||
- [x] Move handlers into adapter folders.
|
||||
- [x] Keep server logic thin: auth/session/HTTP transport/static serving/startup.
|
||||
- [x] Use explicit API runtime exports only.
|
||||
- [x] Keep serving `apps/web/dist` allowed.
|
||||
- [x] Run `pnpm --filter server test`.
|
||||
- [x] Run `pnpm --filter server typecheck`.
|
||||
|
||||
## Task 7: Domain-First Web Reorganization
|
||||
|
||||
**Goal:** Move web code into domain/workflow feature trees so routes become thin shells.
|
||||
|
||||
**Files:**
|
||||
- Create/reorganize under `apps/web/src/features/resume/*`
|
||||
- Create/reorganize under `apps/web/src/features/{command-palette,theme,locale,user,auth,settings,dialogs}/*`
|
||||
- Modify route imports and tests
|
||||
|
||||
- [x] Move resume domain code into workflow folders:
|
||||
- `builder`
|
||||
- `preview`
|
||||
- `public`
|
||||
- `dialogs`
|
||||
- `sections`
|
||||
- `templates`
|
||||
- `export`
|
||||
- `pdf-viewer`
|
||||
- [x] Move PDF.js viewer/canvas UI into web resume feature, not `@reactive-resume/pdf`.
|
||||
- [x] Keep route files responsible for URL params, loaders, redirects, SSR settings, and composition.
|
||||
- [x] Leave `apps/web/src/components` with generic app-level primitives/screens only.
|
||||
- [x] Move app shell concerns into separate features: command palette, theme, locale, user.
|
||||
- [x] Move auth workflows into `features/auth` and settings sections into `features/settings`.
|
||||
- [x] Run focused moved tests.
|
||||
- [x] Run `pnpm --filter web typecheck`.
|
||||
|
||||
Slice 1 notes:
|
||||
|
||||
- Moved resume builder draft state, builder preview, PDF.js canvas preview, dashboard thumbnail PDF rendering helpers, public resume PDF viewer, and web-local PDF document wrappers under `apps/web/src/features/resume/*`.
|
||||
- Removed `apps/web/src/components/resume` and `apps/web/src/routes/$username/-components`; the public resume route now lazy-loads from `features/resume/public`.
|
||||
- Kept PDF.js viewer/canvas code in `apps/web/src/features/resume` and left `@reactive-resume/pdf` limited to PDF generation helpers.
|
||||
- Broader Task 7 remains open for non-resume web feature moves and deeper dialog registry work owned by Task 8.
|
||||
|
||||
Slice 2 notes:
|
||||
|
||||
- Moved command palette, theme, locale, and user shell components/tests from `apps/web/src/components/*` to `apps/web/src/features/{command-palette,theme,locale,user}` and updated consumers.
|
||||
- Moved auth route UI, layout, and social auth component into `apps/web/src/features/auth`, leaving auth route files as guard/search/composition wrappers.
|
||||
- Moved settings page UI and authentication/integration subcomponents into `apps/web/src/features/settings`, leaving settings route files as dashboard-header/composition wrappers; `job-search` stayed route-only because it is already a redirect shim.
|
||||
- `apps/web/src/components` now contains only the remaining generic primitive/screen folders.
|
||||
- Task 8 still owns dialog registry decomposition; this slice only updated imports around existing dialog usage.
|
||||
|
||||
## Task 8: Dialog Registry Rework
|
||||
|
||||
**Goal:** Keep one central dialog runtime but make domain modules own their dialog definitions/renderers.
|
||||
|
||||
**Files:**
|
||||
- Modify/create under `apps/web/src/features/dialogs`
|
||||
- Modify/create domain dialog registry modules under relevant features
|
||||
- Modify root dialog manager import
|
||||
|
||||
- [x] Replace the single giant discriminated union/manager import hub with composable domain registries.
|
||||
- [x] Keep one global dialog store/runtime.
|
||||
- [x] Each domain exports its schema entries and renderers.
|
||||
- [x] Preserve existing dialog behavior and tests.
|
||||
- [x] Run dialog-focused tests.
|
||||
- [x] Run `pnpm --filter web typecheck`.
|
||||
|
||||
## Task 9: Boundary Enforcement
|
||||
|
||||
**Goal:** Make architectural rules executable.
|
||||
|
||||
**Files:**
|
||||
- Modify: `turbo.json`
|
||||
- Modify: `biome.json`
|
||||
- Create: `tooling/grit/no-cross-workspace-src-imports.grit`
|
||||
- Modify package manifests/tsconfigs to remove forbidden direct source path aliases
|
||||
|
||||
- [x] Add `turbo boundaries` package/runtime rules.
|
||||
- [x] Add a Biome GritQL plugin/rule for forbidden cross-workspace `src` imports and path aliases.
|
||||
- [x] Enforce subpath runtime tags: root exports environment-neutral; browser/server code behind explicit subpaths.
|
||||
- [x] Allow wildcard exports only for role-approved leaf libraries such as UI components/hooks.
|
||||
- [x] Run `pnpm exec turbo boundaries`.
|
||||
- [x] Run a focused Biome boundary check.
|
||||
|
||||
## Task 10: Documentation and Source of Truth
|
||||
|
||||
**Goal:** Document the final architecture without duplicating rules in multiple places.
|
||||
|
||||
**Files:**
|
||||
- Modify: `AGENTS.md`
|
||||
- Create: `docs/adr/0001-workspace-boundaries.md`
|
||||
- Modify: `docs/contributing/architecture.mdx`
|
||||
- Modify: `docs/guides/using-the-mcp-server.mdx` if MCP tool names change
|
||||
- Modify: `docs/guides/ai-agent-tools.mdx` if shared tool naming changes
|
||||
|
||||
- [x] Update root `AGENTS.md` with the normative workspace map, import rules, package roles, runtime tags, validation commands, and placement decision tree.
|
||||
- [x] Add ADR rationale for boundaries and rejected alternatives.
|
||||
- [x] Refresh public architecture docs as overview only.
|
||||
- [x] Update MCP docs to mention canonical unprefixed tool names and client refresh/cache-clear guidance.
|
||||
- [x] Avoid local package READMEs for architecture rules unless unavoidable.
|
||||
- [x] Run docs-related checks available in the repo.
|
||||
|
||||
## Task 11: Final Validation and Handoff
|
||||
|
||||
**Goal:** Finish with a validated branch and external-agent-ready handoff.
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Modify: `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
|
||||
- [x] Run focused checks listed in earlier tasks.
|
||||
- [ ] Run final repo checks:
|
||||
- `pnpm install --lockfile-only`
|
||||
- `pnpm exec biome check .`
|
||||
- `pnpm exec turbo boundaries`
|
||||
- `pnpm knip`
|
||||
- `pnpm typecheck`
|
||||
- `pnpm test`
|
||||
- `pnpm build`
|
||||
- [x] Update the handoff with completed tasks, remaining risks, commands run, and useful file paths.
|
||||
- [x] Dispatch final review if subagents are available.
|
||||
@@ -0,0 +1,214 @@
|
||||
# Docker Nightly and Release Tags Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox syntax for tracking.
|
||||
|
||||
**Goal:** Update the Docker image workflow so pushes to `main` publish amd64-only nightly tags, while manual runs and pushed version tags publish release tags with the existing multi-arch behavior.
|
||||
|
||||
**Architecture:** Keep the existing digest-build and manifest-merge pipeline in `.github/workflows/docker-build.yml`. Add event-based mode expressions so nightly and release publishing share setup, registry auth, digest upload/download, signing, and inspection while differing only in triggers, platform inclusion, final tags, and redeploy behavior.
|
||||
|
||||
**Tech Stack:** GitHub Actions, Docker Buildx, `docker/metadata-action@v6`, `docker/build-push-action@v7`, `docker buildx imagetools`, Cosign.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add Workflow Triggers and Mode Outputs
|
||||
|
||||
**Files:**
|
||||
- Modify: `.github/workflows/docker-build.yml`
|
||||
|
||||
- [x] **Step 1: Update workflow triggers**
|
||||
|
||||
Change the workflow `on` block to include:
|
||||
|
||||
```yaml
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- "v*"
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
- `push` to `main` runs the workflow.
|
||||
- `push` to tags matching `v*` runs the workflow.
|
||||
- PR events do not run the workflow.
|
||||
- Manual runs still run the workflow.
|
||||
|
||||
- [x] **Step 2: Add a mode job**
|
||||
|
||||
Add a `mode` job before `build`:
|
||||
|
||||
```yaml
|
||||
mode:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
nightly: ${{ steps.mode.outputs.nightly }}
|
||||
release: ${{ steps.mode.outputs.release }}
|
||||
matrix: ${{ steps.mode.outputs.matrix }}
|
||||
steps:
|
||||
- name: Determine publishing mode
|
||||
id: mode
|
||||
run: |
|
||||
if [[ "${{ github.event_name }}" == "push" && "${{ github.ref }}" == "refs/heads/main" ]]; then
|
||||
echo "nightly=true" >> "$GITHUB_OUTPUT"
|
||||
echo "release=false" >> "$GITHUB_OUTPUT"
|
||||
echo 'matrix={"include":[{"platform":"linux/amd64","runner":"ubuntu-latest","arch":"amd64"}]}' >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "nightly=false" >> "$GITHUB_OUTPUT"
|
||||
echo "release=true" >> "$GITHUB_OUTPUT"
|
||||
echo 'matrix={"include":[{"platform":"linux/amd64","runner":"ubuntu-latest","arch":"amd64"},{"platform":"linux/arm64","runner":"ubuntu-24.04-arm","arch":"arm64"}]}' >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
```
|
||||
|
||||
This job centralizes the event decision so later jobs do not duplicate the full expression.
|
||||
|
||||
### Task 2: Generate the Platform Matrix from Mode
|
||||
|
||||
**Files:**
|
||||
- Modify: `.github/workflows/docker-build.yml`
|
||||
|
||||
- [x] **Step 1: Make `build` depend on `mode`**
|
||||
|
||||
Set:
|
||||
|
||||
```yaml
|
||||
needs: mode
|
||||
```
|
||||
|
||||
on the `build` job.
|
||||
|
||||
- [x] **Step 2: Replace the static build matrix with the mode output**
|
||||
|
||||
Set the build strategy matrix to the JSON emitted by the `mode` job:
|
||||
|
||||
```yaml
|
||||
matrix: ${{ fromJSON(needs.mode.outputs.matrix) }}
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
- Nightly runs create only the `amd64` matrix entry.
|
||||
- Release/manual/tag runs create both `amd64` and `arm64` matrix entries.
|
||||
|
||||
### Task 3: Generate Conditional Final Tags
|
||||
|
||||
**Files:**
|
||||
- Modify: `.github/workflows/docker-build.yml`
|
||||
|
||||
- [x] **Step 1: Make `merge` depend on `mode` and `build`**
|
||||
|
||||
Set:
|
||||
|
||||
```yaml
|
||||
needs:
|
||||
- mode
|
||||
- build
|
||||
```
|
||||
|
||||
on the `merge` job.
|
||||
|
||||
- [x] **Step 2: Update final Docker metadata tags**
|
||||
|
||||
In the merge job's Docker metadata step, replace the unconditional release tag list with conditional tags:
|
||||
|
||||
```yaml
|
||||
tags: |
|
||||
type=sha,prefix=sha-
|
||||
type=raw,value=nightly,enable=${{ needs.mode.outputs.nightly == 'true' }}
|
||||
type=raw,value=nightly-{{date 'YYYYMMDDHHmmss' tz='UTC'}},enable=${{ needs.mode.outputs.nightly == 'true' }}
|
||||
type=raw,value=latest,enable=${{ needs.mode.outputs.release == 'true' }}
|
||||
type=raw,value=v${{ steps.version.outputs.version }},enable=${{ needs.mode.outputs.release == 'true' }}
|
||||
type=raw,value=v${{ steps.semver.outputs.major }}.${{ steps.semver.outputs.minor }},enable=${{ needs.mode.outputs.release == 'true' }}
|
||||
type=raw,value=v${{ steps.semver.outputs.major }},enable=${{ needs.mode.outputs.release == 'true' }}
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
- Nightly runs publish `nightly`, `nightly-{UTC timestamp}`, and SHA tags.
|
||||
- Release/manual/tag runs publish `latest`, version aliases, and SHA tags.
|
||||
|
||||
### Task 4: Make Post-Publish Steps Mode-Aware
|
||||
|
||||
**Files:**
|
||||
- Modify: `.github/workflows/docker-build.yml`
|
||||
|
||||
- [x] **Step 1: Emit canonical image references from the manifest step**
|
||||
|
||||
In `Create manifest list and push`, compute the canonical final tag from mode:
|
||||
|
||||
```bash
|
||||
if [[ "${{ needs.mode.outputs.nightly }}" == "true" ]]; then
|
||||
FINAL_TAG="nightly"
|
||||
else
|
||||
FINAL_TAG="v${{ steps.version.outputs.version }}"
|
||||
fi
|
||||
```
|
||||
|
||||
Use `$FINAL_TAG` for both GHCR and Docker Hub digest lookup.
|
||||
|
||||
- [x] **Step 2: Inspect the canonical final tag**
|
||||
|
||||
Update `Inspect image` to inspect:
|
||||
|
||||
```bash
|
||||
docker buildx imagetools inspect ghcr.io/${{ env.IMAGE }}:${{ steps.manifest.outputs.final_tag }}
|
||||
docker buildx imagetools inspect docker.io/${{ env.IMAGE }}:${{ steps.manifest.outputs.final_tag }}
|
||||
```
|
||||
|
||||
- [x] **Step 3: Keep redeploy release-only**
|
||||
|
||||
Add this condition to `Redeploy Stack`:
|
||||
|
||||
```yaml
|
||||
if: ${{ needs.mode.outputs.release == 'true' }}
|
||||
```
|
||||
|
||||
Expected behavior:
|
||||
- Nightly publishes images, signs them, and inspects them.
|
||||
- Nightly does not redeploy the stack.
|
||||
- Release/manual/tag publishes images, signs them, inspects them, and redeploys.
|
||||
|
||||
### Task 5: Validate Workflow Logic
|
||||
|
||||
**Files:**
|
||||
- Test: `.github/workflows/docker-build.yml`
|
||||
|
||||
- [x] **Step 1: Parse YAML**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
ruby -e 'require "yaml"; YAML.load_file(".github/workflows/docker-build.yml"); puts "yaml ok"'
|
||||
```
|
||||
|
||||
Expected output:
|
||||
|
||||
```text
|
||||
yaml ok
|
||||
```
|
||||
|
||||
- [x] **Step 2: Review the workflow diff**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git diff -- .github/workflows/docker-build.yml docs/superpowers/plans/2026-05-15-docker-nightly-release-tags.md
|
||||
```
|
||||
|
||||
Confirm these requirements in the diff:
|
||||
- `push` to `main` is enabled.
|
||||
- pushed tags matching `v*` are enabled.
|
||||
- no `pull_request` trigger exists.
|
||||
- the matrix output contains only amd64 when `nightly == 'true'`.
|
||||
- nightly tags are enabled only for nightly mode.
|
||||
- release tags are enabled only for release mode.
|
||||
- redeploy is release-only.
|
||||
|
||||
- [x] **Step 3: Check working tree scope**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
git status --short .github/workflows/docker-build.yml docs/superpowers/plans/2026-05-15-docker-nightly-release-tags.md
|
||||
```
|
||||
|
||||
Expected output includes only the workflow and this plan among files changed by this implementation.
|
||||
@@ -0,0 +1,117 @@
|
||||
# Manifest-Only PWA Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Remove Reactive Resume's service-worker and Workbox PWA behavior while keeping install metadata.
|
||||
|
||||
**Architecture:** Keep the manifest link and install meta tags in the web root route. Keep manifest data in
|
||||
`apps/web/public/manifest.webmanifest`, keep head meta tags in `apps/web/src/libs/pwa.ts`, and remove the
|
||||
service-worker registration export plus the `vite-plugin-pwa` build plugin.
|
||||
|
||||
**Tech Stack:** TanStack Start, Vite, TypeScript, pnpm, Vitest, Workbox removal.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Remove service-worker wiring
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/index.html`
|
||||
- Modify: `apps/web/vite.config.ts`
|
||||
- Modify: `apps/web/src/libs/pwa.ts`
|
||||
- Modify: `apps/web/src/routes/__root.tsx`
|
||||
- Delete: `apps/web/src/libs/pwa.test.ts`
|
||||
|
||||
- [x] **Step 1: Remove `vite-plugin-pwa` imports and plugin usage**
|
||||
|
||||
In `apps/web/vite.config.ts`, remove:
|
||||
|
||||
```ts
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import { pwaManifest } from "./src/libs/pwa";
|
||||
```
|
||||
|
||||
Delete the local `pwa()` helper and remove `pwa()` from the `plugins` array.
|
||||
|
||||
- [x] **Step 2: Add install metadata to static HTML**
|
||||
|
||||
In `apps/web/index.html`, add the manifest link, icon links, theme color, and Apple/mobile install meta tags inside
|
||||
`<head>` so install metadata is present without plugin HTML injection.
|
||||
|
||||
- [x] **Step 3: Remove dead manifest export and runtime service-worker registration**
|
||||
|
||||
In `apps/web/src/libs/pwa.ts`, remove the `pwaManifest` and `pwaServiceWorkerRegistrationScript` exports so the
|
||||
module only owns head meta tags.
|
||||
|
||||
In `apps/web/src/routes/__root.tsx`, change the PWA import to:
|
||||
|
||||
```ts
|
||||
import { pwaHeadMetaTags } from "@/libs/pwa";
|
||||
```
|
||||
|
||||
Remove the `scripts` entry that injects `pwaServiceWorkerRegistrationScript` in production.
|
||||
|
||||
- [x] **Step 4: Delete obsolete PWA unit test**
|
||||
|
||||
Delete `apps/web/src/libs/pwa.test.ts`, because the remaining PWA surface is static manifest/head metadata.
|
||||
|
||||
### Task 2: Remove unused dependency graph
|
||||
|
||||
**Files:**
|
||||
- Modify: `apps/web/package.json`
|
||||
- Modify: `pnpm-lock.yaml`
|
||||
|
||||
- [x] **Step 1: Remove direct web dependency**
|
||||
|
||||
Remove this dependency from `apps/web/package.json`:
|
||||
|
||||
```json
|
||||
"vite-plugin-pwa": "^1.3.0"
|
||||
```
|
||||
|
||||
- [x] **Step 2: Refresh lockfile narrowly**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
pnpm install --lockfile-only --offline --ignore-scripts
|
||||
```
|
||||
|
||||
Expected: the lockfile no longer contains `vite-plugin-pwa` or unused Workbox packages required only by that
|
||||
plugin. If the offline lockfile refresh is unavailable, edit the lockfile narrowly and verify with git diff.
|
||||
|
||||
### Task 3: Validate manifest-only behavior
|
||||
|
||||
**Files:**
|
||||
- Inspect: `apps/web/.output` or `apps/web/dist`
|
||||
|
||||
- [x] **Step 1: Run focused checks**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
pnpm --filter web typecheck
|
||||
pnpm --filter web build
|
||||
```
|
||||
|
||||
Expected: both commands complete successfully.
|
||||
|
||||
- [x] **Step 2: Inspect build output for service-worker artifacts**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
find apps/web/.output apps/web/dist -name 'sw.js' -o -name 'workbox-*' -o -name 'registerSW.js'
|
||||
```
|
||||
|
||||
Expected: no service-worker or Workbox files are printed. If one output directory does not exist, the command may
|
||||
print a find warning for that path; inspect the directory that exists.
|
||||
|
||||
- [x] **Step 3: Inspect source for removed service-worker registration**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
rg -n "serviceWorker|register\\(\"/sw\\.js\"|VitePWA|vite-plugin-pwa|workbox" apps/web/src apps/web/vite.config.ts apps/web/package.json
|
||||
```
|
||||
|
||||
Expected: no matches for the removed PWA service-worker path.
|
||||
@@ -0,0 +1,222 @@
|
||||
# Unsafe OAuth Redirect URI Flag Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Replace `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` with `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`, preserving safe defaults and allowing any parseable redirect URI only when the flag is enabled.
|
||||
|
||||
**Architecture:** Keep OAuth redirect URI policy centralized in `packages/utils/src/url-security.node.ts`. Pass the new env flag from both Better Auth hook validation and the server auth preflight so both paths make identical decisions. Update env/docs references and tests in the same slice.
|
||||
|
||||
**Tech Stack:** TypeScript, Zod env schema, Better Auth hook middleware, Vitest, Turborepo env filtering, MDX docs.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Modify `packages/utils/src/url-security.node.ts`: Change the OAuth redirect validator from allowlist-based to mode-based.
|
||||
- Modify `packages/utils/src/url-security.node.test.ts`: Update safe-mode tests and add unsafe-mode coverage.
|
||||
- Modify `packages/env/src/server.ts`: Remove the old allowlist env var and add `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`.
|
||||
- Modify `packages/auth/src/config.ts`: Remove host-list parsing and pass `{ allowUnsafe: env.FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI }`.
|
||||
- Modify `apps/server/src/http/auth.ts`: Remove host-list parsing and pass the same flag to the validator.
|
||||
- Modify `apps/server/src/http/auth.test.ts`: Update env mock shape and preserve existing local edits.
|
||||
- Modify `turbo.json`, `.env.example`, and MDX docs: Replace old env references with the new flag and warnings.
|
||||
|
||||
### Task 1: URL Policy Tests And Validator
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/utils/src/url-security.node.test.ts`
|
||||
- Modify: `packages/utils/src/url-security.node.ts`
|
||||
|
||||
- [ ] **Step 1: Write the failing safe/unsafe OAuth redirect tests**
|
||||
|
||||
Use this shape in `packages/utils/src/url-security.node.test.ts`:
|
||||
|
||||
```ts
|
||||
describe("isAllowedOAuthRedirectUri", () => {
|
||||
const trustedOrigins = ["https://app.example.com"];
|
||||
|
||||
it("returns false for malformed URI", () => {
|
||||
expect(isAllowedOAuthRedirectUri("nope", trustedOrigins)).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true for any parseable URI when unsafe mode is enabled", () => {
|
||||
const options = { allowUnsafe: true };
|
||||
|
||||
expect(isAllowedOAuthRedirectUri("myapp://callback", trustedOrigins, options)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("http://example.com/cb", trustedOrigins, options)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("https://192.168.1.1/cb", trustedOrigins, options)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("https://u:p@app.example.com/cb#x", trustedOrigins, options)).toBe(true);
|
||||
expect(isAllowedOAuthRedirectUri("not a url", trustedOrigins, options)).toBe(false);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the focused utils test and verify it fails**
|
||||
|
||||
Run: `pnpm --filter @reactive-resume/utils test -- src/url-security.node.test.ts`
|
||||
|
||||
Expected before implementation: TypeScript/test failure because `isAllowedOAuthRedirectUri` still requires the removed allowlist argument.
|
||||
|
||||
- [ ] **Step 3: Implement mode-based OAuth redirect validation**
|
||||
|
||||
Use this signature in `packages/utils/src/url-security.node.ts`:
|
||||
|
||||
```ts
|
||||
type OAuthRedirectUriOptions = {
|
||||
allowUnsafe?: boolean;
|
||||
};
|
||||
|
||||
export function isAllowedOAuthRedirectUri(
|
||||
input: string,
|
||||
trustedOrigins: string[],
|
||||
options?: OAuthRedirectUriOptions,
|
||||
) {
|
||||
const parsed = parseUrl(input);
|
||||
if (!parsed) return false;
|
||||
if (options?.allowUnsafe) return true;
|
||||
if (parsed.username || parsed.password) return false;
|
||||
if (parsed.hash) return false;
|
||||
|
||||
const origin = parsed.origin.toLowerCase();
|
||||
const hostname = normalizeHostname(parsed.hostname);
|
||||
|
||||
if (parsed.protocol === "http:") return isOAuthLoopbackRedirectHost(hostname);
|
||||
if (parsed.protocol !== "https:") return false;
|
||||
if (isPrivateOrLoopbackHost(hostname)) return false;
|
||||
|
||||
return trustedOrigins.includes(origin);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the focused utils test and verify it passes**
|
||||
|
||||
Run: `pnpm --filter @reactive-resume/utils test -- src/url-security.node.test.ts`
|
||||
|
||||
Expected after implementation: all tests in `url-security.node.test.ts` pass.
|
||||
|
||||
### Task 2: Env And Runtime Wiring
|
||||
|
||||
**Files:**
|
||||
- Modify: `packages/env/src/server.ts`
|
||||
- Modify: `packages/auth/src/config.ts`
|
||||
- Modify: `apps/server/src/http/auth.ts`
|
||||
- Modify: `apps/server/src/http/auth.test.ts`
|
||||
- Modify: `turbo.json`
|
||||
|
||||
- [ ] **Step 1: Update env schema and Turbo env list**
|
||||
|
||||
In `packages/env/src/server.ts`, remove:
|
||||
|
||||
```ts
|
||||
OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS: z.string().optional(),
|
||||
```
|
||||
|
||||
Add with feature flags:
|
||||
|
||||
```ts
|
||||
FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI: z.stringbool().default(false),
|
||||
```
|
||||
|
||||
In `turbo.json`, remove `"OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS"` and add `"FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI"` beside the other flags.
|
||||
|
||||
- [ ] **Step 2: Wire the flag into Better Auth config**
|
||||
|
||||
In `packages/auth/src/config.ts`, remove `parseAllowedHostList` usage and call:
|
||||
|
||||
```ts
|
||||
if (
|
||||
!isAllowedOAuthRedirectUri(uri, TRUSTED_ORIGINS, {
|
||||
allowUnsafe: env.FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI,
|
||||
})
|
||||
) {
|
||||
throw new APIError("BAD_REQUEST", {
|
||||
message: "redirect_uri is not allowed for dynamic client registration",
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Wire the flag into server preflight**
|
||||
|
||||
In `apps/server/src/http/auth.ts`, remove `parseAllowedHostList` usage and call:
|
||||
|
||||
```ts
|
||||
!isAllowedOAuthRedirectUri(redirectUri, oauthTrustedOrigins, {
|
||||
allowUnsafe: env.FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI,
|
||||
})
|
||||
```
|
||||
|
||||
Update the test env mock in `apps/server/src/http/auth.test.ts`:
|
||||
|
||||
```ts
|
||||
env: {
|
||||
SERVER_PORT: 3001,
|
||||
APP_URL: "http://localhost:3000",
|
||||
FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI: false,
|
||||
},
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run focused typechecks**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
pnpm --filter @reactive-resume/auth typecheck
|
||||
pnpm --filter server typecheck
|
||||
```
|
||||
|
||||
Expected: both commands exit 0.
|
||||
|
||||
### Task 3: Docs And Env Examples
|
||||
|
||||
**Files:**
|
||||
- Modify: `.env.example`
|
||||
- Modify: `docs/self-hosting/docker.mdx`
|
||||
- Modify: `docs/self-hosting/sso.mdx`
|
||||
- Modify: `docs/getting-started/quickstart.mdx`
|
||||
|
||||
- [ ] **Step 1: Replace old env docs with the new flag**
|
||||
|
||||
Remove all `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` references.
|
||||
|
||||
Add this warning wherever feature flags are documented:
|
||||
|
||||
```md
|
||||
`FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`: Allows dynamic OAuth client registration to use any parseable redirect URI, including custom schemes, private hosts, and non-loopback `http://` URLs. Keep disabled unless this is a trusted self-hosted deployment. Enabling it on public or multi-tenant instances can enable phishing or token exfiltration.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Verify the removed env is gone from product code and docs**
|
||||
|
||||
Run: `rg -n "OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS" . --glob "!docs/superpowers/**"`
|
||||
|
||||
Expected: no matches outside the approved design and implementation plan documents.
|
||||
|
||||
Run: `rg -n "FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI" .`
|
||||
|
||||
Expected: matches in env schema, Turbo config, docs, tests, and runtime validation paths.
|
||||
|
||||
### Task 4: Final Verification
|
||||
|
||||
**Files:**
|
||||
- Verify all modified files.
|
||||
|
||||
- [ ] **Step 1: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
pnpm --filter @reactive-resume/utils test -- src/url-security.node.test.ts
|
||||
pnpm --filter server test -- src/http/auth.test.ts
|
||||
```
|
||||
|
||||
Expected: both commands exit 0.
|
||||
|
||||
- [ ] **Step 2: Run focused typechecks and boundaries**
|
||||
|
||||
Run:
|
||||
|
||||
```sh
|
||||
pnpm --filter @reactive-resume/auth typecheck
|
||||
pnpm --filter server typecheck
|
||||
pnpm exec turbo boundaries
|
||||
```
|
||||
|
||||
Expected: all commands exit 0.
|
||||
@@ -0,0 +1,94 @@
|
||||
# Docker Nightly and Release Tagging Design
|
||||
|
||||
## Goal
|
||||
|
||||
Update `.github/workflows/docker-build.yml` so the existing Docker build pipeline supports two publishing modes without duplicating the workflow:
|
||||
|
||||
- Nightly publishing on pushes to the default branch, `main`.
|
||||
- Release publishing only on manual workflow runs or pushed Git tags.
|
||||
|
||||
## Trigger Rules
|
||||
|
||||
The workflow should run for:
|
||||
|
||||
- `workflow_dispatch`, which publishes release tags.
|
||||
- `push` to `main`, which publishes nightly tags only.
|
||||
- `push` to Git tags matching `v*`, which publishes release tags.
|
||||
|
||||
Pull request events should not publish Docker images. This avoids exposing registry credentials to PR contexts and keeps nightly publishing tied to commits that have actually landed on `main`.
|
||||
|
||||
## Publishing Modes
|
||||
|
||||
### Nightly Mode
|
||||
|
||||
Nightly mode applies when:
|
||||
|
||||
- `github.event_name == "push"`
|
||||
- `github.ref == "refs/heads/main"`
|
||||
|
||||
Nightly mode should push these tags to both GHCR and Docker Hub:
|
||||
|
||||
- `nightly`
|
||||
- `nightly-{timestamp}`
|
||||
|
||||
The timestamp should be generated by the workflow at publish time in UTC using `YYYYMMDDHHmmss`, for example `nightly-20260515143000`.
|
||||
|
||||
Nightly builds should use only the `linux/amd64` platform. They should not build or publish `linux/arm64`.
|
||||
|
||||
### Release Mode
|
||||
|
||||
Release mode applies when:
|
||||
|
||||
- The workflow is run manually with `workflow_dispatch`.
|
||||
- A Git tag is pushed.
|
||||
|
||||
Release mode should preserve the existing release tag behavior:
|
||||
|
||||
- `latest`
|
||||
- `v{VERSION}` from root `package.json`
|
||||
- `v{MAJOR}.{MINOR}`
|
||||
- `v{MAJOR}`
|
||||
|
||||
Release builds should preserve the current multi-platform behavior:
|
||||
|
||||
- `linux/amd64`
|
||||
- `linux/arm64`
|
||||
|
||||
## Workflow Shape
|
||||
|
||||
Keep a single `.github/workflows/docker-build.yml` file. The current workflow already has the useful structure:
|
||||
|
||||
1. Build per-platform images and push them by digest.
|
||||
2. Upload digest artifacts.
|
||||
3. Merge digests into final manifest tags.
|
||||
4. Sign and inspect final images.
|
||||
5. Redeploy the stack.
|
||||
|
||||
The implementation should add a small mode-detection step or expression-based conditions rather than creating a second near-identical workflow.
|
||||
|
||||
The build matrix should include both architectures, but the arm64 build should be skipped during nightly mode. This keeps release behavior unchanged while making nightly pushes cheaper and faster.
|
||||
|
||||
The merge job should generate final tags conditionally:
|
||||
|
||||
- Nightly mode emits `nightly` and `nightly-{timestamp}`.
|
||||
- Release mode emits `latest` and version aliases.
|
||||
|
||||
The merge job should inspect and sign whichever final manifest tag is authoritative for the current mode:
|
||||
|
||||
- Nightly mode can inspect/sign `nightly`.
|
||||
- Release mode can inspect/sign `v{VERSION}`.
|
||||
|
||||
## Deployment
|
||||
|
||||
The existing `Redeploy Stack` step should remain release-only. Nightly pushes should publish images but should not redeploy the running stack.
|
||||
|
||||
## Validation
|
||||
|
||||
Validate the change by reviewing the workflow syntax and checking that:
|
||||
|
||||
- A `push` to `main` resolves to a single `linux/amd64` build and nightly tags.
|
||||
- A `workflow_dispatch` run resolves to both architectures and release tags.
|
||||
- A pushed Git tag resolves to both architectures and release tags.
|
||||
- No PR event can publish images.
|
||||
|
||||
If local workflow lint tooling is unavailable, inspect the YAML and relevant GitHub Actions expressions directly.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Manifest-Only PWA Design
|
||||
|
||||
Date: 2026-05-15
|
||||
|
||||
## Goal
|
||||
|
||||
Reduce Reactive Resume's PWA implementation to install metadata only.
|
||||
|
||||
The site should remain installable on supported phones and desktops through the web app manifest, icons,
|
||||
screenshots, and mobile app meta tags. The PWA implementation should not generate, register, or rely on a
|
||||
service worker, and it should not provide offline support, app-shell precaching, runtime caching, or fallback
|
||||
navigation.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not add offline support.
|
||||
- Do not add a no-op service worker.
|
||||
- Do not add runtime caching rules for app assets, API responses, uploaded files, generated PDFs, or routes.
|
||||
- Do not change product UI, routing, auth, resume editing, public resume pages, or PDF generation behavior.
|
||||
- Do not change normal browser or HTTP caching behavior owned by the browser or server.
|
||||
|
||||
## Architecture
|
||||
|
||||
Keep install metadata in `apps/web/public/manifest.webmanifest`. That static manifest remains the source of truth
|
||||
for app name, description, theme color, background color, icons, screenshots, categories, scope, and start URL.
|
||||
|
||||
Keep mobile install/open-as-app meta tags in `apps/web/src/libs/pwa.ts`. That module continues to own:
|
||||
|
||||
- `pwaHeadMetaTags`
|
||||
- app name and theme color used by those head meta tags
|
||||
|
||||
Add the same manifest, icon, and mobile install hints directly to `apps/web/index.html` so install metadata is
|
||||
present in the initial HTML without relying on `vite-plugin-pwa` HTML injection or client-side route head updates.
|
||||
|
||||
Remove the service-worker part of the current architecture:
|
||||
|
||||
- Remove `VitePWA` usage from `apps/web/vite.config.ts`.
|
||||
- Remove the `vite-plugin-pwa` dependency from `apps/web/package.json` and the lockfile.
|
||||
- Remove the now-unused `pwaManifest` and `pwaServiceWorkerRegistrationScript` exports from `apps/web/src/libs/pwa.ts`.
|
||||
- Stop injecting the production `navigator.serviceWorker.register("/sw.js", { scope: "/" })` script from
|
||||
`apps/web/src/routes/__root.tsx`.
|
||||
|
||||
The root route keeps the existing manifest link:
|
||||
|
||||
```tsx
|
||||
{ rel: "manifest", href: "/manifest.webmanifest", crossOrigin: "use-credentials" }
|
||||
```
|
||||
|
||||
The root route also keeps the PWA-related head meta tags, including `theme-color` and Apple mobile web app tags,
|
||||
because those are part of the install/open-as-app experience rather than offline behavior.
|
||||
|
||||
## Data Flow
|
||||
|
||||
Browsers discover install metadata by reading the root document head and fetching `/manifest.webmanifest`.
|
||||
|
||||
Manifest-linked assets continue to be served as static public assets from `apps/web/public`, including:
|
||||
|
||||
- favicon assets
|
||||
- PWA icons
|
||||
- maskable icon
|
||||
- Apple touch icon
|
||||
- installation screenshots
|
||||
|
||||
No service worker is emitted by the web build, no service worker is registered at runtime, and no Workbox
|
||||
precache manifest is produced.
|
||||
|
||||
## Error Handling
|
||||
|
||||
There is no PWA-controlled offline fallback.
|
||||
|
||||
When the network is unavailable, the app behaves like a normal website: navigations, API calls, uploaded assets,
|
||||
and generated downloads fail according to the browser and server behavior already in place. This is intentional
|
||||
because installability is the only remaining PWA goal.
|
||||
|
||||
## Testing
|
||||
|
||||
Remove `apps/web/src/libs/pwa.test.ts`.
|
||||
|
||||
The file currently tests both install metadata and service-worker registration behavior. After this change, the
|
||||
remaining PWA surface is static manifest/head metadata with no behavior-specific module contract worth preserving
|
||||
as a dedicated unit test.
|
||||
|
||||
Implementation should still verify by inspection and build output that:
|
||||
|
||||
- `apps/web/dist/index.html` includes the manifest link and mobile install meta tags
|
||||
- the root route keeps the manifest link
|
||||
- PWA head meta tags remain present
|
||||
- no service-worker registration script is exported or injected by the root route
|
||||
- no `sw.js` or Workbox precache output is emitted by the web build
|
||||
|
||||
Focused validation after implementation:
|
||||
|
||||
```sh
|
||||
pnpm --filter web typecheck
|
||||
pnpm --filter web build
|
||||
```
|
||||
|
||||
If the package dependency removal changes the lockfile, verify the lockfile remains scoped to removing
|
||||
`vite-plugin-pwa` and its now-unused Workbox dependency graph.
|
||||
@@ -0,0 +1,95 @@
|
||||
# Unsafe OAuth Redirect URI Flag Design
|
||||
|
||||
Date: 2026-05-15
|
||||
|
||||
## Goal
|
||||
|
||||
Replace `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` with a single explicit escape hatch:
|
||||
`FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI`.
|
||||
|
||||
By default, dynamic OAuth client registration keeps the existing safe behavior: redirect URIs are allowed only when they target the app origin or local loopback callback hosts. When the flag is enabled, trusted self-hosted deployments may register any parseable redirect URI, including private network URLs, non-loopback `http://` URLs, and custom schemes such as `myapp://callback`.
|
||||
|
||||
## Non-Goals
|
||||
|
||||
- Do not change whether dynamic OAuth client registration is enabled.
|
||||
- Do not change MCP OAuth audience handling, login, consent, token issuance, or public client registration defaults.
|
||||
- Do not reuse `FLAG_ALLOW_UNSAFE_AI_BASE_URL`; OAuth redirect handling is a separate trust boundary.
|
||||
- Do not keep a curated redirect-host allowlist after this change.
|
||||
|
||||
## Architecture
|
||||
|
||||
The existing redirect URI policy remains centralized in `@reactive-resume/utils/url-security.node`.
|
||||
|
||||
Introduce a mode-based OAuth redirect validator API, for example:
|
||||
|
||||
```ts
|
||||
isAllowedOAuthRedirectUri(input, trustedOrigins, { allowUnsafe })
|
||||
```
|
||||
|
||||
Safe mode keeps the current rules:
|
||||
|
||||
- Reject malformed URIs.
|
||||
- Reject embedded credentials.
|
||||
- Reject URI fragments.
|
||||
- Allow `http://localhost`, `http://127.0.0.1`, and `http://[::1]` callbacks.
|
||||
- Allow `https://` callbacks whose origin matches `APP_URL`.
|
||||
- Reject other public HTTPS hosts, private HTTPS hosts, non-loopback HTTP hosts, and non-HTTP schemes.
|
||||
|
||||
Unsafe mode deliberately weakens the redirect URI trust check:
|
||||
|
||||
- Allow any URI accepted by the platform URL parser.
|
||||
- Allow custom schemes such as `myapp://callback`.
|
||||
- Allow private and loopback hosts on any supported URL scheme.
|
||||
- Allow non-loopback `http://` URLs.
|
||||
|
||||
Unsafe mode should still reject strings that are not parseable as URLs. It does not preserve the current credentials or fragment restrictions, because the requested behavior is absolute unsafe: any parseable URI scheme is accepted.
|
||||
|
||||
## Data Flow
|
||||
|
||||
`packages/env/src/server.ts` defines `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` as a `z.stringbool().default(false)` feature flag and removes `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`.
|
||||
|
||||
`packages/auth/src/config.ts` uses the flag in the Better Auth `hooks.before` validation for `/oauth2/register`.
|
||||
|
||||
`apps/server/src/http/auth.ts` uses the same flag in the server-level registration preflight before forwarding the request to Better Auth.
|
||||
|
||||
Both validation paths must make the same allow or reject decision for the same redirect URI. The server preflight remains useful because it returns OAuth-shaped registration errors before the request enters Better Auth, while the Better Auth hook remains a defense-in-depth check.
|
||||
|
||||
## Documentation
|
||||
|
||||
Remove `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS` from:
|
||||
|
||||
- `packages/env/src/server.ts`
|
||||
- `turbo.json`
|
||||
- `.env.example`
|
||||
- Docker and self-hosting docs
|
||||
- Quickstart docs
|
||||
|
||||
Add `FLAG_ALLOW_UNSAFE_OAUTH_REDIRECT_URI` to the feature flag sections and env examples with a warning that it is only appropriate for trusted self-hosted deployments. The warning should call out that enabling it permits arbitrary OAuth redirect URIs and can enable phishing or token exfiltration if used on public or multi-tenant instances.
|
||||
|
||||
## Error Handling
|
||||
|
||||
Rejected safe-mode redirects continue to return the existing error shape:
|
||||
|
||||
- Better Auth hook: `BAD_REQUEST` with `redirect_uri is not allowed for dynamic client registration`.
|
||||
- Server preflight: `400` with `invalid_redirect_uri`.
|
||||
|
||||
Unsafe mode only returns those errors when the redirect URI cannot be parsed as a URL or a non-string entry is supplied in `redirect_uris`.
|
||||
|
||||
## Testing
|
||||
|
||||
Update focused tests in the relevant packages:
|
||||
|
||||
- URL policy tests cover safe mode preserving current allowed and rejected cases.
|
||||
- URL policy tests cover unsafe mode allowing custom schemes, private hosts, non-loopback `http://`, credentials, and fragments when the URI is parseable.
|
||||
- Auth/server tests cover the env mock shape after removing `OAUTH_DYNAMIC_CLIENT_REDIRECT_HOSTS`.
|
||||
- Documentation and env references no longer mention the removed variable.
|
||||
|
||||
Run focused validation after implementation:
|
||||
|
||||
```sh
|
||||
pnpm --filter @reactive-resume/utils test -- src/url-security.node.test.ts
|
||||
pnpm --filter server test -- src/http/auth.test.ts
|
||||
pnpm --filter @reactive-resume/auth typecheck
|
||||
pnpm --filter server typecheck
|
||||
pnpm exec turbo boundaries
|
||||
```
|
||||
@@ -0,0 +1,29 @@
|
||||
# Agent Snapshot Rollback Design
|
||||
|
||||
## Context
|
||||
|
||||
The AI Agent currently stores inverse JSON Patch operations for every applied resume patch. That lets the UI offer a revert action, but it rejects some valid JSON Patch operations because the server must be able to invert them before applying the patch.
|
||||
|
||||
## Goal
|
||||
|
||||
Use persisted resume snapshots for agent rollback so valid JSON Patch operations can be applied without first generating inverse operations.
|
||||
|
||||
## Design
|
||||
|
||||
Agent patch actions store the original JSON Patch operations for audit/debugging and a `snapshotData` copy of the resume data from immediately before the patch was applied. The database column is `snapshot_data`.
|
||||
|
||||
When `apply_resume_patch` runs, the server reads the current working resume, stores `resume.data` as `snapshotData`, applies the model-generated JSON Patch through the existing resume patch validator, and records the resulting `appliedUpdatedAt`.
|
||||
|
||||
The existing action revert endpoint becomes snapshot rollback. Rolling back an action restores that action's `snapshotData`, which means the resume returns to the state before that patch. If the selected action is older than the latest applied action, every applied patch action at or after the selected action is marked `rolled_back` so the chat remains auditable and the UI can show which actions were undone.
|
||||
|
||||
Rollback keeps the version guard. The restore is allowed only when the current resume version still matches the latest applied patch action for that resume/thread. If the builder or another process changed the resume after the latest agent patch, rollback marks the selected action `conflicted` and does not replace the resume JSON.
|
||||
|
||||
Existing rows that only have `inverse_operations` are legacy non-revertible actions after the migration. The migration removes `inverse_operations` and adds nullable `snapshot_data`.
|
||||
|
||||
## UI
|
||||
|
||||
The latest applied patch can be undone with the existing action button. Older applied patches can also be restored, but the label should make the destructive effect clear: rolling back to an older action discards that action and later applied agent patches. Actions with status `rolled_back` display as rolled back and cannot be rolled back again.
|
||||
|
||||
## Testing
|
||||
|
||||
Service tests cover storing `snapshotData`, restoring a snapshot, marking later applied actions as `rolled_back`, conflict handling, and rejecting legacy actions without `snapshotData`. Schema tests cover the `snapshotData` column replacing `inverseOperations`.
|
||||
@@ -0,0 +1,323 @@
|
||||
# Monorepo Architecture Reorg Worklog
|
||||
|
||||
Append-only log for implementation, validation, and delegation notes.
|
||||
|
||||
> **Status note:** This file is chronological. Earlier entries may mention paths that were moved by later tasks. Use the latest handoff plus `AGENTS.md` and `docs/contributing/architecture.mdx` for current-state guidance.
|
||||
|
||||
## 2026-05-14
|
||||
|
||||
- Started implementation on branch `feat/explore-hono-orpc-migration`.
|
||||
- Initial dirty files existed before architecture implementation:
|
||||
- `apps/server/package.json`
|
||||
- `package.json`
|
||||
- `packages/api/package.json`
|
||||
- `packages/env/package.json`
|
||||
- `packages/utils/package.json`
|
||||
- `pnpm-lock.yaml`
|
||||
- Created coordination artifacts:
|
||||
- `docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Task 1 completed:
|
||||
- Created `packages/resume` as `@reactive-resume/resume`.
|
||||
- Moved `packages/utils/src/resume/patch.ts` to `packages/resume/src/patch.ts`.
|
||||
- Moved `packages/utils/src/network-icons.ts` to `packages/resume/src/icons.ts`.
|
||||
- Moved the patch and network icon tests into `packages/resume/src`.
|
||||
- Updated all old `@reactive-resume/utils/resume/patch` and `@reactive-resume/utils/network-icons` imports to `@reactive-resume/resume/patch` and `@reactive-resume/resume/icons`.
|
||||
- Updated package manifests and `pnpm-lock.yaml` for the new package and consumer dependency edges.
|
||||
- Removed `fast-json-patch` from `@reactive-resume/utils` and `@reactive-resume/ai`; it now belongs to `@reactive-resume/resume`.
|
||||
- Did not remove `@reactive-resume/schema` from `@reactive-resume/utils` because DOCX files still import schema and Task 2 owns that extraction.
|
||||
- Validation for Task 1:
|
||||
- Initial `pnpm --filter @reactive-resume/resume test` failed before Vitest because pnpm wanted to refresh modules in a non-TTY.
|
||||
- Retried with `CI=true`; it failed because the new workspace package was not yet represented in the frozen lockfile.
|
||||
- Ran `pnpm install --lockfile-only --no-frozen-lockfile`; completed with no downloads.
|
||||
- A later test run triggered pnpm dependency status install, but `node_modules` had been purged and registry access was blocked by sandbox DNS (`ENOTFOUND` / `fetch failed`).
|
||||
- Ran `pnpm install --no-frozen-lockfile` with network approval to restore `node_modules`; completed.
|
||||
- `pnpm --filter @reactive-resume/resume test` passed: 2 files, 43 tests.
|
||||
- `pnpm --filter @reactive-resume/resume typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/ai typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/api typecheck` passed.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- Additional direct-consumer checks passed: `pnpm --filter @reactive-resume/db typecheck`, `pnpm --filter @reactive-resume/import typecheck`, and `pnpm --filter @reactive-resume/utils typecheck`.
|
||||
- Focused `pnpm exec biome check ...` initially found import-order/type-import issues; `pnpm exec biome check --write ...` fixed 10 touched files.
|
||||
- Focused `pnpm exec biome check ...` passed afterward on the touched package/source/manifests.
|
||||
- Task 1 review correction:
|
||||
- Spec review found `packages/db` had a type-only manifest dependency on `@reactive-resume/resume`.
|
||||
- Replaced the DB import with a local structural `StoredJsonPatchOperation` type for JSONB column annotations in `packages/db/src/schema/agent.ts`.
|
||||
- Removed `@reactive-resume/resume` from `packages/db/package.json` and `pnpm-lock.yaml`.
|
||||
- Task 1 review validation:
|
||||
- `pnpm --filter @reactive-resume/db typecheck` passed after the DB boundary correction.
|
||||
- `pnpm --filter @reactive-resume/api typecheck` passed.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/resume test` passed: 2 files, 43 tests.
|
||||
- `pnpm --filter @reactive-resume/resume typecheck` passed.
|
||||
- Code-quality review noted unrelated dependency bumps in package manifests; those files were already dirty before this architecture work and were not treated as part of Task 1.
|
||||
- Task 2 completed:
|
||||
- Created `packages/docx` as `@reactive-resume/docx` with source-consumed root export.
|
||||
- Moved DOCX implementation/tests from `packages/utils/src/resume/docx` into `packages/docx/src`.
|
||||
- Updated web DOCX callers to import `buildDocx` from `@reactive-resume/docx`.
|
||||
- Removed the old `@reactive-resume/utils/resume/docx` export.
|
||||
- Removed `docx` and `@reactive-resume/schema` from `@reactive-resume/utils`.
|
||||
- Added `@reactive-resume/docx` as a web dependency.
|
||||
- Task 2 validation:
|
||||
- `pnpm --filter @reactive-resume/docx test` passed: 5 files, 46 tests.
|
||||
- `pnpm --filter @reactive-resume/docx typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/utils typecheck` passed.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- Spec review found only stale coordination text; plan and handoff were corrected.
|
||||
- Task 2 completed:
|
||||
- Created `packages/docx` as `@reactive-resume/docx` with source-consumed root export `"." -> "./src/index.ts"`.
|
||||
- Moved `packages/utils/src/resume/docx/*` to `packages/docx/src/*`, keeping the implementation and tests colocated.
|
||||
- Updated builder DOCX export callers and the export-section test mock from `@reactive-resume/utils/resume/docx` to `@reactive-resume/docx`.
|
||||
- Removed the old `@reactive-resume/utils` `./resume/docx` export.
|
||||
- Removed `docx` and `@reactive-resume/schema` from `@reactive-resume/utils`; the new DOCX package owns those dependencies and depends on `@reactive-resume/utils` only for shared color parsing.
|
||||
- Added `@reactive-resume/docx` as a web dependency and refreshed `pnpm-lock.yaml`.
|
||||
- Validation for Task 2:
|
||||
- Initial `pnpm --filter @reactive-resume/docx test` failed on pnpm's non-TTY dependency-status install guard.
|
||||
- `CI=true pnpm --filter @reactive-resume/docx test` then failed because sandbox DNS blocked registry fetches while pnpm recreated `node_modules`.
|
||||
- Retried `CI=true pnpm --filter @reactive-resume/docx test` with network approval; passed: 5 files, 46 tests.
|
||||
- `pnpm --filter @reactive-resume/docx typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/utils typecheck` passed.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- `pnpm --filter web test -- 'src/routes/builder/$resumeId/-sidebar/right/sections/export.test.tsx'` passed; Vitest reported 78 files and 430 tests.
|
||||
- Focused `pnpm exec biome check --write ...` passed on the moved DOCX package, touched web callers, and package manifests; no fixes were applied.
|
||||
- Task 3 in progress:
|
||||
- Added `packages/pdf/src/browser.tsx` with `createResumePdfBlob({ data, template, resolveSectionTitle })`.
|
||||
- Added `packages/pdf/src/server.tsx` with `createResumePdfFile({ data, filename, template, resolveSectionTitle })`.
|
||||
- Added `@reactive-resume/pdf/browser` and `@reactive-resume/pdf/server` package exports.
|
||||
- Kept Lingui locale loading in `apps/web/src/libs/resume/pdf-document.tsx`; the web wrapper now resolves localized section titles and delegates blob generation to `@reactive-resume/pdf/browser`.
|
||||
- Updated `apps/web/src/libs/resume/pdf-document.server.tsx` to delegate file generation to `@reactive-resume/pdf/server` after resolving localized section titles.
|
||||
- Updated `apps/web/src/components/resume/preview.browser.tsx` so the PDF.js canvas viewer remains in web but PDF blob generation goes through the web-local wrapper.
|
||||
- Updated `apps/server/src/handlers/resume-pdf.tsx` to reuse `@reactive-resume/pdf/server` and preserve the existing `File` response body flow.
|
||||
- Added package helper tests for browser/server generation adapters.
|
||||
- Task 3 validation so far:
|
||||
- Red test: `pnpm --filter @reactive-resume/pdf test -- src/browser.test.tsx src/server.test.tsx` failed because `packages/pdf/src/browser.tsx` and `packages/pdf/src/server.tsx` did not exist.
|
||||
- Interim implementation test exposed Vitest/Rolldown JSX transform limits for newly imported package TSX helpers; helpers now use `createElement` while keeping requested `.tsx` filenames.
|
||||
- `pnpm --filter @reactive-resume/pdf test -- src/browser.test.tsx src/server.test.tsx` passed; Vitest reported 17 files and 139 tests.
|
||||
- Initial focused web preview test failed because the test still mocked the old `useLocalizedResumeDocument` generation path.
|
||||
- Updated `apps/web/src/components/resume/preview.browser.test.tsx` to mock/assert `createResumePdfBlob`.
|
||||
- `pnpm --filter web test -- src/components/resume/preview.browser.test.tsx` passed; Vitest reported 78 files and 430 tests.
|
||||
- `pnpm --filter web test -- 'src/routes/builder/$resumeId/-sidebar/right/sections/export.test.tsx' 'src/routes/$username/-components/public-resume.test.tsx' 'src/routes/$username/-components/pdf-viewer.test.tsx'` passed; Vitest reported 78 files and 430 tests.
|
||||
- Task 4 completed:
|
||||
- Created `packages/mcp` as `@reactive-resume/mcp` with source-consumed exports for the compact public surface and direct server-card/tool/prompt/resource subpaths.
|
||||
- Moved MCP helper implementation and tests from `apps/web/src/routes/mcp/-helpers` into `packages/mcp/src`, then removed the empty web MCP helper route directory.
|
||||
- Updated `apps/server/src/handlers/mcp.ts` and `apps/server/src/handlers/metadata.ts` to import from `@reactive-resume/mcp`, removing the app-to-app source imports from `apps/server` into `apps/web`.
|
||||
- Preserved in-process MCP execution through the injected oRPC `RouterClient`; no HTTP RPC calls were introduced inside the server process.
|
||||
- Renamed MCP tool values to canonical unprefixed snake_case names: `list_resumes`, `list_resume_tags`, `read_resume`, `get_resume_analysis`, `create_resume`, `import_resume`, `duplicate_resume`, `apply_resume_patch`, `update_resume`, `delete_resume`, `lock_resume`, `unlock_resume`, and `get_resume_statistics`.
|
||||
- Updated prompts, server instructions, server-card metadata, and tests to use the canonical names and removed `reactive_resume_*` aliases/instructions.
|
||||
- Added `packages/ai/src/tools/resume-tool-contracts.ts` with the shared JSON Patch operations contract and reused it from `packages/ai/src/tools/patch-resume.ts` and MCP patch schemas.
|
||||
- Updated package manifests and `pnpm-lock.yaml`; `web` no longer owns `@modelcontextprotocol/sdk`, and `server` now depends on `@reactive-resume/mcp`.
|
||||
- Validation for Task 4:
|
||||
- Initial `pnpm --filter @reactive-resume/mcp test` reported no matching package before creation.
|
||||
- After package creation, `pnpm --filter @reactive-resume/mcp test` hit pnpm's non-TTY dependency-status install guard.
|
||||
- `CI=true pnpm --filter @reactive-resume/mcp test` then failed with a frozen-lockfile mismatch after moving dependencies.
|
||||
- Ran `pnpm install --lockfile-only --no-frozen-lockfile`; completed.
|
||||
- A later `CI=true pnpm --filter @reactive-resume/mcp test` failed because sandbox DNS blocked registry fetches while pnpm restored `node_modules`.
|
||||
- Retried `CI=true pnpm --filter @reactive-resume/mcp test` with network approval; passed: 4 files, 29 tests.
|
||||
- `pnpm --filter @reactive-resume/mcp typecheck` initially failed because the package type graph reaches API/auth TSX and tests had optional text access; fixed by enabling JSX in the package tsconfig and narrowing test content access.
|
||||
- Final validation commands passed: `pnpm --filter @reactive-resume/mcp test`, `pnpm --filter @reactive-resume/mcp typecheck`, `pnpm --filter server typecheck`, `pnpm --filter @reactive-resume/ai typecheck`, focused `pnpm exec biome check ...`, and `rg -n "\\.\\./\\.\\./\\.\\./web/src|apps/web/src|web/src/routes/mcp" apps/server/src packages/mcp/src`.
|
||||
- Initial final validation found `pnpm exec biome check ...` import/format issues in touched PDF/web files and type errors around the React element type passed into `pdf(...)` and `renderToBuffer(...)`.
|
||||
- Added narrow render-boundary casts using `Parameters<typeof pdf>[0]` and `Parameters<typeof renderToBuffer>[0]`.
|
||||
- Ran `pnpm exec biome check --write ...`; Biome fixed 4 touched files.
|
||||
- `pnpm --filter @reactive-resume/pdf test` passed: 17 files, 139 tests.
|
||||
- `pnpm --filter @reactive-resume/pdf typecheck` passed.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- `pnpm --filter server typecheck` passed.
|
||||
- `pnpm exec biome check packages/pdf/src/browser.tsx packages/pdf/src/server.tsx packages/pdf/src/browser.test.tsx packages/pdf/src/server.test.tsx packages/pdf/package.json apps/web/src/libs/resume/pdf-document.tsx apps/web/src/libs/resume/pdf-document.server.tsx apps/web/src/components/resume/preview.browser.tsx apps/web/src/components/resume/preview.browser.test.tsx apps/server/src/handlers/resume-pdf.tsx docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md` passed.
|
||||
- `pnpm --filter web test -- src/components/resume/preview.browser.test.tsx` passed; Vitest reported 78 files and 430 tests.
|
||||
- `pnpm --filter web test -- 'src/routes/builder/$resumeId/-sidebar/right/sections/export.test.tsx' 'src/routes/$username/-components/public-resume.test.tsx' 'src/routes/$username/-components/pdf-viewer.test.tsx'` passed; Vitest reported 78 files and 430 tests.
|
||||
- Task 4 review correction:
|
||||
- Spec review found stale MCP guide references to removed prefixed tool names and the unavailable `tailor_resume` prompt.
|
||||
- Updated `docs/guides/using-the-mcp-server.mdx` to document canonical tool names such as `list_resumes`, `read_resume`, and `apply_resume_patch`, and removed `tailor_resume`.
|
||||
- Removed the last code-side literal reference to the old prefixed tool-name family from `packages/mcp/src/tool-annotations.test.ts`.
|
||||
- Re-ran `rg "reactive_resume_|tailor_resume|web/src/routes/mcp|apps/web/src|\\.\\./\\.\\./\\.\\./web" apps/server packages/mcp packages/ai/src docs/guides/using-the-mcp-server.mdx -n`; no matches.
|
||||
- Re-ran `pnpm --filter @reactive-resume/mcp test`, `pnpm --filter @reactive-resume/mcp typecheck`, `pnpm --filter server typecheck`, `pnpm --filter @reactive-resume/ai typecheck`, and focused `pnpm exec biome check ...`; all passed.
|
||||
- Task 5 review correction:
|
||||
- Spec review found stale root project guidance that still pointed API work at `packages/api/src/routers/*` and `packages/api/src/services/*`.
|
||||
- Updated `AGENTS.md` so the normative guidance now points API work at `packages/api/src/features/*` and documents the new `resume`, `docx`, `pdf`, and `mcp` package ownership boundaries.
|
||||
- Re-ran `pnpm --filter @reactive-resume/api test`: 19 files, 155 tests passed.
|
||||
- Re-ran `pnpm --filter @reactive-resume/api typecheck`, `pnpm --filter server typecheck`, and `pnpm --filter web typecheck`; all passed.
|
||||
- Re-ran focused `pnpm exec biome check ...` on `AGENTS.md`, API/server/web touchpoints, and coordination docs; passed.
|
||||
- Task 7 slice 1 review correction:
|
||||
- Spec review found the moved public resume route missing its expected `ssr: "data-only"` setting and the builder preview route missing `ssr: false`.
|
||||
- Restored `ssr: "data-only"` in `apps/web/src/routes/$username/$slug.tsx`.
|
||||
- Restored `ssr: false` in `apps/web/src/routes/builder/$resumeId/index.tsx`.
|
||||
- Updated `AGENTS.md` and the handoff notes to point browser-only resume preview/public viewer code at `apps/web/src/features/resume/*` instead of the removed `components/resume` and `libs/resume` paths.
|
||||
- Task 5 completed:
|
||||
- Moved API routers and service/helpers from the old technical-layer folders into `packages/api/src/features/*`.
|
||||
- Agent is now feature-owned under `features/agent`, with procedure modules for `threads`, `messages`, `attachments`, and `actions`, run-state in `runs`, tool construction in `tools`, and remaining shared runtime orchestration in `service.ts`.
|
||||
- Finer-grained follow-up: `features/agent/service.ts` still contains shared thread/message/action orchestration because the run lifecycle, message persistence, attachment linking, and patch transaction helpers are tightly coupled; splitting that service body further should be a dedicated follow-up with behavior-specific tests.
|
||||
- Resume is now feature-owned under `features/resume`, with procedure modules for `crud`, `tags`, `statistics`, `analysis`, `event-router`, `sharing`, and `export`; access helpers and event publication moved under the same feature.
|
||||
- Finer-grained follow-up: `features/resume/service.ts` remains the DB-backed facade for shared transaction helpers, update notifications, access/statistics coupling, and storage cleanup. The public procedure surface is capability-split, and further DB-service extraction should preserve the existing transaction and event behavior.
|
||||
- Moved the authenticated PDF download procedure from `apps/server/src/handlers/resume-pdf.tsx` into `packages/api/src/features/resume/export.ts`; it now calls `@reactive-resume/pdf/server`.
|
||||
- Removed the old API package `./services/*` and `./helpers/*` wildcard exports.
|
||||
- Added explicit API runtime/type exports for `./features/storage`, `./features/resume`, `./features/resume/export`, and `./features/flags`.
|
||||
- Updated `apps/server` to import storage/PDF runtime surfaces from explicit API feature exports.
|
||||
- Updated `apps/web` to keep API imports type-only, including the `FeatureFlags` type from `@reactive-resume/api/features/flags`.
|
||||
- Added `@reactive-resume/pdf` as an API package dependency and refreshed `pnpm-lock.yaml`.
|
||||
- Validation for Task 5:
|
||||
- Initial `pnpm --filter @reactive-resume/api typecheck` failed because pnpm attempted a non-TTY dependency-status install after package metadata changes.
|
||||
- Retried with `CI=true`; sandbox DNS blocked registry fetches while rebuilding `node_modules`.
|
||||
- Retried `CI=true pnpm --filter @reactive-resume/api typecheck` with network approval; dependency restoration completed and the first compiler pass found moved import paths that needed correction.
|
||||
- `pnpm --filter @reactive-resume/api test` initially failed because the moved agent service test mocked the new `./service` module instead of its relocated AI/resume dependencies; fixed the mocks.
|
||||
- Final focused validations passed: `pnpm --filter @reactive-resume/api test`, `pnpm --filter @reactive-resume/api typecheck`, `pnpm --filter server typecheck`, `pnpm --filter web typecheck`, and `pnpm --filter @reactive-resume/mcp typecheck`.
|
||||
- Focused Biome check initially reported import-order/format issues; `pnpm exec biome check --write ...` fixed 7 touched API files.
|
||||
- Re-ran focused Biome check after the write pass; it passed.
|
||||
- Repo scans passed for no remaining `@reactive-resume/api/services/*` or `@reactive-resume/api/helpers/*` imports outside `packages/api`, no internal `../services` or `../helpers` imports, and no `./services/*` or `./helpers/*` wildcard exports in `packages/api/package.json`.
|
||||
- Task 6 completed:
|
||||
- Reorganized `apps/server/src` into runtime adapter areas: `http`, `rpc`, `mcp`, `openapi`, `static`, and `startup`.
|
||||
- Moved route registration into `apps/server/src/http/app.ts` and kept `apps/server/src/index.ts` as the process entrypoint that runs startup checks and starts Hono.
|
||||
- Moved common response/cookie helpers to `http/headers.ts`, auth and health HTTP handlers to `http`, oRPC request handling and locale extraction to `rpc`, MCP auth/server setup/transport handling to `mcp`, OpenAPI and well-known metadata to `openapi`, uploads/schema/web-dist serving to `static`, and migrations/local-storage lifecycle checks to `startup`.
|
||||
- Kept the public route order and paths from the previous `index.ts`, including serving `apps/web/dist`.
|
||||
- Preserved explicit API runtime imports only: `@reactive-resume/api/routers`, `@reactive-resume/api/features/storage`, and `@reactive-resume/api/features/resume/export`.
|
||||
- Validation for Task 6:
|
||||
- Baseline before moving files: `pnpm --filter server test` passed: 1 file, 1 test.
|
||||
- Interim validation after moving files: `pnpm --filter server typecheck` passed.
|
||||
- Interim validation after moving files: `pnpm --filter server test` passed: 1 file, 1 test.
|
||||
- Final `pnpm --filter server test` passed: 1 file, 1 test.
|
||||
- Final `pnpm --filter server typecheck` passed.
|
||||
- Final `pnpm --filter @reactive-resume/api typecheck` passed.
|
||||
- Focused `pnpm exec biome check apps/server/src docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md` initially found import-order issues in `apps/server/src/http/app.ts` and `apps/server/src/mcp/handler.ts`; after manual import ordering fixes, the same command passed.
|
||||
- `rg -n "apps/web/src|from ['\"][^'\"]*web/src|@reactive-resume/api/services/" apps/server/src` returned no matches.
|
||||
- `rg -n "@reactive-resume/api/services/" apps/server packages/api apps/web packages` returned no matches.
|
||||
- `rg -n "from ['\"][^'\"]*apps/web/src|from ['\"][^'\"]*web/src|apps/web/src" apps/server` returned no matches.
|
||||
- Task 7 slice 1 completed:
|
||||
- Moved `apps/web/src/components/resume/builder-resume-draft.ts` to `apps/web/src/features/resume/builder/draft.ts` and updated builder/dialog consumers to import the feature-owned draft store directly.
|
||||
- Moved builder preview files and colocated tests from `apps/web/src/components/resume` to `apps/web/src/features/resume/preview`, including `preview.tsx`, `preview.browser.tsx`, `preview.shared.tsx`, `pdf-canvas.tsx`, and preview shared tests.
|
||||
- Moved dashboard resume thumbnail sizing helpers to `features/resume/preview/resume-thumbnail.shared.ts` and moved PDF.js thumbnail rendering into `features/resume/preview/pdf-thumbnail.ts`, so direct `pdfjs-dist` usage stays under `features/resume`.
|
||||
- Moved web-local PDF document wrappers from `apps/web/src/libs/resume/pdf-document*.tsx` to `apps/web/src/features/resume/export/pdf-document*.tsx`; the wrappers still resolve localized section titles in web and call `@reactive-resume/pdf/browser` or `@reactive-resume/pdf/server`.
|
||||
- Moved public resume route components and tests from `apps/web/src/routes/$username/-components` to `apps/web/src/features/resume/public`, including `public-resume.tsx`, `pdf-viewer.tsx`, `pdf-viewer.css`, and their tests.
|
||||
- Updated the public resume route to lazy-load `features/resume/public/public-resume` while preserving its loader, redirects, metadata, and route settings.
|
||||
- Removed the now-empty `apps/web/src/components/resume` and `apps/web/src/routes/$username/-components` directories.
|
||||
- Broader Task 7 remains open for command palette, theme, locale, user, auth, settings, and Task 8 dialog-registry work.
|
||||
- Task 7 slice 2 completed:
|
||||
- Moved command palette implementation/tests to `apps/web/src/features/command-palette`.
|
||||
- Moved theme provider, combobox, toggle button, and tests to `apps/web/src/features/theme`.
|
||||
- Moved locale combobox implementation/tests to `apps/web/src/features/locale`.
|
||||
- Moved user dropdown implementation to `apps/web/src/features/user`.
|
||||
- Moved auth layout, page UI, and social auth component into `apps/web/src/features/auth`; auth route files now keep route guards/search validation and compose feature pages.
|
||||
- Moved settings page UI and authentication/integration subcomponents into `apps/web/src/features/settings`; settings route files now keep dashboard headers and compose feature pages.
|
||||
- Left `apps/web/src/routes/dashboard/settings/job-search.tsx` route-only because it is already a redirect shim.
|
||||
- `pnpm --filter web test -- src/features/command-palette src/features/theme src/features/locale` passed; Vitest reported 74 files and 402 tests.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- Focused Biome passed on the moved shell/auth/settings files and their route/import consumers; it checked 64 files with no fixes applied after a formatting write pass.
|
||||
- `rg -n "@/components/(command-palette|theme|locale|user)" apps/web/src` and `rg -n "components/(command-palette|theme|locale|user)" apps/web/src` returned no matches.
|
||||
- Task 8 completed:
|
||||
- Added dialog schema registries under `apps/web/src/dialogs/{auth,api-key,resume}/schema.ts` and composed them through `apps/web/src/dialogs/schemas.ts`.
|
||||
- Added domain renderer registries under `apps/web/src/dialogs/{auth,api-key,resume}/registry.tsx`, with shared renderer helper types in `apps/web/src/dialogs/renderer-registry.ts`.
|
||||
- Reduced `apps/web/src/dialogs/store.ts` to the single global dialog runtime/store while preserving typed `openDialog(type, data)` and `DialogProps<T>` exports.
|
||||
- Reduced `apps/web/src/dialogs/manager.tsx` to render via composed registries instead of directly importing all auth/API-key/resume dialog components.
|
||||
- Added a store test assertion that the central schema union is built from the domain schema registries.
|
||||
- Validation for Task 8 so far:
|
||||
- Red check: `pnpm --filter web test -- src/dialogs/store.test.ts` failed because `./schemas` did not exist.
|
||||
- `pnpm --filter web test -- src/dialogs/store.test.ts` passed after the registry implementation.
|
||||
- Initial `pnpm --filter web typecheck` found renderer-entry variance errors; fixed by typing renderer entries as an existential union of concrete dialog renderers.
|
||||
- `pnpm --filter web typecheck` passed after the renderer type fix.
|
||||
- `pnpm --filter web test -- src/dialogs/store.test.ts src/dialogs/resume/template/data.test.ts src/dialogs/resume/template/gallery.test.tsx` passed; Vitest reported 74 files and 403 tests.
|
||||
- Final `pnpm --filter web typecheck` passed.
|
||||
- Final `pnpm --filter web test -- src/dialogs/store.test.ts src/dialogs/resume/template/data.test.ts src/dialogs/resume/template/gallery.test.tsx` passed; Vitest reported 74 files and 403 tests.
|
||||
- Focused `pnpm exec biome check ...` passed on the touched dialog registry/store/manager files and Task 8 coordination docs; Biome checked 12 TypeScript files and ignored Markdown.
|
||||
- `rg -n "ts-pattern|Create[A-Za-z]+Dialog|Update[A-Za-z]+Dialog|TemplateGalleryDialog|from \"\\./(api-key|auth|resume)" apps/web/src/dialogs/manager.tsx` returned no matches.
|
||||
- `rg -n "@reactive-resume/schema/resume/data|awardItemSchema|certificationItemSchema|coverLetterItemSchema|customSectionSchema|educationItemSchema|experienceItemSchema|interestItemSchema|languageItemSchema|profileItemSchema|projectItemSchema|publicationItemSchema|referenceItemSchema|skillItemSchema|summaryItemSchema|volunteerItemSchema|z\\.object\\(\\{ type" apps/web/src/dialogs/store.ts` returned no matches.
|
||||
- Task 9 completed:
|
||||
- Inspected installed Turbo and Biome versions and local schemas/docs:
|
||||
- `pnpm exec turbo --version`: `2.9.12`
|
||||
- `pnpm exec biome --version`: `2.4.15`
|
||||
- `node_modules/.pnpm/turbo@2.9.12/node_modules/turbo/schema.json` includes root `boundaries`, `dependencies`, `dependents`, `implicitDependencies`, and workspace `tags`.
|
||||
- Context7/Biome docs and `node_modules/@biomejs/biome/configuration_schema.json` confirm local `.grit` plugins can be loaded through `biome.json` `plugins`.
|
||||
- Updated `turbo.json` with executable boundaries:
|
||||
- Deny dependencies on app workspaces `web` and `server`, preventing package-to-app and app-to-app imports.
|
||||
- Add root test-tool implicit dependencies for `vitest`, `@testing-library/jest-dom`, `@testing-library/react`, and `@testing-library/user-event` so test imports do not require duplicating root test devDependencies in every workspace package.
|
||||
- Add tag rules for app, server, browser, universal, domain, and UI layers.
|
||||
- Added workspace `turbo.json` files with `extends: ["//"]` and boundary tags for both apps and all packages.
|
||||
- Added `@boundaries-ignore root shared Vitest config` to every workspace `vitest.config.ts` import of `../../vitest.shared`; this is the only allowed cross-package source import left for the shared root test config.
|
||||
- Updated `biome.json` with `style.noRestrictedImports` patterns for:
|
||||
- `@reactive-resume/*/src/**`
|
||||
- `apps/**`
|
||||
- `packages/**`
|
||||
- Added `tooling/grit/no-cross-workspace-src-imports.grit` and registered it in `biome.json` as a second layer for import/export/dynamic import sources that reference another workspace's `src` tree.
|
||||
- Removed the cross-workspace `@reactive-resume/ui/* -> ../../packages/ui/src/*` path alias from `apps/web/tsconfig.json`; web now relies on the UI package export map.
|
||||
- Files changed for Task 9:
|
||||
- `turbo.json`
|
||||
- `apps/server/turbo.json`
|
||||
- `apps/web/turbo.json`
|
||||
- `apps/web/tsconfig.json`
|
||||
- `biome.json`
|
||||
- `tooling/grit/no-cross-workspace-src-imports.grit`
|
||||
- `apps/server/vitest.config.ts`
|
||||
- `apps/web/vitest.config.ts`
|
||||
- `packages/ai/turbo.json`
|
||||
- `packages/ai/vitest.config.ts`
|
||||
- `packages/api/turbo.json`
|
||||
- `packages/api/vitest.config.ts`
|
||||
- `packages/auth/turbo.json`
|
||||
- `packages/auth/vitest.config.ts`
|
||||
- `packages/config/turbo.json`
|
||||
- `packages/config/vitest.config.ts`
|
||||
- `packages/db/turbo.json`
|
||||
- `packages/db/vitest.config.ts`
|
||||
- `packages/docx/turbo.json`
|
||||
- `packages/docx/vitest.config.ts`
|
||||
- `packages/email/turbo.json`
|
||||
- `packages/email/vitest.config.ts`
|
||||
- `packages/env/turbo.json`
|
||||
- `packages/env/vitest.config.ts`
|
||||
- `packages/fonts/turbo.json`
|
||||
- `packages/fonts/vitest.config.ts`
|
||||
- `packages/import/turbo.json`
|
||||
- `packages/import/vitest.config.ts`
|
||||
- `packages/mcp/turbo.json`
|
||||
- `packages/mcp/vitest.config.ts`
|
||||
- `packages/pdf/turbo.json`
|
||||
- `packages/pdf/vitest.config.ts`
|
||||
- `packages/resume/turbo.json`
|
||||
- `packages/resume/vitest.config.ts`
|
||||
- `packages/runtime-externals/turbo.json`
|
||||
- `packages/schema/turbo.json`
|
||||
- `packages/schema/vitest.config.ts`
|
||||
- `packages/scripts/turbo.json`
|
||||
- `packages/ui/turbo.json`
|
||||
- `packages/ui/vitest.config.ts`
|
||||
- `packages/utils/turbo.json`
|
||||
- `packages/utils/vitest.config.ts`
|
||||
- `docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- `docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md`
|
||||
- Validation for Task 9:
|
||||
- Baseline `pnpm exec turbo boundaries` failed with 321 issues before configuration, mostly undeclared root test-tool imports and shared root Vitest config imports.
|
||||
- Intermediate `pnpm exec turbo boundaries` passed after adding implicit dependencies and the Vitest config boundary-ignore comments.
|
||||
- Final `pnpm exec turbo boundaries` passed: checked 660 files in 20 packages, no issues found.
|
||||
- `pnpm exec biome check biome.json turbo.json tooling/grit/no-cross-workspace-src-imports.grit apps/web/tsconfig.json apps/server/turbo.json apps/web/turbo.json packages/*/turbo.json` passed: checked 24 files, no fixes applied.
|
||||
- `pnpm --filter web typecheck` passed.
|
||||
- `pnpm --filter @reactive-resume/api typecheck` passed.
|
||||
- `pnpm --filter server typecheck` passed.
|
||||
- Remaining risks for Task 9:
|
||||
- The current Turbo tags are coarse layer tags. They enforce the current obvious app/browser/server/domain/UI direction, but future package splits may need more granular tags or per-package dependency/dependent rules.
|
||||
- The GritQL plugin is intentionally narrow: it only covers code import/export sources. JSON/tsconfig source-path aliases are covered separately by `noRestrictedImports`, manual scans, and the removal of the web-to-UI source alias.
|
||||
- The shared root `vitest.shared` import remains intentionally ignored in workspace Vitest configs. Moving that helper into a package would remove the ignore comments but would be a broader test-infra reorg.
|
||||
- Task 10 completed:
|
||||
- Updated `AGENTS.md` with executable boundary rules, package-role/runtime tags, a placement decision tree, and the `pnpm exec turbo boundaries` validation command.
|
||||
- Replaced the stale public architecture guide at `docs/contributing/architecture.mdx`, which still described an old single-`src` layout, with the current monorepo runtime map, workspace ownership table, boundary rules, feature placement guide, API layout, PDF/DOCX boundaries, and MCP boundary.
|
||||
- Added `docs/adr/0001-workspace-boundaries.md` with the accepted decision, context, consequences, and rejected alternatives.
|
||||
- Left MCP and AI Agent user guides unchanged in this task because Task 4 already updated MCP tool names, and `docs/guides/ai-agent-tools.mdx` already documents the canonical `read_resume` and `apply_resume_patch` tool names.
|
||||
- Did not add local package READMEs; `AGENTS.md`, the architecture guide, the ADR, `turbo.json`, workspace `turbo.json` tags, and `biome.json` are the source of truth.
|
||||
- Validation for Task 10:
|
||||
- `rg -n "src/integrations|src/components/resume|packages/api/src/services|packages/api/src/helpers|reactive_resume_|tailor_resume|Radix UI|ORPC" AGENTS.md docs/contributing/architecture.mdx docs/adr/0001-workspace-boundaries.md docs/guides/using-the-mcp-server.mdx docs/guides/ai-agent-tools.mdx` returned only the expected `ORPCClient` labels in the architecture mermaid diagram.
|
||||
- `pnpm exec biome check AGENTS.md docs/contributing/architecture.mdx docs/adr/0001-workspace-boundaries.md docs/superpowers/plans/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/worklogs/2026-05-14-monorepo-architecture-reorg.md docs/superpowers/handoffs/2026-05-14-monorepo-architecture-reorg.md` did not process files because this Biome config ignores Markdown/MDX; there is no dedicated docs check script in `package.json`.
|
||||
- Task 11 completed:
|
||||
- Ran `pnpm install --lockfile-only`; first pass was already up to date, and a later pass updated `pnpm-lock.yaml` after removing stale app dependencies.
|
||||
- Ran `pnpm install` after manifest cleanup so pnpm's dependency-status check would stop trying to purge modules from non-TTY subcommands.
|
||||
- Cleaned up final `pnpm knip` findings:
|
||||
- Deleted unused `apps/web/src/features/resume/export/pdf-document.server.tsx`.
|
||||
- Removed unused app dependencies from `apps/server/package.json` and `apps/web/package.json`.
|
||||
- Removed unused internal exports in dialog registries, startup checks, API AI helpers, and agent tool helpers.
|
||||
- Reattached `pdfExportRateLimit` to `downloadResumePdfProcedure`.
|
||||
- Removed stale jobs rate-limit middleware exports that no current router uses.
|
||||
- `pnpm knip` now exits successfully with only the existing configuration hint: `src/server.ts apps/web knip.json Refine entry pattern (no matches)`.
|
||||
- Final validation for Task 11:
|
||||
- `pnpm install --lockfile-only` passed.
|
||||
- `pnpm exec biome check .` passed: 756 files checked, no fixes applied.
|
||||
- `pnpm exec turbo boundaries` passed: 659 files checked in 20 packages, no issues found.
|
||||
- `pnpm knip` passed with one configuration hint and no unused files/dependencies/exports.
|
||||
- `pnpm typecheck` passed: 18 successful tasks.
|
||||
- `pnpm test` passed: 18 successful tasks; notable totals include web 74 files/403 tests, UI 42 files/421 tests, API 19 files/155 tests, PDF 17 files/139 tests.
|
||||
- `pnpm build` passed: web and server builds completed.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "AI Resume Builder"
|
||||
description: "Use Reactive Resume with optional BYO-provider AI assistance for resume analysis, builder edits, imports, and agent drafts."
|
||||
---
|
||||
|
||||
Reactive Resume can be used as an AI-assisted resume builder, but AI is optional and bring-your-own-provider: you configure the provider, model, endpoint, and API key you want Reactive Resume to use.
|
||||
|
||||
## What AI does in Reactive Resume
|
||||
|
||||
AI features can help with resume analysis, AI-assisted changes in the builder, AI agent drafts, and supported import workflows. The core resume builder still works without AI.
|
||||
|
||||
Use these guides for the full setup:
|
||||
|
||||
- [Using Artificial Intelligence](/guides/using-ai)
|
||||
- [Using AI in the builder](/guides/using-ai-in-the-builder)
|
||||
- [Using the AI Agent Workspace](/guides/using-ai-agent)
|
||||
- [AI Agent Tools](/guides/ai-agent-tools)
|
||||
|
||||
## Bring your own provider
|
||||
|
||||
Reactive Resume supports configuring an AI provider from the Integrations settings. The provider settings include the provider type, model, base URL when needed, and API key.
|
||||
|
||||
This is not positioned as a hosted AI-writing product. You choose whether to enable AI, which provider to connect, and when to send resume content to that provider.
|
||||
|
||||
## Where AI fits in the workflow
|
||||
|
||||
AI assistance is most useful after you already have resume content or structured source material. You can review suggestions, apply changes in the builder, or use agent drafts as isolated workspaces before deciding what belongs in your final resume.
|
||||
|
||||
## When not to use AI
|
||||
|
||||
Do not use AI features if you do not want resume content sent to the provider you configure. Do not rely on AI output as final application material without review, and use the regular builder when you need direct manual control over exact wording.
|
||||
|
||||
## Next action
|
||||
|
||||
Configure and test a provider with [Using Artificial Intelligence](/guides/using-ai). Then use [Using AI in the builder](/guides/using-ai-in-the-builder) for in-place assistance or [Using the AI Agent Workspace](/guides/using-ai-agent) for draft-based work.
|
||||
|
||||
<Warning>
|
||||
Review AI-generated resume content before using it in applications. You are responsible for the accuracy of the final
|
||||
resume.
|
||||
</Warning>
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
title: "API and MCP Resume Automation"
|
||||
description: "Automate resume workflows in Reactive Resume with API keys, the Patch API, the MCP server, AI agent tools, and the JSON resume schema."
|
||||
---
|
||||
|
||||
Reactive Resume supports resume automation through authenticated API access and an MCP server that lets compatible tools list, read, create, import, duplicate, and patch resumes.
|
||||
|
||||
## Automation options
|
||||
|
||||
Use the API when you want direct programmatic access from scripts, services, or integrations. Use MCP when you want an AI tool or agent that supports the Model Context Protocol to work with your resumes through exposed tools.
|
||||
|
||||
Key docs:
|
||||
|
||||
- [Using the API](/guides/using-the-api)
|
||||
- [Using the Patch API](/guides/using-the-patch-api)
|
||||
- [Using the MCP Server](/guides/using-the-mcp-server)
|
||||
- [AI Agent Tools](/guides/ai-agent-tools)
|
||||
- [JSON Resume Schema](/guides/json-resume-schema)
|
||||
|
||||
## Common automation workflows
|
||||
|
||||
You can build workflows that:
|
||||
|
||||
- Create a resume from structured data.
|
||||
- Import a full resume JSON document.
|
||||
- Read resume data for review or transformation.
|
||||
- Patch targeted fields without replacing the whole resume.
|
||||
- Connect an MCP-compatible client to operate on resumes with authenticated tools.
|
||||
|
||||
The Patch API is the safest starting point for targeted resume updates because it is designed around explicit changes to existing resume data.
|
||||
|
||||
## Authentication and scope
|
||||
|
||||
API requests use API keys. MCP can use OAuth2 in clients that support it, with API keys available as a fallback. If you self-host, use your own instance URL for API and MCP endpoints.
|
||||
|
||||
<Info>
|
||||
Automation changes affect the resumes available to the authenticated account or instance. Test workflows on a copy of a
|
||||
resume before using them for important application materials.
|
||||
</Info>
|
||||
|
||||
## When not to use automation
|
||||
|
||||
Do not start with API or MCP automation when you only need to edit one resume manually. The builder is usually faster for one-off changes, template selection, and visual review. Avoid automation for important resume updates unless you can test the exact changes on a copy first.
|
||||
|
||||
## Next action
|
||||
|
||||
For scripts and integrations, create an API key with [Using the API](/guides/using-the-api), then use [Using the Patch API](/guides/using-the-patch-api) for targeted edits. For agent workflows, start with [Using the MCP Server](/guides/using-the-mcp-server).
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: "Export and Share Resumes"
|
||||
description: "Export Reactive Resume resumes as PDFs and share public resume URLs with human recipients."
|
||||
---
|
||||
|
||||
Reactive Resume lets you export resumes as PDFs and, when useful, share a public resume URL with human recipients such as recruiters, hiring managers, collaborators, or your own portfolio visitors. Public resume URLs are not search-indexed by default.
|
||||
|
||||
## Export a PDF
|
||||
|
||||
PDF export is the right choice when an application requires a file upload or when you want a fixed document to send by email.
|
||||
|
||||
See [Exporting your resume](/guides/exporting-your-resume) for the export workflow.
|
||||
|
||||
## When to export
|
||||
|
||||
Export a PDF when a job application requires an uploaded document, when you need a fixed copy for records, or when you want to send a file that will not change after you submit it.
|
||||
|
||||
## When to share a link
|
||||
|
||||
Share a public resume URL when a human recipient can open a link and you want them to see the current version of your resume.
|
||||
|
||||
## Share a public resume URL
|
||||
|
||||
Public sharing creates a URL you can send to someone else. The public page shows the current version of your resume and can include a download option for viewers.
|
||||
|
||||
Public resume URLs are intended for human recipients who receive or discover the link from you.
|
||||
|
||||
## When not to share only a link
|
||||
|
||||
Do not send only a public URL when an application explicitly requires a PDF or document upload. Do not use public sharing for restricted audiences unless you also use password protection or keep the resume private until you are ready to share it.
|
||||
|
||||
See [Sharing your resume publicly](/guides/sharing-your-resume-publicly) for setup, password protection, statistics, and how to turn public access off.
|
||||
|
||||
## Use the builder dock
|
||||
|
||||
The builder dock includes shortcuts that support common editing and sharing actions from the resume builder.
|
||||
|
||||
See [Using the builder dock](/guides/using-the-builder-dock) for the available actions.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="PDF export" icon="file-pdf" href="/guides/exporting-your-resume">
|
||||
Download a fixed resume file.
|
||||
</Card>
|
||||
<Card title="Public sharing" icon="share" href="/guides/sharing-your-resume-publicly">
|
||||
Share a live resume URL with selected recipients.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Next action
|
||||
|
||||
Use [Exporting your resume](/guides/exporting-your-resume) if you need a file, or [Sharing your resume publicly](/guides/sharing-your-resume-publicly) if a link is acceptable.
|
||||
@@ -0,0 +1,62 @@
|
||||
---
|
||||
title: "Free Resume Builder"
|
||||
description: "Use Reactive Resume as a free resume builder for creating, editing, exporting, and sharing resumes."
|
||||
---
|
||||
|
||||
Reactive Resume is a free resume builder for creating resumes in a browser, choosing a template, exporting a PDF, and sharing a human-readable public URL when you want someone else to view it. Public resume URLs are shareable for human recipients and are not search-indexed by default.
|
||||
|
||||
## What you can do for free
|
||||
|
||||
- Create and manage resumes from the dashboard.
|
||||
- Edit resume content with a live preview in the builder.
|
||||
- Choose from the templates included with Reactive Resume.
|
||||
- Export your resume as a PDF.
|
||||
- Share a public resume URL with recruiters, hiring managers, or collaborators.
|
||||
|
||||
Start with [Introduction](/getting-started/index) for the product overview or follow the [Quickstart](/getting-started/quickstart) to create your first resume on [rxresu.me](https://rxresu.me).
|
||||
|
||||
## Builder workflow
|
||||
|
||||
Reactive Resume focuses on the core resume workflow: add your profile, work history, education, skills, projects, and other sections, then adjust layout and template choices before exporting.
|
||||
|
||||
Useful guides:
|
||||
|
||||
- [Creating your first resume](/guides/creating-your-first-resume)
|
||||
- [Choosing a template](/guides/choosing-a-template)
|
||||
- [Fitting content on a page](/guides/fitting-content-on-a-page)
|
||||
- [Using the builder dock](/guides/using-the-builder-dock)
|
||||
|
||||
## When to use this path
|
||||
|
||||
Use the hosted app when you want to create a resume quickly without running infrastructure. It fits personal resume editing, template selection, PDF export, and sharing a public resume URL with people who need to review your resume.
|
||||
|
||||
## When not to use this path
|
||||
|
||||
Do not rely on the hosted app alone if your organization requires a controlled deployment, custom auth, or specific data residency rules. In that case, review [Self-hosting with Docker](/self-hosting/docker). If an application requires a file upload, export a PDF instead of sending only a public URL.
|
||||
|
||||
## Export and sharing options
|
||||
|
||||
You can download a PDF for applications that require a file. You can also enable a public resume URL for human recipients when a link is more convenient than an attachment.
|
||||
|
||||
Public resume URLs are for sharing with people you choose to send the link to. For details, see [Exporting your resume](/guides/exporting-your-resume) and [Sharing your resume publicly](/guides/sharing-your-resume-publicly).
|
||||
|
||||
## Next action
|
||||
|
||||
Open [rxresu.me](https://rxresu.me) to create a resume, or follow [Creating your first resume](/guides/creating-your-first-resume) if you want the guided workflow.
|
||||
|
||||
## Related resources
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Use the hosted app" icon="rocket" href="https://rxresu.me">
|
||||
Open the official Reactive Resume instance.
|
||||
</Card>
|
||||
<Card title="Source code" icon="github" href="https://github.com/amruthpillai/reactive-resume">
|
||||
View the project repository.
|
||||
</Card>
|
||||
<Card title="License" icon="scale-balanced" href="/legal/license">
|
||||
Review the project license.
|
||||
</Card>
|
||||
<Card title="Privacy policy" icon="shield-check" href="/legal/privacy-policy">
|
||||
Learn how the hosted service describes data handling.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
title: "Open-Source Resume Builder"
|
||||
description: "Learn how Reactive Resume works as an open-source resume builder with public source code, an MIT license, and self-hosting support."
|
||||
---
|
||||
|
||||
Reactive Resume is an open-source resume builder: its source code is public, it is licensed under MIT, and you can use the hosted app or run your own instance.
|
||||
|
||||
## What open source means here
|
||||
|
||||
The project repository is available on [GitHub](https://github.com/amruthpillai/reactive-resume). You can inspect the code, report issues, contribute improvements, and review how the resume builder, API, self-hosting setup, and documentation are implemented.
|
||||
|
||||
The license is documented in [License](/legal/license).
|
||||
|
||||
## Product capabilities
|
||||
|
||||
Reactive Resume includes a browser-based builder, resume templates, PDF export, public sharing links for human recipients, API access, MCP support, and optional AI-assisted workflows. Public resume URLs are shareable for human recipients and are not search-indexed by default. The main user workflow starts in the hosted app at [rxresu.me](https://rxresu.me) or on a self-hosted deployment.
|
||||
|
||||
Helpful starting points:
|
||||
|
||||
- [Introduction](/getting-started/index)
|
||||
- [Quickstart](/getting-started/quickstart)
|
||||
- [Creating your first resume](/guides/creating-your-first-resume)
|
||||
- [Managing resumes from the dashboard](/guides/managing-resumes-from-the-dashboard)
|
||||
|
||||
## When to use this path
|
||||
|
||||
Use the open-source path when you want transparency, want to inspect how resume data and exports are handled, want to contribute fixes or translations, or need the option to run your own deployment.
|
||||
|
||||
## When not to use this path
|
||||
|
||||
Do not start with the source code if your immediate goal is only to build a resume. Use [rxresu.me](https://rxresu.me) or the [Quickstart](/getting-started/quickstart) first. Do not assume open source automatically covers your compliance needs; if you operate an instance, review the self-hosting and privacy docs.
|
||||
|
||||
## Contribution and customization
|
||||
|
||||
If you want to contribute or understand the codebase, start with the contributor docs. They describe the local development setup, package layout, and project conventions.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Development setup" icon="code" href="/contributing/development">
|
||||
Set up the repository locally.
|
||||
</Card>
|
||||
<Card title="Architecture" icon="diagram-project" href="/contributing/architecture">
|
||||
Understand the app and package boundaries.
|
||||
</Card>
|
||||
<Card title="Translations" icon="language" href="/contributing/translations">
|
||||
Help translate the app.
|
||||
</Card>
|
||||
<Card title="GitHub repository" icon="github" href="https://github.com/amruthpillai/reactive-resume">
|
||||
Browse issues, pull requests, and source code.
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
## Next action
|
||||
|
||||
If you want to contribute, set up the project with [Development setup](/contributing/development). If you want to operate it yourself, start with [Self-hosting with Docker](/self-hosting/docker).
|
||||
@@ -0,0 +1,46 @@
|
||||
---
|
||||
title: "Privacy-Focused Resume Builder"
|
||||
description: "Understand the privacy-focused parts of Reactive Resume, including open source transparency, self-hosting, public sharing controls, and optional AI provider configuration."
|
||||
---
|
||||
|
||||
Reactive Resume is a privacy-focused resume builder because it is open source, can be self-hosted, supports private-by-default resume editing, and makes sharing and optional AI features explicit choices.
|
||||
|
||||
## Privacy controls in the resume workflow
|
||||
|
||||
Resumes are edited inside your account. When you want someone else to view a resume, you can enable a public URL and send that link to human recipients. Public resume URLs are not search-indexed by default. Public access can be turned off again, and public resumes can optionally require a password.
|
||||
|
||||
For the sharing workflow, see [Sharing your resume publicly](/guides/sharing-your-resume-publicly). For account-level security, see [Setting up two-factor authentication](/guides/setting-up-two-factor-authentication) and [Setting up passkeys](/guides/setting-up-passkeys).
|
||||
|
||||
## When to use this path
|
||||
|
||||
Use this path when you want to understand the privacy tradeoffs before choosing hosted use, self-hosting, public sharing, API automation, or optional AI features. It is especially useful if your resume contains sensitive job-search information or if you plan to share links selectively.
|
||||
|
||||
## When not to use this path
|
||||
|
||||
Do not treat public sharing as private access control by itself. Use password protection or keep the resume private if the link should only be opened by specific people. Do not enable AI features unless you are comfortable sending relevant prompts and resume content to the provider you configure.
|
||||
|
||||
## Open source and self-hosting
|
||||
|
||||
The source code is available on [GitHub](https://github.com/amruthpillai/reactive-resume), so you can inspect how the application works. If you need direct control over infrastructure, storage, auth providers, and deployment policy, you can self-host Reactive Resume.
|
||||
|
||||
Start with:
|
||||
|
||||
- [Self-hosting with Docker](/self-hosting/docker)
|
||||
- [Self-hosting examples](/self-hosting/examples)
|
||||
- [Single sign-on](/self-hosting/sso)
|
||||
- [Privacy policy](/legal/privacy-policy)
|
||||
|
||||
## Optional AI features
|
||||
|
||||
AI features are optional. Reactive Resume does not need AI to create, edit, export, or share a resume. When you choose to use AI-assisted features, you configure a provider and key for the provider you want to use.
|
||||
|
||||
For setup details, see [Using Artificial Intelligence](/guides/using-ai), [Using AI in the builder](/guides/using-ai-in-the-builder), and [Using the AI Agent Workspace](/guides/using-ai-agent).
|
||||
|
||||
<Info>
|
||||
Review the privacy policy for the instance you use. If you self-host, you are responsible for the deployment's data
|
||||
handling, storage, email, and third-party provider configuration.
|
||||
</Info>
|
||||
|
||||
## Next action
|
||||
|
||||
Review [Privacy Policy](/legal/privacy-policy), then choose either [Sharing your resume publicly](/guides/sharing-your-resume-publicly) for link controls or [Self-hosting with Docker](/self-hosting/docker) for infrastructure control.
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
title: "Self-Hosted Resume Builder"
|
||||
description: "Run Reactive Resume as a self-hosted resume builder with Docker, PostgreSQL, optional storage, SSO, and migration guidance."
|
||||
---
|
||||
|
||||
Reactive Resume can be run as a self-hosted resume builder when you want to operate the app on your own infrastructure instead of using the hosted instance.
|
||||
|
||||
## When self-hosting is a good fit
|
||||
|
||||
Self-hosting is useful when you need control over deployment, domain, database, storage, email delivery, authentication providers, and operational policies. The Docker guide is the main setup path for most deployments.
|
||||
|
||||
Start here:
|
||||
|
||||
- [Self-hosting with Docker](/self-hosting/docker)
|
||||
- [Self-hosting examples](/self-hosting/examples)
|
||||
- [Single sign-on](/self-hosting/sso)
|
||||
- [Migration guide](/self-hosting/migration)
|
||||
|
||||
## Core deployment pieces
|
||||
|
||||
A typical self-hosted deployment uses:
|
||||
|
||||
- Reactive Resume application container.
|
||||
- PostgreSQL database.
|
||||
- SMTP configuration for account emails, or console-logged emails in simple development setups.
|
||||
- Optional S3-compatible storage for uploads.
|
||||
- Optional SSO or custom OAuth configuration.
|
||||
|
||||
The Docker guide includes the environment variable reference and a Compose example.
|
||||
|
||||
## Feature considerations
|
||||
|
||||
Some features require extra configuration. For example, saved AI providers need server-side encryption configuration, and the AI Agent workspace uses Redis. Self-hosted deployments can also expose API and MCP endpoints from their own domain.
|
||||
|
||||
For automation setup on self-hosted instances, see [Using the API](/guides/using-the-api) and [Using the MCP Server](/guides/using-the-mcp-server).
|
||||
|
||||
<Tip>
|
||||
Replace hosted URLs such as <code>https://rxresu.me</code> with your own instance URL when following API, MCP, or
|
||||
sharing examples.
|
||||
</Tip>
|
||||
Reference in New Issue
Block a user