The core loop for driving Claude Code by hand: open a session in your project, ask for a change in plain English, review the diff, run tests, commit. The real subject is the gap between a session that starts cold and one that already knows your codebase, and how a request that names a file, a current behaviour, a wanted behaviour, and a constraint outperforms a vague ask, because the model fills any gap with its own taste rather than yours.
That gap gets closed with a Markdown memory file, written by running the in-session init command, which surveys the package manifest, test setup, build, and git history before drafting instructions. The file should stay short: how to run the app, how to run the tests, and two or three conventions a new contributor always gets wrong — nothing derivable by reading the code. These files stack from broadest to most local at session start, and the automatically maintained per-project memory file is capped at roughly two hundred lines or twenty-five kilobytes, while everything else is not.
A worked example — moving email validation from submit-time to on-blur — shows the loop end to end: Claude reads the form and schema, proposes a scoped fix, runs the test command from memory unprompted, surfaces a failing existing test, and waits for a human call on which behaviour is correct. The diff stays small because the request stayed narrow.
Sessions persist as transcripts on disk automatically, restarted with a continue flag for the most recent one or a resume flag for a picker across past sessions, with branching and renaming available mid-session — though transcripts older than thirty days get cleaned up by default. The closing warning: an unmaintained memory file rots into a wiki of contradictory, stale instructions that Claude will obey confidently anyway, so it needs treating as short, living, and checked every quarter.
The current CLI release adds a per-loop breakdown to the usage command and separate cache lifetime settings for main sessions versus subagents, plus a model-picker setting and managed pricing for enterprise contracts. Recent releases also fixed lost plan-mode state on restarted remote workers and added automatic resumption after a rate limit, with a matching auto-continue toggle on the desktop app's limit notification. A research-preview design command now opens an editable canvas for implementing frontend interfaces, and the Python SDK has hit version one with a CLI migration helper for teams still on the old client conventions.
Start at the end, because it explains everything that follows.
Somebody files an issue on your repository. Maybe it is a real bug report from a real customer, maybe it is you, standing in a queue somewhere, firing off one more thing from your phone into a chat channel that is wired to your project. Nothing happens on your laptop, because your laptop is in a drawer. The work gets picked up anyway. The feature gets implemented against your codebase, on a branch, following your conventions. A pull request opens. A second pass reviews that pull request and fixes what it finds wrong. The tests run. The rollout is gated, a slice of traffic first, and then the rest. It deploys. You find out by reading the notification later, the way you would read a note from a colleague who had a productive afternoon.
That is the end state this show climbs toward, and it is genuinely reachable. It is also nowhere near where we start, because that pipeline is made of parts, and each part is something you first have to be able to do by hand, on purpose, while watching. So the first stretch of this show is all keyboard work. You, one session, one terminal, driving. Every rung after that is built out of the rungs below it, which is why we do not begin with the fleet.
This is rung one. One session. One real change. On the app you already ship.
And the thing rung one is actually about, underneath the three pieces we are going to use, is a single distinction. There is a session that starts cold, and there is a session that starts already knowing your project. Those are two different tools with the same name. If you have been using Claude Code for a few weeks and it feels like a very good autocomplete that you have to brief every morning, you are living in the first one. The whole of this episode is moving you into the second, and then making that state survive lunch.
Let us do the loop first, because everything else is a modification of it.
You are in your project directory. You start a session. You type a request in plain English. Claude reads some files, decides what to change, and either shows you a diff or asks to run something. You approve, or you steer. It changes files on disk. You run the tests, or it runs them for you. You look at the diff. You commit. That is the loop, and it is not complicated. What is complicated is that the loop has a quality dial on it, and the dial is your request.
Here is the mechanism, plainly. The model does not have your repository in its head. It has a context window, and at the start of a session that window contains almost nothing about you. So the first thing that happens after your request is a search. It looks around, opens files, greps for names, forms a guess about where the thing you asked for lives. That search costs time and tokens, and worse, it is the step most likely to go wrong, because a wrong guess about where the work lives produces a confident, well-written change in the wrong place.
Which is why a request that names a file and a goal beats a request that names a vibe. Compare two ways of asking for the same thing. The first: make the signup flow less confusing. The second: in the signup form component, the email validation error only appears after submit; make it appear on blur instead, and keep the existing error copy. The second request is not more polite, and it is not more technical. It is narrower in a specific way. It names where to look, so the search step mostly collapses. It names the current behaviour and the wanted behaviour, so there is a test for done. And it names a constraint — keep the copy — which is the sentence that stops a tidy-minded agent from rewriting three adjacent things while it is in there.
That last part deserves a moment, because it is the most common surprise for people new to driving this thing. Given a vague goal, the model will fill the gap with its own taste. Its taste is decent. It is not your team's taste, and it does not know which of your oddities are accidents and which are load-bearing. Every constraint you state up front is a decision you keep for yourself.
So where do you steer, and where do you let it run? A useful rule: steer at boundaries, let it run inside them. Deciding which file, which approach, which library — steer. Renaming the variables, threading the prop through, updating the four call sites that broke — let it run. If you are reading every line of a mechanical refactor, you are doing a job you delegated. If you are approving an architectural choice you never made, you have delegated a job you should be doing.
Now, the obvious problem with everything I just said. Naming the file and the goal works beautifully, and you have to do it every single time, because tomorrow's session knows nothing about today's. And a good chunk of what you type into that first request is not about the task at all. It is the same six facts about your stack. That the app runs with one particular dev command and not the obvious one. That the tests are run through a specific runner and that there is a separate command for the integration ones. That database migrations are generated, never hand-written. That there is a legacy directory nobody touches. You will type those facts, in some form, every morning for the rest of your life, unless you write them down somewhere the tool actually reads.
That somewhere is a Markdown file called CLAUDE.md, and the thing that writes the first version of it is the in-session command called init.
Run it in a session at the root of your project. What it does is a survey, and it is worth knowing what it looks at, because that tells you what it will get right and what you will have to correct. It walks the directory tree. It reads your package manifest — the dependencies, and especially the scripts block, which is where most projects accidentally document themselves. It looks at how tests are configured and what the build does. It reads some git history, which is how it forms an opinion about what changes often and what is stable. And it checks whether you have configuration lying around from some other AI coding tool, in which case it will offer to bring those instructions across rather than making you retype them.
Then it writes a file. Where that file lands is worth being precise about, because there are several valid homes and they mean different things. The project one goes at your repository root, or inside the project's Claude configuration folder — either location works, and the root is the common choice because it is visible and it gets committed and reviewed like any other file. That is the point of the project file: it is shared, it is in git, and when a teammate corrects it, everybody's sessions get better. Separately, there is a personal file for a single repository, meant for your own preferences and deliberately kept out of git. And there is a user-level file in your home configuration directory that applies to every repository you open, which is the right place for facts about you rather than facts about the project. You can edit any of them mid-session with the memory command instead of leaving the session to open an editor.
One thing to know about how these combine, because people expect the wrong thing. A more specific file does not replace a broader one. They stack. At session start, Claude Code loads them from broadest to most local — anything your organisation has deployed machine-wide, then your user-level file, then the project file, then any modular rule files kept in the project's rules directory, then memory files in nested subdirectories, walking down from the repository root toward wherever you actually are, then your private local overrides. All of it goes into the window, concatenated. There is also an automatically maintained memory file that the tool keeps per project, and that one is capped — roughly the first two hundred lines, or twenty-five kilobytes, whichever it hits first. Everything else is not capped, which is the fact that comes back to bite people later in this episode.
So what should the first version actually contain, for a TypeScript web app on the ordinary stack — Next.js on the front, Postgres behind it, deployed to Vercel, GitHub for everything else? Three things, and nothing else yet.
How to run the app. Not the framework's documented command, yours. If the dev server needs an environment file pulled down first, that is the sentence.
How to run the tests. Both commands if there are two, and which one is the fast one you want run after every change. This single line changes the feel of the tool more than anything else in the file, because it converts "I made the change" into "I made the change and it passes".
And the two or three conventions a new contributor always gets wrong. Every codebase has them. Data fetching happens in server components and the client components stay dumb. Nothing talks to the database outside the data access layer. Migrations are generated by the toolchain and committed, never edited by hand. You already know yours, because you have written the same review comment three times.
What does not belong in there: your dependency list, your directory tree, your API schema, chunks of actual code. Claude can open those files itself, on demand, and they change without telling you. The file is for things that cannot be derived by reading the repository. That is the whole test. If the answer is discoverable in ten seconds by looking, leave it out.
Right. Let us spend the memory on something.
The change: our signup form shows a validation error for a malformed email only after the user hits submit, and support has complained twice. I want it on blur. I start a session at the repository root. Because init has run, I do not open with a briefing. I open with the request: in the signup form, move email validation from submit-time to on-blur, keep the existing error message text, and add a test.
What comes back first is not an edit. It reads the signup form component and the validation schema next to it, and it tells me what it found: the form uses a schema-based validator, validation currently fires on submit because of one mode setting, and the error copy lives in the schema rather than in the component. Then it proposes the change. One setting flipped so validation runs on blur, and nothing done to the copy, because I said not to.
I read that proposal before approving it, and I want to be honest about why. Not because I expect it to be wrong. Because this is the step where a wrong guess about where the work lives is cheap to fix, and every later step is expensive. Thirty seconds of reading here is the best-value thirty seconds in the loop.
It makes the edit. Two files: the form component, and a new test file beside it. Then it runs the test command — the one from the memory file, which is the payoff, because I did not have to tell it — and one test fails. Not the new one. An existing one, which asserted that the error appeared after submit. It shows me the failure. And here is a place to steer rather than let it run, because there are two honest fixes and only I know which is right. Either the old test is now wrong and should be updated, or the old behaviour was also required and both need to work. It is the first. I say so, in one sentence. It updates the assertion, reruns, everything passes.
Then I look at the diff myself, with git, in my own terminal, the same way I would look at a colleague's branch. Four lines changed in the component, one changed assertion, one new test. That is a diff I can hold in my head, which is not an accident — it is a consequence of having asked for one thing.
And now the part that separates a demo from a working habit. Say it is one o'clock and I am going to lunch, and the change is not committed because I want to check one thing with the designer first.
You do not have to re-explain any of this tomorrow, because the session is on disk. Every session is written out as a transcript — the messages, the tool calls, all of it — kept per project in your Claude configuration directory, with an index across all of them. Which means resuming is not a feature you set up. It is already happening; the only question is whether you know the commands.
There are two, and the difference matters. Starting Claude with the continue flag reopens the most recent session for the directory you are in, immediately, no questions. That is the lunch case, and it is the one you will type most. Starting it with the resume flag instead, with nothing after it, opens a picker listing the past sessions for this project, which you can search through and choose from. That is the Tuesday-afternoon case, when the thing you want is from three days and five sessions ago. You can also hand the resume flag a specific session identifier, or a name, and go straight there. Inside a running session there is a resume command that does the same switching without dropping to the shell, a branch command if you want to fork a conversation and try a second approach without losing the first, and a compact command that compresses the history when a long piece of work starts pressing on the context window.
Named sessions are the small habit that makes the picker usable. There is a rename command, and if you never use it the tool will still title things for you — a fast model writes a title from your opening prompt or the plan you accepted, which is better than a bare identifier and worse than a name you chose. Two of your own names, signup validation and neon migration, beat six auto-titles that all begin with the word fix.
There is also a clock running on all this, and you should know about it before it surprises you. Startup runs a cleanup pass, and by default it deletes transcripts older than thirty days. That is a setting you can raise in your user settings if you keep long-lived work around. Nothing dramatic happens when you hit it; a session you half-remember is simply not in the picker any more.
Which brings me to the habit rather than the command. Resuming works mechanically no matter what state you left things in, but the value of resuming depends entirely on that state, and this is where people underuse it. If you stop mid-thought, with a half-applied refactor and a failing test you had not diagnosed, then tomorrow you resume into a conversation whose last exchange is confusing to both of you, and you spend ten minutes reconstructing. So leave the session somewhere worth returning to. Get to green, or get to a clearly named single failure. Then say what is next, out loud, into the session — next: check the error copy with design, then commit — and let that be the final message. Now the transcript ends with the instruction that starts tomorrow. It costs one sentence and it is the difference between resuming a session and merely reopening one.
Now the failure mode, because you will hit this one, probably around month three, and it is easy to misread.
The memory file grows into a wiki. It starts at forty useful lines. Someone adds a paragraph explaining the auth flow. Someone pastes in the whole environment variable list. A long section appears about the migration process, and then, months later, a second section about the new migration process, and nobody deletes the first. Six hundred lines, all of it once true.
The symptoms are specific enough to diagnose. Every session starts slower and feels heavier before you have asked for anything, because that entire file is loaded into the window at session start, every single time. Instructions inside it start contradicting each other, and you can watch the model hesitate, or split the difference, or pick the wrong one. And the ugly one: it follows a convention your team abandoned in the spring, confidently, in a diff that looks correct — because from where it is sitting, that convention is a direct instruction from you and the code is just some code. It is not hallucinating. It is obeying.
Why does it happen? Because nobody rewrites the file when the codebase moves. It is not in the test suite. Nothing fails when it goes stale. It has no owner, and it is read at the start of every session forever. Documentation that humans stop reading becomes harmlessly irrelevant. This file does not have that mercy — it stays load-bearing while quietly becoming false.
The fix is a stance, not a tool. Treat it as short, living, and verified. Short: if it does not fit on a screen or two, something in it is derivable and should go. Living: when you get bitten by a stale instruction, do not sigh and correct the model in chat, because the chat is gone tomorrow and the file is not — open the memory command and fix the line. Verified: once a quarter, or whenever a big refactor lands, read the whole thing top to bottom and delete anything you cannot confirm is still true today. And when it genuinely does need more depth, split it rather than growing it, either into memory files sitting in the subdirectories they describe, or into separate rule files in the project's rules directory, so the instruction about your payment code loads alongside your payment code instead of alongside everything.
Here is the whole thing as a sequence you can run in the next ten minutes.
Do that once and the second time costs you nothing, because the six facts are on disk and the session is on disk. That is rung one: one session, driven by hand, that starts already knowing your project. Rung two is taking the wheel more deliberately — settings, permissions, and the difference between planning and acting.
Three things from the last couple of weeks, and the first one is the one to actually go and touch today.
The current CLI release — the two-point-one line, latest as of the twenty-fifth of August — added a breakdown of loops inside the usage command, so when you ask where your tokens went you now get per-loop run counts and token totals rather than one aggregate number. Alongside it came two cache lifetime settings you can set independently: one for the prompt cache on your main session, one for subagents. The intended shape is a long-lived cache for the session you sit in all day, around an hour, and a short one, a few minutes, for the throwaway work. If you have ever looked at a bill and had no idea which part of your day produced it, this is the release that answers that. There is also a model picker setting for curating your own ordered list of models, and, for anyone on a negotiated enterprise contract, a managed pricing setting so cost readouts reflect your actual rates rather than list price. Smallest next action: run the usage command in your next session and read the loops breakdown. It takes five seconds and it will probably surprise you.
Second, and this one matters for the resume habit we just spent time on. Recent releases hardened session continuity in two places. Cloud and remote worker sessions were losing plan-mode state when an idle worker restarted, and that is fixed. And the execution loop gained automatic limit resumption — a turn that stalls because you hit a rate limit now resumes itself once the window resets, instead of leaving you to restart the process. Desktop got the same idea as a visible control: an auto-continue toggle right on the limit notification card, so you can flip it at the moment you are annoyed rather than going hunting in settings. If you work in the desktop app, that toggle is the whole action item.
Third, at the SDK and integration layer. There is now a research preview of a design command, in both the CLI and the desktop app, which opens an editable canvas for frontend interfaces and then implements what you select on it — built on artifacts, and clearly aimed at the loop where you are describing a layout in words when you would rather be pointing at it. Same batch brought a concise output style and a default-model environment variable. And on the library side, the Python SDK reached version one, with a migration helper in the CLI that walks a local Python project from the old client conventions to the new ones, the timeout handling being the change most likely to break you quietly. The TypeScript and Python agent libraries are at parity now. If you are on the old Python client, run the migration helper on a branch this week rather than discovering the timeout change in production. If you are not, the action here is just to read the settings documentation page once with the new cache and pricing keys in mind — that page has moved further in the last month than most people's mental model of it has.