# AGENTS.md Guidance for AI coding agents working in this repository. ## Project Next.js frontend for a Paradox save file parser. It uploads a game save file to a backend service, polls until parsing finishes, and displays the result in a grid. - **Next.js 16** (App Router, Turbopack) with **React 19** - **TypeScript 6 and 7** side by side (see "Things that will bite you"), **ESLint 9** (flat config), **Prettier 3** - **ag-grid** for the data grid, **Bootstrap 5** for styling Source files live in `src/`: - `app/` for routes, - `components/` for shared UI - `services/` for backend calls - `lib/` for helpers - `config/` for app config - `types/` for .d.ts type declaration files ## Architecture The parsing backend is a **separate service in its own repository**, not part of this project. Start it manually before running the frontend; it is expected at `http://127.0.0.1:5226`. The browser never calls that backend directly. Requests go through a Next route handler that forwards them: ``` SaveParserBackendService -> /api/proxy/saveParserBackend/* -> backend (client, fetch) (server, route handler) ``` - [src/services/SaveParserBackendService.ts](src/services/SaveParserBackendService.ts) is the only place that talks to the backend. It exposes `uploadSave`, `getSaveStatus` and `getParsedSave` — upload, poll until parsing finishes, then fetch the result. - [src/app/api/proxy/saveParserBackend/[[...path]]/route.ts](src/app/api/proxy/saveParserBackend/[[...path]]/route.ts) forwards every method to the configured backend URL. It exists only to keep requests same-origin, and is meant to be deleted once the backend handles CORS itself. - Route paths live in [src/app/api/proxy/paths.ts](src/app/api/proxy/paths.ts) rather than as string literals, so both sides of the proxy stay in sync. Configuration comes from [src/config/AppConfig.ts](src/config/AppConfig.ts) (defaults) merged with `runtimeConfig.json` at the repo root, which holds the backend URL. That file is gitignored, so a fresh clone starts without one and falls back to the defaults. ### Current status Much of the app is still scaffold. Do not assume a feature works because the UI for it exists: - **The grid is not wired up.** [src/app/browse/page.tsx](src/app/browse/page.tsx) renders two hardcoded rows and never calls the backend. Nothing connects the upload flow to it yet. - **Auth does not work.** `AuthService` posts to `/api/auth/login` and `/api/auth/register`, which do not exist as routes. The service is slated for removal in favour of Discord OAuth. - **`hashPassword` is a no-op** that returns the password unchanged. Treat any current auth code as a placeholder, not as something to build on. ## Terminal **On Windows, prefer `bash.exe`** — but only a real MSYS2, Cygwin, or MinGW/Git Bash installation, for example `D:\msys2\usr\bin\bash.exe`. **Never use `C:\Windows\System32\bash.exe`.** Despite its name it is not a POSIX bash; it is the launcher stub for WSL. Do not start WSL to run ordinary commands. If no MSYS/Cygwin/MinGW bash is installed, **use PowerShell** as the terminal. PowerShell is the fallback, never WSL. If bash reports `command not found` for `node` or `npm`, that is a `PATH` problem rather than a missing install: MSYS2 defaults `MSYS2_PATH_TYPE` to `minimal`, which discards the Windows `PATH`. Setting it to `inherit` fixes it. ## Commands ```sh npm run dev # dev server npm run build # production build npm run lint # eslint npm run format # prettier, rewrites everything except .gitignore/.prettierignore npx tsc --noEmit # typecheck ``` ### Checking your work **Always run `npm run lint` and `npm run build` after changing code.** They are the primary way to find errors in this project, and they catch different things: - `npm run lint` reports code errors and style violations. It must finish with zero problems — no errors and no warnings. - `npm run build` catches type errors and build-time problems that lint cannot see, such as Node APIs leaking into the edge runtime. It must exit cleanly and print no warnings. `npx tsc --noEmit` is a faster way to check types alone while iterating, but it does not replace a full build. Do not report a change as finished until both commands pass. ## Version control This project is managed with **git**. **Read-only git commands are fine to use without asking.** `git status`, `git log`, `git diff`, `git show`, `git blame` and similar inspection commands may be run freely. **Never run a git command that changes the repository without the user's explicit permission.** This is a strict ban, and it covers everything that writes to the history, the index, or the working tree including `add`, `commit`, `checkout`, `restore`, `reset`, `stash`, `branch`, `merge`, `rebase`, `push`, `rm`, etc. Ask first and wait for a clear yes before running such git command. ## Conventions Prettier settings live in `.prettierrc` and are not negotiable: **4 spaces, no semicolons, single quotes, 80 column width**. Run `npm run format` after editing, or match the surrounding style exactly. Import from within `src/` using the `@/` alias, for example `import { getMsgFromError } from '@/lib/errors'`. Write doc comments as JSDoc (`/** ... */`), which editors show on hover. Plain `//` and `///` comments do not appear in tooltips. In `.ts` files put only prose in the JSDoc and let the signature carry the types; do not duplicate types in `{braces}`. Catch blocks should not annotate the error. Let it be `unknown` and convert it with the shared helper: ```ts } catch (err) { const errMsg = getMsgFromError(err) ... } ``` Avoid `any`; `no-explicit-any` is enforced. Prefer `unknown` plus narrowing. ## Things that will bite you **Both TypeScript 6 and 7 are installed on purpose, under swapped names.** TypeScript 7 is the native Go compiler and type-checks much faster, but typescript-eslint refuses to run under it, which would break `npm run lint` entirely. So each tool gets the version it can use: | package.json entry | resolves to | used by | | -------------------- | ------------------------------------------------ | ----------------- | | `typescript` | `@typescript/typescript6` (TS 6 API, bin `tsc6`) | typescript-eslint | | `@typescript/native` | `typescript@7` (Go compiler) | `next build` | The alias is deliberately reversed: typescript-eslint looks the compiler up by the package _name_ `typescript`, so that name must stay on TS 6. Do not "fix" this by renaming the entries. `next.config.ts` sets `experimental.useTypeScriptCli`, which makes Next spawn a `tsc` binary instead of using the compiler API. Next resolves that binary from the package named `typescript` too, so it would also get TS 6. The `prebuild` script [scripts/replace-tsc6-with-tsc7.mjs](scripts/replace-tsc6-with-tsc7.mjs) works around this by rewriting `node_modules/typescript/bin/tsc6` into a one-line stub that imports TS 7's `lib/tsc.js`. Consequences to keep in mind: - The patch lives in `node_modules`, so `npm install` undoes it. `prebuild` reapplies it, so `npm run build` is always correct; nothing else needs doing. - `npx tsc --noEmit` is unaffected either way. `node_modules/.bin/tsc` links straight to `@typescript/native`, so the standalone typecheck is always TS 7. - Do not drop the `prebuild` script while leaving `useTypeScriptCli` enabled. Nothing breaks visibly — the build just silently goes back to type-checking with TS 6. **All of this is temporary.** The only reason TS 6 is still here is that TypeScript 7.0 ships no stable programmatic JS API, so typescript-eslint cannot run on it. That API arrives in **TypeScript 7.1, planned for 2026-11-24**. Once it is out and typescript-eslint supports it, tear the whole arrangement down: drop `@typescript/native` and the `@typescript/typescript6` alias for a plain `"typescript": "^7.1"`, delete `scripts/replace-tsc6-with-tsc7.mjs` and the `prebuild` script, and re-check whether `useTypeScriptCli` is still worth keeping. **Env vars in npm scripts must go through `cross-env`.** The `dev` and `build` scripts set `NEXT_TELEMETRY_DISABLED=1`, which stops Next from collecting anonymous usage data and saves the ~0.5s the telemetry flush adds to every build. The `FOO=1 cmd` prefix is POSIX shell syntax, and on Windows npm runs scripts through `cmd`, where it is a syntax error — so the prefix is written as `cross-env NEXT_TELEMETRY_DISABLED=1 next ...` instead. Keep `cross-env` on any new script that needs an env var. Setting the variable from `prebuild` would not work: `prebuild` and `build` are sibling processes, so an env var exported by one is not visible to the other. Each script that needs it sets it itself. `prebuild` has no `cross-env` prefix because it only rewrites the tsc bin and never talks to telemetry. **`instrumentation.ts` is compiled for the edge runtime too and it can't be disabled.** Anything using `fs`, `path`, or other Node APIs must be imported behind the `process.env.NEXT_RUNTIME === 'nodejs'` guard, or the build warns and pulls Node-only code into the edge bundle.