← All Articles 🧹 Code Quality High By Nishant Kumar Verma

Vibe Code Cleanup: Fixing an AI-Built App You Inherited

Inherited a Lovable, Bolt or Cursor app? A 7-step vibe code cleanup order with exact commands: secrets, access rules, dependencies, build, map, tests, CI.

SEC-014 SEC-001 TST-001 GIT-001

Quick Answer

Do vibe code cleanup in order of risk, not in order of ugliness. First scan the files and the whole Git history for secrets with Gitleaks, and rotate anything real. Then check who can read which data, then dependency CVEs, then whether the app installs, builds and type-checks. Only after that, map the code, add tests around what you will change, and put the same checks in CI so the mess does not come back.

The Triage Order at a Glance

When you take over an app someone built with Lovable, Bolt, Cursor or a similar tool, the urge is to start refactoring. Wait. The first three steps deal with damage that may already be happening, and none of them needs you to understand the code yet.

Step What you check Main command Done when
1Secrets in files and Git historygitleaks git -v --log-opts="--all" .Every real key is rotated
2Access rules and authOne SQL query, then read each API routeEvery table has rules, every route checks the user
3Dependenciesnpm audit or trivy fs .No critical or high left without a reason
4Install, build, type-checknpm ci, npm run build, npx tsc --noEmitAll three pass
5Map the codenpm run, git logA one-page note exists
6Tests around changesnpx vitest runEach function you change has a test
7Checks that stay onA CI workflow and a pre-commit hookEvery push runs steps 1, 3, 4 and 6

Before step 1, get access to the repository, the hosting and database accounts, and every API provider account. You cannot rotate a key in an account you cannot log in to. If you are checking your own app before launch rather than after a handover, our pre-ship checklist covers that case.

Step 1: Secrets in the Files and the Git History

Start here, because a leaked key can be used right now. A key deleted in a later commit still sits in every earlier one, as our guide to secrets in Git history explains. Gitleaks, an MIT-licensed scanner, checks both. Its git mode reads the patches from git log -p, so it sees past commits. Its dir mode scans the files as they are today:

# Install (macOS)
brew install gitleaks

# The files as they are today
gitleaks dir -v .

# Every commit on every branch (--log-opts is passed to git log)
gitleaks git -v --log-opts="--all" .

Gitleaks exits with 1 when it finds leaks or hits an error, and 0 when it finds none. Older guides use gitleaks detect and gitleaks protect. Version 8.19.0 deprecated both, though they still run. The --all option makes git log start from every ref.

For every real finding, rotate the key at the provider first. The git-filter-repo manual and GitHub's guide to removing sensitive data both put revoking and rotating before anything else, and both say rotation may make a history rewrite unnecessary. If you still want the old commits gone, rewrite them in a fresh clone:

# BAD: deleting the line only removes the key from the newest commit
git commit -am "remove hardcoded key"

# GOOD: rotate the key at the provider first, then rewrite history in a fresh clone
git clone https://github.com/YOUR-USERNAME/YOUR-REPOSITORY
cd YOUR-REPOSITORY
git-filter-repo --sensitive-data-removal --replace-text ../passwords.txt

# Confirm the old key is gone from every ref, then push every ref
git log -S"YOUR-OLD-KEY" --all -p --
git push --force --mirror origin

Each line of passwords.txt is one secret. By default it is replaced with ***REMOVED***, or you can end the line with ==> and your own text. The rewrite gives those commits new hashes, and GitHub warns that copies in forks stay reachable. The filter-repo manual adds that everyone else should stop working during the cleanup, because the force push discards anything they pushed in the meantime.

Step 2: Access Rules and Auth

Next, find out who can read what. OWASP ranks this first in its API Security Top 10 as API1:2023 Broken Object Level Authorization: an API must check that the logged-in user may act on the exact record requested. An app can look finished and still let any signed-in user fetch anyone's rows.

If the app uses Supabase, list the tables without row level security. In PostgreSQL's pg_tables view, rowsecurity is true when it is switched on:

-- Which public tables have row level security switched off?
select tablename, rowsecurity from pg_tables where schemaname = 'public';

-- BAD: the query returns  invoices | false

-- GOOD: switch it on and add a policy in the same change
alter table public.invoices enable row level security;

create policy "Users can view their own invoices"
on invoices for select
to authenticated
using ( (select auth.uid()) = user_id );

Do both at once. Supabase's docs say that once RLS is on, the API returns no data with a publishable key until policies exist, so switching it on alone breaks the app. The same page says service_role bypasses RLS and should stay server-side, and of secret keys: "Never use a secret key in the browser or expose it to customers." If a secret or service_role key turns up in frontend code, go back to step 1.

Then open every API route and server function and answer two questions. Does it check on the server who the user is? Does it check that the record belongs to them? A list of routes with yes or no next to each is the output of this step.

Steps 3 and 4: Dependencies, Then a Clean Build

npm audit is built into npm. By default it needs a package-lock.json or npm-shrinkwrap.json. Trivy, under the Apache 2.0 licence, scans a whole project directory, and its fs mode looks for vulnerabilities and secrets by default:

# Report only, change nothing
npm audit

# Preview what fix would change, then apply the compatible updates
npm audit fix --dry-run --json
npm audit fix

# Or Trivy, showing only high and critical findings
brew install trivy
trivy fs --severity HIGH,CRITICAL .

Leave npm audit fix --force alone unless you know why you need it. npm's docs say it can install SemVer-major changes, and they strongly advise against it if you are unsure what you want. Our Trivy guide shows how to read the report and fix packages you never installed directly.

Then prove the app installs and builds from its lockfile, the way a server would:

