| check_dependenciesA | Run the full dependency audit. Read-only. Returns structured results. Returns the raw per-check dicts (name, status, message,
fix_cmd, instructions, critical) — NOT the formatted
doctor table. Read status/fix_cmd/instructions per
issue; status="skipped" means a prerequisite is missing. Args:
project_root: Project root directory. Auto-detected from CWD if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
list[dict]: one dict per check, DepCheckResult fields via asdict. |
| check_ollamaA | Check whether the LLM backend is running and the configured embedding/chat model is installed. Read-only: yes. No side effects. Call before smart_search,
semantic_search, or explain_symbol (when on-demand fallback is
expected — pre-computed analysis returns instantly without the LLM
backend). Args:
project_root: Project root. Auto-detected if omitted. Used to
locate the project's LLM configuration.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
dict: {ollama_enabled (bool), status (str — "ok"|"disabled"|
"not_configured"|"model_missing"|"embedding_unavailable"|"error"),
ollama_running (bool), ollama_url (str), configured_model (str),
num_ctx (int), installed_models (list[str]),
chat_api (dict — {configured (bool), model (str)}: whether an
external chat API replaces Ollama, and the model it names),
configured_embed_model (str), embedding_installed (bool),
message (str, on error/disabled), model_details (list[dict], when
Ollama running), suggest_cloud (bool), vec_available (bool),
vec_error (str, optional), debug_log (str, optional — only when
debug logging is enabled)} |
| configure_llmA | Configure LLM settings for the current project. Writes to <project>/.fw-context/local.toml ONLY (gitignored,
per-developer). Does NOT modify the global config or the shared
project config.toml. After writing, tests the configuration
by making a simple API call (skipped when LLM is disabled). IMPORTANT: When chat_api_base points to an external host, source
code snippets in chat prompts will be sent to that endpoint. Ensure
this complies with your organization's data security policies.
Consider using local Ollama or an internal API proxy first. Args:
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
chat_api_base: Chat API URL (see description for format details).
chat_api_key: Bearer token for cloud/proxy APIs.
chat_api_format: Override auto-detection: "auto", "ollama", "openai".
model: Chat model name.
embed_model: Embedding model name (Ollama only).
auto_pull: Whether to auto-pull models on 404.
stream: Stream chat responses via SSE. True avoids reverse-proxy idle timeouts. Returns:
dict: {status ("ok"|"error"), chat_api (dict — configured, endpoint,
format, model), model (str), auto_pull (bool), stream (bool),
test_latency_s (float, on success), test_response (str, on success),
compliance_warning (str, when chat_api_base is external),
message (str)} |
| get_active_buildA | MANDATORY FIRST CALL for C/C++ projects. Return metadata about the
most recently indexed build configuration — check index health before
using any other fw-context tools. Read-only, and it spawns no subprocess — the startup daemon thread and
the file watcher own the background reindex. Act on status: "ready" — up to date. Continue.
"reindexing" — background reindex running; queries stay accurate.
Continue. reindex_progress holds its last log line.
"reindex_needed" — schema mismatch, an old row format, changed
compile_commands.json, or a source file that compile_commands.json
does not cover. Queries still work on existing data. Read
reindex_reasons: a missing source file needs
fw-context index --build, the others need only
fw-context index.
Read bg_reindex_running before you name any command here. A run
already under way does the work, thus a second one only waits on the
same lock, and index_message and the branch reason both say so.
That field covers a run of the daemon AND a run the operator started
— the arguments of neither are readable, thus a reason that needs
--build can outlive a run without it. This status wins over
"reindexing" deliberately: the answers of this moment come from
the rows of the last FINISHED run. "no_index" — initialized, never indexed. Run fw-context index.
"not_initialized" — run fw-context init.
A failure sets NO status. DB corruption, or no access to the
index, gives a dict that holds error alone. Read that key first:
a reader that waits for status == "error" waits for a value this
tool does not produce. Four conditions set reindex_needed: an outdated schema, an outdated
ROW FORMAT, a changed compile_commands.json, and a source file that is
on disk but absent from compile_commands.json. The last one needs a
build, because only the build system writes that file — a plain reindex
has no translation unit for the file and skips it without a word.
Modified source files are something else: they are handled per-query,
and never set it. row_format_mismatch means that the same columns hold text with an
older meaning. Take it seriously: an index written before
fw-context-rows/1 keeps every inactive #ifdef branch, thus a
body or a file from it can show code that the compiler never sees. An
index written before fw-context-rows/3 is wrong the other way: it
ANSWERS LESS than it should. A definition that begins and ends on one
line has no stored body there, thus search_bodies cannot reach an
inline accessor; no macro is marked function-like; and no instance links
to its template, thus get_template_instances gives an empty list for
every template. Each of those looks like a legitimate empty answer.
The value of a macro there also holds its parameter list glued to
its replacement text, thus neither can be read out of it.
client_restart_required is the OPPOSITE case, and no command repairs
it. The index carries a NEWER row format than this server process
reads, thus the index is the correct one and this process is the old
reader. A reindex makes it worse than useless: the indexer writes the
same new format again, and the message comes back over an index that had
nothing wrong with it. status therefore stays "ready" and
reindex_needed stays False — every query keeps working.
Do NOT run a reindex for this field. Tell the operator to restart the
LLM client — Claude Code, opencode, or whichever one is in use. The MCP
server is a child process of that client, thus nobody can restart the
server by itself. client_restart_reason holds the wording, and
index_message opens with it. indexed_at and first_indexed_at are UTC; file mtimes are local
time. Never compare the two directly — in UTC+2 a correctly indexed
file looks 2 hours newer than indexed_at. Call with fast=False
to find modified files.
analysis splits the LLM-analysis coverage into project and vendor
symbols:
model — the model of the analysis, or None. One model only, even
when several were used.
analyze_vendor — the value at index time, not the current config.
project / vendor — {analyzed, skipped, total}.
skipped = tried, but not analyzable (body larger than the model
context, an unparseable answer, or a body that was not readable).
complete — no work left: every project symbol is analyzed or
skipped. True exactly when reindex_reasons holds no "unanalyzed
symbols" entry. Vendor symbols excluded by analyze_vendor=False
never block it, thus vendor.total large with vendor.analyzed=0
is expected, not a defect.
Args:
project_root: Project root directory. Auto-detected from CWD if
omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
fast: When True (default), the header check reuses the cached
manifest hashes. Both modes run the per-file scan, thus
modified_files_count is accurate either way — a tool that
reported "ready" while the search tools warned about the same
file gave the caller two readings and no way to choose.
False recomputes the header hashes and costs several times more. Returns:
dict: {config_hash, project_id, project_root, build_system,
compile_commands, indexed_at (str — "YYYY-MM-DD HH:MM:SS" in UTC,
the completion time of the last full index), symbol_count, file_count,
reference_count, modified_files_count (int — files whose content no
longer matches the index; counted in both modes),
header_affected_tus (int — number of TUs with stale header
dependencies), manifest_verification (str —
"full" when manifest.json exists, "none" otherwise),
analysis (dict — LLM-analysis coverage split by project/vendor:
{model, analyze_vendor, project: {analyzed, skipped, total},
vendor: {analyzed, skipped, total}, complete}),
description (str), first_indexed_at (str — UTC, same format as
indexed_at),
vendor_paths (list[str] — config index.vendor_paths),
project_paths (list[str] — config index.project_paths),
effective_vendor_patterns (list[str] — the SQL LIKE patterns that
this build really used to mark vendor code, for example
["mbed-os/%"]; empty when the manifest cannot be read. The two
lists above hold what the config asks for, this one what the index
did),
bg_reindex_running (bool),
reindex_progress (str or None — last log line when reindex is running),
schema_version (int — DB schema version),
current_schema (int — code expects), status (str — "ready"|"reindexing"|
"reindex_needed"|"no_index"|"not_initialized"; a failure sets no
status and gives error alone), reindex_needed (bool —
structural mismatch requiring a full reindex),
reindex_reasons (list[str] — why reindex is needed, empty when False.
One of them asks for fw-context index --build rather than a plain
reindex: when the tree is on a different branch than the index,
compile_commands.json belongs to the OLD branch and carries its file
list and its compiler flags, so only a build regenerates it. Read
the reason text — it names the command it needs),
stale (bool — True when reindex_needed or header_affected_tus > 0),
_warning (str, optional — when manifest verification is not "full"),
vec_available (bool), vec_error (str, optional),
index_message (str — human-readable summary of index state),
multi (bool — True for a multi-variant project),
variants (list[dict] — {name, description, board}),
images (list[dict] — {name, description, dir, type}),
variant_images (dict — variant name to its image names),
active_variant (str or None — [build] default_variant),
active_image (str or None — [build] default_image),
entry_point (str — the ENTRY() of the linker script of the build
that the other fields describe, empty when no script names one),
memory (list[dict] — the MEMORY regions of that build:
{name, attributes, origin, length, origin_value, length_value,
file_path, line})} About ``memory``: ``origin`` and ``length`` hold the expression the
script writes, thus they differ by platform — an mbed script writes
`0xefe00` and a Zephyr script writes `((673792) - 0xe6)`.
``origin_value`` and ``length_value`` hold the number, and both are
None for an expression that names a symbol, such as
`ORIGIN(RAM) + LENGTH(RAM)`. The end of a region is
``origin_value + length_value``.
``memory`` and ``entry_point`` describe ONE build. For a
multi-variant project they follow ``config_hash``, which is the
build named by ``[build] default_variant``, and both are empty when
the config names no default. Use ``list_variants`` for the map of
every build.
``memory`` is empty for a build system that records no linker
script. A PlatformIO project is the measured case: SCons writes no
ninja file and no link command the index can read, and the map file
never names the script. An empty list means "not recorded", never
"no memory".
``defines`` (dict — the `-D` flags of that build) and
``defines_varying`` (int). ``defines`` holds only the names that
EVERY translation unit of the build carries with the same value, so
the tool never shows the defines of one file as the defines of the
build. ``defines_varying`` counts the names left out, thus a name
absent from ``defines`` is either not defined at all or not defined
everywhere — measured on the Mbed project: 27 names in all 881 units, 59
in only some, where the three assembly files get a shorter set.
This is the configuration the BUILD states, not every macro the
preprocessor saw. The second is three orders of magnitude larger —
27800 distinct names on the STM32 project — and almost all of it comes from the
headers and the compiler. A Zephyr build keeps its real
configuration in ``autoconf.h`` (740 `CONFIG_*` names) and passes
few `-D` flags, so ``defines`` says little there and a great deal on
an mbed build, where it holds `APPLICATION_ADDR`,
`APPLICATION_SIZE`, and `CMSIS_VECTAB_VIRTUAL`.
For a project that is not initialized, the result holds only
``status``, ``project_root``, and ``index_message``. When no index
exists, the result adds ``project_id``.
|
| get_environment_statusA | Return the complete project environment status in one call. Read-only. Aggregates five domains into a single call so the LLM can
see everything at session start without extra round-trips: deps — dependency audit (run_full_check), each entry with an
optional action (message + shell command). status="skipped"
means a prerequisite is missing (e.g. libclang-so skipped because
libclang-python is absent) — not a failure.
build_system — detected build system, None when unknown.
compile_db — whether compile_commands.json exists and its entry count.
Reported as {"exists": false, ...} before init (no config to resolve
the path from, and loading one would create empty config files).
index — the FULL get_active_build() result, unchanged (its action
lives in index_message).
llm — LLM backend status with an optional action.
When the project is not initialized (index.status == "not_initialized"),
only the config-independent dependency subset runs (checks that do not need
a project config) — Ollama/model/db/build checks are skipped. Args:
project_root: Project root directory. Auto-detected from CWD if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
dict: {init_status (str — "initialized" or "not_initialized"),
deps (list[dict] — name, status, message, and an optional action),
build_system (str or None),
compile_db (dict — {exists (bool), path (str or None),
entry_count (int or None — None before init, and when fw-context
cannot read the file)}),
index (dict — the full get_active_build result),
llm (dict — {enabled, ollama_running, chat_model, embed_model}, plus
ollama_enabled when the LLM check ran, plus an optional
action)}. |
| get_project_infoA | Return project metadata (name, type, root_path) for a project ID. Looks up the global project registry at ~/.fw-context/projects.db.
Use this to identify a project from its UUID4 — find out what build
system it uses, its name, and where it was last indexed. Read-only. No side effects. Args:
project_id: Project ID (UUID4 hex) to look up. Returns:
dict: {project_id, name, project_type, root_path, created_at, updated_at}
or {"error": "..."} when the project_id is not registered. On failure the dict holds only ``error`` with the reason.
|
| list_projectsA | List all indexed firmware projects with their statistics. Read-only. No side effects. Use at session start to discover available
projects; use get_active_build for details on the currently active project. indexed_at and first_indexed_at are UTC, in "YYYY-MM-DD HH:MM:SS" format — the same format that get_active_build returns.
analysis holds the project and vendor counts only. For the
model, analyze_vendor, and complete fields, call
get_active_build for that project.
Args:
project_root: Project root. Auto-detected if omitted. Pass to
distinguish multiple indexed projects.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
list of dicts, each with: project_id, name, root_path, build_system,
symbol_count, file_count, indexed_at (str — UTC), description (str),
first_indexed_at (str — UTC), schema_version, current_schema,
reindex_needed (bool), status (str — "ready" or "reindex_needed"),
db (path to SQLite database file),
variant_count (int — number of build variants),
image_count (int — number of sysbuild images),
analysis (dict — LLM-analysis coverage
{project: {analyzed, skipped, total},
vendor: {analyzed, skipped, total}}, or None when no build is
indexed). When no project has an index, the result is a single dict with an
``info`` key. When fw-context cannot read a database, the result
holds a dict with ``db`` and ``error`` keys for that file.
|
| list_variantsA | List every indexed build with its (variant, image, board) identity. Read-only diagnostic — shows what is actually indexed, not what the config
declares. Each row is one (variant, image) build with its own
config_hash and symbol count. For single-project indexes this returns
one row with variant/image empty. This and get_active_build are the two tools that say what a query can
choose from. Both read the index, thus a build written by
fw-context index --build --variant X is listed even when config.toml
no longer declares X — and resolve_build fails closed on that same
fact, so the two never disagree about whether a choice must be made. Use get_active_build for the mandatory first-call health check and the
human-readable variants/images discovery; use this tool to see the
per-build config_hash and symbol counts (authoritative per-build state). Args:
project_root: Project root directory. Auto-detected from CWD if
omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
dict: {builds (list[dict]), multi (bool — True when the config
declares variants or a build has a non-empty variant name)}. Each build dict holds: variant (str — empty for a single-project
index), image (str — empty for a single-project index), board (str),
config_hash (str), symbol_count (int), file_count (int),
manifest_verification (str — "full" or "none"),
entry_point (str — the `ENTRY()` of the linker script of this build,
empty when no script names one),
memory (list[dict] — the `MEMORY` regions of this build:
{name, attributes, origin, length, origin_value, length_value,
file_path, line}). `origin` and `length` are the expression the
script writes; `origin_value` and `length_value` are numbers, and
they are None for an expression that names a symbol such as
`ORIGIN(RAM) + LENGTH(RAM)`. Empty for a build whose system
records no linker script — see the note below.
THIS is where a per-build memory map lives, not in the `images`
list of ``get_active_build``: that list holds one entry per image
NAME, and one name can belong to two variants with different
addresses.
When the project is not initialized, or has no index, the result is
{builds: [], multi: False, error (str)}.
|
| reindex_fileA | Re-parse a single source file with libclang and update its symbols in the index. Not read-only — uses the exact compiler flags from compile_commands.json.
Use after editing a file to keep the index current without a full rebuild. A source file must be listed in compile_commands.json. A header is
not listed there, thus it is re-parsed through one unit that includes
it. That answer covers a single compilation context, thus the result
carries a warning — only a full fw-context index covers every
unit that includes the header. Also regenerates LLM analysis and method override relationships for
affected symbols when those features are enabled in config. An
unchanged symbol keeps its stored analysis. Args:
file_path: Path to the file to re-parse. A source file must be in
compile_commands.json; a header goes through one including unit.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both. Returns:
dict: {file, translation_units, symbols_updated, elapsed_s,
analysis_updated (if LLM enabled), or error}. |
| reindex_file_implA | Re-parse a single source file with libclang and update its symbols in the index. Not read-only — uses the exact compiler flags from compile_commands.json.
Use after editing a file to keep the index current without a full rebuild. A source file must be listed in compile_commands.json. A header is
not listed there — compile_commands.json names translation units — so it
is re-parsed through one unit that includes it, taken from the manifest.
That answer describes a single compilation context, thus the result
carries a warning: another unit can see the header under a different
set of #define values and still hold stale symbols. Only a full
fw-context index covers every context. One unit and not all of them
is a cost decision — an application header reaches a median of 3 units
but as many as 266 on a real project, at tens of seconds each. Also regenerates LLM analysis and method override relationships for
affected symbols when with_analysis=True. The analysis is
content-addressed, thus an unchanged symbol is never re-analysed. Args:
file_path: Path to the file to re-parse. A source file must be in
compile_commands.json; a header goes through one including unit.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
with_analysis: When True (default), also regenerates LLM symbol analysis,
method override relationships, PageRank, and embeddings. Set False
for a fast symbol-only update (used by background auto-reindex). Returns:
dict: {file, translation_units, symbols_updated, elapsed_s,
analysis_updated (if LLM enabled with analysis), or error}. On failure the dict holds only ``error`` with the reason.
|
| reset_indexA | Delete the entire symbol index for a project. Not read-only — permanently deletes the SQLite database and WAL files.
Call with confirm=False first (dry-run) to see what would be deleted.
Re-index with fw-context index afterwards. Handles corrupt databases gracefully — you can delete a corrupt index
without needing to open it first. Args:
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
confirm: Must be True to execute. Call without first as dry-run. Returns:
dict: {project_root, db, project_id, action: "dry_run"|"deleted",
message, symbol_count, indexed_at (dry-run)}. A ``warning`` key means that the database is corrupt — the
integrity check failed, thus the counts can be incomplete.
On failure the dict holds only ``error`` with the reason.
|
| lookup_symbolA | Look up a C/C++ symbol by name via libclang index — exact or prefix
matching. Finds symbols text-based search can miss: build-conditional
code, template instantiations, macro-expanded names. Macros are
extracted via clang -dM -E during indexing so #ifdef-conditional
macros resolve correctly for the active build config. Prefer this over
search_code when you know the exact symbol name or a prefix
(uart_ finds all UART symbols). Use search_code for
keyword/concept search. Read-only: yes. May auto-reindex stale files (non-blocking). Args:
name: Symbol name (exact match) or prefix (set exact=False).
E.g. 'uart_init' finds the exact function; 'uart_' finds
all symbols starting with 'uart_'.
project_root: Project directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
exact: True = exact name match, False = prefix LIKE match (default).
limit: Maximum results of one page (default 50, max 100).
offset: Skip this many results (default 0). A common method name
lives in many classes — read and write match dozens of
symbols — and this walks past the ones already seen. The page
notice names the offset to use. The order is stable (a
definition first, then the line, then the file and the USR),
thus two pages never overlap and never skip a symbol.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list[dict]: The page notice leads the answer — total, offset,
shown, more — where total counts every symbol the name
matches. Read it before you conclude that a page holds them all.
Each symbol that follows has name, qualified_name, kind, file,
line, signature, docstring, is_definition, is_template, is_virtual,
is_pure_virtual fields, and class — the class, struct or union
that declares the symbol, absent for a free function. class is
what tells two same-name methods apart at a glance.
Enum constants include enum_value
with the integer value. Macro results include kind="macro",
signature (how the macro is invoked: #define NAME,
#define NAME() or #define NAME(a, b)), is_function_like,
value (the replacement text ALONE — the parameter list is not
part of it), and expanded_value (preprocessor-resolved value).
May also include template_usr,
parent_usr, and llm_analysis ({summary, inputs, outputs}) when available. A model wrote the text in
llm_analysis, and the code did not — use it to find a symbol,
and quote signature, docstring, or get_source instead.
When no results found, may include _did_you_mean with suggested
symbol names. When no symbol matches, the list is empty — there is
then no page notice, because there is no page. An info entry
comes back for one case only: an offset past the end of an answer
that does hold rows. **Note:** C++ constructors share their name with the enclosing
class, so ``lookup_symbol("Foo")`` may return both ``class Foo``
and ``constructor Foo::Foo()``. Use the ``kind`` field to
filter when you need a specific symbol type.
A symbol that comes from the relaxed prefix fallback carries
``_fallback: True`` — the name is not an exact match of *name*.
A list with one dict that holds an ``error`` key means that the
project has no index, or that the lookup failed. Read that key
before you read the result fields.
|
| search_codeA | Find C/C++ symbols by name — searches function/class/enum NAMES. Searches symbol names, qualified names, signatures, docstrings, and
pre-computed name tokens (CamelCase/snake_case split). Does NOT search
function bodies — for patterns in code like .attach(,
interrupt handler registrations, callback attachments use search_bodies instead. Use when you know the concept but not the exact name
("interrupt handler", "modem init"). Prefer lookup_symbol
when you already know the exact or prefix name. Results hold the metadata of each symbol — name, location, signature,
docstring — not its implementation code. FTS5 syntax: Every bare term gets a trailing * and the terms are OR-joined:
modem init goes to FTS5 as modem* OR init* and answers with
the symbols that hold EITHER word. search_bodies does the
opposite — it takes the query literally, where a space is an AND. init* matches init, init_uart, initialize (trailing wildcard)
"spi init" matches the exact phrase "spi init"
Do NOT use an underscore in a query. The tokenizer splits
modem_init into two tokens and looks for them NEXT TO EACH
OTHER. That is a phrase and not an AND, thus the query misses
modem_parser_oob_init. Measured on one firmware index,
serial_write gave 1 result and serial write gave 200.
Write modem init instead. Punctuation is not searchable. The tokenizer drops it, thus
.attach( becomes a phrase that looks for the token attach.
The query is repaired, never rejected.
Progressive relaxation: when a step matches nothing, the next one
runs. Six steps can run. Step 1 is the primary path and its results
carry NO _fallback key; each step after it names itself there: FTS5 with the kind filter. No _fallback key. FTS5 without the kind, when the kind matched nothing; operators
often guess the wrong kind — _fallback="fts5". name_tokens substring match over the pre-computed CamelCase /
snake_case tokens (BuildType is indexed as "build type").
Needs N−1 of N query terms — "name_tokens_like".
LIKE over the docstring column, for a single-term query that the
token steps missed — "docstring_like". FTS5 per query word, results merged — "individual_terms". macros_fts for #define names and values, kind="macro" —
"macros_fts".
Kind filter values: function, method, constructor,
destructor, class, struct, union, enum, enum_constant,
typedef, varglobal, varlocal, variable, field,
namespace. Local variables are out. FTS5 indexes the qualified name, thus a
local matches through the function that holds it: a query for
sensor used to answer with V, ret and tmp_value from
inside read_sensor_value, 4 of 20 results on one measured query.
A local is never the answer to "which symbol is about X", thus
varlocal and the legacy variable kind are excluded.
varglobal stays — a global carries architectural weight. Ask for
them explicitly with kind="varlocal", or use find_variables. After fw-context index --analyze, a result also holds
llm_analysis — {summary, inputs, outputs}. A model wrote that
text, and the code did not. Treat it as a hint that points you at a
symbol, never as a fact to quote. Quote source from
get_source, signature, or docstring. Read-only: yes. May auto-reindex stale files (non-blocking). When a
file that the answer names changed on disk, this tool starts the
watcher daemon in the background and answers from the index it has. Args:
query: FTS5 search terms. Keep queries short — 1–3 words.
project_root: Project root directory. Auto-detected from CWD if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
kind: Optional filter to return only symbols of this kind.
limit: Maximum results of one page (default 20, max 100).
offset: Skip this many results. Reads the next page of a topic
that many symbols carry; the page notice names the offset to
use. One relaxation step owns the whole answer, thus a walk
never changes the step under the reader.
project_only: When True, exclude vendor SDK directories and return only
application code. Default False.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts. The page notice leads the answer — total,
offset, shown, more — where total counts the
answer of the step that answered. Each symbol that follows has
name, qualified_name, kind, file, line, is_definition, signature,
docstring, is_template, is_virtual, is_pure_virtual. Enum
constants include enum_value with the integer value. May also
include template_usr, parent_usr, and llm_analysis
({summary, inputs, outputs} — written by a model, not by the
code. get_active_build().analysis.model names it). Fallback
results include _fallback with the method name. No match gives ``[]``. A dict with ``error`` means the query
failed. A stale index prepends a dict with ``warning`` + ``hint``,
and that dict comes BEFORE the page notice — find the notice by
its keys, and not by its position.
|
| search_bodiesA | Find patterns in the TEXT OF A DEFINITION — the code inside its extent. Searches ifdef-filtered text — only the code that compiles for the
current build. A line of an inactive #if branch holds nothing, thus
a pattern that lives only in a dead branch gives no result here. That
empty answer is the correct one: the code does not compile. Searches the stored text of every definition (is_definition=1), and
a definition is not only a callable. Measured on one project of 60,877
symbols, the text covers: Callables — function, method, constructor, destructor.
Call patterns (.attach(, .rise(, callback(&), ISR
registration, one case label of a long switch. Types — class, struct, union, enum, namespace.
An enum constant, a bit field, a member declaration such as
InterruptIn _pin; — all inside the body of the type that holds
them. Definitions of data — varglobal, varlocal, typedef. A
table with a multi-line initializer is found by its content.
A match on a type reports the type as the result, thus a query for one
enum constant answers with the enum, and match_lines gives the line
of the constant itself. Only the text matches. The query is bound to the stored body: a
hit in the NAME, the signature, the docstring or the llm_analysis
of a symbol is not a hit here. Measured on one project, sensor
used to give 36 results of which 22 matched only through a summary that
a model wrote — untrusted text that cannot be cited, and a
_match_snippet with no match in it. Use search_code to reach a
name or a concept. A column filter you write yourself
(summary : sensor) overrides the binding. When to use search_bodies and when search_code: search_bodies — patterns in the code (what the code DOES or
DECLARES): self test, attach, SELF_TEST.
search_code — symbols by NAME (what the code IS): modem init,
interrupt handler.
The query goes to FTS5 as you wrote it. This tool alone adds no
wildcard, and that is what keeps a pattern precise: A space is an AND of two exact tokens, NOT an OR. CommandType NUM
answers with the definitions that hold both. No prefix is implied. SELF_TEST matches the tokens self test
and misses Self tester; write SELF_TEST* to reach the second.
Measured on one project, the wildcard added the one caller that the
bare query missed. Punctuation is not searchable. FTS5 cannot parse .attach( at
all, thus the query is repaired into the phrase ".attach(" — and
the tokenizer inside a phrase drops the punctuation too, so what runs
is the word attach. Such a result carries _fallback: "sanitized" and _query_used. The hits whose body really holds
.attach( are the ones with match_lines. search_code and search_content behave the OTHER way: each of
their terms gets a trailing * and the terms are OR-joined.
Limitation — the extent of a definition is the boundary. Text that
belongs to no definition is out of reach: #include, #define, #ifdef — preprocessor directives.
search_code covers a macro name and value. search_content
covers the directive as text.
extern "C" — a linkage specifier is no symbol.
A comment or a declaration at file scope, outside every definition.
For those, use search_content, which indexes the full file text. Set project_only=True for a question about YOUR code ("where do we register interrupt handlers?"). Leave it False (default) when the
vendor SDK code — the framework or OS code that your team did not write
— is also relevant. Results include _match_snippet — a highlighted excerpt that shows
each match in context (e.g. _timeout.<b>attach</b>(callback(...))) —
and match_lines, the line numbers of the matches inside the
definition. line is where the definition starts, which for a large
function is far from the match. Cite from match_lines instead.
Project code sorts before vendor code in the output. Read-only: yes. Requires the FTS5 index. May auto-reindex stale files
(non-blocking) — see search_code. Args:
query: FTS5 search terms, 1-3 words. A bare multi-word query is an
AND of exact tokens, and no wildcard is added — see the query
rules above. A single word is the broadest form: 'attach'
reaches every .attach(...) pattern. Add * for a prefix
('attach*'), and double quotes for a phrase
('"attach callback"').
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
kind: Optional filter to return only symbols of this kind.
limit: Maximum results of one page (default 20, max 100).
offset: Skip this many results. Reads the next page of a pattern
with many hits; the page notice names the offset to use.
project_only: When True, exclude vendor SDK directories and return only
application code. Default False.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts. The page notice leads the answer — total,
offset, shown, more — and each that follows holds:
name, qualified_name, kind, file, line (first line of the
definition), is_definition, signature, _match_snippet (excerpt
around the match), source (the text of the definition). Also, when they carry an answer:
* ``match_lines`` (list[int]) — absolute line numbers of the
matches, up to 20. Computed from the full text, thus a match
after the cut below still has a number. Use these to cite
``file:line``, and not the ``line`` of the definition. The name
carries no leading underscore for a reason: a field the caller
must cite is an answer, while ``_``-prefixed fields
(``_match_snippet``, ``_fallback``, ``_source_truncated``) tell
where the answer came from.
* ``_source_truncated`` (True) — ``source`` is cut. A callable
keeps 2000 characters, any other kind 500, because the body of a
type is mostly members that the match has nothing to do with.
``get_source`` gives the whole text.
* ``_fallback`` (``"sanitized"``) with ``_query_used`` — FTS5 could
not parse the query as written, thus a repaired one ran. The
repair drops punctuation, so the answer is wider than the text
that was asked for. Every query FTS5 accepts runs untouched and
carries neither field.
``source`` here is bare text with no line-number prefix. Only
``get_source`` numbers its lines.
No match gives ``[]``. A dict with ``error`` means the query
failed. A stale index prepends a dict with ``warning`` + ``hint``,
and so does a query that FTS5 refuses to parse — an empty list
always means "no such code", never "bad query".
|
| search_contentA | Find patterns in FULL file content — the whole file, not only the
text that belongs to a definition. Searches ifdef-filtered file text — only code that actually compiles
for the current build configuration. Inactive #ifdef branches are
replaced with blank lines (preserving original line numbers). A pattern
that lives only in a dead branch therefore gives no result, and that
empty answer is the correct one: the code does not compile. Covers the text that belongs to no definition, which is what
search_bodies cannot see: #include, #define, #ifdef,
extern "C", and a comment or declaration at file scope. It covers
the text of definitions too. To find a symbol by NAME (modem init,
interrupt handler), use search_code. Not a fallback of search_bodies — its complement. The two
answer different questions and reach different text: search_bodies answers WHICH DEFINITION holds the pattern, and
takes the query literally (no wildcard, space = AND).
search_content answers WHICH FILES the topic touches, and widens
the query: every term gets a trailing * and the terms are
OR-joined. The wider query reaches text the literal one misses —
measured on one project, SELF_TEST found 6 files here and the
same word found 5 through search_bodies, the extra file holding
the comment Self tester.
For the footprint of one feature, run both. Results are file-level — one entry per matching file, with
match_lines for the lines that hold a query term.
project_only=True filters to is_project = 1 files; the default
False includes the vendor SDK files. When files_fts is missing (legacy index), falls back to LIKE
search on files.content — results include _fallback: "like"
and no snippet highlighting. Run fw-context index to upgrade. Read-only: yes. Requires the FTS5 index with file content. May
auto-reindex stale files (non-blocking) — see search_code. Args:
query: FTS5 search terms. 1-3 words. Bare multi-word queries are
OR-joined (prefix-wildcarded). Prefer single-word queries.
E.g. 'InterruptIn', 'extern C', '#define'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results of one page (default 20, max 100).
offset: Skip this many results. Reads the next page of a topic
that many files touch; the page notice names the offset to use.
project_only: When True, filter to project code only (files with is_project = 1).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts. The page notice leads the answer — total,
offset, shown, more — and each that follows holds:
file, language, mtime, _match_snippet (highlighted excerpt around
the match). Also, when it carries an answer:
* ``match_lines`` (list[int]) — line numbers of the lines that hold
a query term, up to 20. They are the line numbers of the file
itself: an inactive ``#ifdef`` branch is a blank line, thus the
count never shifts. Cite ``file:line`` from here.
The field is absent when FTS5 matched a variant of the token that
the term is not a substring of — ``SELF_TEST`` matches the file
that writes ``Self tester``, and no line holds ``self_test``.
Read ``_match_snippet`` in that case.
No match gives ``[]``. A dict with ``error`` means the query
failed. A stale index prepends a dict with ``warning`` + ``hint``,
and so does a query that FTS5 refuses to parse — the answer then
comes from the LIKE path and carries ``_fallback: "like"``.
|
| semantic_searchA | Semantic search using pre-computed libclang symbol embeddings. Finds
symbols by meaning, not by text — matches concepts even when query
words don't appear literally in the code. Uses cosine similarity over
variable-dimension embeddings generated during fw-context index.
Dimensions vary by model: mxbai-embed-large → 1024,
qwen3-embedding → 4096. When to prefer over search_code: When you're describing a concept
rather than searching for a known keyword. Examples: "parcel locker state" finds door-state and shipment methods even
though "parcel" and "locker" don't appear in their names.
"cell modem" finds _socket_t and ModemMsg* classes.
"delivery box" finds set_shipment and get_zrtdata.
"power consumption" finds get_load_power and INA260 class.
When to prefer search_code instead: When you know the exact keyword
or symbol name ("fram_write", "cbor encode"). FTS5 is faster
and more precise for lexical matches. Threshold guidance (mxbai-embed-large model): 0.50 — exploratory: more results, lower precision
0.55 — balanced (~1000 results)
0.60 — precise: ~175 avg, high precision (default)
0.65 — strict: few results, may miss relevant symbols
Source-aware ranking: the similarity of a project symbol is
multiplied by 1.2, and the similarity of every other symbol by 0.85.
The index marks each file as project code or not, thus the two tiers
are all there are. _similarity in the result holds the multiplied
score, and not the raw cosine distance. Requires an LLM with an embedding model.
Falls back to search_code with a warning if the LLM is unavailable. This tool names no build. It takes neither variant nor
image, and it answers for the build that get_active_build
reports as the active one. On a project that holds several builds,
use search_code or search_bodies to ask about one named build. Read-only: yes. The fallback to search_code may auto-reindex stale
files (non-blocking). Args:
query: Natural language description of what you're looking for.
Be specific — 5–15 words works best.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
threshold: Minimum cosine similarity (0.0-1.0). Default 0.60.
limit: Maximum number of results (default 20, max 100). Returns:
list of dicts, each with: name, qualified_name, kind, file, line,
is_definition, signature, docstring, plus _similarity (cosine
similarity score) and _method ("embedding" or
"search_code_fallback"). When the best similarity is below the relevance floor (0.68), the
result is one dict with ``warning``, ``_best_similarity`` (float),
``_fallback_suggestion`` (``"search_code"``), and ``_results`` (the
low-similarity results). Treat those results as noise, and use
``search_code`` instead.
When the LLM is not running, or the embedding fails, this tool falls
back to ``search_code``. The results then carry
``_method: "search_code_fallback"``, and a leading dict holds a
``warning`` with the reason.
No match gives ``[]``. One dict with ``error`` means the query
failed — check that key first.
|
| smart_searchA | Natural-language search: an LLM generates FTS5 keywords, then searches
the libclang index. Finds concepts by meaning rather than exact text
match. Prefer this when you don't know the exact keywords and want to
describe what you're looking for ("how does the modem connect?",
"handle BLE pairing failure"). Read-only: yes. Slow (10-30 s) — delegates to the full
SMART_SEARCH pipeline (translate → rough_search → llm_query →
fts5_search → refine → embedding → adaptive_fusion → deduplicate →
expand_context → format). This tool names no build. It takes neither variant nor
image, and it answers for the build that get_active_build
reports as the active one. On a project that holds several builds,
use search_code or search_bodies to ask about one named build. Multi-phase approach: Translate non-English queries Rough search to gather sample symbols for naming conventions LLM sees those samples + query and generates FTS5 terms FTS5 search with generated terms Refine: LLM checks results and course-corrects query terms Semantic embedding search (cosine similarity re-rank) Deduplicate, score, and format results
When to prefer over search_code: When you don't know the exact keywords
and want to describe what you're looking for ("how does the modem connect?",
"handle BLE pairing failure"). Fallback: When LLM is unavailable, falls back to direct FTS5 search
with word-split terms from the query. Args:
query: Natural language description of what you're looking for.
Be specific — 5–15 words works best.
project_root: Project root directory. Auto-detected from CWD if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum number of results (default 20). The pipeline holds
it between 5 and 100: a smaller number becomes 5, because the
re-rank steps need a set to choose from. Returns:
list of dicts with metadata entries (_generated_queries, _rough_queries,
_translated_from) followed by symbol results with name, qualified_name,
kind, file, line, is_definition, signature, docstring. When the LLM stalls, the tool gives the FTS5 results that it has.
The leading dict then holds ``_partial: True``, a ``warning`` with
the timeout, and a ``hint``. The result is incomplete: make the
query more specific, or increase the LLM timeout.
When the index is stale, a leading dict holds a ``warning`` and a
``hint`` to reindex.
No match gives ``[]``. One dict with ``error`` means the query
failed — check that key first.
|
| find_all_callers_recursiveA | Find all transitive C/C++ callers — who calls name, directly or
indirectly, through the libclang call graph including
function-pointer edges, implicit constructors, and synthetic
dispatch edges. libclang-powered: follows function-pointer
assignments and ISR vector registrations across the full call tree. Use for impact analysis: "if I change this function, how far does the
ripple go?" Returns callers at depth 1 (direct), depth 2 (callers of
callers), up to max_depth (default 5). Results are deduplicated —
each caller appears once at its shortest distance to the target. Edge types traversed: Includes call, indirect (function
pointers / ISRs), implicit_construct (constructors reachable through
file-scope global objects), and dispatch (synthetic edges through
event loops and thread starts). Limitation — ambiguous name resolution: When a source-line fallback
cannot disambiguate which method is called (e.g. attach() matching
both Timeout::attach and SerialBase::attach), the edge is
conservatively omitted to avoid false callers. If you suspect a
missing caller, verify with search_bodies("target_name") and
find_indirect_targets. For a flat, single-level caller list use find_callers (faster).
For the reverse direction use find_callees_recursive. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). BFS from the target
outward; performance scales with call-graph fan-out. Args:
name: Symbol name to find transitive callers of.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
max_depth: Maximum BFS depth for transitive search (default 5).
limit: Maximum results (default 50).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: name (str — the caller),
qualified_name (str), kind (str), signature (str),
depth (int — distance from the target), file (str — absolute). This tool gives no line, because one caller can hold several call
sites. For the line of each call use ``find_callers`` on the name
that this tool reports.
When *name* matches more than one symbol, such as two classes with
a method of the same name, the answer holds the callers of all of
them. A ``warning`` dict then comes first and names the symbols,
and each result carries ``target_qualified_name``, which tells the
symbol that it calls. Give the full qualified name to ask about
one symbol only.
Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| find_call_pathA | Find call paths between two C/C++ functions via BFS in the libclang
call graph, including function-pointer edges, ISR vector
registrations, implicit constructors, and synthetic dispatch edges
(event loops, thread starts). libclang-powered: follows
function-pointer edges and ISR vector registrations that text-based
search cannot resolve. Use to answer "how does A reach B?" — e.g. tracing how a high-level
event handler eventually calls a low-level driver. Returns up to 5
shortest paths, each with depth (edge count) and chain
(e.g. "main → app_run → modem_init"). Edge types traversed: The BFS includes call, indirect
(function pointers / ISRs), implicit_construct (global/static
object constructors), and dispatch (synthetic edges through event
loops like EventQueue::dispatch_forever and thread starts like
Thread::start). Limitations: Dispatch bridges: callbacks registered through
EventQueue::call_every, k_work_submit, or xTimerStart
reach their dispatch entry point (dispatch_forever,
z_work_q_main) through a built-in map for mbed-os, Zephyr, and
FreeRTOS. Add other RTOS patterns in
[call_graph.dispatch_bridges] (.fw-context/config.toml); a
bridge whose entry symbol is not in the index is skipped silently. Ambiguous fallback names: for a call that libclang cannot
resolve (template-obscured _timeout.attach(...)), a source-line
regex matches the method name. When several methods share that
unqualified name and neither the receiver field type nor the caller
class disambiguates, fw-context creates NO edge — conservative, to
avoid false paths. Global constructors: file-scope implicit_construct edges
hang off a synthetic <global ctors> node between main and
every global constructor. Any query that can reach main uses
it, not only a query that starts at main.
On an empty result that you expected to hold a path: look for async
dispatch (search_bodies("call_every"), search_bodies("attach")),
trace the intermediate symbols with find_callers, raise
max_depth, and check the function-pointer wiring with
find_indirect_call_sites / find_indirect_targets. For one-sided exploration use find_all_callers_recursive (who reaches
this?) or find_callees_recursive (what does this reach?).
For exact call-graph verification use find_callers or
find_references. Read-only. No side effects. Requires both symbols to be in the index
and refs enabled (fw-context index — refs on by default). Args:
from_name: Starting symbol for path search.
to_name: Target symbol to find path to.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
max_depth: Maximum BFS depth for path search (default 10). No
clamp holds this number. What bounds a deep search is the node
budget of the walk — 5000 expansions — thus a large depth
gives up on that budget and not on the depth.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
At most 5 paths, each a dict with: depth (edge count, int), chain (str —
e.g. "main → app_run → modem_init"), target_usr (str — the USR
of the symbol the path ends at, which tells two overloads apart).
When no path exists within the depth limit, the list holds one
info dict. When *to_name* matches more than one symbol, the search reaches
all of them. A ``warning`` dict then comes first and names the
symbols, and each path carries ``target_qualified_name`` next to
``target_usr``. Give the full qualified name to ask about one
symbol only.
Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| get_vector_tableA | Read the interrupt vector table, and say what services each interrupt. The vector table is how an interrupt reaches code. Nothing CALLS a
handler — the hardware reads a slot and jumps — so a handler has no
caller, and every other tool shows it as unreferenced. This tool
reads the table itself, from the assembly the build compiles. Use it to answer "which interrupts does this firmware service", to
find the handler for one interrupt, or to find the interrupts that
reach the trap loop. The slot number is the position in the table. What that position
means belongs to the architecture, not to the index. On Cortex-M
slots 0 to 15 are the system exceptions and slot 16 + n is external
interrupt n, so TIM2_IRQHandler in slot 44 is TIM2_IRQn = 28.
On other architectures the same position means something else. The status field says what services the interrupt: "c" — a definition outside assembly. Code runs. When the
index also holds the weak definition that this one replaced, the
row has overridden with its file and line.
"assembly" — a strong assembly definition. Assembly services
the interrupt.
"unhandled" — a weak assembly definition that nothing
overrode. A CMSIS startup file makes this an alias of
Default_Handler, which is an infinite loop. If the interrupt
fires, the device stops.
"runtime" — the image holds that same alias, and the code
installs a real handler into this slot by calling
NVIC_SetVector. A target that defines
CMSIS_VECTAB_VIRTUAL keeps its vector table in RAM and fills
it that way, so the interrupt IS serviced once the registering
code has run — and not before it. The row holds installed,
one entry per call site with the handler name, its file and line,
and at, where the registration happens. Follow at to see
WHEN it happens: on the Mbed project us_ticker_irq_handler
reaches slot 25 from us_ticker_init, so the tick source is
unserviced until the ticker starts. A row with a real static
definition keeps its own status and still carries installed.
"data" — the slot holds an address BUILT from the symbol it
names (.word z_main_stack + CONFIG_MAIN_STACK_SIZE), so the
symbol is a base and nothing jumps to it. On Cortex-M this is
slot 0 of a Zephyr table: the initial stack pointer. Do not read
it as code.
"linker" — the linker script gives the address and no compiled
file defines the name. Slot 0 holds the initial stack pointer, not
a handler, and looks like this. When the index read the script,
file and line name the assignment in it — on the Mbed project,
__StackTop at .link_script.ld:148. Do not read this row as
code: there is no function to follow.
"dispatcher" — the slot reaches a function that holds more than
one slot of this table AND calls through a pointer. It cannot be
servicing one particular interrupt; it decides at run time where to
go. Zephyr fills every external IRQ slot with _isr_wrapper,
which reads the interrupt number and jumps through
_sw_isr_table. Follow it: get_symbol_context on the name,
then find_references on the table it uses. A handler that
merely calls one registered callback is NOT this — it holds a
single slot and keeps "c".
A "c" row with overridden is the CMSIS pattern: the startup
file defines each handler weakly, the project defines the same name
again, and the linker keeps the strong one. Two sources are read, and source says which one a row came
from: "assembly" — a table of address words, .word or .long
in a vector section, which is what a CMSIS startup file writes.
"c" — an array whose elements are addresses of functions, which
is what a build that generates its table produces. Zephyr writes
its external interrupts this way, with gen_isr_tables.py. These
rows also carry table_name, the array the slot belongs to.
"build" — the registration the build itself recorded, for a slot
the other two could not name. A generator writes a resolved ADDRESS
into every slot that is in use, so those slots have no name in the
source at all — and they are the interrupts the firmware actually
services. Measured on an nRF54L application: 284 of 290 slots name
the spurious stub, and the 6 without a name are IRQ 89, 198, 219,
228, 269 and 270, which these rows fill in.
Such a row can carry argument, the symbol the build passes to
the handler. Read it as an argument and not as a second handler:
behind the nrfx_isr shim it is the real worker
(nrfx_power_clock_irq_handler), while for another driver it is
the device (__device_dts_ord_116). When the build enables
run-time registration, a dict with info says so, because an
interrupt connected at run time leaves nothing to read and the rows
are then not all of them.
Recognition is by shape, never by name, so any array of function
addresses is reported and the row names its table. A table of
interrupt handlers and a table of state machine steps are the same
construct, and table_name is how they are told apart. Slot numbers are not joined across tables. Each slot is the index
inside its own table, so two tables both start at 0 — read slot
together with table_name and source. They are not renumbered
into one run because the index does not hold the length of the
assembly table, only its occupied slots, and an offset derived from
that would be silently wrong for every entry of a 290-entry table. A coverage row follows the slots for each table longer than the
number of slots that name a function. It says how many of the declared
elements were named and which slot numbers were not, because a name is
not always there to be read: an element can be a zero, or an address
the linker resolved before the table was written. Read it in both directions. A hole in a table of handlers is a vector
nothing services. A hole in Zephyr's _sw_isr_table is the
opposite — measured on an nRF54L application, 284 of 290 slots name the
spurious stub and the 6 without a name are the interrupts in use. The
tool reports where to look; which meaning applies depends on the table. An interrupts row answers "which are unserviced" wherever the
build recorded its registrations, and it is the answer under
unhandled_only too. The row-level unhandled status is read
from an alias edge, which a CMSIS startup writes and a generator does
not — measured, zero unhandled rows on all eleven images of a Zephyr
project against 39 to 72 on four CMSIS and Mbed ones. The
registrations settle it from the other side: what the build connected
is the whole list, so anything else has nothing servicing it, and no
handler has to be recognised by name. The complement is taken over the length of the table, NOT over the
slots that hold a stub. Measured on an mcuboot image: its software
table names 44 of 48 slots, and one of those 44 is
uarte_0_direct_isr, an interrupt wired straight into the vector
table. It IS serviced, and counting stubs would report it as not. What is still not covered: an architecture that builds its table from
branch instructions (arm64, Xtensa, MIPS) writes no table of
addresses in either form. A handler whose address the build resolved
at link time has no name to report either — coverage names its slot
but not the function. For an interrupt this tool cannot show,
find_references on the handler name still gives every reference
the index holds. Read-only. No side effects. Requires an index of the assembly
(fw-context index). Args:
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
unhandled_only: When True, return only the "unhandled" slots.
limit: Maximum slots (default 400, max 1000).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts sorted by source, then table, then slot. Each holds:
slot (int), name, file, line, source ("assembly", "c" or
"build"),
status ("c", "assembly", "unhandled", "runtime",
"data", "linker" or "dispatcher"), and table_file and table_line
(where the slot is written). A "c" source row also holds
table_name and table_usr. A "c" status row can hold
overridden, a dict with file and line. Any assembly row can hold
installed, a list of dicts with name, file, line and at. Never empty: one dict with ``error`` (no index) or ``info`` (no
vector table in this build). Check both keys first. A dict with
``coverage`` follows the slots for each table that has unnamed
elements, and a dict with ``interrupts`` says which are connected
and which are not — the latter in both modes. Neither is subject
to ``limit``: they describe the whole table, and the longest table
is where they matter most. When more slots exist than ``limit``,
a dict with ``truncated`` sits between the slots and those two,
saying how many slots are not shown.
|
| find_callees_recursiveA | Find all transitive C/C++ callees — what name calls, directly or
indirectly, through the libclang call graph including
function-pointer edges, implicit constructors, and synthetic
dispatch edges. libclang-powered: follows function-pointer
calls and indirect invocations across the full dependency tree. Use for dependency analysis: "what does this function depend on to do
its job?" Returns callees at depth 1 (direct), depth 2 (callees of
callees), up to max_depth (default 5). Results are deduplicated
by shortest distance. Edge types traversed: Includes call, indirect (function
pointers / ISRs), implicit_construct (constructors reachable through
file-scope global objects), and dispatch (synthetic edges through
event loops and thread starts). Limitation — ambiguous name resolution: When a source-line fallback
cannot disambiguate which method is called, the edge is conservatively
omitted to avoid false callees. If you suspect a missing callee,
verify with search_bodies("target_name"). For direct callees only, get_symbol_context gives a faster flat
list along with the function body and callers. For the reverse
direction use find_all_callers_recursive. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
name: Symbol name to find transitive callees of.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
max_depth: Maximum BFS depth for transitive search (default 5).
limit: Maximum results (default 50).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: name (str — the callee),
qualified_name (str), kind (str), signature (str),
depth (int — distance from the source), file (str — absolute). This tool gives no line, because one function can call the same
callee several times. For the line of each call use
``find_callers`` on the name that this tool reports.
When *name* matches more than one symbol, the answer holds the
callees of all of them. A ``warning`` dict then comes first and
names the symbols, and each result carries
``target_qualified_name``, which tells the symbol that calls it.
Give the full qualified name to ask about one symbol only.
Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| find_callersA | Find who calls a C/C++ function — direct calls AND indirect via
function pointers, callbacks, interrupt vector registrations, and
struct init lists. libclang-powered: detects function-pointer
assignments and ISR vector registrations that text-based search
cannot see. Falls back to macro lookup when the symbol is not found as a
function/method: returns the macro definition (kind="macro") and
files that use it (ref_kind="macro_use"). Use when you need a quick, flat list of immediate callers. For the full
transitive call tree (who calls this indirectly through other functions),
use find_all_callers_recursive. For all references including reads
and member accesses, use find_references. For a path between two
specific symbols, use find_call_path. Read-only. No side effects. Requires the reference index
(fw-context index — refs are on by default). Only direct call
sites are returned; callers more than one hop away are not included. Indirect edges (ref_kind: "indirect") are detected when a function
pointer references a function through: Call arguments: callback(&Class::method, this),
EventQueue::call_every(ms, obj, &handler) Assignments: driver.onData = &handleData,
global_cb = &handler Variable initializers: static void (*fp)(int) = &handler Struct/array init lists: {.on_data = &handler},
{&fn_a, &fn_b}
Args:
name: Symbol name to find callers of. Uses the same three-tier
resolution as find_references (exact name, exact qualified,
suffix LIKE).
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results of one page (default 50, max 200).
offset: Skip this many results. Reads the next page of a symbol
with many call sites; the page notice names the offset to use.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
The page notice first — total, offset, shown, more
— then a dict per call site with: file, line, ref_kind ("call",
"indirect", "implicit_construct", or "macro_use"),
caller (enclosing function name), caller_kind ("function",
"method", …). Macro fallback puts a dict with kind="macro",
signature (#define free: NAME or NAME(a, b)),
is_function_like, value (the replacement text ALONE) and
expanded_value between the notice and the rows;
that answer pages too, and its total counts the uses in active
code only — a use inside a comment is not one. When *name* matches more than one symbol, such as two classes with
a method of the same name, the answer holds the call sites of all
of them. A ``warning`` dict then comes first and names the
symbols, and each result carries ``target_qualified_name``. Give
the full qualified name to ask about one symbol only.
A virtual method with no call site of its own answers with the
call sites of the methods that override the same base method.
Those rows reach a PEER and not the symbol you named, and the page
notice counts them, thus a ``warning`` dict always leads such an
answer and says so. Read it before you report a caller count.
Never empty: one dict with ``error`` (symbol not resolved) or
``info`` (no references of this kind). Check both keys first.
|
| find_dead_codeA | Find C/C++ functions that are defined but never called —
libclang-powered dead code detection across the entire indexed
codebase. Distinguishes called from uncalled symbols globally, not
just within a single file — text-based search cannot determine
whether a function is actually reachable. What "dead" means: zero references in the index — no call, no
function-pointer assignment, no indirect call site. This is a
single-layer reference check, NOT a reachability analysis from the
entry points (main, ISR, exported symbols): a function that only a
second dead function calls still has a reference, thus this tool does
not mark it. For transitive reachability, trace from your entry points
with find_callees_recursive. The status field splits the results: "dead" — no reference at all. Likely unused.
"possibly_dead" — assigned to a function pointer (Phase 1
ref_kind="indirect"), but no call site through that pointer
resolved (Phase 3). Unindexed code or a type-erased API can still
call it. Treat it as uncertain, and check each hit with
find_indirect_targets before you delete anything.
fw-context detects a constructor call through global/static object and
member-field initialization as an implicit_construct reference.
Known false positives remain: constructors from factories, ISRs,
virtual method overrides, and weak-aliased symbols. Always verify
before you delete. project_only=True (default) excludes the SDK and vendor paths
through the is_project column, which follows the vendor_paths
and project_paths config. Set project_only=False to see the
vendor results too.
Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results of one page (default 100, max 200).
offset: Skip this many results. Reads the next page; the page
notice names the offset to use.
exclude_paths: Additional LIKE patterns to exclude (user-supplied
tool parameter, not config). E.g. ['lib/%'].
project_only: When True (default), filters to is_project = 1
symbols. Set False to see all results.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: name, qualified_name, kind, signature,
file (str — absolute), line, status ("dead" or
"possibly_dead"), and reason (str — explains why the function
is classified as dead or possibly dead). Never empty: one dict with ``info`` replaces an empty result.
Check that key first.
|
| find_hotspotsA | Find the most-called C/C++ functions ranked by caller count —
libclang call-graph hotspot detection. Identifies functions with
the most architectural weight — good targets for refactoring,
optimization, or extra testing. Text-based search cannot aggregate
caller statistics across the full call graph. Use for high-level impact assessment: changing a hotspot affects many
call sites. The result tells you which functions carry the most
"architectural weight" across the entire codebase. By default, SDK/vendor paths are auto-excluded so hotspots reflect
project code. Use project_only=False to see all results including
vendor code. For the callers of a specific hotspot, follow up with find_callers
or find_all_callers_recursive. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Number of top-called functions per page (default 20, max 50).
offset: Skip this many results. Reads further down the ranking;
the page notice names the offset to use.
project_only: When True (default), filters to is_project = 1
symbols so hotspots reflect project code.
exclude_paths: Additional LIKE patterns to exclude (user-supplied
tool parameter). E.g. ['lib/%'].
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: name, qualified_name, kind, signature,
file (str — absolute), line,
caller_count (int — total number of call sites). Never empty: one dict with ``info`` replaces an empty result.
Check that key first.
|
| find_indirect_call_sitesA | Find indirect call sites where a C/C++ function pointer field or
variable is invoked. libclang-powered: resolves calls through
function pointers (e.g. driver.onData(buf, len)), which
text-based search cannot detect. Returns locations where a function pointer is called through a field
access (driver.onData(buf, len)) or variable dereference
(stored_callback(42)). Read-only. No side effects. Use this to answer "where is this function
pointer invoked?" as opposed to find_callers which answers "who
calls this function?" and find_references which answers "where is
this symbol read or assigned?" For the reverse query — which functions are assigned to a given field
or parameter — use find_indirect_targets. Requires the reference index (fw-context index — refs on by default). Args:
name: Name of the function pointer field or variable.
E.g. "onData" finds every call through a field named
onData. Uses three-tier resolution: exact name, exact
qualified, suffix LIKE.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results (default 50, max 200).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: file, line, expr_text (the callee
expression, e.g. "driver.onData"), target_usr, target_name,
fn_ptr_type (the function pointer type signature), caller
(enclosing function name), caller_kind. Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| find_indirect_targetsA | Find functions assigned to a C/C++ function pointer field or
variable. libclang-powered: links assignment sites to call sites
via the field's unique symbol reference, which text-based search
cannot resolve. Links assignment sites (driver.onData = &handler) to call
sites (driver.onData(buf, len)) via the field's USR. Returns each function that could be invoked through the named function
pointer, showing both the assignment location and the call site(s).
When a function is assigned but no call site is found, call_file
and call_line are null — the assignment exists but the
invocation may be in unindexed code. For the reverse query — where is this field or parameter called — use
find_indirect_call_sites. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
name: Name of the function pointer field, variable, or parameter.
E.g. "onData" finds every function assigned to a field
named onData. Uses three-tier resolution.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results (default 50, max 200).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: rhs_name (assigned function),
rhs_qname, fn_ptr_type, method (assignment/call_arg/var_init/
init_list), assign_file, assign_line, assign_caller,
call_file, call_line, call_expr_text. An entry can carry ``_note`` (str) when fw-context cannot resolve
the direct call site — the callee is template-obscured, or the call
site comes from the type-based fallback. Read that note before you
act on ``call_file`` and ``call_line``.
Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| find_referencesA | Find ALL references to a C/C++ symbol — calls, reads, member accesses,
function pointer registrations, template references, and macro
usages. libclang-powered: detects function-pointer registrations
(interrupt vector table writes, callback attachments, ISR handler
assignments) that text-based search cannot see. Falls back to macro lookup when the symbol is not found as a
function/method: returns the macro definition (kind="macro") and
files that reference it (ref_kind="macro_use"). Read-only. No side effects. Returns every reference in the indexed codebase,
including call sites, variable reads, struct member accesses, indirect
function-pointer references, and macro usages. Requires the reference
index (fw-context index — refs on by default). For direct callers only use find_callers. For transitive callers use
find_all_callers_recursive. For call paths between two symbols use
find_call_path. Args:
name: Symbol name to find all references of.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results of one page (default 50, max 200).
offset: Skip this many results. Reads the next page of a symbol
with many references; the page notice names the offset to use.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
The page notice first — total, offset, shown, more
— then a dict per reference with: file, line, ref_kind, caller,
caller_kind.
ref_kind is one of: "call", "ref", "member",
"indirect" (function-pointer reference in arguments, assignments,
initializers, or init lists), "implicit_construct" (implicit
constructor call from global/static object or member-field
initialization), "macro_use" (macro usage
in file). Macro fallback puts a dict with kind="macro",
signature (NAME or NAME(a, b)), is_function_like,
value (the replacement text ALONE) and expanded_value
between the notice and the rows;
that answer pages too, and its total counts the uses in active
code only — a use inside a comment is not one. When *name* matches more than one symbol, the answer holds the
references of all of them. A ``warning`` dict then comes first and
names the symbols, and each result carries
``target_qualified_name``. Give the full qualified name to ask
about one symbol only.
A virtual method with no reference of its own answers with the
references of the methods that override the same base method.
Those rows reach a PEER and not the symbol you named, and the page
notice counts them, thus a ``warning`` dict always leads such an
answer and says so. Read it before you report a reference count.
Never empty: one dict with ``error`` (symbol not resolved) or
``info`` (no references). Check both keys first.
|
| find_wrapper_callersA | Find C/C++ wrapper classes that call methods of a driver class —
libclang-powered adapter pattern detection. Traces method ownership
across class boundaries to reveal the wrapper/adapter architecture
(e.g. UART wraps UART_DRIVER). Text-based search cannot
distinguish which class owns each method call. Returns wrapper methods grouped by wrapper class, showing which driver
methods each wrapper calls. Useful for understanding the adapter/wrapper
architecture (e.g. UART wraps UART_DRIVER). For the reverse perspective — finding who calls a specific driver method
— use find_callers. For class member listing use
get_class_members. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
class_name: Driver class name to find wrappers for.
E.g. 'UART_DRIVER' or 'hal::UART_DRIVER'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum wrapper method results (default 50, max 50).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: wrapper_class (str — "(global)" for a
free function), method_count (int),
methods (list of dicts — each with method, qualified_name, kind,
file (str — absolute path of the file that holds the body of that
method), and calls (list of dicts — driver_method (str) and
line (int) of each call into the driver))). The path sits on the method, not on the class, because one wrapper
class often spans several files.
Never empty: one dict with ``error`` (cannot resolve) or ``info``
(no results) replaces the results. Check both keys first.
|
| trace_data_flowA | Trace how C/C++ data of a given type flows to a target function via
libclang call paths. libclang-powered: finds functions by type
signature and maps call paths through the full call graph, which
text-based search cannot trace across translation units. Finds functions whose signature mentions type_name, then looks for call
paths from those functions to to_symbol. Returns a data flow map —
useful for understanding how a data structure travels through the system
to its destination. Works best for synchronous driver stacks (e.g. sensor read → I2C write).
Cannot follow async flows (message queues, interrupts, RS485 callbacks).
For exact call-graph queries use the find_* family;
verify specific paths with find_call_path. Read-only. No side effects. Requires the reference index
(fw-context index — refs on by default). Args:
type_name: Type name to trace. E.g. 'SensorData' or
'Config::SensorData'.
to_symbol: Target symbol name. E.g. 'uart_send' or
'UART_DRIVER::send'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
max_depth: Maximum call path depth (default 8, max 20).
limit: Maximum source functions to trace (default 15, max 15).
timeout_ms: Maximum total execution time in milliseconds
(default 30000). Clamped to 1000–300000.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts with a leading _summary entry:
{_summary (str), _type (str), _target (str)}, followed by source
entries each with: source_name, source_qualified_name, source_kind,
source_file, source_line, caller_count, reachable (bool), and
paths (list of call path dicts — empty when unreachable). A source entry with ``timed_out: True`` means that the path search
stopped at the time limit for that source. Its ``reachable: False``
thus means "not proved reachable", not "proved unreachable".
Never empty: one dict with ``info`` replaces an empty result.
Check that key first.
|
| explain_symbolA | Explain what a C/C++ symbol does in plain English — libclang-aware
analysis. Uses pre-computed LLM analysis when available (instant),
falls back to on-demand LLM. Falls back to macro explanation when
the name matches a #define. Read-only. No side effects — uses pre-computed LLM analysis when available
(instant, generated during fw-context index --analyze), falls back to
calling an LLM on-demand. Returns the symbol's purpose, inputs, outputs,
and side effects. For raw source code use get_source. For symbol metadata without
explanation use lookup_symbol. For body + callers + callees use
get_symbol_context. Args:
name: Symbol name to explain. E.g. uart_init, ModemMsg::send.
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
context_lines: Lines of source context around the symbol definition
(default 40, max 200). Only used when no pre-computed analysis exists.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {name, kind, file, line, signature, explanation, llm_analysis
(if pre-computed)}, plus source/explain_prompt on fallback. Macro
fallback returns kind="macro", signature (as #define NAME
or #define NAME(a, b)), is_function_like, value (the
replacement text ALONE), and expanded_value. A ``warning`` key means that the local LLM gave no explanation —
the request timed out, or the model is not available. The dict then
holds ``source`` and ``explain_prompt``: read the source, and answer
the prompt yourself.
When the file changed after the last index run, the dict adds
``stale`` (True) and ``stale_warning`` (str). ``stale_warning`` is
separate from ``warning``, which the LLM error paths use. A symbol
that moved gives its indexed body, not the code that now sits at the
stored line number.
An ``ambiguous_warning`` key means that *name* matched more than one
symbol, such as two classes with a method of the same name. This
answer is about ONE of them, and the key names it and lists the
others. Give the full qualified name to ask about one symbol only.
It is separate from ``warning``, which the LLM error paths use.
On failure the dict holds ``error`` with the reason. One failure
carries more than that: when the best match for *name* is in a file
outside the project root, the dict also holds ``candidates``,
``candidates_total`` and a ``hint``. Read them — a common name
matches many symbols, and one of the others is often inside the
project.
|
| get_file_mapA | Fast structural map of all C/C++ symbols in a file grouped by kind —
libclang-powered table of contents. Like a table of contents before
reading a chapter: see what functions, classes, and enums a file
defines at a glance. The index decides which file a path may reach: a path that no file of
this build answers for is refused, and a header of the SDK that the
build compiles is reachable like any file of the application. Pass a path relative to the project root (src/main.cpp) or just the
filename (main.cpp). Returns symbols keyed by kind (function, method,
class, struct, enum, ...). Each kind has count (total) and items (first N,
default 30). Set max_per_kind=0 for unlimited, signatures=true for full sigs. Enum constants (enum_constant) are grouped into subgroups by
parent enum. Each subgroup has name, count, and constants
(list of {name, qualified_name, line, enum_value}). The subgroup
count reflects the real total even when max_per_kind limits the
constants list. For detailed symbol information use get_symbol_context or
lookup_symbol. Read-only. No side effects. Use before reading a large file to
orient yourself — see what functions, classes, and enums it defines. Args:
file_path: Path relative to project root, or just the filename.
project_root: Project directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
signatures: Include full function signatures. Default: False.
max_per_kind: Max items per kind group (default 30, 0 = unlimited).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {file, total_symbols, symbols: {kind: {count, items[],
subgroups?[]}}} Each item holds ``name``, ``qualified_name``, and ``line``, plus
``end_line`` when the symbol is a definition. The two line numbers
are the extent, thus ``file:line-end_line`` is the citation.
On failure the dict holds only ``error`` with the reason.
|
| get_sourceA | Read a C/C++ function/method/enum/macro body using libclang exact
extents — no guessing line numbers. Uses AST-precise {start, end}
extents so you get exactly the function body. Generic file readers
don't know where a function actually ends — libclang tracks exact
{start, end} from the AST. The body is ifdef-filtered: a line of an inactive #if branch
comes back blank, thus the text holds only the code that compiles for
this build. The line numbers do not move. source_origin says where
the text came from — "index" is the filtered copy, "disk" is the
file itself and holds EVERY branch. A body reaches you from the disk
only when the file changed after the last index run, and
stale_warning says so. For enums, includes a constants array listing all member constants
with their values. For macros, returns kind="macro" with signature
(#define NAME or #define NAME(a, b)), is_function_like,
value (the replacement text ALONE — the parameter list is not part
of it) and expanded_value (preprocessor-resolved). For rich context (who calls this, what does it call) use
get_symbol_context instead — it returns body, callers, and callees
in a single call. For the full file, use a normal file read. Read-only. No side effects. Args:
name: Fully qualified symbol name. Returns exact function body
via libclang extent.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {name, qualified_name, kind, file, line, signature,
docstring, is_definition, is_template, is_virtual, is_pure_virtual,
source (str — the function/enum/macro body, truncated at 8000 chars),
warning (str, optional — when source file cannot be read)}.
May also include end_line (the last line of the extent),
template_usr, parent_usr, enum_value, constants
(list for enums), value (raw macro definition),
expanded_value (preprocessor-resolved macro value) when
applicable. A declaration has no extent, thus it gets no
end_line. ``line`` and ``end_line`` are the extent of the symbol, thus they
are the citation: quote ``file:line-end_line``. Do not count the
lines of ``source`` to find the end.
``source`` carries a line-number prefix on every line — four
columns, right-aligned, then two spaces (``" 20 bool ..."``).
This tool always numbers its text. ``read_file`` numbers its
``content`` when you pass ``line_numbers=True``, and the ``source``
of ``search_bodies`` is always bare. Strip the prefix before you
compare the text with anything.
When the file changed after the last index run, the dict adds
``stale`` (True) and ``stale_warning`` (str). ``source_origin`` then
tells where the body comes from: ``"disk"`` when the symbol did not
move, ``"index"`` when it did and the body comes from the index
instead. A moved symbol never gives the code of another symbol.
``_source_truncated`` (True) marks a body that a cap cut:
``index.max_symbol_body_lines`` bounds the number of lines, and a
second cap bounds the characters. The character cut lands in the
middle of a line, thus a body with this mark can end in an
unbalanced brace. Read the rest with ``read_file`` and a range.
An ``ambiguous_warning`` key means that *name* matched more than one
symbol, such as two classes with a method of the same name. This
body belongs to ONE of them, and the key names it and lists the
others. Give the full qualified name to get one symbol only. It is
separate from ``warning``, which reports a body that could not be
read from the disk.
On failure the dict holds ``error`` with the reason. One failure
carries more than that: when the best match for *name* is in a file
outside the project root, the dict also holds ``candidates``,
``candidates_total`` and a ``hint``. Read them — a common name
matches many symbols, and one of the others is often inside the
project.
|
| get_symbol_contextA | Rich one-shot context for a C/C++ symbol: body, signature, all direct
callers and callees. Answers "what does this do and how does it fit in
the system?" in a single response — libclang powers the call graph,
not regex. Falls back to macro display when the symbol is not found. Prefer this over get_source when you also need callers, callees,
indirect call sites, or LLM analysis — all returned in a single call.
If you only need the raw function body (no metadata), get_source is
slightly faster. For transitive call-graph exploration use
find_all_callers_recursive or find_callees_recursive. Returns ALL callers and callees including vendor/SDK code — the call
graph naturally spans project and vendor boundaries in both directions
(project → vendor API, vendor callback → project handler). The body is ifdef-filtered, the same as in get_source: a line of
an inactive #if branch comes back blank and the line numbers do not
move. source_origin says whether the text is the filtered copy
("index") or the file itself ("disk", every branch present). Read-only. No side effects. Args:
name: Symbol name. Returns body, signature, all direct callers
and callees.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict with: name, qualified_name, kind, file, line, signature,
docstring (raw Doxygen comment text), is_definition, callers (list),
callees (list), source (body text),
indirect_call_sites (list, for field/variable symbols — where the
function pointer is actually invoked).
For field and variable symbols that have function pointer type,
also includes resolution: {assignments_found, call_sites_found,
resolved, note} indicating whether assignments and call sites are
linked (Phase 3). resolved=False with a note when parts are
missing — LLM can detect uncertainty.
For enums also returns constants and enum_value.
For macros returns kind="macro", signature (#define NAME
or #define NAME(a, b)), is_function_like, value (the
replacement text ALONE) and expanded_value
(preprocessor-resolved).
When LLM analysis has been generated (fw-context index --analyze),
includes llm_analysis: {summary, inputs, outputs, model, analyzed_at}
with a structured description of the symbol's purpose, parameters, and
return values/side effects. When the file changed after the last index run, the dict adds
``stale`` (True) and ``stale_warning`` (str), and ``source_origin``
tells where the body comes from: ``"disk"`` when the symbol did not
move, ``"index"`` when it did. The callers and callees come from the
index in all cases, thus a stale dict can hold an incomplete list.
The dict also carries the libclang flags of the symbol:
is_virtual, is_pure_virtual, is_template, parent_usr, and
template_usr. For a virtual method it adds ``overrides`` (the base
methods that this method overrides) and ``overridden_by`` (the
derived methods that override it) — use these two before you change
a virtual method.
An ``ambiguous_warning`` key means that *name* matched more than one
symbol, such as two classes with a method of the same name. Every
part of this answer — body, callers and callees — is about the ONE
symbol that the key names, and the key lists the others. Give the
full qualified name to ask about one symbol only.
On failure the dict holds ``error`` with the reason. One failure
carries more than that: when the best match for *name* is in a file
outside the project root, the dict also holds ``candidates``,
``candidates_total`` and a ``hint``. Read them — a common name
matches many symbols, and one of the others is often inside the
project.
|
| read_fileA | Read a complete C/C++ source file with ifdef-filtered content —
only code that actually compiles for the current build configuration.
Inactive #ifdef branches are replaced with blank lines (preserving
original line numbers). Use this to read a file without leaving the fw-context ecosystem.
Unlike generic file readers, this tool returns build-accurate content:
code gated behind #ifdef BOARD_V2 stays visible only when
BOARD_V2 is actually defined for this build. Line numbers match
the original file — inactive branches appear as blank lines, and the
text spans the whole file, thus lines is the length of the file. A blank line is an answer, and not a defect: it says that the line is
in a branch the build does not take. When EVERY line of the file is
blank, the dict carries all_lines_inactive and a warning — such
a file holds code, and the active build compiles none of it. content is bare text by default and carries NO line-number prefix —
unlike the source of get_source, which numbers every line.
Never count the lines here to find a number. Take it from a field
instead: the match_lines of search_bodies or search_content,
the line / end_line of get_source and get_file_map, or
pass line_numbers=True and read the number off the line.
start_line and end_line cut a window out of the file (1-based,
both ends inclusive, 0 = no bound on that side). Reading around a
known line costs a fraction of the whole file — 40 lines around a match
instead of 2000 lines of a header.
A comment and a preprocessor directive are part of the answer. Both
are text that the file holds and the build reads, thus both stay — an
include guard, a #define, and the description of a register in a
vendor header included. A blank line is therefore an inactive line, or
a line that is blank on disk, and nothing else. For reading a single function body with libclang exact extents use
get_source. For body + callers + callees in one call use
get_symbol_context. For a structural overview without content use
get_file_map. For searching patterns across files use
search_content. Read-only. No side effects. Falls back to raw disk content (with a
warning) when the indexed files.content column is empty — e.g. on
a legacy index that predates this feature. Run fw-context index
to populate the ifdef-filtered content. Args:
file_path: Path relative to project root, or just the filename.
E.g. src/main.cpp or main.cpp.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
line_numbers: Prefix every line with its number, right-aligned and
followed by two spaces, as get_source does. Default False.
start_line: First line to return, 1-based inclusive. 0 = file start.
end_line: Last line to return, 1-based inclusive. 0 = file end.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {file (str), language (str — "c" or "cpp"),
mtime (float), lines (int — total line count of the WHOLE file,
whatever range was asked for),
content (str — the ifdef-filtered text, bare unless
line_numbers was set),
warning (str, optional — when reading from raw disk instead of
indexed content, or when no line of the file is active),
all_lines_inactive (True, optional)}. ``all_lines_inactive`` marks a file that the active build compiles
no line of: every line is inside an inactive ``#if`` branch, thus
``content`` holds the correct number of lines and no text. Without
this field that answer reads as an empty file, and the two mean
opposite things.
A range adds ``start_line`` and ``end_line`` — the first and last
line the ``content`` really holds, after the end was clamped to the
length of the file.
On failure the dict holds only ``error`` with the reason: a
negative bound, an ``end_line`` before ``start_line``, or a
``start_line`` past the end of the file.
|
| get_class_membersA | Return all methods, fields, and nested types of a C/C++ class/struct —
libclang-powered member table. Groups members by kind (method,
constructor, field, enum, etc.), distinguishing class members from
free functions across the entire codebase. Members are grouped by kind (method, constructor, destructor, field, enum,
typedef, class, struct). Each member includes its signature, virtual flags,
and source line. Works for C structs too — they just won't have methods. For inheritance hierarchy use get_inheritance_chain. For individual
method details use get_symbol_context. Read-only. No side effects. Args:
class_name: Class or struct name. E.g. 'ModemManager' or
'comm::MODEM'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {name, qualified_name, kind, file, line, members: {kind:
[{name, qualified_name, signature, is_virtual, is_pure_virtual,
line}]}, member_count} On failure the dict holds only ``error`` with the reason.
|
| get_inheritance_chainA | Return the C++ inheritance chain for a class or struct —
libclang-aware hierarchy. Resolves base/derived class relationships
across all translation units, which single-file reading cannot do. Shows direct base classes (what this inherits from) and direct derived
classes (what inherits from this), along with access level and virtual
flag for each edge. When transitive=True, walks the full hierarchy up to all ancestors
and down to all descendants (bounded by max_depth). Uses BFS with
cycle detection to handle diamond inheritance. For class members use get_class_members. For virtual method
override chains use get_method_overrides. Read-only. No side effects. Args:
class_name: Class or struct name to get inheritance information for.
E.g. 'UART_DRIVER' or 'comm::MODEM'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
transitive: When True, walk the full inheritance tree both up
(ancestors) and down (descendants). Default: False (direct
bases and derived only).
max_depth: Maximum BFS depth for transitive walk (default 10).
The schema holds it between 1 and 50, thus a number outside
that range is REFUSED and not cut down to fit.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {
name, qualified_name, kind, file, line,
bases: [{name, usr, access, is_virtual, file}],
derived: [{name, usr, access, is_virtual, file}],
all_bases: [...] (when transitive=True, ancestors sorted by depth),
all_derived: [...] (when transitive=True, descendants sorted by depth)
} On failure the dict holds only ``error`` with the reason.
|
| get_method_overridesA | Return C++ virtual method override information — libclang-powered
vtable analysis. Resolves virtual dispatch across class hierarchies:
shows which base-class method this overrides, and which derived-class
methods override this one. Text-based search cannot resolve virtual
dispatch across translation units. Shows what base-class method this method overrides, and what derived-class
methods override this one. Built from the overrides table which is
populated during fw-context index via post-processing of the inheritance
graph and virtual method signatures. For class-level inheritance, use get_inheritance_chain. For symbol
details, use get_symbol_context. Read-only. No side effects. Args:
method_name: Method name to get override information for. Use
qualified name for disambiguation, e.g.
'UART_DRIVER::write'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
dict: {
name, qualified_name, kind, file, line, signature,
overrides: [{usr, name, qualified_name, kind, file, line}],
overridden_by: [{usr, name, qualified_name, kind, file, line}]
} On failure the dict holds only ``error`` with the reason.
|
| get_template_instancesA | Find all template instantiations for a C/C++ class or function
template — libclang template-aware lookup. Finds concrete
instantiations spread across all translation units, each with its
full type signature. Text-based search cannot resolve template
specializations across translation units. Returns concrete instantiations of the template — each with its full type
signature (e.g. Callback<void(int)>). The template declaration itself
is also returned as the first result when found. Uses the template_usr column populated during indexing via libclang's
cursor.specialized_template. Known limitation: libclang's specialized_template does not
reliably resolve implicit instantiations or template methods of
template classes. Header-only templates (e.g. RingBuffer<T>)
may report zero instances even when used in the codebase. Explicit
specializations and class/struct instantiations are detected more
reliably than method-level instantiations. For finding the template declaration itself use lookup_symbol. Read-only. No side effects. Args:
template_name: Template name to find instantiations for.
E.g. 'Callback' or 'mbed::Callback'.
project_root: Project root. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
limit: Maximum results (default 50, max 200).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list[dict] with one element wrapping the template declaration:
{name, qualified_name, kind, file, line, is_definition,
signature, instances (list of dicts, each with name,
qualified_name, kind, file, line, signature, is_definition),
instance_count (int)} No match gives ``[]``. One dict with ``error`` means the query
failed — check that key first.
|
| find_variablesA | Find C/C++ variables by name or prefix and trace who reads or
writes them through the call graph. libclang-powered: splits
variables into global (varglobal — file/namespace/class-scope)
and local (varlocal — inside a function body). Each result includes a type signature (bool timeSet,
const IPAddress modbus_ip), the enclosing function for locals
("<file scope>" for globals), and a references list showing
every function that reads or writes the variable — the same
ref_kind values as find_references ("call", "ref",
"member"). Use when you need to understand shared state, find who modifies a
global variable, trace side effects, or distinguish important globals
from loop counters. For general symbol search use search_code or
lookup_symbol. For all references to a specific variable
(including reads in expressions), use find_references. This tool is the way to a LOCAL variable: search_code drops the
varlocal kind, because a local matches every topic query aimed at
the function around it. search_code(..., kind="varlocal") reaches
them as well. Legacy indexes with kind="variable" (pre-split) are detected and
included in results — reindex to fully benefit from the split. Read-only. No side effects. Args:
name: Variable name or prefix to search. Uses LIKE match
(e.g. g_ finds g_debug_level, g_state).
project_root: Project root directory. Auto-detected if omitted.
project: Project name or project_id — call list_projects to get them. Use
it to ask about a project that is not the project of the current
directory. It is an alternative to project_root, which takes a root
path. Give one of the two, not both.
kind: Optional kind filter — "varglobal", "varlocal",
"field", or None (all). Default None. The legacy
"variable" is also accepted, for an index made before the
kind was split.
limit: Maximum results (default 20, max 100).
variant: Build variant (multi-build project). Omit to use
default_variant. One query answers for ONE build.
image: Sysbuild image within the variant. Required when the
variant holds several: each image is a separate program. Returns:
list of dicts, each with: name (str), qualified_name (str),
kind (str — "varglobal", "varlocal", "field", or
"variable" on an index made before the kind was split),
file (str),
line (int), signature (str — e.g. "const IPAddress modbus_ip"),
enclosing_function (str — function name for varlocal,
"<file scope>" for varglobal), enclosing_class (str — class
name for static members, empty otherwise),
references (list[dict] — function, file, line,
ref_kind). No match gives ``[]``. One dict with ``error`` means the query
failed — check that key first.
A ``warning`` key marks a partial result.
|