One Claude Code Tip a Day: Use Shell Checks as Ground Truth
Use Claude Code’s ! shell commands to verify reality without leaving the session: run the exact failing command, inspect logs, check git state, and feed the evidence back before the second pass.
This post is part of the “One Claude Code Tip a Day” series — a daily guide to using Claude Code more effectively.
A Claude Code session becomes dangerous when it sounds confident while the program is still broken. The fix is not a better adjective in your prompt. It is evidence. Today’s tip is to use `!` shell commands inside the conversation so Claude can see the same ground truth you would use: failing tests, server logs, generated files, current branch state, and the exact error message.
The failure: a fix that only looked fixed
Imagine you are debugging a React checkout page. A customer reports that the discount banner disappears after selecting Apple Pay. You ask Claude Code to inspect the component and it proposes a reasonable patch: move a derived `showDiscountBanner` flag above the payment-method condition. The diff looks clean. Claude says the issue is resolved.
But when you run the app manually, the banner is still invisible. The DOM node exists, yet the text is white on a white gradient because a class from the Apple Pay state overrides the banner theme. This is the kind of failure that a pure code-reading loop often misses. The next prompt should not be “try again.” The next prompt should bring runtime evidence back into the session.
The command: run shell checks without leaving context
In Claude Code, prefix shell commands with `!` when you want to execute them from the current session:
``` !pnpm test checkout-banner -- --runInBand !pnpm dev !curl -I http://localhost:5173/checkout !git diff -- src/features/checkout ```
The point is not that Claude can run commands. The point is that shell output changes the collaboration. Instead of asking Claude to reason from vibes, you force it to reconcile its plan with observable facts. A good workflow is: ask for a narrow hypothesis, run the smallest command that could disprove it, then make Claude explain the output before editing again.
A realistic session pattern
Start with a bounded, read-first prompt:
``` Read @folder src/features/checkout and @file tests/checkout-banner.test.tsx. Do not edit yet. Explain why the banner might disappear only after Apple Pay is selected. List the smallest shell checks that would confirm or reject each hypothesis. ```
Claude might propose three checks: run the component test, inspect the generated class names, and verify the browser state after selecting Apple Pay. Now run the first one:
``` !pnpm test tests/checkout-banner.test.tsx -- --watch=false ```
If the test passes, do not let the session continue as if testing is complete. A passing unit test only proves the current assertion is weak. Feed that back:
``` The test passes, but the browser still hides the banner. Read the test output and tell me what behavior is not being asserted. Add one failing assertion before changing production code. ```
Then run the targeted test again. If Claude adds an assertion for text existence but not visibility, push once more:
``` The DOM contains the text. The bug is visual. Update the test or add a Playwright check that proves the banner is visible after Apple Pay is selected. ```
That second-pass correction is where the value appears. Shell checks expose the gap between “the code compiles” and “the product works.”
Use logs as a debugging contract
The same habit works for backend and deployment issues. Suppose a cron job works locally but fails on Railway. Do not paste a vague summary. Ask Claude to name the evidence it needs, then run the commands or capture the logs:
``` We have a Railway cron deployment failure. Do not guess. First list the environment, database, and async-loop checks that could explain a local-only success. ```
Then bring real output into the conversation:
``` !railway logs --service price-worker --since 30m !railway variables !python -m scripts.price_worker --dry-run ```
A strong Claude Code loop will notice `localhost` in a production database URL, a missing API key, or an event loop already running. A weak loop will keep rewriting the worker. Make the logs the contract: Claude can propose the next edit only after explaining which log line supports it.
Inspect before and after every shell-assisted edit
Shell commands should not become random thrashing. Put them around edits like checkpoints:
``` !git status --short !pnpm test checkout-banner -- --watch=false ```
After Claude edits, run:
``` !git diff -- src/features/checkout tests !pnpm test checkout-banner -- --watch=false !pnpm lint --filter checkout ```
Then ask for an explicit review:
``` Review the diff against the original failure. Which line fixes the Apple Pay visibility bug? Which test would fail if we reverted that line? What risk remains? ```
This prevents a common failure mode: Claude fixes the visible bug while smuggling in unrelated refactors. The shell gives you facts; `git diff` gives you scope control.
Failure modes
The first failure mode is running commands that are too broad. `!pnpm test` may take ten minutes and return a wall of unrelated failures. Prefer the smallest command that can challenge the current hypothesis. The second failure mode is letting Claude ignore the output. If a command fails, ask Claude to quote the important line and explain it before editing. The third failure mode is trusting generated commands blindly. Destructive commands, migrations, deploys, and deletes still need human judgment.
Also remember that a shell check is only as good as the environment. If you run tests in the wrong repo, with stale dependencies, or against a different database, you have created fake certainty. Pair this tip with the habit of starting Claude Code in the right repository and checking `!pwd`, `!git branch --show-current`, and `!git status --short` before serious work.
Rule of thumb
Use `!` whenever the next claim can be tested. If Claude says a bug is fixed, run the test. If it says a server is healthy, hit the endpoint. If it says the change is small, inspect the diff. The best Claude Code sessions are not uninterrupted generation sessions; they are short loops of hypothesis, shell evidence, targeted correction, and verification.