A developer shares a script that creates a temporary shell with a clangd wrapper pointing to a project-specific compilation database, allowing multiple firmware projects in one repository to use separate clangd configurations simultaneously.
# Felix' Blog - Project-Specific clangd Configuration with a Temporary Shell
Source: [https://felix-knorr.net/posts/2026-07-31-lsp-config.html](https://felix-knorr.net/posts/2026-07-31-lsp-config.html)
I've been writing C and some C\+\+ in terminal\-based editors for the past five years\.
By default, clangd searches the current file's parent directories for a`compile\_commands\.json`\. This becomes annoying when a repository contains several independently built firmware projects that share code and have a common repository root\.
Each project needs its own compilation database\. Replacing a single repository\-level`compile\_commands\.json`whenever I switch projects already sucks, but running separate editor instances for different projects breaks the whole thing, because both clangd instances would need the same file to contain different data\.
I recently came up with a solution that I like enough \(and found non\-obvious enough\) to justify a short post\.
The script below takes a build target as its argument and creates a temporary, target\-specific development environment\.
It:
1. Generates the target's`compile\_commands\.json`in a temporary directory\.
2. Creates a`clangd`wrapper in the same directory\.
3. Configures that wrapper to use the generated compilation database and also applies other settings we need\.
4. Starts an interactive Bash session with the temporary directory prepended to`PATH`\.
5. Adds the selected target to the shell prompt as a reminder\.
Because the wrapper is named`clangd`and appears first in`PATH`, editors started from this shell launch the correctly configured clangd without needing any project\-specific editor configuration\.
The`bashrc`prepends the directory after sourcing my normal`~/\.bashrc`, which also prevents tools such as direnv from accidentally putting another clangd ahead of it during shell initialization\.
```
#!/usr/bin/env bash
# Start a shell whose clangd uses the compilation database for one Bob target.
set -euo pipefail
if (($# != 1)); then
echo "usage: $(basename "$0") <bob-target>" >&2
exit 2
fi
target=$1
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
repo_root=$(cd "$script_dir/.." && pwd -P)
firmware_dir=$repo_root/firmware
clangd=$repo_root/dev-tools/clangd
if [[ ! -d $firmware_dir || ! -x $clangd ]]; then
echo "device-env must be located in the repository's dev-tools directory" >&2
exit 1
fi
if ! command -v uv >/dev/null; then
echo "uv is not on PATH; install it before running device-env" >&2
exit 1
fi
tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/device-env.XXXXXX")
trap 'rm -rf "$tmpdir"' EXIT
(
cd "$firmware_dir"
uv run bob ninja-tool -f compdb "$target"
) >"$tmpdir/compile_commands.json"
cat >"$tmpdir/clangd" <<EOF
#!/usr/bin/env bash
exec "$clangd" \\
"--compile-commands-dir=$tmpdir" \\
"--query-driver=**" \\
"\$@"
EOF
chmod +x "$tmpdir/clangd"
cat >"$tmpdir/bashrc" <<'EOF'
if [[ -f ~/.bashrc ]]; then
source ~/.bashrc
fi
export PATH="$DEVICE_ENV_TMPDIR:$PATH"
PS1="[denv:${DEVICE_ENV_TARGET}] ${PS1}"
EOF
export DEVICE_ENV_TARGET=$target
export DEVICE_ENV_TMPDIR=$tmpdir
bash --noprofile --rcfile "$tmpdir/bashrc" -i
```
I can now start a project\-specific shell with:
and launch Helix, or any other editor that invokes`clangd`through`PATH`from there\.
Each shell gets its own compilation database and clangd process, so multiple projects can be open simultaneously without interfering with one another\.
The mechanism is not specific to clangd btw\. A temporary directory containing wrapper executables can be a useful way to provide project\-specific behavior to tools that are normally configured globally or by the editor launching them\.
Clawk is an open-source tool that gives coding agents like Claude Code a disposable Linux VM, allowing them to run freely without risking the host machine. It uses network allow-lists and file isolation to keep secrets and system files safe.
Anthropic researcher demonstrates using a team of 16 parallel Claude instances to autonomously build a C compiler in Rust capable of compiling the Linux kernel. The article details the architecture, cost, and lessons learned from this multi-agent autonomous coding experiment.
A tutorial demonstrating four ways to set up development shells using Nix, including interactive one-offs, config files, and hermetic Nix Flakes, using GoCV and OpenCV as an example.
An article detailing the exact configuration and workflows that Anthropic's own engineers use with Claude Code, including parallel instances, CLAUDE.md patterns, writer/reviewer separation, skills folders, plugins, hooks, and batch operations.
A lean CLAUDE.md configuration file that enforces project conventions for Claude Code, with battle-tested rules to improve output quality without exceeding 200 lines.