# Clean install from the lockfile; stops if package.json and the lockfile disagree
npm ci

# The project's own build script
npm run build

# Type-check only, write no files
npx tsc --noEmit

npm ci removes node_modules first and never writes to package.json or the lockfile, so a failure here is real, not a leftover from someone's laptop. It also needs a lockfile, so if there is none, that is your first finding. In the TypeScript compiler options, --noEmit switches off output, which turns tsc into a type-check. Do not deploy anything until all three commands pass.

Step 5: Map the Code Before You Refactor

Now you can read. Start with what the project says it does: npm run with no script name lists every script in package.json. Then see which files changed most often, which are good places to start reading:

# List the project's scripts
npm run

# The 20 most-changed files in the history
git log --name-only --format="" | sort | uniq -c | sort -rn | head -20

Write a one-page note: how a request enters the app, where login is checked, where data is written, and which outside services it calls. AI tools write code faster than people read it, which our post on comprehension debt covers, and a large generated codebase is easier to follow as a map than as a file tree (see when an AI codebase is too big to understand). You can also use a code map such as Vibe X-Ray.

Do not refactor while you map. Rename nothing and move nothing until steps 6 and 7 are in place.

Steps 6 and 7: Tests Around Changes, Then Checks That Stay On

Before you change a function, pin down what it does today with a test. Vitest installs with one command, and vitest run runs the tests once without watching for changes:

npm install -D vitest
npx vitest run
// GOOD: record today's behaviour before you touch the function
import { expect, test } from 'vitest'
import { invoiceTotal } from './billing'

test('totals line items the way production does today', () => {
  expect(invoiceTotal([{ price: 1000, qty: 3 }])).toBe(3000)
})

If it fails after your change and you meant to keep that behaviour, you caught a regression before your users did. AI tools rarely write useful tests on their own, as our guide to test coverage in vibe-coded apps explains.

Last, make every push run the checks. This is the Node.js workflow from GitHub's Actions guide, with the audit and type-check added and npm test swapped for npx vitest run. We changed Node 20 to 24, because nodejs.org lists 20 as end-of-life and 24 as LTS (checked on 24 October 2026):

name: Node.js CI

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - name: Use Node.js
        uses: actions/setup-node@v7
        with:
          node-version: '24.x'
      - run: npm ci
      - run: npm audit --audit-level=high
      - run: npx tsc --noEmit
      - run: npm run build --if-present
      - run: npx vitest run

--audit-level=high makes npm audit fail the job only for high or critical findings. For secrets, the Gitleaks README gives a pre-commit hook. Save this as .pre-commit-config.yaml, then run pre-commit autoupdate and pre-commit install:

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.24.2
    hooks:
      - id: gitleaks

Tools like VibeDoctor's Vibe Check (vibedoctor.io) automatically scan the repository and the live site with 149+ checks, including secrets via Gitleaks, dependency CVEs via Trivy and a registry lookup for every import, and flag each problem with specific file paths and line numbers. Free to sign up.

Do It Yourself or Hire a Vibe Code Cleanup Specialist?

Development agencies now sell this work as vibe code cleanup. The service pages we read name security fixes, refactoring, tests and CI pipelines, and list no prices (checked on 24 October 2026). One says a code review comes first and sets the timeline.

Steps 1, 3 and 4 are commands you can run yourself, and their output is the brief you would hand a specialist anyway. Bring in help when:

  • The tables hold other people's personal or payment data, and you cannot tell which rules protect them.
  • The build or type-check fails in code you cannot follow, even after step 5.
  • A history rewrite needs several people to stop work and re-clone, and nobody owns the coordination.

Whatever the label (cleanup specialist, fixer, rescue or vibe coding consultant), ask for the same seven outputs:

  1. Every secret found, and the date each was rotated.
  2. RLS status and policies for every table, and a yes or no for every API route.
  3. npm audit or Trivy output before and after.
  4. npm ci, the build and tsc --noEmit passing.
  5. The one-page map.
  6. Tests around every function they changed.
  7. A CI file that runs the checks on every push.

FAQ

How do you clean up vibe coded code?

In order of risk: scan the files and the whole Git history for secrets and rotate anything real, check who can read which data, fix dependency CVEs, make sure the app builds and type-checks, map the code before refactoring, add tests around what you change, then keep the checks running in CI.

What does a vibe code cleanup specialist do?

Agencies selling the service describe reviewing AI-generated code, fixing security gaps such as exposed credentials, refactoring, adding tests and setting up CI. The triage in this guide covers the same ground, in order of risk: secrets, access rules, dependencies, a clean build, then a map, tests and checks.

What should I fix first in an inherited Lovable or Bolt app?

Secrets. Run gitleaks git -v --log-opts="--all" . to scan every commit on every branch, and rotate every real key at its provider. Then check access rules, so no user can read another user's data.

Do I have to rewrite Git history after a key leaks?

Not always. GitHub and the git-filter-repo manual both say to revoke and rotate the key first, and that this may make a rewrite unnecessary. If you do rewrite, use git-filter-repo --sensitive-data-removal in a fresh clone, then force-push every ref.

Is npm audit enough, or do I need Trivy?

For a project with only a package-lock.json, npm audit is a good start and needs no install. Trivy scans the whole project directory, and looks for secrets as well as vulnerabilities by default.

How much does vibe code cleanup cost?

The two agency pages we read do not list prices (checked on 24 October 2026), and one says it starts with a code review. Gitleaks (MIT licence) and Trivy (Apache 2.0) are open source, and npm audit and tsc come with npm and TypeScript, so steps 1 to 4 cost only your time.

Diagnose your codebase - free

VibeDoctor checks for SEC-014, SEC-001, TST-001, GIT-001 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →