pi-cpp-lsp1.2.0
Pi extension for C/C++ development with clangd integration, auto-formatting, linting, and compilation checks
Untested on omp
This is a pi package. No one has reported whether it works on omp yet. If you try it, add your result below.
Reports 0
No reports yet.
Readme
pi-cpp-lsp
A pi extension providing C/C++ development support: formatting, static analysis, project-aware compile checks, clangd navigation, and CMake builds and tests.
In a CMake project, the extension configures its own build directory, reads the CMake File API, and tells pi how the project is built: targets, C++ standards, dependencies and source files that are not registered in any CMakeLists.txt. Navigation tools use a clangd background index, so pi can find references, callers and derived classes instead of using grep.
Install
From npm:
pi install npm:pi-cpp-lsp
From GitHub:
pi install git:github.com/martin-juul/pi-cpp-lsp
Or try it for a single session without installing:
pi -e npm:pi-cpp-lsp
Quick Setup
Run the /cpp-setup command after installing to check your toolchain:
/cpp-setup
It lists each tool and its version. It also shows which navigation features your clangd supports and whether your CMake is new enough for the project model and for presets (see Version Requirements).
Or use the cpp-setup skill (/skill:cpp-setup) for a guided walkthrough.
Features
Custom Tools
| Tool | Description |
|---|---|
cpp_check |
Run clang-tidy and/or cppcheck static analysis |
cpp_compile |
Syntax-check using project compilation flags or standalone C/C++ defaults |
cpp_format |
Format with clang-format |
cpp_gen_compdb |
Generate compile_commands.json via CMake or Bear |
cpp_hover |
Get type information and documentation from clangd |
cpp_definition |
Find a symbol's definition with clangd |
cpp_build |
Build a configured CMake project |
cpp_test |
Run a configured project's CTest tests |
cpp_run |
Run a built executable target with arguments |
cpp_project |
Query the CMake project model: overview, targets, one target, a file's owner, presets, or refresh |
cpp_references |
Find all references to a symbol across the project |
cpp_symbols |
Search symbols across the project, or outline one file |
cpp_calls |
List the callers (incoming) or callees (outgoing) of a function |
cpp_types |
List the base classes (supertypes) or derived classes (subtypes) of a type |
cpp_rename |
Rename a symbol across the project in one transaction |
Project Model
When the session's working directory is inside a CMake project, the extension finds the source root. That is the topmost directory with a CMakeLists.txt in an unbroken chain of parent directories, stopping at the git top level. The extension configures the project in the background, in a build directory it manages (see Managed Cache). It never configures into your own build/ directory and never edits CMakeLists.txt.
Configuring runs the project's CMake code, so it only happens in projects pi trusts (pi's project trust, described in pi's security documentation). In a project pi does not trust, the extension does not run CMake, the managed clangd, or its git and CTest drift checks. The session behaves like a non-CMake one, and a one-time notification says why. To enable project support, trust the project with /trust and restart pi, or start pi with --approve. pi treats a folder without project-local pi resources (such as .pi/settings.json or .pi/extensions) as trusted.
- Session briefing. The first prompt of a session carries a project briefing: the preset, configuration and generator; a table of targets with their kind, directories, C++ standard and dependencies; open drift findings; and notes. Large projects get a shorter summary, and
cpp_project overviewreturns the full briefing. The briefing is sent again after a context compaction or a preset change. - Drift notes. After each
write,editandbashtool call, the extension checks the project again. If CMake files changed, it reconfigures first. A drift note lists new findings and findings that no longer hold. A source file (.c,.cc,.cpp,.cxx) in no target is reported with its likely owning target and theCMakeLists.txtline to edit. A source listed in CMake but missing on disk is reported with the line that lists it. The extension never edits build files; pi makes the edit. - Not built in this configuration. Source files that are named in a
CMakeLists.txtbut not built in the active configuration, for example behind anoption()or a platform check, are listed as information in the briefing. They are never reported as drift. - Post-edit note. After a
writeoreditof a C/C++ file, one note reports the clang-format result, drift, the owning targets and the C++ standard the file must stay valid for, and clangd's diagnostics for the file. Errors come first, at most 20 diagnostics are listed, and the note reads "diagnostics pending" when clangd does not answer in time. - Presets. With a
CMakePresets.jsonorCMakeUserPresets.json, the extension uses the first configure preset whose name mentionsdebugordev, or else the first one./cpp-presetlists the presets and/cpp-preset <name>switches to one and reconfigures. The choice is remembered per project. Without a preset, the managed build usesCMAKE_BUILD_TYPE=Debug. - Guidelines. In a CMake session, the fixed guidelines below are replaced by one rule: write code that is valid for the C++ standard of the target that owns the file.
cpp_project answers questions about the model:
overviewreturns the full briefing.targetslists every project target with its kind, standard, directories and dependencies.target(withname) returns one target's sources, include directories, defines, standard, dependencies and artifacts.owner(withpath) returns the targets that build a file and the standard it must stay valid for.refreshreconfigures now and returns the result.presetslists the configure presets, marking the active and the default one. It only lists them; switching is the user's/cpp-preset <name>command.
While a configure is running, cpp_project says so. After a failed configure, it answers from the last successful model and says the answer may be stale.
Outside a CMake project, in a project pi does not trust, or with CMake older than 3.14, the extension runs without the project model: no briefing, no drift notes, and cpp_hover and cpp_definition use whatever compilation database exists. For an untrusted CMake project or one whose CMake is too old, a one-time notification says why, and cpp_project reports the reason.
Default Build Directory (Behavior Change)
When you omit build_dir and the project model is ready, cpp_build, cpp_test, cpp_compile, cpp_check, cpp_hover and cpp_definition now use the managed build directory instead of build/ or the auto-detected compilation database. Each result names the directory or compilation database it used. Pass build_dir to use your own build directory. Outside a CMake project, or before the model is ready, the previous defaults apply.
Project-Aware Checks
cpp_compile uses compile_commands.json when available, preserving the source file's include paths, defines, language flags, and working directory. Use build_dir to select a custom compilation database directory. Explicit compiler and standard options override the database settings. Without a database, standalone checks use C17 for .c files and C++17 for C++ files.
cpp_check also accepts build_dir to point clang-tidy at the project's compilation database. Compilation checks remain syntax-only; use cpp_build to catch linker errors.
Database checks currently support direct GCC/Clang commands. Compiler wrappers, response files, MSVC commands, and module/plugin or compiler pass-through flags are reported as unsupported. A malformed database or missing source entry produces an error rather than silently using standalone flags.
Symbol Navigation
cpp_hover and cpp_definition accept file, line, and column, plus an optional build_dir for clangd's compilation database. Positions are 1-based, with columns counted in UTF-16 code units. Results reflect files saved on disk. Clangd starts on demand and stops when the session shuts down.
For example, use cpp_hover with {"file":"src/main.cpp","line":12,"column":5} to inspect the symbol at that position. clangd needs accurate project flags to resolve project headers and types.
Navigation
In a CMake project, the extension runs one clangd per session against the managed compilation database, with a background index. It starts when the project model is ready and restarts when compile_commands.json changes. The index is stored in the managed cache, not in the project tree.
cpp_referencestakesfile,lineandcolumn, and lists every reference, including the declaration and definition.cpp_symbolstakesqueryto search the whole project by name, orfileto outline one file with members nested under their class.cpp_callstakes a position anddirection:incoming(callers) oroutgoing(callees).cpp_typestakes a position anddirection:supertypes(base classes) orsubtypes(derived classes).cpp_renametakes a position andnew_name, and edits every file that uses the symbol. The rename waits for the index. The whole rename is refused, with nothing changed, when clangd cannot rename the symbol, the index is still building, a file changed during the rename, or an edit would land outside the project or in a build or dependency directory. The result lists each changed file with its clangd diagnostics.
These tools need the project model and always use the managed, indexed clangd. They return an error outside a CMake project or while the first configure is running. Positions are 1-based UTF-16, as for cpp_hover. Results are capped at 100 entries (300 for a file outline). On first use in a large project the index may still be building; results then end with a note that they may be incomplete. A feature your clangd does not support returns "not supported by this clangd version" with the version it needs.
Building and Testing
In a CMake project the managed build directory is configured for you. Elsewhere, configure first, using cpp_gen_compdb or your project's normal CMake configuration command. Then:
cpp_buildacceptsbuild_dir(default: the managed build directory when the project model is ready, otherwisebuild), optionaltarget,config, andparallel.cpp_testacceptsbuild_dir(same default), optional test-nameregex,config, andparallel. Build test executables before running it. Selecting no tests is reported as a failure.cpp_runaccepts an executabletarget, optionalargs, andcwd(default: the CMake source root). It finds the executable in the project model, so build the target first. It needs the project model.- All three tools accept
timeout_ms(default 120000, maximum 600000), support cancellation, and report command output, exit status and the build directory used. Large output is saved to a temporary log.
Auto-Formatting
C/C++ files are formatted with clang-format after every write or edit tool call. If clang-format is not installed, this step is skipped silently. Formatting uses your project's .clang-format when present.
Commands
/cpp-setup— Check and report C/C++ toolchain availability, the navigation features your clangd supports, and your CMake version against the project-model and preset thresholds/cpp-preset [name]— List the CMake configure presets, or switch to one and reconfigure
Context Injection
Outside CMake projects, adds C++ guidelines to the system prompt:
- Prefer smart pointers over raw new/delete
- Ensure
compile_commands.jsonexists for clangd - Use minimal diffs when editing
In CMake projects, the guideline is to write code valid for the owning target's C++ standard, and the session briefing is added to the first prompt (see Project Model).
Managed Cache
The extension keeps all of its state outside the project tree, in ${XDG_CACHE_HOME:-~/.cache}/pi-cpp-lsp/<hash>/. The hash is derived from the project's real source-root path. The directory holds:
builds/— one managed build directory per preset (defaultwithout a preset), withcompile_commands.jsonand the CMake File API replies- the clangd background index, in
.cache/clangd/indexinside the managed build directory state.json— the remembered preset
Nothing is written into the project tree. The cache is safe to delete at any time; the next session configures again. The first configure of a project that uses FetchContent downloads its dependencies again, into the managed build directory.
Skills
cpp-setup— Guided environment setup walkthrough
Required Tools
All tools are optional; each feature reports when its tool is missing.
| Tool | macOS | Debian/Ubuntu | Purpose |
|---|---|---|---|
clang-format |
brew install llvm |
apt install clang-format |
Formatting |
clang-tidy |
brew install llvm |
apt install clang-tidy |
Linting |
clangd |
brew install llvm |
apt install clangd |
Hover, definition, navigation, rename and post-edit diagnostics |
cppcheck |
brew install cppcheck |
apt install cppcheck |
Static analysis |
cmake |
brew install cmake |
apt install cmake |
Project model, managed builds, compilation database generation |
ctest |
Included with CMake | Included with CMake | Project test execution |
ninja |
brew install ninja |
apt install ninja-build |
Preferred generator for the managed build (optional) |
bear |
brew install bear |
apt install bear |
compile_commands.json for Make projects |
On macOS, Homebrew's LLVM is keg-only. Add $(brew --prefix llvm)/bin to your PATH.
Version Requirements
| Feature | Needs |
|---|---|
| Project model, briefing, drift, managed build | CMake 3.14 or newer (File API codemodel v2) |
| Configure presets | CMake 3.19 or newer; older versions ignore preset files and say so in the briefing |
| References, symbol search, rename | clangd with a background index |
Incoming calls (cpp_calls) |
clangd 12 or newer |
Type hierarchy (cpp_types) |
clangd 16 or newer |
Outgoing calls (cpp_calls) |
clangd 21 or newer |
The managed build uses the Ninja generator when ninja is installed, otherwise Unix Makefiles, unless the preset names a single-configuration generator. An existing managed build directory keeps the generator it was created with.
Ubuntu 24.04 ships clangd 18, which has no outgoing calls. For clangd 21 or newer on Debian or Ubuntu, use the apt.llvm.org packages (for example clangd-21) and put that clangd first on your PATH.
Project Structure
pi-cpp-lsp/
├── extensions/
│ ├── index.ts # Pi extension (tools, commands, hooks)
│ └── lib/ # Compilation database, clangd, navigation, rename, diagnostics and build support
│ └── project/ # CMake root discovery, managed configure, File API model, drift and briefing
├── skills/
│ └── cpp-setup/
│ └── SKILL.md # Guided setup skill
├── tests/
│ ├── *.test.ts # Automated tool and integration tests
│ └── sample_test.cpp # Sample file for manual testing
└── package.json # Pi package manifest
Development
npm install
npm run typecheck
npm test
pi -e ./ # load the local checkout into pi
Tests run on Node.js 24. Integration tests use local C/C++ tools when installed; tests requiring unavailable tools, or a clangd feature the local clangd lacks, are skipped. CI installs Ninja and clangd 21 from apt.llvm.org, so no navigation test is skipped there.
License
Copyright 2026 Martin Juul Christiansen. Licensed under the Apache License 2.0.