Skip to content
Residual Stream
Go back

How Coding Agents Work: Claude Code and Cursor, from Their Documentation

Updated:
Tan Le
ENVI

The agents in From Function Calling to the Agent Loop and Engineering Around the Model had a handful of tools and short tasks. A coding agent works in a repository of thousands of files, with tools that can run anything a developer can, on tasks that take dozens of steps. It is the most demanding agent harness in wide use, and also the one with the best feedback: code can be compiled and tested, so “done” can be checked by a program. This post describes how two widely used coding agents work, Anthropic’s Claude Code and Cursor’s agent, using only what their documentation and engineering posts state, as of October 2026. Both products change quickly; the design questions they answer don’t.

A model in a loop, with the controls outside it context CLAUDE.md, rules compaction subagent summaries model gather, act, verify controls hooks permissions sandbox inner to outer call files + checkpoints search: grep, glob shell: build, test web code intelligence tests, type errors results done?

A coding agent is a harness around a model: what it sees is managed, what it does passes through controls outside the model, and tests make “done” a check rather than a claim. Hooks run every time but are not a boundary on their own; the sandbox is what contains a command.

At a glance
  • A coding agent is a harness around a model: five groups of tools (files, search, execution, web, code intelligence) in a gather-act-verify loop, with executable checks that make “done” real.
  • Both Claude Code and Cursor now find code with fast exact search driven by the model, plus exploration subagents. Cursor reported a 12.5% gain from its embedding index in 2025 and dropped the index in 2026, saying models had become good enough with grep.
  • Controls sit outside the model: permission modes, sandboxing, and checkpoints, kept about 30 days, that undo file edits but not shell or remote side effects.
  • Hooks run every time but are not a security boundary alone: the one here blocked 4 of 8 pending commands, and let a Python delete of the same directory through.

Table of contents

Open Table of contents

The loop and its tools

Claude Code’s documentation describes its loop in three phases that blend together: gather context, take action, verify results. A question about the code may need only the first; a bug fix cycles through all three. “Claude Code is the layer around the model that provides the tools and manages the context the model sees,” in the documentation’s words: the harness. Its built-in tools fall into five groups:

GroupWhat the model can do
File operationsRead files, edit code, create and reorganize files
SearchFind files by pattern, search content with regular expressions
ExecutionRun shell commands: builds, tests, git, servers
WebSearch the web, fetch documentation
Code intelligenceType errors after edits, jump to definition, find references (via plugins)

Plus tools for orchestration: spawning subagents, asking the user a question. “Fix the failing tests” becomes, in the documentation’s own example: run the test suite, read the failures, search for the relevant source files, read them, edit, run the tests again. Each tool result decides the next step, the loop of From Function Calling to the Agent Loop with a real development environment as its world.

gather context take action verify results “fix the failing tests” 1. run the test suite 2. read the failures 3. search for the source files 4. read them 5. edit 6. run the tests again

The three phases blend; each tool result decides the next step.

Finding code: grep or an embedding index

Before an agent can change code, it has to find it. There are two approaches, and the two products show both, at different times.

Agentic search. Claude Code’s search tools are pattern matching on file names and regular-expression search over contents, the tools a developer uses from a terminal. The model decides what to search for, reads what it finds, and searches again: retrieval as a loop, the agentic retrieval of Beyond Vector RAG, with grep as the retriever. There is no index to build or keep fresh, exact identifiers are found exactly, and nothing about the code leaves the machine except what the model reads.

An embedding index, alongside grep. Cursor’s agent used both. Its engineering posts describe splitting each file into syntactic chunks, embedding them with a model of its own (trained on what agents should have retrieved in real sessions), and keeping the index current with a Merkle tree: a hash of every file, and of every folder from its children’s hashes, so comparing two trees finds exactly which files changed (Semantic and Hybrid Search’s semantic search, applied to code). In November 2025 Cursor reported that adding semantic search to grep improved its agent’s answers by 12.5% on average (6.5% to 23.5% depending on the model), and that the combination worked best.

