get_project_status
Returns the health of a C/C++ project's index and its builds, indicating if a reindex is needed. Call this before any other fw-context tool.
Instructions
MANDATORY FIRST CALL for C/C++ projects. Return the health of the index of the project, and the list of its builds — check it before you use any other fw-context tool, and call it again while you wait for a reindex.
Read-only with two exceptions, and it spawns no subprocess — the
startup daemon thread and the file watcher own the background reindex.
The exceptions: the last index run can leave a marker that says that
the build is not there, and when the tool finds that build again, it
removes the marker. It also removes a reindex.pid or a
reindex.pending of a process that no longer runs. It creates no
.fw-context file in a project that is not initialized. It takes no
variant and no image: it reports every build. For the detail
of one build (its config_hash, memory map, defines), call
get_build_info.
builds lists every build of the index. A project of more than one
build has no active build: every query tool must get the parts of one
entry that are not empty, its variant, its image, or the two.
Select the build from the task, and
ask the user when the task does not identify one.
Act on reindex_state of the build that the task needs, in
builds (a project of one build has it at the top level too):
null— the index of the build is current. Continue."reindexing"or"pending"— the index of the build is not current, and an index run is active or scheduled. Wait, and call this tool again after about 30 s. Continue with the old index only when the user tells you to."not_scheduled"— the index of the build is not current, and no run updates it. Tell the user the command that the reasons name."failed"— the last index run failed. Give its text to the user: it names the command that repairs it.
Each query answer carries the state of its build as index_state,
from quick checks only. A query thus can miss a changed header, a
changed file that its answer does not name, or a new source file when
its answer is not empty. This tool does the full check.
status gives the state of the project:
"ready"— up to date. Continue."reindexing"— background reindex running or scheduled, or fw-context builds new source files on its own.reindex_stateof a build tells whether that build waits for the run.reindex_progressholds the last log line of a running reindex."reindex_needed"— the last index run failed, the build is not there, schema mismatch, an old row format, a changed or missing compile_commands.json, a checkout of another branch, tag or commit since the index, or a source file that compile_commands.json does not cover and fw-context will not build itself. A changed compile_commands.json includes one that fw-context cannot compare with the index. Queries still work on existing data. Readreindex_reasons: each reason names the action that it needs.Read
bg_reindex_runningbefore you name any command here. A run already under way does the work, thus a second one only waits on the same lock, andindex_messageand the checkout reason both say so. That field covers a run of the daemon AND a run the operator started, and also work that the daemon has scheduled and not yet started — the arguments of no run are readable, thus a reason that needs--buildcan 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. Runfw-context index."not_initialized"— runfw-context init."config_error"— a config file cannot be used: it cannot be read, it is not UTF-8 or not valid TOML, a section is not a table, a key that selects the data or where data goes has a value of the wrong type, or a URL is not valid.config_filenames it, andindex_messagegives the problem. Correct the file, or runfw-context doctor --fix --only configwhen the message names it: it removes each value that cannot be used.fw-context initcannot repair it.
A project of several builds gets the worst status of its builds. A
reason that every build gives comes one time, and any other reason
names its build: build <variant>/<image>: <reason>.
A failure sets NO status. An index with no build config 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. A database that fw-context cannot read (damage, no access)
makes the call fail with the error of SQLite. The message of the
check db-integrity of check_dependencies tells if it is
damaged.
Seven conditions set reindex_needed: a failed last index run, a
build that is not there, an outdated schema, an outdated ROW FORMAT, a
changed or missing compile_commands.json, a checkout of another
branch, tag or commit since the index, and a source file that is on
disk but absent from compile_commands.json when fw-context will not
build it on its own. A missing build, a missing
compile_commands.json, a checkout and such a source file need the
action that makes compile_commands.json again: a build, or for a
database of the user ([index] compile_commands) a new file from the
user. A plain reindex reads the same file again, and it has no
translation unit for a new source file and skips it without a word. A
failed run needs the repair that its reason names first. 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. An index
written before fw-context-rows/4 parsed a GCC build with guessed
toolchain headers and the macros of clang: an ESP32 or AVR index there
holds host headers from /usr/include and takes the #if branches
of the host, for example #if __XTENSA__ as false, and an ARM index
saw __GNUC__ 4. A build whose GCC driver does not answer the query
keeps that fault also after /4; the index run names such a compiler
in a warning.
client_restart_required is the OPPOSITE case, and no command repairs
it. This server process does not run the code that it must run. Two
causes set it, and client_restart_reason names each one that
applies:
The index carries a NEWER row format than this 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.
The operator installed a different fw-context after the LLM client started this process. This cause does not need an index, thus it shows also for
not_initialized,no_indexandconfig_error.
status therefore stays as it is 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.
update_notice is not a status either. It shows when PyPI has a
newer fw-context than the installed one. Tell the operator one time,
and do NOT upgrade fw-context yourself: the operator knows the tool
that installed it. After a request that succeeded, the server asks
PyPI again only after 24 hours. The request runs in a background
thread, and no tool waits for it. [updates] check = false
in the global config, or FW_CONTEXT_NO_UPDATE_CHECK=1, stops 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. Read
modified_files_count 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, and every vendor symbol too whenanalyze_vendoris True. Vendor symbols excluded byanalyze_vendor=Falsenever block it, thusvendor.totallarge withvendor.analyzed=0is expected, not a defect.index_messageends with the same counts as text.
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: {project_id, project_root, build_system, schema_version (int
— DB schema version), current_schema (int — code expects),
vendor_paths (list[str] — config index.vendor_paths),
project_paths (list[str] — config index.project_paths),
status (str — "ready"|"reindexing"|"reindex_needed"|"no_index"|
"not_initialized"|"config_error"; a failure sets no status and
gives error alone), config_file (str — only with
"config_error": the file that cannot be used), reindex_needed (bool), reindex_reasons (list[str] — why a
reindex is needed, empty when False, except that a new source file
that fw-context builds on its own is listed while
reindex_needed is False. Some of them ask for a build rather
than a plain reindex. For example, after a checkout of another
branch, tag or commit, compile_commands.json belongs to the OLD
checkout 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 a
build needs a reindex or has stale header dependencies),
index_message (str — human-readable summary of index state),
builds (list[dict] — every build: {variant, image, board,
symbol_count, file_count, indexed_at, status, reindex_state (str
or None — "reindexing", "pending", "not_scheduled", "failed", or
None for a current index), reindex_needed, stale,
modified_files_count, header_affected_tus, reasons}, and
manifest_verification on a build whose manifest is not "full"),
declared_not_indexed (list[dict], optional — each
variant that the config declares and the index does not hold:
{variant, command}; no query reaches it until the command ran),
bg_reindex_running (bool), reindex_progress (str or
None — last log line when reindex is running), _warning (str,
optional — when the manifest verification of a build is not
"full"), vec_available (bool), vec_error (str, optional),
client_restart_required (bool, optional — only when True),
client_restart_reason (str, optional — with the field above),
update_notice (str, optional — a newer release is on PyPI)}
A project of ONE build also gets the fields of that build at the
top level: indexed_at (str — "YYYY-MM-DD HH:MM:SS" in UTC, the
completion time of the last full index), first_indexed_at,
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 source files with
stale header dependencies; a file that compile_commands.json lists
more than once counts once), manifest_verification (str — "full"
when manifest.json exists, "partial" when it exists and the
embeddings are not complete, "none" without one), analysis (dict — LLM
analysis coverage split by project/vendor: {model, analyze_vendor,
project: {analyzed, skipped, total}, vendor: {analyzed, skipped,
total}, complete}), description (str). A project of several
builds gets no sums: the counts of each build are in ``builds``,
and ``reference_count``, ``analysis``, ``description`` and
``first_indexed_at`` of a build are in ``get_build_info``.
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``. For ``"config_error"``, it
adds ``config_file``. The two version fields
(``client_restart_*`` for an upgrade, ``update_notice``) can come
with every result, these two included. ``config_warnings`` (a
list) can come with each result that has a ``status`` other than
``"config_error"``: values of a config file that fw-context did not
use. Tell the user each line.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| fast | No | When True (default), reuse the cached manifest hashes for the header check. Both modes count modified files. Pass False to recompute the header hashes, which is far slower. | |
| project | No | 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. | |
| project_root | No | Project root directory. Auto-detected from CWD if omitted. This field also accepts a project name or a project_id, but project is the clear field for those. |