Use malveon from the terminal
The terminal is the text window where you type commands (Terminal on a Mac, PowerShell on Windows). Run one command before your AI starts and one after it says it’s done. The second one tells you what is really built.
Check it’s installed
Open a new terminal window. The one you installed from doesn’t know about the change yet. Then type:
malveonYou should see a list of commands. If you see “command not found”, close the terminal, open another and try again. Not installed yet? Pick the line for your computer.
The two commands
Run both from inside your project’s folder.
1. Before your AI starts
malveon session startThis saves a snapshot of your code, so malveon can tell later what changed. Changes you haven’t committed yet are part of the snapshot, so they aren’t blamed on your AI. Forgot? The first malveon check starts one for you and says so. It then covers changes from that moment on, but not work that was already committed before it.
2. After your AI says it’s done
malveon checkmalveon finds your plan, reads your code and your git history, and prints a report. It exits with an error if anything is proven wrong.
Your plan file
You don’t have to point malveon at your plan. It looks through the project for a file named like one (anything with “plan”, “feature”, “checklist” or “todo” in the name). If none matches, it looks for a Markdown checklist or a feature list in JSON. It always shows you what it found and asks before using it:
found a possible plan file (by name): docs/PLAN.md — use it? [Y/n]Skip the question by naming the file. Use the option more than once to check several plan files in one run.
malveon check --features docs/PLAN.mdA plan can be a Markdown checklist, a JSON list, or plain text with one item per line:
- [ ] Refund button
- [x] Cancel order- Tick a box when the plan says the item is done. A ticked line (
- [x]) or a plain bullet blocks when the code disagrees. An unticked line (- [ ]) is still checked and listed, but it only blocks if it is BROKEN. - Be specific. “Refund button” works if some code mentions refunds. A line that names its URL, file or component is checked exactly, like
`RefundButton` sends a refund to `POST /api/refunds`. A line that names nothing malveon can look up is skipped, and the report lists the closest routes and pages so you can add the right name.
Keep the choice with your project
Commit a file called malveon.json at the project root, so you, your AI and your git hook all check the same way. malveon offers to write it the first time you pick a plan.
malveon.json
{
"plans": ["docs/PLAN.md"],
"exec": true
}A flag on the command line always wins over the file.
Reading the result
This is a real run on a small demo app (an orders dashboard where an AI cancelled an order through the wrong address and left a note that saves nothing). It is the full output, so scroll inside the box.
~/landing-shop $ malveon check --features PLAN.md
VERDICT: FAILED
==============================================================================
3 problems are proven wrong in the code (details below):
- plan items: 1 BROKEN, 0 NOT BUILT, 1 HALF BUILT
- new bugs this session: 1 introduced (something that worked now broken, or a known bug pattern added)
- agent's claims vs reality: 1 claim(s) contradicted by what was actually verified
malveon reads the code; it never ran your app, so a real click or page load is still yours to try.
FIX THESE FIRST
==============================================================================
1. BROKEN — `CancelButton` cancels an order with `DELETE /api/orders/[id]`
`CancelButton` exists (src\components\CancelButton.tsx) — but it calls DELETE /api/order/:param (src\components\CancelButton.tsx:5), and no server route handles that (src\app\api\orders\[id]\route.ts:4)
2. new call to nowhere — DELETE /api/order/:param
DELETE /api/order/:param is a call added this session, and no route in the project serves it (src\components\CancelButton.tsx:5)
3. claim contradicted — `OrderNotes` saves a note on an order with `POST /api/notes`
said "OrderNotes saves notes, ready to ship.", but claimed this works, but the plan-items check says HALF BUILT: POST /api/notes reaches its server route (src\app\api\notes\route.ts:4), but the route never changes any data: it only reads table `notes` (src\app\api\notes…
…and 1 more — details below
OVERVIEW
==============================================================================
read from the code ✔ · your build/tests: not run (turn on "exec" in malveon.json, or pass --exec) · your real app: not verified (malveon never runs it)
Plan items built & connected 2 CONNECTED · 1 BROKEN · 1 NOT BUILT · 1 HALF BUILT · 2 SKIPPED (1 of those name nothing malveon can look up) (1 not ticked yet, don't block)
Your prompts vs the code not set up (malveon hooks install)
Frontend & backend agree on data 2 MATCH · 0 MISMATCH (request fields compared on 2)
Backend route conflicts clean
UI elements may overlap clean
Unplanned code 2 flagged
New bugs this session 1 found
Unfinished code clean
Agent's claims vs reality 0 CONFIRMED · 1 MISMATCH · 6 NOT CLAIMED
Agent's words vs the code clean
Build, lint & tests actually ran not run (pass --exec to include)
Plan items — built and connected? (wiring check)
for every plan line: is it in the code, and does the screen actually reach the server?
(CONNECTED means built and connected in the code — read from it, not run or clicked through)
------------------------------------------------------------------------------
BROKEN (1) — built, but it won't work as written: fix these first
`CancelButton` cancels an order with `DELETE /api/orders/[id]`
`CancelButton` exists (src\components\CancelButton.tsx) — but it calls DELETE /api/order/:param (src\components\CancelButton.tsx:5), and no server route handles that; the server route for /api/orders/:id exists (src\app\api\orders\[id]\route.ts:4), but no frontend call to it was found (if something outside the app calls it — a health check, webhook, cron job or public API — say so in the line)
evidence: src\app\api\orders\[id]\route.ts:4
evidence: src\components\CancelButton.tsx:5
NOT BUILT (1) — the plan names it, the code doesn't have it
Export all orders as CSV from `GET /api/reports/orders` (not ticked in your plan yet, so it doesn't block)
nothing in the code serves or calls /api/reports/orders
HALF BUILT (1) — one part exists, the other doesn't
`OrderNotes` saves a note on an order with `POST /api/notes`
POST /api/notes reaches its server route (src\app\api\notes\route.ts:4), but the route never changes any data: it only reads table `notes` (src\app\api\notes\route.ts:6) — and this line expects a change
path: page /orders → POST /api/notes (src\components\OrderNotes.tsx:9) → route POST /api/notes (src\app\api\notes\route.ts:4) → changes no data: only reads table `notes` (src\app\api\notes\route.ts:6)
evidence: src\components\OrderNotes.tsx:9
evidence: src\app\api\notes\route.ts:4
evidence: src\components\OrderNotes.tsx
evidence: src\app\api\notes\route.ts:6
CONNECTED (2) — built and connected in the code
Orders page at `/orders` lists orders from the `orders` table
path: page /orders → reads table `orders` (src\app\orders\page.tsx:16)
`RefundButton` sends a refund to `POST /api/refunds`, which saves it to `refunds`
path: page /orders → POST /api/refunds (src\components\RefundButton.tsx:5) → route POST /api/refunds (src\app\api\refunds\route.ts:4) → inserts into table `refunds` (src\app\api\refunds\route.ts:6)
SKIPPED (2) — not checked, and not a problem in your code
a design or styling change — malveon reads code, it can't see how the page looks (1)
names no URL, file or code name, and no code clearly matches its words (1)
- Make orders better
closest in the code: `/orders` (src\app\orders\page.tsx:1); `DELETE /api/order/:param` (src\components\CancelButton.tsx:5); `DELETE /api/orders/:id` (src\app\api\orders\[id]\route.ts:4) — name one in backticks if it's what this line means
to have a line checked, name the URL, file or component it's about, in backticks
every skipped line, with its reason, is in the JSON report (malveon check --json report.json)
Unplanned code (not-in-plan check)
did the agent build anything this session neither your plan nor your own prompts asked for?
------------------------------------------------------------------------------
route /api/receipts
method: POST
evidence: src\app\api\receipts\route.ts:4
call /api/receipts
method: POST
evidence: src\components\ReceiptButton.tsx:5
New bugs this session (hero-act check)
did this session's own changes break something that worked, or add a known bug pattern?
(compared with the code at the session start, read straight from git and the code, never from the agent)
------------------------------------------------------------------------------
NEW CALL TO NOWHERE — DELETE /api/order/:param
src\components\CancelButton.tsx:5
DELETE /api/order/:param is a call added this session, and no route in the project serves it
Agent's claims vs reality (confidence check)
when the agent said "done"/"working" in a commit message, did the other checks actually agree?
------------------------------------------------------------------------------
CONFIDENCE MISMATCH (1) — claimed done, but not what got verified
`OrderNotes` saves a note on an order with `POST /api/notes`
claimed: OrderNotes saves notes, ready to ship.
reason: claimed this works, but the plan-items check says HALF BUILT: POST /api/notes reaches its server route (src\app\api\notes\route.ts:4), but the route never changes any data: it only reads table `notes` (src\app\api\notes\route.ts:6) — and this line expects a change
GATE: blocked — a commit gated on this run should not proceed
- plan items: 1 BROKEN, 0 NOT BUILT, 1 HALF BUILT
- new bugs this session: 1 introduced (something that worked now broken, or a known bug pattern added)
- agent's claims vs reality: 1 claim(s) contradicted by what was actually verifiedHow to read it, top to bottom:
- VERDICT is the one-line answer:
FAILED,NEEDS YOUorVERIFIED BY CODE. - FIX THESE FIRST lists up to three of the most serious problems, each with the file and line behind it. It only appears when something blocks.
- OVERVIEW has one line per check. A check that found nothing says “clean” and has no section below.
- GATE says whether a commit gated on this run should go ahead.
The command exits with code 1 when the gate is blocked and 0 when it is clean.
Stopping a bad commit
By default malveon check fails whenever the gate is blocked. To make that stop a commit, add a git hook, a small script git runs before it saves a commit. malveon never installs one for you.
Save this as .git/hooks/pre-commit, then run chmod +x .git/hooks/pre-commit:
.git/hooks/pre-commit
#!/bin/sh
malveon checkA hook can’t answer questions, so it needs a committed malveon.json that names your plan file. Without one the check stops right away instead of waiting for an answer nobody can give.
| What | Does it block? |
|---|---|
| BROKEN, NOT BUILT, HALF BUILT | Yes. |
| A request the page sends that the server doesn’t expect | Yes. |
| A new bug the AI introduced | Yes. |
| A claim in the AI’s notes that the code contradicts | Yes. |
| Code your plan never asked for | No. It’s listed for you to review. |
| Unfinished-code notes and layout warnings | No. A function that throws “not implemented” and that something calls does block. |
| Plan lines malveon couldn’t look up | No. |
| Unticked lines that are NOT BUILT or HALF BUILT | No, until you tick them. |
Want the full report but never a failing exit code? Add --no-gate.
Running your build and tests
malveon check --execThis also runs your project’s own build, lint, type check and test commands, and reports what they really said. It only runs commands your project already defines (a Makefile target, a package.json script, Go’s standard tools, Python tools on your PATH). Nothing is invented. It is off by default because it can take a few minutes.
malveon asks before running a project’s commands. A malveon.json comes with the project, so one you just cloned shouldn’t be able to run its scripts the moment you check it. The first time, malveon shows the exact commands and asks. Your answer is remembered for that project until its malveon.json changes. To approve ahead of time, for example so a git hook can run them:
malveon trustTyping --exec yourself needs no approval. You asked for it. Each command gets three minutes by default; change it with --exec-timeout 5m. A command that times out is reported as “no proof”, not as a pass or a fail.
All commands
| Command | What it does |
|---|---|
| malveon check | Checks your plan and prompts against the code. Exits with an error if something is broken or missing. |
| malveon session start | Marks where the AI’s work begins, so “what changed” has a baseline. |
| malveon trust | Approves running this project’s own build and test commands. |
| malveon hooks install | Also checks the prompts you type to Claude Code, Codex, Cursor or Antigravity. Add --gate shadow or --gate enforce for Claude Code’s edit and “done” hooks. |
| malveon prompt drop N | Stops checking prompt number N, for one you cancelled. |
| malveon prompt confirm N | Approves the lines your AI wrote for prompt N. Needs a terminal. |
| malveon prompt waive N words | Says you will check some words of prompt N yourself. |
| malveon mcp | Runs malveon so your AI tool can call it. See Let your AI run the check. |
| malveon update | Installs the latest version. |
| malveon version | Prints the version. |
| malveon register | Opens a pre-filled GitHub discussion where you can ask for a check for your stack. |
Options for malveon check
| Option | What it does |
|---|---|
| --features <path> | Use this plan file and skip the question. Repeat it for several files. |
| --exec | Also run your build, lint, type check and tests. |
| --exec-timeout <time> | How long each command gets. Default 3 minutes. Example: 5m, 90s. |
| --no-gate | Print the full report but always exit successfully. |
| --json <path> | Also write the full report as JSON. Use - to send the JSON to the terminal output and the text report to the error output. |
| --root <path> | Check a project in another folder. |
| --no-telemetry | Don’t send the anonymous usage signal for this run. |
Privacy switches
malveon sends one small anonymous usage signal per run and checks GitHub once a day for a newer version. Neither includes anything about your project.
- Turn the usage signals off: add
--no-telemetry, or setDO_NOT_TRACK=1. - Turn the daily update check off: set
MALVEON_NO_UPDATE_CHECK=1.
The full list of what is sent is in the privacy policy.
If something goes wrong
- “command not found”. Close the terminal and open a new one.
- “found a possible plan file, but there’s no terminal to confirm it”. A script can’t answer the question. Name the file with
--features, or commit amalveon.json. - My build and tests didn’t run in a git hook. Run
malveon trustonce in the project, from a normal terminal. - It says no session was started. Run
malveon session start, or runmalveon checkagain, which starts one for you.
Still stuck, or using a stack malveon handles badly? Open a discussion. That’s the one place the person who built it is reading.
Run the check on your own project.
curl -fsSL https://get.malveon.com/install.sh | shWindows: irm https://get.malveon.com/install.ps1 | iex. Also on npm, Homebrew, Scoop, and the VS Code and Cursor extension. Free during the beta. No account. Your code stays on your computer.