root: h(A, B) folder A: h(1, 2) folder B: h(3, 4) file 1: h(text) file 2: h(text) file 3: h(text) ✎ file 4: h(text) one edited file changes only the hashes on its path to the root: compare trees, re-embed what changed

How Cursor’s index stayed current: compare hash trees and re-embed only the files on changed paths. The tree is schematic.

By July 2026 it had turned the embedding index off. Its documentation now describes the agent’s search as Instant Grep, a regular-expression search whose index is built and queried on the user’s machine, and states that Cursor “does not upload file paths or code to build a search index, and it does not store embeddings of your codebase for search” (files the agent opens are still sent to the model, as with any coding agent). For broad questions, the agent can spawn an Explore subagent, running a faster model, that does many searches in its own context and returns only the findings. Cursor’s staff gave the reason on its forum: models “have gotten really good at using grep / indexed search, so the older dedicated semantic search path was rapidly no longer helping in a meaningful way.”

So in mid-2026 both products search the same way: fast exact search driven by the model, with subagents to keep exploration out of the main context. My reading of why that suffices for code (not a reason either company gave): identifiers and error strings are exact, so exact search finds them, and a capable model compensates for not knowing a name by searching again with what it learned. Semantic search helped most when models were worse at that loop.

agentic search model picks a pattern grep / glob reads what it finds no index to keep fresh; exact names found exactly Cursor’s index, then without it Nov 2025 grep + embedding index: +12.5% on average Jul 2026 index off: Instant Grep, built on the machine plus an Explore subagent for broad questions “models have gotten really good at using grep”

Two products, one destination: exact search, driven by a model good enough to search again with what it learned.

Verification: a definition of done that runs

What makes coding agents work better than most agents is that their output can be checked mechanically. Tests pass or fail; code compiles or doesn’t; a type checker reports errors. Claude Code’s code-intelligence tools surface type errors and warnings after each edit, and its loop treats running the tests as part of the task, not a separate step. Its best-practices guide gives the same advice first: give the agent a way to verify its work (a test command, an expected output, a screenshot to compare against). It also offers ways to make the check binding rather than a request: a goal condition that a separate evaluator re-checks after every turn, or a hook on the Stop event that runs the check and refuses to let the turn end until it passes. A coding agent without a check is a generator of plausible diffs; with one, it’s a loop that converges.

Memory and context

A coding session can run for hours over a codebase far larger than any context window, which makes context management (Context Engineering) central.

  • Project memory files. Claude Code reads CLAUDE.md files (project instructions, conventions, commands) into every session, and can read AGENTS.md, the convention other coding agents use. It also keeps auto memory, notes it saves as it works, of which the first 200 lines or 25 KB load at the start of each session. Cursor’s equivalent is rules files, and its documentation lists the same extension points this post describes for Claude Code: skills, subagents, hooks, MCP and worktrees.
  • Compaction. As the window fills, Claude Code first clears older tool outputs, then summarizes the conversation; the documentation warns that detailed early instructions can be lost, which is why persistent rules belong in CLAUDE.md.
  • Loading on demand. Tool definitions from MCP servers (Giving Agents Tools and Knowledge) are deferred and loaded through a tool search; skills show only their descriptions until used; subagents work in their own context window and return a summary. Each keeps something out of the main context until it’s needed.

Permissions and checkpoints

A coding agent can run any command its user can, so both products put controls around actions.

  • Permission modes. Claude Code offers several, cycled with a key: a mode where a classifier reviews actions in the background and blocks risky ones; a manual mode that asks before edits and commands; one that accepts file edits but asks for other commands; and a plan mode that explores and proposes without editing. (The edit-accepting mode also runs common file-system commands like mkdir and mv without asking.) Specific commands (npm test, git status) can be allowed in a settings file shared with the team.
  • Sandboxing. The containment layer underneath: operating-system-level limits on which files and network destinations commands can reach, so a command that slips past every other check still can’t touch what’s outside the sandbox.
  • Checkpoints. Each prompt that starts a turn creates a checkpoint, and files are snapshotted before the agent’s edits, so changes can be rewound; snapshots are kept for about 30 days. The documentation is explicit about the limits: checkpoints track only changes made through the file-editing tools, not files changed by shell commands; most edits made by subagents aren’t restored by a rewind; and actions on remote systems (databases, APIs, deployments) can’t be undone at all, which is what permissions are for. Checkpoints don’t replace git.
sandbox OS limits on files and network permissions modes, allowlists, a classifier hooks deterministic checks, run every time the agent’s actions checkpoints undo the agent’s file edits, not shell changes or remote side effects

Defense in depth: each outer layer bounds what the inner one misses.

Subagents and hooks

Subagents are agents the main agent starts for a delegated task, each with its own context, tools and instructions (Many Agents’s many-agent patterns, built into the product). Their value is mostly context hygiene: a search that reads fifty files happens in a subagent, and the main conversation gets a summary.

Hooks are where a team’s rules become code. Claude Code runs a hook at fixed points in its lifecycle (before and after each tool call, when the user submits a prompt, when the agent stops, before compaction, and more), and a hook can be a shell command, an HTTP endpoint, an MCP tool call, a model prompt or a subagent. A command hook receives the event as JSON on standard input; for a PreToolUse event, exiting with code 2 blocks the pending tool call, and whatever it printed is shown to the model as the reason (a JSON reply with a “deny” decision is the structured alternative). A small one that stops recursive force deletes and force pushes, registered for the Bash tool in the project’s .claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      { "matcher": "Bash", "hooks": [{ "type": "command", "command": "python3 .claude/hooks/guard.py" }] }
    ]
  }
}

The hook, guard.py:

#!/usr/bin/env python3
# A PreToolUse hook for Claude Code: it receives the pending tool call as
# JSON on stdin; exit code 2 blocks the call, and what it prints to stderr
# is shown to the model as the reason. Exit 0 lets the call proceed.
# A check like this shows the mechanism; it is not a security boundary (a
# command can always be written another way).
import json
import re
import shlex
import sys

def dangerous(command):
    try:
        words = shlex.split(command)
    except ValueError:
        return False
    for i, w in enumerate(words):
        args = []
        for a in words[i + 1:]:                                  # this command's arguments end at a separator
            if a in ("&&", "||", "|", ";"):
                break
            args.append(a)
            if a.endswith(";"):                                  # "main;" ends the command too
                break
        flags = "".join(a.lstrip("-") for a in args if a.startswith("-") and not a.startswith("--"))
        long = {a for a in args if a.startswith("--")}
        if w == "rm" and ("r" in flags.lower() or "--recursive" in long) and ("f" in flags or "--force" in long):
            return True                                          # rm -rf, -fr, -r -f, -Rf, --recursive --force
        if w == "push" and i > 0 and words[i - 1] == "git":
            if "f" in flags or "--force" in long or any(re.match(r"\+", a) for a in args):
                return True                                      # force push (--force-with-lease is allowed)
    return False

event = json.load(sys.stdin)
if event.get("tool_name") == "Bash" and dangerous(event.get("tool_input", {}).get("command", "")):
    print("Blocked by project policy: recursive force delete or force push. Ask the user first.", file=sys.stderr)
    sys.exit(2)
sys.exit(0)

Full script: guard.py

try_guard.py feeds it some pending calls, as Claude Code would:

import json
import subprocess
import sys

# Feed the hook pending tool calls, as Claude Code would.
for command in ("npm test", "rm -rf build/", "rm -fr build/", "rm -r -f build/", "rm build/old.log",
                "git push --force-with-lease", "git push -f origin main", "python -c 'import shutil; shutil.rmtree(\"build\")'"):
    event = {"hook_event_name": "PreToolUse", "tool_name": "Bash", "tool_input": {"command": command}}
    r = subprocess.run([sys.executable, "guard.py"], input=json.dumps(event), capture_output=True, text=True)
    print(f"{command!r:58s} -> {'allowed' if r.returncode == 0 else 'blocked (exit 2)'}")

