All jobs

Find Dead Code in a React Project

Yes. An agent lists the unused files, exports and dependencies in your React codebase, checking each one against the build and tests.

What the agent does

The agent clones the repository into its own cloud sandbox and reads how it is put together before running any tool, because a dead-code finder is only as right as its idea of the entry points. In a React codebase like excalidraw/excalidraw it opens the root package.json and finds yarn@1.22.22 as the packageManager and three workspace globs: excalidraw-app, packages/* and examples/*. Under packages/ it finds seven packages (common, element, excalidraw, fractional-indexing, laser-pointer, math, utils), each with its own package.json and exports map, and it notes that @excalidraw/excalidraw is published to npm at 0.18.0 with packages/excalidraw/index.tsx as its public entry.

For a TypeScript and React repository it uses knip, which reports unused files, unused exports and unused or unlisted dependencies per workspace. It writes a knip config that matches the repository instead of running the defaults. The root tsconfig.json maps every @excalidraw/* import to source with paths, and vitest.config.mts repeats those mappings as aliases, so the agent makes sure knip resolves them; without that, every cross-package import looks broken and every package export looks unused. It marks packages/excalidraw/index.tsx and each package's exports as entries, excalidraw-app/index.tsx as the app's entry, setupTests.ts as a test entry (it is the setupFiles entry in vitest.config.mts) and the scripts under scripts/ as entries, since build-node and buildPackage.js are run from package.json rather than imported.

Then it treats knip's output as a list of suspects, not a verdict. Static analysis misses code reached through a side-effect import, a config file or a published API. In this repository vitest-canvas-mock and fake-indexeddb are only imported for their side effects in setupTests.ts, rewire is only required by scripts/build-node.js, and an export reached only through index.tsx is used by everyone who installs the npm package. So an export that is part of a published package's public surface is listed separately as public API to decide on, never as dead.

Each remaining candidate is checked by removing it on a scratch branch inside the sandbox and running what the repository already uses to catch breakage: yarn test:typecheck (plain tsc against the noEmit root config), yarn test:code (ESLint with --max-warnings=0), yarn build:packages and yarn build for the esbuild package builds and the Vite app build, and yarn test:app --watch=false for the Vitest suite. A dependency is checked by removing it from its workspace's package.json, reinstalling with yarn install and running the same commands. Anything that turns one of them red goes back, with the failing output, into a short section of things that looked dead and were not. Nothing is pushed and no pull request is opened.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Clone https://github.com/excalidraw/excalidraw. Find unused files, exports and package dependencies with knip, configured for this Yarn workspaces monorepo: resolve the @excalidraw/* path aliases from tsconfig.json and vitest.config.mts, and treat each package's exports, excalidraw-app/index.tsx, setupTests.ts and the scripts run from package.json as entry points. Then check each candidate by removing it on a scratch branch and running yarn test:typecheck, yarn test:code, yarn build:packages, yarn build and yarn test:app --watch=false. Write dead-code.md listing only what passed that check, grouped into files, exports and dependencies, each with its path and the command output that proved it. List exports of published packages separately as public API, and list every candidate that failed the check with the error. Do not push or open a pull request."
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is one file, dead-code.md, as an artifact on the session; reading the session returns a download link for it that works for an hour, and a fresh link on every read. It has three sections of things that are safe to delete: files nothing imports, exports nothing imports, and dependencies nothing requires, each line with its path, the workspace it belongs to and the commands that still passed with it removed. A fourth section lists exports that only a published package's entry reaches, so the maintainers can decide whether they are public API. A fifth lists the suspects that failed the check, with the error each one produced, which is usually where knip's config needs an extra entry point.

The proof sits beside the report. The session's transcript keeps every command the agent ran with its output, including the knip run and each delete-and-rebuild cycle, so a reviewer can open the step for any line in the report and see the type check, build and test output it rests on.

Before deleting anything, a reviewer checks the public API section against the package's changelog and semver promises, looks for code loaded by a path a build tool cannot see (a dynamic import() with a computed path, a file named in an HTML template or a deploy config), and confirms that the commands the agent ran are the ones the team's CI runs. The deletions themselves are a follow-up: send the session a message asking it to open a pull request that removes the confirmed items.

Run it on every first Monday of the month

Dead code builds up slowly, so a monthly report is usually enough. Have a cron job on your server send this same request on the first Monday of each month, with an Idempotency-Key built from the date so a retried request does not start a second run. Each run starts from a fresh clone, so the report reflects the default branch as it is that day.

Set a webhook URL on the API key and Rush POSTs run.completed when the run's turn finishes, with the session_url. Your server reads the session from that URL, takes the dead-code.md link from its artifacts and posts it to the team channel or attaches it to a tracking issue.

The use case this job follows, with its automation and trigger: Produce a report or file, no repo.

Why run it on Rush?

Proving that code is dead means deleting it and rebuilding, many times over. On a laptop that is an afternoon of stashing, reinstalling and waiting on a type check across seven packages, and it leaves the working tree in a state someone has to clean up. In a Rush sandbox the agent does every delete-and-rebuild cycle on its own disk, with its own node_modules, and nothing it removes touches your checkout or your branches.

The result also has to be trusted by someone who did not run it. A list from a static tool alone invites the question of whether each line was checked; this report comes with the transcript of every command and its output, so each claim that a file is safe to remove can be traced to a passing build and test run. Several of these can run at once, one per repository or per workspace, up to the key's concurrent run limit.

Frequently asked questions

Does it work on a private repository?

Yes. Install the Rush GitHub App on that repository and add it to the request as a resource, {"url": "https://github.com/your-org/your-app"}; the agent starts with the repository cloned. The example clones a public repository by URL, which needs no GitHub App at all.

Will it delete anything from my repository?

No. Every deletion happens on a scratch branch inside the sandbox and is only used to run the build and tests. The prompt says not to push or open a pull request, and the result is a report you download. If you want the deletions, ask for a pull request in a follow-up message to the same session.

Why not just run knip in CI?

Knip is a good finder, and the agent uses it. What it cannot tell you is whether removing a reported item breaks a build, a test or a side-effect import it could not see. The agent configures knip for your workspaces, then proves every candidate by removing it and running your real commands, and lists the ones that failed with their errors.

What about exports that other projects use from my published package?

Those look unused from inside the repository, because the code that imports them lives elsewhere. The agent treats each package's exports and its entry file as entry points, and anything reached only through them is listed as public API for you to decide on, not as dead code.