Adds first-class support for xAI's Grok Build CLI, following the
existing client-integration pattern (Claude Code, CodeBuddy, Codex):
- grok context: single-project, shaped like claude-code.yml, with the
file-tool exclusions appropriate for a CLI agent that has its own
read/edit/shell tools. structured_tool_output is left at the auto
default (as in the codex and codebuddy contexts); claude-code's
explicit false is a Claude-Code-specific bug workaround that is not
carried over.
- serena setup grok: registers Serena as a user-scoped MCP server via
"grok mcp add" after detecting a compatible grok CLI.
- serena-hooks --client=grok: Grok-native PreToolUse output
({decision, reason}) instead of the Claude-style hookSpecificOutput
envelope; grep/read detection extended to Grok's tool names and
shell payloads (target_file/targetFile path keys, non-string command
values handled defensively). The payload-parsing refinements live in
the remind hook's client-agnostic parsing and apply to all hook
clients: a non-string command value previously raised, and
target_file/targetFile paths were not recognized.
- Docs (030_clients.md #grok), unit tests for setup/hooks/context, and
scripts/live_test_grok.py - a zero-inference, state-preserving live
smoke test against a real grok installation (documented in
CONTRIBUTING.md). Its M5 check verifies the structured-output wire
shape: the grok context (auto default) serves an output schema and
structuredContent for string-returning tools, while the claude-code
context (explicit false workaround) serves plain text only.
Review on #1728 found two blocking issues in the previous commit:
1. pyrefly_server.py wraps send_request with its own 5x/0.2s retry for
RequestCancelled/ContentModified. The base-class retry nested inside
it, so a persistent ContentModified produced 5x5=25 round-trips.
2. The base-class retry applied to every request on every server, but
general.staleRequestSupport.retryOnContentModified in InitializeParams
is the client's contract with the server about exactly which requests
it will reissue. rust-analyzer declares only the three semanticTokens
methods and haskell-language-server declares none; blanket-retrying
e.g. textDocument/hover (or workspace/executeCommand, which is not
idempotent) contradicts that declared capability.
Fix: send_request now only retries a method if it was registered via
the new LanguageServerInterface.set_content_modified_retry_methods,
which SolidLanguageServer._create_initialize_params populates from
each server's own general.staleRequestSupport.retryOnContentModified
list right after building it. The mechanism is opt-in per method, per
server, and mirrors what we actually told the server -- so it's a
no-op wherever nothing is declared.
rust-analyzer now declares textDocument/hover in that list (the method
issue #1724's repro -- test_find_symbol[rust_add_function] with
include_info=True -- actually needs), in addition to the pre-existing
semanticTokens methods.
This also resolves the pyrefly double-retry: pyrefly never declares
general.staleRequestSupport, so the base-class retry is a no-op for
every pyrefly request and pyrefly's own wrapper remains the sole
handler for both RequestCancelled and ContentModified there, exactly
as before this PR. pyrefly_server.py is intentionally left untouched;
narrowing its retry tuple, as literally suggested, would have removed
ContentModified handling for it entirely once the base layer stopped
covering it too.
Also: reduced _CONTENT_MODIFIED_MAX_ATTEMPTS from 5 to 3 (now the max
per allowlisted method only, so worst-case added latency for hover is
2 * request_timeout + 0.4s instead of 4 * request_timeout + 0.8s), and
restructured send_request around a _send_request_once helper so the
final failed attempt raises directly instead of falling through to a
trailing `raise AssertionError("unreachable")`. Retry tests now
monkeypatch time.sleep and gained coverage for the allowlist gate
(undeclared method, executeCommand, default-empty registration).
Fixes#1724
Per the LSP spec, ContentModified means the server discarded a stale,
in-flight computation because the workspace changed underneath it, not
that the request itself is invalid, so a well-behaved client is
expected to retry. LanguageServerInterface.send_request previously
converted any such response straight into a hard SolidLSPException,
with no retry, for every language server.
This was flaking test_find_symbol[rust_add_function] on windows-latest:
rust-analyzer returns ContentModified for hover requests cancelled by
concurrent indexing (its internal retry set does not cover hover), and
nothing on the client side retried it.
send_request now retries ContentModified responses a bounded number of
times with a short delay before giving up. This is generic to the base
request path, so it applies to every language server, not just
rust-analyzer. RequestCancelled (-32800) is left alone since the LSP
spec defines it as client-initiated.
Fixes#1724
causing some symbolic operations (such as `find_referencing_symbols`) to report stale information.
An explicit file system synchronisation mechanism is now used to detect changes prior to the
affected tool executions.
Note that tools like `find_symbol` and `get_symbols_overview` do not need the synchronisation,
because they explicitly open all the relevant files themselves.
Resolves#1718
Co-authored-by: Niccolò Franzin <niccolo@cognitivext.com>
- PHP/Intelephense: expose `file_filter` via `ls_specific_settings["php"]`, so that additional
extensions containing PHP sources (e.g. Drupal's `.module` / `.install` / `.inc` / `.theme`)
become visible to the symbol tools and are indexed by the language server
- PHP: treat `.phtml` files as PHP sources by default (all PHP language servers)
* Do not unnecessarily call apply_ex instead of apply (timeout can occur)
* Increase tool timeout to 10 minutes (for slow CI cases)
* TestPromptProvision: Raise exceptions in apply_ex (such that outputs don't
inadvertently check against exception messages)
Resolves#1695
gopls reports the document-symbol range of single type/var/const
declarations starting at the identifier (after the keyword), unlike func
declarations whose range includes the func keyword. Serena derives both
the displayed body and the replacement range from that range, so the
keyword was dropped from both for these declarations and a
keyword-inclusive edit corrupted the file (type Foo became type type Foo).
Override request_document_symbols in gopls to extend the range start back
to the keyword when only the keyword and indentation precede the
identifier, recomputing the body so it stays consistent with the
replacement range. Funcs, grouped declarations, and selectionRange are
left untouched.
Adds a unit test for the symbol body and range plus an end-to-end
replace_symbol_body round-trip regression test.
* Java (JDT-LS): allow registering additional JRE/JDK runtimes via config
JDT-LS resolves each imported module's JRE_CONTAINER/.../JavaSE-NN/
classpath entry against its own "Installed JREs" list, which Serena
previously hardcoded to a single JavaSE-21 entry pointing at the
bundled JRE. Projects whose source/target level exceeds that bundled
JDK (e.g. sourceCompatibility = VERSION_25) get a container that never
resolves, so every JDK type -- java.lang.Object, java.util.*, etc. --
comes back "cannot be resolved", silently breaking diagnostics and
cross-module resolution. There was no config key to tell JDT-LS about
an installed newer JDK.
Add an optional `runtimes` list under ls_specific_settings.java,
mirroring VS Code's java.configuration.runtimes shape (name, path,
optional default/sources/javadoc). Entries are validated (name+path
required, path must exist) via the new _resolve_configured_runtimes(),
then merged with the bundled JavaSE-21 default: configured runtimes
extend the list; an entry reusing the "JavaSE-21" name overrides the
bundled one; if a configured entry claims `default`, the bundled
runtime's own default flag is cleared so JDT-LS doesn't see two
defaults. The `runtimes` key is also added to the JDTLS workspace-hash
inputs so changing it lands in a fresh workspace instead of reusing a
stale Buildship/Maven import.
Testing: added 14 unit tests in
test/solidlsp/java/test_jdtls_path_resolution.py covering
_resolve_configured_runtimes validation (missing keys, wrong types,
nonexistent paths) and the merge behavior in
_create_base_initialize_params (extension, name-collision override,
default hand-off). Ran `poe format`, `poe type-check`, and
`pytest test/solidlsp/java -m "not java"` (69 passed, 8 deselected
real-JDTLS tests that require a JDK) locally.
Fixes#1478
* docs: document java.runtimes in configuration guide
Address review feedback on #1715 by documenting ls_specific_settings.java.runtimes
in 050_configuration.md (settings table, note, and example).
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: arimu1 <19286898+arimu1@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
loadLastExecution()'s success handler only called displayLastExecution()
when the backend returned a non-null last_execution, so on a fresh server
(no tool run yet) the initial "Loading..." placeholder was never replaced.
displayLastExecution(null) already renders the correct empty state
("No executions yet.") — it just wasn't being called.
Add the missing else branch, mirroring how the Tool Usage and Executions
Queue panels already handle their empty states.
Fixes#1713
Co-authored-by: arimu1 <19286898+arimu1@users.noreply.github.com>
An LSP server can end a symbol range one line past EOF: the convention
for a range covering whole lines ends it at the start of the following,
non-existent line. SymbolBody.get_text indexes self._lines[end_line]
directly, so that well-defined case raised IndexError.
get_text now corrects exactly that case (end_line == len(lines), end_col=0) to end
at the actual last line, in get_text itself so it also applies to a
cached SymbolBody, not just a freshly constructed one. Any other
out-of-range end_line now raises InvalidTextLocationError.
Fixes#1498
`search_text` mapped the match's exclusive end index to a line number via
`get_line_from_index`, which resolves an index at a line start to the following
line. A match ending with a newline was therefore attributed one line too many,
and `context_lines_after` was shifted by the same amount.