To refactor a legacy C++ module without breaking its callers, Atlas maps the module's public surface first with the lsp tool's documentSymbol operation, then runs findReferences on every exported symbol to enumerate the callsites that a header-only grep would miss. Atlas pins the current behavior by running GoogleTest via ctest through bash and recording the green baseline, restructures with apply_patch, which anchors each hunk on its context lines and refuses to apply against a drifted file, and re-runs GoogleTest via ctest after every hunk lands rather than once at the end. Remaining callsites live in a todowrite list so a half-migrated module is never mistaken for a finished one.
How does Atlas refactor a legacy C++ module without breaking callers?
Atlas refactors a legacy C++ module by closing the callsite gap before touching code. The lsp tool's findReferences operation returns the true reference set from your C++ language server, catching the 3 kinds of caller a grep across headers cannot: overload resolutions, templates instantiated elsewhere, and inherited virtuals in another translation unit.
The failure mode in a C++ refactor is silent breakage at a callsite you did not know about, and C++ hides callsites better than most languages. A method reached through a base-class pointer, an overload picked by argument-dependent lookup, a template instantiated only in tests: none of these show up reliably in a text search of include/legacy/engine.hpp. Atlas therefore starts with the language server, not with the editor. It runs documentSymbol on the module to get the full exported surface, then findReferences on each symbol to build a callsite inventory. Only when that inventory is complete does Atlas propose a restructure, and every proposed edit still arrives as a unified diff surfaced for approval before writing.
How do you pin C++ behavior before restructuring?
Atlas pins C++ behavior by recording a green baseline from 1 full GoogleTest via ctest run through bash before changing anything. In a CMake project, that baseline is what makes any later red attributable to the refactor rather than to a failure that was already there before the work started.
A legacy C++ module usually arrives with a suite that was already flaky or already partially red, and refactoring against an unknown baseline makes every subsequent failure ambiguous. Atlas's bash tool runs GoogleTest via ctest and captures the full output, including the process exit code in its metadata, so the pass and fail states are unambiguous. If the baseline is not green, the honest move is to record which cases were already failing in a todowrite list before starting. Atlas also reads git branches, status, and diffs, so the baseline commit is identified precisely and you can return to it if the restructure goes sideways.
Why does Atlas use apply_patch instead of edit for a C++ restructure?
Atlas uses apply_patch for a C++ restructure because apply_patch seeks each hunk's context and old_lines, and fails with Failed to find context when the file has drifted. Moving a class out of src/legacy/engine.cpp into 2 new translation units spans many hunks, and a drifted anchor should stop the patch, not corrupt it.
Structural C++ work is rarely one edit. Splitting a 3000-line engine.cpp means moving definitions, adding include guards to a new header, updating CMakeLists.txt target sources, and rewriting forward declarations, all of which land as separate hunks in one coherent change. apply_patch anchors on real context lines rather than line numbers, which is what you want when the file has already shifted underneath an earlier hunk in the same patch. When the anchor is gone, apply_patch fails with Failed to find context instead of writing into the wrong place, and Atlas re-reads the file and recomputes the hunk. Atlas also snapshots file changes as git patches, so a landed hunk can be rolled back.
How often should the C++ tests run during a refactor?
Atlas re-runs GoogleTest via ctest through bash after each hunk lands, not once at the end. A legacy C++ refactor that runs the suite only once produces a single red result across 40 changed files, which localizes nothing. Running after each hunk keeps the blast radius of any failure to one change.
The rhythm matters more than the tooling. After each apply_patch hunk, Atlas invokes GoogleTest via ctest with a generous timeout in milliseconds so a slow C++ build is not killed mid-compile, and reads the result. When a hunk turns the suite red, the cause is the hunk, and Atlas can revert it from a snapshot rather than bisecting a large diff. Formatting is deliberately kept out of the loop: run clang-format only at the end, and only on the files the refactor touched, so intermediate ctest runs are not comparing reformatted noise. Dependency changes go through vcpkg, and a vcpkg.json edit that pulls in a new library should land as its own hunk with its own test run.
How does Atlas track the remaining C++ callsites?
Atlas tracks remaining C++ callsites in a todowrite list, 1 entry per callsite returned by the lsp tool's findReferences. A partially migrated module is the worst outcome of a refactor, because the old API and the new one both compile, and a todowrite list prevents a half-done migration from looking finished.
In C++ the danger is that both APIs keep building. Leave the old free function next to the new class method and every existing caller still compiles, so nothing forces the migration to complete. Atlas turns the findReferences output into an explicit list of pending callsites with todowrite, then works through them, re-running GoogleTest via ctest as each one migrates. When the list is empty, Atlas greps once more for the old symbol name to prove zero remaining hits, including in comments, docs, and CMakeLists.txt. Only then is the legacy module actually gone rather than merely unused.
How do you set up Atlas on a C++ project in 2026?
Run atlas in a C++ project with a CMakeLists.txt in 2026, and let Atlas read your headers, translation units, and build targets. Ask Atlas to modernize a legacy module to smart pointers or add GoogleTest cases, then review the unified diff Atlas surfaces before any file is written.
Atlas is a terminal-native TUI, so it starts in the same shell where you already run cmake and ctest. Its index is built from AST declarations using tree-sitter rather than blind line windows, which means a C++ class, its methods, and its free functions are indexed as declarations, and a codebase_search query returns whole symbols. Every Atlas tool call is permission-gated against allow, ask, and deny rules, so bash invocations of GoogleTest via ctest and vcpkg install can be allowed while writes to your build directory stay denied. For teams that cannot send source to third-party servers, Atlas can build its code index with local Ollama embeddings.
Step by step
- 01Run atlas at the root of the C++ project that contains CMakeLists.txt and your vcpkg manifest.
- 02Map the legacy module's public surface with the lsp tool's documentSymbol operation, then run findReferences on each exported symbol to enumerate every callsite, including template instantiations and virtual overrides.
- 03Pin behavior first: run GoogleTest via ctest with bash and record the green baseline commit before changing a single line of src/legacy/engine.cpp.
- 04Restructure with apply_patch, which seeks each hunk's context and old_lines and fails with Failed to find context if the file has drifted underneath an earlier hunk.
- 05Re-run GoogleTest via ctest with bash after each hunk lands, not once at the end, so a red suite points at exactly one change.
- 06Track the remaining callsites in a todowrite list so a partially migrated C++ module cannot be mistaken for a finished one.
- 07Land any dependency change through vcpkg as its own hunk, with its own GoogleTest via ctest run.
- 08Finish by running clang-format on the touched files only, then grep for the old symbol name to prove zero remaining hits in headers, sources, and CMakeLists.txt.
Frequently asked questions
- how to safely refactor legacy C++ code with an AI agent
- Have Atlas enumerate every callsite with the lsp tool's findReferences before it edits, pin the baseline by running GoogleTest via ctest through bash, restructure with apply_patch, and re-run GoogleTest via ctest after each hunk. Atlas surfaces a unified diff for every file edit before writing.
- what is apply_patch in Atlas and why not just use edit
- apply_patch is Atlas's structural edit tool. It anchors each hunk on context and old_lines instead of line numbers, and it fails with Failed to find context when the file has drifted. For a C++ restructure that spans many hunks in one file, that failure is a safety property, not an inconvenience.
- does Atlas work with CMake and vcpkg
- Yes. Run atlas in a project with a CMakeLists.txt and let it read your headers, translation units, and build targets. Atlas can run GoogleTest via ctest through bash and land vcpkg dependency changes as reviewable hunks.
- how do I find every caller of a C++ method before changing it
- Use the lsp tool's documentSymbol operation to list the module's exported symbols, then findReferences on each one. The language server resolves overloads, template instantiations, and virtual overrides that a text search of the header would miss or wrongly match.
- how does Atlas prove a C++ refactor did not change behavior
- Atlas records a green baseline by running GoogleTest via ctest with bash before the refactor, then re-runs the same command after every apply_patch hunk. The bash tool records the process exit code in its metadata, so the pass and fail states are unambiguous.
- can I undo a bad hunk from an Atlas C++ refactor
- Yes. Atlas snapshots file changes as git patches, so any hunk applied to engine.cpp or a new header can be diffed and rolled back. Atlas also reads git branches, status, and diffs, so the baseline commit is easy to return to.
- should clang-format run during or after a C++ refactor
- Run clang-format after the restructure and only on the files the refactor touched. Reformatting mid-refactor makes each intermediate GoogleTest via ctest diff unreadable and mixes mechanical churn into a semantic change.
Try Atlas in your terminal
The terminal-native AI coding agent. Free core, single binary.
Install AtlasRelated guides
Refactor a Legacy Module with Atlas in 2026
How to refactor a legacy module with Atlas in 2026: findReferences maps every callsite, apply_patch refuses to apply against a drifted file, and bash proves behavior.
Atlas for C++ in 2026
In 2026, C++ developers adopt Atlas, the terminal-native AI coding agent, to enhance productivity. Atlas offers secure, context-aware assistance for modern C++ projects, integrating with CMake and ensuring code quality
Diagnose a Hanging or Long-Running C++ Command with Atlas (2026)
Diagnose a hanging or long-running C++ build or test with Atlas in 2026. Read the bash shell_metadata block to tell a slow ctest run from one blocked on stdin.
Extract a Shared Helper From Duplicated C++ Code With Atlas (2026)
Duplication in C++ is semantic, not textual. Atlas finds near-duplicate logic with codebase_search, writes one header, and swaps each copy with apply_patch.
Run Atlas Headless in CI for C++ Projects in 2026
In 2026, C++ developers can run Atlas headless in CI pipelines to automate code tasks. Get machine-readable output, integrate with vcpkg and GoogleTest via ctest, and ensure safety.
Review a C++ Pull Request with Atlas (2026)
How Atlas reviews a C++ pull request in 2026: get the diff with bash, read whole translation units, and use lsp findReferences to catch callers the diff never shows.
Automate GitHub Issue and Pull Request Triage in C++ with Atlas in 2026
Automate GitHub issue and pull request triage for C++ projects using Atlas. Safely respond to events, enforce permissions, and manage context overflow in 2026 with your existing C++ toolchain.
Upgrade a C++ Dependency and Fix Breakage with Atlas in 2026
In 2026, C++ developers use Atlas to efficiently upgrade major dependencies with vcpkg, automatically fixing compile and GoogleTest failures. Atlas streamlines the entire migration process.