Full script: try_guard.py

'npm test'                                                 -> allowed
'rm -rf build/'                                            -> blocked (exit 2)
'rm -fr build/'                                            -> blocked (exit 2)
'rm -r -f build/'                                          -> blocked (exit 2)
'rm build/old.log'                                         -> allowed
'git push --force-with-lease'                              -> allowed
'git push -f origin main'                                  -> blocked (exit 2)
'python -c \'import shutil; shutil.rmtree("build")\''      -> allowed

A hook is deterministic where an instruction in CLAUDE.md is only a request: the model may forget a rule after compaction, but the hook runs every time. Running every time isn’t the same as being unbypassable, though: the last case deletes the same directory through Python, and the hook can’t see it. A hook like this catches mistakes; containment against a determined or manipulated agent comes from sandboxing and permissions. That’s the general lesson of Guardrails and Prompt Injection applied to a coding agent: enforce what must hold outside the model, at the layer that actually bounds it.

the PreToolUse hook on pending Bash commands npm test allowed rm -rf build/ blocked, exit 2 rm -fr build/ blocked, exit 2 rm -r -f build/ blocked, exit 2 rm build/old.log allowed git push --force-with-lease allowed git push -f origin main blocked, exit 2 python -c 'import shutil; shutil.rmtree("build")' allowed the last one deletes the same directory through Python, which the hook can’t see

The hook catches every spelling of the shell command it knows, and none of the other ways to do the same thing.

Data: the output above

Cursor’s other models: tab and apply

Cursor’s agent is one part of an editor built around several specialized models.

  • Tab predicts the developer’s next edit, not just the next tokens at the cursor, and offers it inline. Cursor reported training it with online reinforcement learning on users’ accept and reject signals, rolling out a new checkpoint every hour and a half to two hours; the resulting model made 21% fewer suggestions with a 28% higher acceptance rate. Knowing when not to suggest is half the job.
  • Apply turns a model’s proposed change into an edit of the actual file. Cursor’s 2024 post describes a fine-tuned 70B model that rewrites the whole file at about 1,000 tokens per second using speculative edits: speculative decoding (Speculative Decoding, Derived) where the draft is the original file itself, since most of an edited file is unchanged and the model mostly confirms it.
draft = the original file def load( path ): with open( path ) as f: return json .load(f) the model verifies in one pass, regenerates only where it disagrees def load( path ): with open( path ) as f: return yaml .safe_load(f) accepted from the draft generated speculative decoding, the file as its own draft (tokens illustrative)

Speculative decoding with the file itself as the draft model. The tokens are illustrative.

Where this leads

Takeaways

  • A coding agent is a harness around a model: tools for files, search, execution, web and code intelligence, in a gather-act-verify loop.
  • Claude Code searches with grep and glob driven by the model; Cursor used grep plus an embedding index (+12.5% in 2025) and dropped the index in 2026, saying models had become good enough with grep. Both now use exact search plus exploration subagents.
  • Executable checks (tests, type checkers) make the definition of done real, and they’re why coding agents converge.
  • Project memory files, compaction, deferred tool loading and subagents manage a context far smaller than the codebase.
  • Permission modes, allowlists and sandboxing control actions; checkpoints undo the agent’s own file edits but not shell changes or remote side effects; hooks run deterministically outside the model but are not a security boundary on their own.
  • Cursor’s tab model was tuned with online RL to suggest less and be accepted more; its apply model uses the original file as a speculative draft.

References

Start with Claude Code’s How Claude Code works and Best practices, then Cursor’s semantic-search post and its current Search documentation, read together for the change between them.


Written by

Tan Le

AI engineer. I write these notes to learn LLM internals and AI engineering in depth, and to keep what I learn where I can find it again.