The Rust core is the shared application runtime for macOS SwiftUI and Windows React/Tauri. macOS calls the stable C ABI while the Tauri host links the Rust crate directly. The C ABI remains:
const char *lithe_core_version(void);
char *lithe_core_execute_json(const char *request);
char *lithe_core_lsp_provider_catalog_json(const char *workspace_root);
int32_t lithe_core_cancel(const char *operation_id);
void lithe_core_free_string(char *value);The macOS package uses the small C bridge in macos/Sources/LitheRustCore/. The
canonical C declarations are in rust/lithe-core/include/lithe_core.h.
Native clients can link the same staticlib or cdylib; Rust hosts call
lithe_core::execute_json and lithe_core::cancel_operation directly.
Strings returned by the core are UTF-8 JSON allocated by Rust. The caller must
release response strings with lithe_core_free_string.
Every request has this shape:
{
"id": "request-id",
"operationId": "operation-id",
"timeoutMilliseconds": 30000,
"command": "workspace.search",
"payload": {}
}operationId is optional for compatibility and defaults to id when id is
present. timeoutMilliseconds is optional; a positive value starts a
cooperative deadline. lithe_core_cancel is thread-safe and returns 1 when
the operation is active. Cancellation and deadlines are checked at command
boundaries, workspace traversal points, and Git process waits. They return
cancelled or timed_out in the standard error envelope.
Successful responses contain ok: true and data. Failed responses contain a
stable error code and a user-facing message:
{
"id": "request-id",
"ok": false,
"error": {
"code": "invalid_request",
"message": "Invalid JSON request"
}
}| Command | Purpose |
|---|---|
core.ping |
Verify the ABI and protocol version |
community.discourse.auth.begin |
Create an ephemeral RSA-OAEP authorization session and return the Discourse browser URL |
community.discourse.auth.complete |
Decrypt, validate, and consume one Discourse user API key callback |
community.discourse.auth.revoke |
Revoke the current Discourse user API key |
community.discourse.topics |
List normalized latest or top topic summaries |
community.discourse.topic |
Read one topic with ordered, sanitized post HTML |
community.discourse.categories |
List normalized visible categories |
community.discourse.search |
Search normalized topics and sanitized posts |
workspace.snapshot |
Enumerate visible workspace nodes and relative file paths |
workspace.search |
Search visible file names and UTF-8 text files |
workspace.searchEverywhere |
Search visible file names, Java types/methods, and UTF-8 text files |
workspace.replacePreview |
Return deterministic replacement lines and complete replacement text |
file.read |
Read a UTF-8 file using a workspace-relative path |
file.write |
Write a UTF-8 file using a workspace-relative path |
document.lifecycle |
Reduce a shared document save, external-change, or conflict event without reading text or disk |
history.record |
Store a versioned text snapshot and metadata |
history.entries |
List valid history entries for one file or a workspace |
history.content |
Read a stored history snapshot by relative storage path |
history.relocate |
Move a file's history records after a rename |
history.rename |
Set or clear a user-visible label on a history entry |
history.delete |
Delete one history entry and its snapshot |
maven.scan |
Parse a Maven project descriptor and recursively return modules/profiles |
maven.launchPlan |
Produce a deterministic Maven invocation from a versioned project context |
maven.diagnostics |
Parse stable Maven compiler diagnostics from build output |
debug.createSession |
Create a transport-neutral DAP session and return its initialize frame |
debug.launch |
Queue a launch or attach request, including during initialization |
debug.javaTestLaunch |
Normalize JUnit or TestNG launch metadata into Java DAP arguments |
debug.steppingFilters |
Return adapter defaults or normalize portable stepping filters |
debug.relocateBreakpoints |
Move source breakpoints across one exact UTF-16 editor replacement |
debug.setBreakpoints |
Replace and deterministically order one source's DAP breakpoints |
debug.setExceptionBreakpoints |
Replace and deterministically order one session's exception filters |
debug.setFunctionBreakpoints |
Replace and deterministically order one session's named function breakpoints |
debug.dataBreakpointInfo |
Resolve an adapter-owned data breakpoint identity for a paused variable or field |
debug.setDataBreakpoints |
Replace and deterministically order one session's resolved data breakpoints |
debug.setVariable |
Replace one visible variable value in its adapter-owned parent container |
debug.cancelOperation |
Cancel or time out one pending operation and ignore its late response |
debug.execute |
Submit continue, pause, next, step-in, or step-out control |
debug.inspect |
Request normalized threads, frames, scopes, variables, or evaluation |
debug.receive |
Reduce base64-encoded bytes received from a platform-owned DAP transport |
debug.runInTerminalResponse |
Complete one adapter-requested native terminal launch |
debug.disconnect |
Begin the DAP disconnect handshake without closing the native transport |
debug.destroySession |
Remove a session after the platform closes its native transport |
lsp.applyTextEdits |
Apply LSP UTF-16 text edits with range validation |
lsp.plainSnippet |
Convert LSP snippet insert text into plain editor text |
lsp.builtinCompletions |
Return lightweight current-file identifier completions |
lsp.builtinHover |
Return lightweight current-symbol hover text |
lsp.builtinNavigation |
Return lightweight current-file definition/reference locations |
lsp.startServer |
Start one Rust-owned process/session and begin initialization |
lsp.jdtWorkspaceKey |
Derive the deterministic JDT LS workspace-state directory key |
java.workspacePolicy |
Decide Java workspace activation and classify changed paths |
java.jdtWorkspaceFingerprint |
Reduce platform build-file observations to the portable JDT LS workspace fingerprint |
java.jdtCacheRetention |
Select expired inactive JDT LS workspace-state keys from platform metadata |
lsp.stopServer |
Gracefully shut down a session, with a bounded force-stop fallback |
lsp.syncDocument |
Open a document or apply a full-text or incremental didChange with monotonic versions |
lsp.workspaceFilesChanged |
Publish normalized created, changed, or deleted workspace files to one session |
lsp.closeDocument |
Close a document and clear its diagnostics |
lsp.request |
Submit a typed semantic request and return an opaque operation ID |
java.navigationMarkers |
Resolve versioned Java gutter markers from bounded JDT LS semantic requests |
java.resolveNavigation |
Resolve one Java gutter marker to normalized parent or implementation locations |
lsp.cancelOperation |
Cancel one pending semantic operation |
lsp.pollEvents |
Drain ordered typed lifecycle/feature/diagnostic/result/log events |
lsp.waitEvents |
Block until queued events exist or a timeout elapses, then drain them |
lsp.clearDiagnostics |
Clear every diagnostic owned by a session |
lsp.snapshot |
Return a diagnostic runtime snapshot for testing and control surfaces |
lsp.destroyServer |
Remove a terminal session handle from the registry |
java.runConfigurations |
Scan Java sources for main classes and return Maven/Spring run configurations |
java.codeVision |
Return Java declaration usage counts for editor code vision |
java.className |
Resolve a Java source package and simple name into a runtime class name |
java.sourceDefinition |
Locate a Java type, method, or field declaration in source text |
java.serverPort |
Parse Spring server port settings from properties or YAML text |
java.structure |
Parse Java editor folds, inlay hints, and portable syntax roles |
spring.index |
Build a deterministic Spring configuration, bean, injection, and endpoint index |
runConfig.inspect |
Inspect .lithe run documents, versions, and staleness without writing files |
runConfig.generate |
Generate deterministic Java/Maven configurations and toolchain requirements |
runConfig.resolve |
Merge generated, project, and local layers and return diagnostics |
runConfig.updateOptions |
Apply typed option edits and return an updated project or local document |
runConfig.saveEditorChanges |
Prepare the local and optional project documents for one editor save |
runConfig.createUserConfiguration |
Validate a typed user configuration and return an updated document |
runConfig.createLaunchPlan |
Project one effective configuration into a platform-neutral Run or Debug plan |
git.status |
Resolve the repository, current branch, and working-tree changes |
git.watchContext |
Resolve the repository and absolute Git metadata roots needed by native file watchers |
git.worktrees |
Return deterministic registered-worktree metadata without scanning each checkout |
git.pullRequestContext |
Resolve worktree-aware PR branch defaults, publication state, and uncommitted-change state |
git.command |
Execute one argument-based Git operation and return its arguments, streams, exit code, and ordered subprocess invocations |
git.write |
Validate and execute shared Git mutations such as stage, commit, branch, checkout, remote sync, clone, and stash |
git.diff |
Produce a structured working-tree, index, reference, or commit patch |
git.apply |
Apply or check a patch in stage, unstage, discard, or Shelf restore mode |
git.history |
Return the legacy combined reference snapshot and first bounded commit page |
git.references |
Return deterministic refs, recent local branches, ahead/behind state, and effective Git identity without scanning commit history |
git.historyPage |
Return one bounded commit page, parent hashes, decorations, and an opaque continuation cursor |
git.historyCursorClose |
Release an unfinished incremental history cursor and its Git process |
git.pushPreview |
Resolve a local branch push destination and the bounded commits not present on that remote base |
git.commit |
Return one structured commit by revision |
git.commitFiles |
Return files changed by one commit |
git.comparison |
Return files changed between a reference and the working tree |
git.stashes |
Return structured stash references and messages |
git.checkoutPreflight |
Return local paths that would block switching to a reference |
git.pullPreflight |
Report the configured upstream, ahead/behind counts, divergence, and tracked local changes without fetching |
git.integrationPreflight |
Return local paths that block a merge, rebase, cherry-pick, or revert |
git.conflictMarkers |
Return staged text files that still contain conflict markers |
git.operationState |
Report an interrupted merge, rebase, cherry-pick, or revert and its conflicted paths |
git.blame |
Return structured line blame metadata |
github.parseRemote |
Parse a canonical GitHub HTTPS or SSH remote into owner/name |
github.requestPlan |
Validate one GitHub operation and produce a trusted platform HTTP request plan |
github.normalizeResponse |
Normalize raw GitHub JSON and HTTP status into deterministic data or a stable error |
Workspace paths in responses are relative and use / separators. Line numbers
are one-based. git.status.repositoryRoot may be an absolute path when the
opened workspace is a subdirectory of the repository; all Git change paths are
relative to that repository root. git.status.ahead and behind report the
current branch's tracking counts and are zero when no upstream is configured.
git.worktrees.worktrees is ordered with the primary worktree first and then
by path. Each entry contains path, head, nullable branch, isCurrent,
isPrimary, isBare, isDetached, isLocked, nullable lockReason,
isPrunable, and nullable pruneReason. The path is absolute because linked
worktrees may live outside the opened workspace; clients must treat it as an
opaque native boundary value and must not persist it as a portable identifier.
Core reads the list with one porcelain operation and does not run status in
each checkout.
For a rename or copy, each change uses the destination as path and preserves
the source as originalPath; platform mutations that act on the Git entry pass
both paths back to Core.
The core rejects absolute paths and ..
traversal for file commands. Native file dialogs, file watching, PTY/ConPTY,
Java processes, and runtime discovery remain platform adapters.
git.history.recentReferences contains at most five existing local branches in
most-recently-used order. The current branch is first. Core derives checkout
history from the repository's HEAD reflog, de-duplicates branch names, ignores
detached or deleted references, and fills missing entries deterministically.
The remote HEAD target is preferred as the default branch, followed by main,
master, and the remaining local references in refname order.
The protocol version is currently 1. Add a fixture under shared/fixtures/
before changing a response shape or search rule.
document.lifecycle accepts a discriminated state (clean, dirty,
saving, or conflict) and one typed event. It returns the next state plus
one platform effect such as writeToDisk, reloadFromDisk, or
showConflict. saving carries the snapshot revision and operationId, so a
stale completion cannot clear newer edits. Live text, editor models, selections,
watchers, and native file I/O stay platform-owned; local keystrokes update the
same revision semantics in-process and never cross the Rust boundary. The
portable examples are in shared/fixtures/documents/lifecycle-v1.json.
GitHub command shapes, authorization behavior, and supported pull-request
operations are documented in github.md. Rust Core performs no
network or credential I/O for these commands.
community.discourse.auth.begin accepts an HTTPS origin, stable clientId,
user-visible applicationName, platform-owned authRedirect, and a non-empty
array of supported scopes. It returns an opaque flowId, an
authorizationUrl that requests RSA-OAEP padding, and an expiresAt Unix
timestamp. The private key and nonce remain in Rust memory and expire after ten
minutes. community.discourse.auth.complete accepts that flowId and the full
callbackUrl; it consumes the flow, verifies the callback target, decrypts the
payload, and checks the nonce before returning userApiKey and apiVersion.
Platform hosts open the browser, receive their registered URL scheme, and store
the returned credential in Keychain or Windows Credential Manager. They do not
implement Discourse cryptography or callback validation.
The authenticated community commands accept origin, userApiKey, and
clientId plus their operation-specific fields. Rust owns HTTPS requests,
authentication headers, a 30-second request timeout, a 5 MB response limit,
Discourse JSON decoding, deterministic post ordering, and HTML sanitization.
Platform clients never issue a parallel Discourse request or parse a second
response shape. Credential vault reads and writes remain native adapters; the
credential is passed to Core only for the duration of one command.
git.watchContext accepts { "root": string }. When root is not inside a
Git repository, it returns null. Otherwise it returns
{ "repositoryRoot": string, "gitDirectory": string, "gitCommonDirectory": string };
all three fields are absolute filesystem paths.
git.pullRequestContext accepts { "root": string } and returns
currentBranch, suggestedBaseBranch, suggestedPublishBranch,
requiresPublish, detached, and hasUncommittedChanges. For detached
worktrees, Core uses the worktree HEAD reflog's oldest commit and refs pointing
at that commit to suggest the branch from which the worktree started. For a
named branch, requiresPublish remains true until its current HEAD is present
on the same branch under origin, because GitHub repository identity is also
resolved from origin.
git.command accepts { "root": string, "arguments": string[], "input": string? }.
Arguments are passed directly to the Git executable without a shell. A
successful process launch returns { "arguments": string[], "output": string, "stdout": string, "stderr": string, "exitCode": number, "invocations": GitCommandInvocation[], "operationError": CoreError? } even when Git exits
non-zero. GitCommandInvocation is { "arguments": string[], "stdout": string, "stderr": string, "exitCode": number }. The top-level arguments, streams,
and exit code always equal the final invocation for compatibility, and output
is that invocation's stdout followed by stderr; invocations records every
subprocess in execution order. Validation, process-start, and workspace failures
that occur before Git starts use the standard error envelope. If a follow-up
validation or probe fails after at least one subprocess was recorded, the
response retains the invocation trace and includes the failure as
operationError.
git.write accepts a typed mutation request. Its required operation values are
stage, unstage, discard, discardAll, stageAll, commit, ignore, exclude, cherryPick, revert,
reset, editCommitMessage, deleteCommit, squashCommits, createBranch, publishBranch,
renameBranch, setUpstream, unsetUpstream, deleteBranch, merge, rebase, createWorktree,
removeWorktree, lockWorktree, unlockWorktree, repairWorktrees, pruneWorktrees,
fetch, pull, push, checkout, checkoutAndRebase, checkoutRevision, clone, stashPush,
stashApply, stashPop, stashDrop, deleteRemoteBranch, operationContinue,
operationAbort, operationSkip, createTag, and deleteTag. Optional fields are paths, reference, referenceKind,
gitReference, revision, revisions, name, message, remote, destination, mode,
includeUntracked, checkout, amend, force, pushTags, expectedPush, and autoStash.
The core validates pathspecs, revisions, branch names, references, reset modes,
stash references, and operation-specific required fields before invoking Git.
setUpstream requires a typed remote gitReference and passes its complete
refs/remotes/* identity to Git, so a same-named local branch cannot make the
upstream ambiguous. createWorktree likewise requires a typed reference; for a
remote reference Core executes one git worktree add --track -b mutation using
the complete remote ref, so branch creation, checkout, and tracking setup do not
form separate platform-visible success states. Worktree mutations re-read Git's
registered list and reject arbitrary paths. Removal rejects the current,
primary, or locked worktree; dirty worktrees require an explicit force value.
repairWorktrees refreshes administrative links after a repository or worktree
has moved. pruneWorktrees removes registrations whose checkout is already missing and
does not recursively delete an arbitrary directory.
Successful process launch returns { "arguments": string[], "output": string, "stdout": string, "stderr": string, "exitCode": number, "invocations": GitCommandInvocation[], "operationError": CoreError?, "stashRestore": GitStashRestore?, "warnings": GitOperationWarning[] } even when Git exits non-zero.
GitOperationWarning is { "code": string, "message": string, "details"?: string }
and reports a non-fatal follow-up failure after the requested mutation already
succeeded. Platform clients must retain the successful operation outcome while
presenting the warning. The top-level process fields
always describe the final subprocess, and output is that subprocess's
stdout followed by stderr. invocations records every Git subprocess for
composite operations such as discardAll and Smart Checkout in execution
order; each item contains the exact argument vector (excluding the executable
name), separate streams, and exit code. A follow-up validation or probe failure
after Git has started is returned in operationError alongside the retained
trace. A stash restore conflict is a logical operation failure represented by
stashRestore, even when a later diagnostic invocation exits successfully.
Consumers must therefore consider operationError and stashRestore in
addition to the compatibility exitCode. The shared compatibility fixtures are
shared/fixtures/git/command-response-v1.json and
shared/fixtures/git/command-error-response-v1.json. Invalid arguments found
before any Git subprocess use the standard invalid_request error envelope.
checkout uses referenceKind values
local, remote, or tag; clone uses remote as its source and
destination as its target path. publishBranch validates name, creates
and checks out that branch at a detached HEAD when needed, then pushes it with
an upstream. If the push fails, the local branch is intentionally retained so
the user can fix credentials or connectivity and retry without losing commits.
git.pushPreview accepts root, an optional complete local gitReference or
legacy reference, an optional bounded limit, and pushTags. It returns localBranch,
localHead, remote, remoteBranch, nullable remoteTrackingOid, nullable
upstream, exact reviewed tags, commits, and hasMore using
shared/fixtures/git/push-preview-v1.json. The push destination follows
branch.<name>.pushRemote, then remote.pushDefault, the configured upstream
remote, branch.<name>.remote, and finally origin or the first configured
remote. A destination without a fetched tracking reference previews commits not
reachable from that remote. A reviewed push mutation sends these resolved fields
back as expectedPush; Core rejects a stale local tip, destination, or tracking OID
before starting Git. force binds --force-with-lease to the reviewed destination
OID when expectedPush is present, and Core also validates the reviewed tag
identities; pushTags accepts none, all, or reachable
and maps to no tag option, --tags, or --follow-tags respectively. Legacy push
callers may omit expectedPush. A reviewed push uses the preview's immutable
localHead OID as the refspec source, so a repository change after validation
cannot add unreviewed commits to the operation. Because Git cannot infer an
upstream from an OID source, Core explicitly configures the reviewed local
branch after a successful first push.
New reference-based workflows send gitReference as { "fullName": string, "shortName": string, "kind": "local" | "remote" | "tag" }. Core verifies
that all three fields describe the same namespace and validates the full ref
with Git. The legacy reference and referenceKind fields remain accepted for
existing platform calls. checkoutAndRebase requires a local or remote branch
reference and a completely clean worktree; Core records the current local
branch before switching and rebases the checked-out branch onto that original
branch. A dirty tree or detached HEAD is rejected before checkout begins. When
remote checkout finds an existing same-named local branch, Core uses it only if
its configured upstream is the selected complete remote reference.
pull without an explicit reference retains current-upstream behavior. An
explicit remote reference may use either the preferred gitReference shape or
the legacy reference plus referenceKind: "remote" fields. Core validates and
safely splits refs/remotes/<remote>/<branch> against configured remote names,
then invokes pull with the explicit remote and branch using mode ffOnly,
merge, or rebase. Platforms must not parse the remote reference or construct
these Git arguments themselves.
deleteRemoteBranch requires a complete remote gitReference. Core resolves
the configured remote with longest-prefix matching and invokes a structured
remote branch deletion; platforms must not split shortName themselves.
When commit includes paths, Core stages the complete working-tree state of
those paths, including untracked files and deletions, then commits only those
paths. Other paths already present in the index remain staged and are not part
of the new commit. Core checks conflict markers after preparing that final
snapshot in an isolated temporary index initialized from the operation's HEAD
tree. The real index is not changed on any
staging, validation, hook, signing, or commit failure; successful commits
reconcile only the selected paths, preserving unrelated staging created while
the operation ran. Selected paths used to prepare and reconcile the snapshot
are passed to Git over NUL-delimited stdin with --pathspec-from-file, avoiding
platform command-line limits and preserving rename source/destination identity.
git.diff accepts worktreeSnapshot: true to review that same complete
working-tree snapshot against HEAD through an isolated temporary index. This
mode includes staged, unstaged, untracked, deleted, and same-path recreated
files without reading or mutating the real index and cannot be combined with
other diff reference modes.
A commit request without paths retains
the legacy behavior of committing the existing index. ignore appends root-anchored patterns to the
repository's top-level .gitignore; exclude appends the same patterns to the
worktree-aware Git metadata path for info/exclude. Both ignore operations
preserve existing content, escape Git pattern characters, de-duplicate rules,
and interpret a trailing / as a directory rule.
editCommitMessage rebuilds the selected commit and its later first-parent
descendants with the new message. squashCommits requires at least two
distinct, contiguous revisions, uses the newest selected tree, and rebuilds
later descendants. Both preserve commit author and committer attribution and
atomically update the checked-out branch reference. deleteCommit drops a
non-root commit and replays later commits; deleting HEAD resets to its parent.
All three operations reject a dirty worktree, detached HEAD, an active Git
operation, a target outside the current branch's first-parent chain, a rewrite
range containing a merge commit, or any rewritten commit reachable from
refs/remotes.
createTag uses name for the new tag, revision as its target commit or
revision, and an optional message: when the field is present (including an
empty value), it creates an annotated tag (git tag -a); an absent field
creates a lightweight tag. UI callers trim new user-entered messages. Core
passes the supplied annotation with verbatim cleanup so restore preserves
CRLF and trailing blank lines, and an explicit empty value preserves an empty
annotated tag. Tag names must satisfy the git check-ref-format refname rules and must not
begin with a dash; shared/fixtures/git/tag-names.json pins the boundary cases
for Core and host-side validation. Before invoking Git, createTag probes the
repository so a duplicate tag (A tag named '<name>' already exists) and an unresolvable
non-commit target (Could not resolve tag target '<rev>') fail with stable
invalid_request messages instead of localized Git output. deleteTag uses
name and removes refs/tags/<name>; a missing tag fails with
The tag '<name>' does not exist. On success the response carries a
structured tagDeletion record — { "name": string, "deletedTarget": string, "kind": "lightweight" | "annotated", "message": string? } — where
deletedTarget is the peeled commit the deleted ref resolved to and
message is the original annotation with its line breaks preserved. Hosts
can rebuild the tag by replaying createTag with name, deletedTarget,
and message; the tagger identity and timestamp are intentionally not
preserved. Deletion supplies the observed unpeeled object ID to update-ref,
so a concurrent force-update fails atomically instead of deleting new state and
returning a stale recovery target. deleteBranch applies the same expected-OID
guard after checking the branch is fully merged and not checked out, then
on success, carries a structured branchDeletion record —
{ "name": string, "deletedTarget": string } — so hosts can offer to
recreate the branch at its previous commit; a missing branch fails with
The branch '<name>' does not exist. If the ref deletion succeeds but branch
configuration cleanup fails, the response contains both branchDeletion and a
branch_config_cleanup_failed warning; hosts must preserve the Restore action while surfacing the
cleanup diagnostic.
operationContinue, operationAbort, and operationSkip inspect Git metadata
to select the active merge, rebase, cherry-pick, or revert instead of accepting
an operation kind from the caller. Continue is rejected while conflicted paths
remain, and skip is supported only for a rebase. All three return the normal
Git process result when Git is invoked;
an absent or unsupported operation state uses the invalid_request envelope.
git.checkoutPreflight accepts { "root": string, "reference": string } or
the preferred { "root": string, "gitReference": GitReference } shape and
returns { "blockingPaths": string[] }. The sorted, de-duplicated result
contains tracked paths that are both locally modified and different between
HEAD and the target, plus untracked paths that the target reference tracks.
git.pullPreflight accepts { "root": string } and returns upstream as a
string or null, numeric ahead and behind counts, diverged, and
hasLocalChanges. It reads the existing tracking reference without fetching;
diverged is true only when both counts are non-zero. hasLocalChanges checks
tracked changes and excludes untracked files. A branch with no configured
upstream returns null, zero counts, and false for both booleans.
git.integrationPreflight accepts either reference or gitReference with
root and operation, where operation is merge, rebase, cherryPick,
or revert. It returns sorted, de-duplicated blockingPaths and
blocksEntirely. Merge, cherry-pick, and revert report only dirty tracked paths
that overlap files the operation would write. Rebase reports every dirty
tracked path and sets blocksEntirely to true when that set is non-empty.
git.conflictMarkers accepts { "root": string } and returns
{ "paths": string[] }. Paths are sorted and de-duplicated staged text files
whose staged content has a line beginning with an opening, closing, or diff3
conflict marker. A bare
======= line is not treated as a conflict marker.
git.operationState accepts { "root": string } and returns kind,
reference, step, total, and sorted, de-duplicated conflictedPaths.
kind is an empty string when no operation is active; otherwise it is merge,
rebase, cherryPick, or revert. reference, step, and total are
nullable, and the progress counters are populated only for a rebase. State is
read from Git's own metadata, so operations started outside Lithe are reported.
git.diff accepts root, pathspecs, optional reference, gitReference,
targetGitReference, or commit, plus emptyTreeBase for a legacy target
reference whose comparison must begin at the repository's object-format-specific empty tree,
staged, untracked, contextLines, and ignoreAllWhitespace, and returns { "patch": string, "rows": [], "hunks": [] }. Rows contain one-based oldLine/newLine values where
available, left/right text, a kind (context, changed, addition,
removal, or information), and an optional hunkID. For context and
information rows both sides carry identical text, so right is omitted and
clients must fall back to left. Hunk entries contain their header and the
patch text needed for partial apply; rows are not duplicated per hunk, so
clients group rows by hunkID instead.
New reference-tree workflows use gitReference; Core validates its full
identity before constructing the diff invocation. When targetGitReference is
present, Core validates both complete identities and constructs the two-ref
range. The legacy reference field remains available for existing revision and
range comparisons.
git.comparison accepts root plus the same reference or gitReference /
targetGitReference forms and returns the deterministically ordered changed
files. Platforms must not construct a two-ref range themselves.
git.apply accepts root, patch, and mode; supported modes are stage,
unstage, discard, restoreIndex, worktree, restoreIndexCheck, and
worktreeCheck. The two *Check modes only test whether the reverse patch
already applies, so Shelf restoration can be retried after a partial failure.
It returns the normal Git process result. restoreIndex applies a saved
index patch to both the index and worktree; worktree applies only to the
worktree. Pathspecs must be workspace-relative and must not contain absolute
paths or .. components.
git.history accepts root, an optional full reference, and limit (the
core clamps it to 1...5000). It remains the compatibility command that
combines git.references with the first git.historyPage. New clients use
git.references with { "root": string } and request commits separately with
git.historyPage using root, optional full reference, nullable opaque
cursor, and limit. The first request omits cursor; each later request
returns the prior page's nextCursor. Core keeps one bounded, backpressured
git log stream behind that cursor and clamps the stream to the first 5,000
commits, so later pages continue traversal instead of replaying earlier commits.
A history page returns commits, nullable nextCursor, and hasMore. Clients
call git.historyCursorClose with root and cursor when abandoning an
unfinished stream, and discard and close a late page when its repository,
selected reference, or owning operationId is stale. Core also expires idle
cursors and caps the number of live streams. Commit parents are explicit so
clients can render merge topology without re-parsing Git output. The optional
effective userName and userEmail returned by git.references let clients
implement a stable me filter without guessing from recent commits. Each
reference includes peelsToCommit; hosts use it to disable commit-only
actions for legal tree/blob tags before the user reaches a failing mutation.
Each local
reference with an upstream also returns numeric ahead and behind counts
against that fetched remote-tracking reference. References without an upstream,
remote references, and tags return zero for both fields. Portable examples are
shared/fixtures/git/references-response-v1.json and
shared/fixtures/git/history-page-response-v1.json.
For compatibility, a request that explicitly contains the deprecated numeric
offset field still uses the bounded offset implementation and returns
nextOffset. New clients must omit offset; repository size does not select
between the two protocols.
git.commit accepts root and a revision, returning one commit object.
git.blame accepts root and a workspace-relative path; its line numbers
are one-based and author timestamps are Unix seconds.
workspace.search accepts maxResults for a total result cap. Callers that
need separate buckets may also provide maxFileResults and
maxContentResults; each category is capped independently and the total cap
still applies.
workspace.searchEverywhere uses the same query options and visibility fields,
and additionally accepts maxSymbolResults. Results are ordered as file,
type, symbol, and content matches. Java type and method results include a
one-based line, symbolName, and the matching source line in preview.
workspace.replacePreview accepts root, query, replacement, the same
query options, optional workspace-relative paths, optional textOverrides
keyed by relative path, and visibility fields. It returns { "files": [] }
where each file contains replacement matches and the complete
replacementText to write. The command never writes files; callers can record
history before using file.write for the selected files.
lsp.applyTextEdits accepts { "text": string, "edits": [] }, where each edit
has an LSP range with zero-based line and UTF-16 utf16Column fields plus
newText. Ranges are validated and overlapping edits return
invalid_request with details overlappingEdits; invalid positions return
details invalidRange. Successful responses return { "text": string }.
lsp.plainSnippet accepts { "value": string } and returns { "text": string }
after removing LSP tab stops and replacing simple placeholder defaults such as
${1:name} with name.
The debug.* commands are the shared Debug Adapter Protocol boundary. Rust
owns DAP framing, request sequences, response correlation, initialization and
execution state, deterministic breakpoint sets, and normalized thread, stack,
scope, variable, evaluation, output, stop, continue, and termination events.
Platforms own adapter discovery, JDT LS activation, sockets or process pipes,
native process termination, persistence, and UI rendering.
debug.createSession accepts { sessionId, adapterId, rootPath, supportsRunInTerminalRequest }. It does not
open a socket or launch a process. It returns a session update in
initializing state with an ordered outboundFrames array. Each frame is a
complete Content-Length-framed byte sequence encoded as base64. Every Debug
command returns the same update shape: { sessionId, state, outboundFrames, events }. The platform writes frames in array order and feeds received chunks
back through debug.receive as { sessionId, dataBase64 }; partial and
consecutive messages are buffered and reduced in Rust.
When supportsRunInTerminalRequest is true, the initialize frame advertises
the native host's terminal capability. An adapter runInTerminal reverse
request becomes a deterministic runInTerminalRequested event containing a
Core-generated requestId, terminal kind, title, working directory, ordered
argument vector, sorted environment changes, and shell-interpretation flag.
The platform launches the process through its PTY/ConPTY adapter and calls
debug.runInTerminalResponse with { sessionId, requestId, success, processId?, shellProcessId?, message? }. Core validates process identifiers, emits the DAP
response, ignores duplicate or expired completions, and fails pending terminal
requests when the session disconnects. The shared compatibility cases are in
shared/fixtures/debug/run-in-terminal-v1.json.
debug.launch accepts an operationId and a language-neutral configuration
containing name, request kind (launch or attach), provider arguments, and
optional portable steppingFilters.
debug.javaTestLaunch accepts JDT LS-owned working directory, main class,
project, classpath, module path, VM arguments, program arguments, Java test
framework, and a platform-owned loopback result port. JUnit placeholder ports
are replaced deterministically. TestNG appends the packaged runner once and
uses its selected method names. Core serializes JDT's VM and program argument
arrays into the string fields required by Java Debug Server's DAP launch model.
JDT LS remains responsible for resolving file,
class, and method selections to this metadata; Core does not parse Java source
or infer a test framework in this command. The command creates no process,
socket, timer, or persistent session; compatibility cases live in
shared/fixtures/debug/java-test-launch-v1.json.
Launch submitted during initialization is retained until the initialize
response. For Java, Core projects those filters into the adapter's stepFilters
launch object unless the provider arguments already contain an explicit value.
debug.steppingFilters accepts { adapterId, filters? }; omission of filters
returns deterministic adapter defaults, while a supplied value is trimmed,
sorted, de-duplicated, and validated before persistence or launch. Omitted
fields inside a supplied value are empty or false, so future adapters never
inherit Java policy accidentally. Java class
patterns support $JDK, $Libraries, and adapter-compatible wildcards. Other
adapters default to an unfiltered policy until their integration defines one.
Java defaults include both $JDK and $Libraries, matching the IDE convention
of collapsing platform and dependency frames while retaining project frames.
The portable cases are in
shared/fixtures/debug/stepping-filters-v1.json.
Normalized stack frames include isFiltered. Core derives it from the active
class filters using the DAP frame name, source path, presentation hint, and
session root. This classification is presentation metadata only: Core returns
the complete ordered stack, while native UIs may collapse consecutive matching
frames and must allow users to expand them. debug.setBreakpoints accepts
one-based line and optional column,
enabled state, condition, hit condition, and log message values. Rust sorts and
de-duplicates the complete source set, retains disabled entries without sending
them to the adapter, waits for the DAP initialized event, then sends all
sources in deterministic path order followed by configurationDone when the
adapter supports it. This allows native products to mute or restore breakpoints
without maintaining a second protocol representation.
debug.setExceptionBreakpoints accepts adapter-defined filter identifiers,
enabled state, and an optional condition. Rust trims, sorts, and de-duplicates
the complete selection, retains disabled filters without sending them, and uses
DAP filterOptions only when the adapter negotiated that capability. Before a
native client has configured a selection, Rust adopts the adapter's declared
defaults so the first initialized flow sends exception filters before source
breakpoints and configurationDone.
debug.setFunctionBreakpoints accepts a method or function name, enabled
state, condition, and hit condition. Rust retains the complete sorted set,
omits disabled entries, and sends DAP setFunctionBreakpoints before source
breakpoints only when the adapter negotiated function-breakpoint support.
Data breakpoints use DAP's required two-step flow. The native client first calls
debug.dataBreakpointInfo with the selected variable name plus its parent
variablesReference and current frame. Rust Core correlates the response by
operationId and returns the adapter-owned dataId, display description,
allowed access modes, and canPersist. The client then calls
debug.setDataBreakpoints; Core keeps the complete deterministic set, omits
disabled entries, and sends access type, condition, and hit count only when the
adapter negotiated data-breakpoint support. Native clients must discard IDs
whose canPersist is false when the debug session ends.
debug.setVariable accepts the selected variable's parent variablesReference,
name, and replacement text. Core permits mutation only while paused and after
the adapter advertises supportsSetVariable, then returns the adapter's
normalized replacement value and optional type through the caller's
operationId.
debug.execute covers continue, pause, step over, step in, step out, step back,
restart, terminate, and capability-gated single-thread execution. Rust Core rejects stepping unless the session is paused
and a thread is selected, and gates step back, restart, and terminate against
the adapter capabilities negotiated during initialization. Restart and
terminate are session-level requests and never receive a stale threadId.
Single-thread pause, continue, and stepping preserve the paused session when
the adapter reports that other threads remain stopped.
debug.cancelOperation removes the matching pending request before emitting a
terminal failure, so a late adapter response cannot mutate current UI state. If
the adapter advertises supportsCancelRequest, Core also sends DAP cancel
with the original request sequence. Native hosts own monotonic deadlines and
invoke this command with cancelled or timedOut; the macOS reference product
uses a bounded 10-second deadline for interactive inspections and mutations.
Smart step into and run to cursor keep DAP's target lookup explicit. Clients
use debug.inspect with stepInTargets and a frame, or gotoTargets with a
source path and one-based cursor coordinates. Core normalizes the returned
targets and correlates them to the caller's operation. The selected target is
then passed as targetId to debug.execute using stepIn or goto; both
flows are rejected unless the adapter advertised the matching capability.
The successful DAP initialize response emits a normalized capabilities event.
It includes conditional, hit-count, log, function, data, and exception
breakpoint support; variable mutation; restart and terminate requests; step
back; exception information; request cancellation; single-thread execution;
step-in targets; goto targets; and ordered exception filters. Native UIs
must treat capability state as unknown until this event arrives and hide or
disable unsupported actions after negotiation.
debug.execute correlates continue, pause, next, step-in, and step-out to the
caller's operationId. debug.inspect supports threads, stackTrace,
scopes, variables, evaluate, and capability-gated exceptionInfo;
required thread, frame, variable reference, and expression fields are validated
before a request is emitted. A variables inspection may additionally carry
variableFilter (named or indexed), zero-based start, and positive
count; Core maps them to DAP filter, start, and count and rejects those
fields for every other inspection kind. Normalized scopes, variables,
evaluations, and variable-mutation results include non-negative
namedVariables and indexedVariables counts, using zero when the adapter
omits or reports an invalid negative value. The compatibility cases are in
shared/fixtures/debug/variable-paging-v1.json.
Exception information is available only while
paused and normalizes the exception type, description, break mode, optional
stack trace, evaluation name, and nested exception details. The Java adapter
currently supplies the type, description, and break mode but no expandable
exception object reference, so native clients continue to inspect ordinary
frame scopes for local state.
Terminal operation events are exactly one of operationCompleted with a typed
result or operationFailed with the adapter command and safe message. Other
ordered events are stateChanged, initialized, output, stopped,
continued, terminated, and breakpoint. Source coordinates are one-based.
The compatibility flow is captured in
shared/fixtures/debug/dap-session-v1.json; exception normalization cases are
captured in shared/fixtures/debug/exception-info-v1.json.
debug.disconnect emits the protocol handshake and enters terminating.
Core derives DAP terminateDebuggee from the session's request kind: launch
uses true, while attach and a session stopped before either request use
false. This prevents a remote detach from killing a JVM the IDE does not own.
The compatibility cases are in
shared/fixtures/debug/disconnect-policy-v1.json. The platform keeps the
socket or process alive long enough to flush the frame, then closes it and
calls debug.destroySession. A session allocates no process, socket, timer, or
background task, and no session exists until Debug is used.
lsp.builtinCompletions, lsp.builtinHover, and lsp.builtinNavigation are
the no-process lightweight language path. They accept current-file text, an
absolute filePath, and a zero-based LSP position. Completion returns
current-file identifiers with text edits for the active prefix. Hover returns
the current identifier as markdown. Navigation returns current-file locations;
definition prefers declaration-looking occurrences, while references returns
all matching identifier occurrences. These commands are deliberately
text-level fallbacks; precise type-aware behavior belongs to a started language
server.
The LSP provider catalog is returned by lithe_core_lsp_provider_catalog_json.
Each provider descriptor may include languageServerLaunch with ordered
executableNames and arguments; Swift adapters use this metadata when they
need to discover a real language-server executable; the selected launch plan is
then submitted to the Rust-owned runtime. Built-in descriptors are merged by provider ID with the optional
.lithe/lsp/language-providers.json workspace document. See
language-tooling.md for routing,
discovery, lifecycle, and compatibility rules.
The lsp.*Server, lsp.*Document, lsp.request, lsp.pollEvents, and
lsp.waitEvents
commands are the semantic LSP runtime boundary. lsp.startServer accepts the
provider ID, selected executable/arguments/environment, root URI, working
directory, initialization options, optional runtime executable,
jdtlsLaunchResources, cache directory, and workspaceFingerprint, plus
initialize, post-initialize readiness, request, and shutdown deadlines.
Java callers may also provide the versioned mavenContext accepted by
maven.launchPlan. Core validates its reactor and recursively declared modules,
publishes settingsPath through
java.configuration.maven.userSettings, and, after ServiceReady, sends one
java.project.updateSettings command per Maven project with
org.eclipse.m2e.core.selectedProfiles. The session becomes ready only after
every command succeeds; a command error or timeout terminates the session with
mavenContextFailed or mavenContextTimeout at the serviceReady stage.
initializeTimeoutMilliseconds bounds only the standard LSP handshake. For a
provider such as JDT LS that has a later readiness signal,
serviceReadyIdleTimeoutMilliseconds bounds time without changed work-done
progress and serviceReadyAbsoluteTimeoutMilliseconds is the final safety cap.
The defaults are 45 seconds idle and 10 minutes absolute; duplicate progress
does not refresh the idle deadline. jdtlsLaunchResources, when present,
contains launcherJarPath, configurationDirectory, lombokAgentPath, the
legacy optional javaDebugBundlePath, and ordered
javaExtensionBundlePaths. It is valid only for the Java provider and requires
runtimeExecutablePath. Rust loads the legacy Debug bundle first when present,
then appends the extension bundle paths with stable de-duplication. Rust
then uses runtimeExecutablePath as the process executable and constructs the
complete deterministic JDT LS JVM argument list. When the structured object is
absent, the selected executablePath and legacy wrapper arguments remain the
compatibility path. Rust owns the returned
session's child process, stdin/stdout/stderr, framing buffer, JSON-RPC request
IDs, document versions, pending deadlines, capabilities, diagnostics, and
graceful/forced termination.
JDT LS remains initializing until language/status: ServiceReady. During this
phase Rust reduces changed $/progress notifications into throttled JSON log
details containing the current phase, percentage, project, observed project
count, artifact name, repository host, downloaded/total bytes, calculated
throughput, elapsed/idle durations, and cache disposition. Progress parsing is
observability-only and never substitutes for ServiceReady. Idle and absolute
failures use serviceReadyTimeout at stage serviceReady and retain the final
diagnostic snapshot in underlyingMessage.
Platform adapters own filesystem discovery and validate that packaged JDT LS
contains the Equinox launcher, platform configuration directory, Lombok agent,
Java Debug Server, and bundled Java. Java Test-capable hosts additionally
validate their extension bundles and runner. They do not construct JVM commands.
Packaged macOS and Windows plans always use structured direct launch, so runtime
startup has no shell, PowerShell, or user-PATH dependency. Wrapper launch
remains optional only for external or older plans.
For JDT LS, platform adapters observe root Maven/Gradle descriptor timestamps
and sizes, names of direct Maven module directories, and the selected JDT LS
version. They submit those raw observations to java.jdtWorkspaceFingerprint;
Rust Core validates, sorts, de-duplicates, and constructs the sole portable
fingerprint representation. Core then hashes the normalized workspace identity
followed by a null separator and that opaque fingerprint to select
cacheDirectory/jdtls/<workspaceKey>.
Omitting the fingerprint preserves the legacy path-only key for older clients.
Changing structure selects a new directory without deleting the old one, so a
later switch back can reuse it.
java.jdtWorkspaceFingerprint accepts
{ buildFiles, directMavenModules, jdtlsVersion }. Each build-file observation
contains a workspace-relative path, modifiedUnixMilliseconds, and
sizeBytes. It returns { workspaceFingerprint }; platforms must not recreate
or parse this opaque string. Compatibility cases are in
shared/fixtures/lsp/jdt-workspace-fingerprint-v1.json.
lsp.jdtWorkspaceKey accepts { workspaceRoot, workspaceFingerprint? } and
returns { workspaceKey } through the same normalization and SHA-256 algorithm
used by lsp.startServer. Platform cache-maintenance actions use it to remove
only the current workspace/fingerprint directory; they do not clear sibling
workspaces or older structural states.
java.workspacePolicy accepts workspacePaths and changedPaths as
workspace-relative paths. It starts Java tooling when any non-ignored .java
source exists, regardless of Maven or Gradle metadata, chooses one deterministic
representative source, and classifies changes as ignored, source,
buildConfiguration, or other. The compatibility examples are in
shared/fixtures/lsp/java-workspace-policy-v1.json.
java.jdtCacheRetention accepts platform-observed cache directory metadata as
{ nowUnixSeconds, activeWorkspaceKey?, entries }. Each entry contains a
lowercase 64-character SHA-256 workspaceKey and
lastModifiedUnixSeconds. Core ignores invalid candidate names, de-duplicates
observations using the newest timestamp, never selects the active key, and
returns deterministically sorted expiredWorkspaceKeys older than the fixed
30-day retention period. Platform adapters own the last-used marker, directory
enumeration, revalidation, deletion, and error logging; Core performs no cache
filesystem I/O. See shared/fixtures/lsp/jdt-cache-retention-v1.json.
lsp.syncDocument accepts { sessionId, uri, languageId, text?, contentChanges? }.
The first sync emits didOpen at version 1. Later syncs emit didChange with
increasing versions. When the server advertised incremental textDocumentSync
and contentChanges includes LSP ranges, the notification carries those
range-based edits and does not require a full document text field. Otherwise
the change is a full-text replacement. The response is
{ documentVersion, changed }; submitting identical full text returns
changed: false, preserves the version, and emits no LSP notification.
lsp.workspaceFilesChanged accepts a session ID and ordered file URI changes
whose kind is created, changed, or deleted. It emits one
workspace/didChangeWatchedFiles notification. Open documents remain owned by
versioned lsp.syncDocument; adapters must not duplicate those edits as watcher
events.
java.navigationMarkers accepts
{ sessionId, operationId?, uri, documentVersion? } and completes with
{ documentVersion, markers }. Rust combines JDT LS implementation CodeLens,
textDocument/implementation, and java/findLinks results with parser-selected
Java declaration candidates. Work is capped at 64 semantic tasks, individual
task failures preserve other verified markers, stale document versions cancel
the batch, and the latest completed version is cached per URI. Markers are
sorted by line, UTF-16 column, and relation. A marker contains direction
(up or down) and relation (interface or inheritance); zero-target
declarations are omitted. The cross-platform examples are in
shared/fixtures/lsp/java-navigation-v1.json.
java.resolveNavigation accepts the marker position, direction, relation, and
document version. Downward markers use textDocument/implementation; upward
markers use JDT LS java/findLinks with superImplementation. Its terminal
result is { documentVersion, locations }, using the same normalized physical
and virtual-location representation as ordinary navigation.
lsp.request accepts a semantic operation plus
the operation-specific URI, position, range, diagnostics, item, action, or
command fields, and returns { operationId }. Supported operations include
completion, hover, definition/declaration/type-definition, references,
implementation, rename, formatting, code actions and resolve, execute command,
inlay hints, folding ranges, code lens, and provider virtual documents.
The virtualDocument operation accepts { sessionId, operation, virtualUri } without a document uri. Its terminal requestCompleted event
returns { text }, where text is the provider-resolved UTF-8 source for the
opaque virtual URI.
lsp.pollEvents drains events ordered by per-session sequence. lsp.waitEvents
accepts { sessionId, timeoutMilliseconds } and waits on a session event
channel until events are queued or the timeout elapses, then drains the same
typed events. Hosts should use waitEvents so idle sessions do not poll.
Event types
include stateChanged, featuresChanged, diagnostics,
requestCompleted, serverInfoChanged, and log. Every request completes at
most once with either result or a structured runtime error containing
provider/session, stage, optional method/document/request, stable code, and
optional process-exit detail. Late responses after cancellation or deadline
are ignored. Diagnostics are accepted only for documents open in the current
session, and versioned diagnostics must match the current document version.
The client reducer, raw JSON-RPC message, frame, and parser functions are
internal Rust implementation seams; they are not public application commands.
Completion, hover, navigation, edit, hint, folding, and code-lens responses are
normalized by Rust before they cross the application boundary. Unknown server
requests receive JSON-RPC Method not found instead of being silently ignored.
The history.* commands accept an adapter-selected storageRoot; history
metadata never stores an absolute workspace or storage path. history.record
accepts workspaceRoot, a relative path, a reason, and optional UTF-8
content; when content is omitted the core reads the workspace file. Records
are versioned, de-duplicated against the latest snapshot, capped at 100 entries
per file, and pruned after 30 days. Invalid metadata and missing snapshot files
are ignored. history.entries returns Unix-second timestamps and relative
contentPath values. history.content rejects traversal,
history.relocate updates metadata and storage paths, and history.rename and
history.delete validate both the relative file path and entry ID before
changing stored metadata.
maven.scan accepts { "root": string, "paths"?: string[] } and returns
null when neither the root nor the supplied visible workspace-relative paths
contain a readable pom.xml. Candidates are tried in shallowest-first order,
with /-normalized lexical paths breaking ties, until one parses successfully;
a malformed candidate does not hide a valid nested project. A project response
contains its workspace relativePath,
groupId, artifactId, version, packaging, recursive modules, profiles,
and hasWrapper. Module paths are relative to the selected Maven root and use
/ separators. Malformed XML returns parse_failed.
maven.launchPlan accepts a workspace root, a versioned context, an
optional reactor-relative module, and an ordered goals array whose first
entry is a lifecycle or custom goal. Later entries may be ordinary Maven CLI
arguments such as -Dname=value or -q. They remain separate process arguments
and are never interpreted by a shell. Context version 1 contains the
workspace-relative reactorPath,
selected profiles, optional platform-local settingsPath, skipTests, and
optional Maven/JDK paths used only for the configuration fingerprint. The
response contains the project-maven toolchain reference, an argument array,
the workspace-relative reactor working directory, and a deterministic SHA-256
configuration fingerprint. Profiles are sorted and de-duplicated. Module plans
from maven.launchPlan use -pl <module> -am; Run and Debug plans use
-pl <module> without -am. Settings use -s; skipped tests use
-DskipTests. Explicit Run cwd, Profiles, and extensions.maven.skipTests
values override the project context, including skipTests: false.
The core never reads settings.xml and never copies its path into a portable
project document. Maven itself continues to read .mvn/maven.config; the plan
does not expand or duplicate that file's arguments. Fixtures are in
shared/fixtures/maven/launch-plan-v1.json.
maven.diagnostics accepts { "root": string, "output": string } and returns
{ "issues": [] }. Diagnostic paths may be absolute or workspace-relative;
the response preserves the path text, uses one-based line and column values,
and normalizes severity to error or warning. Duplicate issue lines are
removed deterministically.
java.runConfigurations accepts { "root": string, "paths": string[], "modulePaths": string[] }. Java and module paths are workspace-relative. The response
contains detected mainClasses and deterministic configurations. Each
configuration carries the exact workspace-relative sourcePath that produced
it and a sourceSet of main, test, or other; consumers must not recover a
source by matching the qualified class name. Process launching remains a
platform adapter responsibility.
The runConfig.* commands implement the versioned project protocol described
by the JSON Schemas in this directory. runConfig.inspect accepts root and
never writes files. runConfig.generate accepts root, relative Java paths,
and relative modulePaths; it returns generated configuration and toolchain
requirement documents for the platform adapter to write atomically. Maven root
discovery checks pom.xml along each supplied path's ancestor chain, so a
reactor nested below the opened workspace does not depend on the platform
including build descriptors in paths. Maven ownership is resolved per Java
entry: standalone sources keep the JDK launch path, while entries from
independent nested reactors retain their own reactor working directory and
module selector. Generated fingerprints include both project inputs and the
detector revision; either changing marks persisted output stale and requires
regeneration.
runConfig.resolve accepts root, optional local toolchainCandidates, and
optional localDocument. When localDocument is present, Core uses that JSON
object as the local layer instead of reading .lithe/run/local.json. It merges
configurations by stable ID using this precedence:
local.json > configurations.json > generated.json. Scalars and arrays are
replaced by the higher layer, while toolchain maps merge by key. It returns
effective configurations, their source, the team default, structured
diagnostics for stale, orphaned, missing, disabled, and toolchain mismatch
states, the effective global toolchain, and the machine-local
localToolchains document. Toolchain diagnostics carry the affected run
configuration ID when a requirement is consumed by one or more configurations;
requirements with no configuration consumer do not emit a blocking diagnostic.
A process detector declares a runtime binding only when that command genuinely
consumes the runtime. npm, pnpm, and Yarn scripts consume project-node; Bun
scripts keep their independent bun command and do not acquire a Node
requirement. Go, Python, Cargo, and Gradle remain command-based until every host
provides the corresponding configurable runtime registry, so their PATH-based
launch behavior is not blocked by an unavailable platform selector.
During resolution, Core also reconciles npm, pnpm, and Yarn commands from older
v2 generated documents with the same project-node binding. This compatibility
normalization is based on the effective command, does not mutate the stored
document, and keeps legacy hybrid projects scoped without requiring regeneration.
A document-level toolchain
object in the local layer (e.g.
{ "java": { "homePath": ... }, "maven": { "executablePath": ..., "javaHomePath": ... } })
provides defaults for every configuration's extensions.java.*. A non-empty
per-configuration toolchain path overrides the corresponding project default.
runConfig.updateOptions and runConfig.createUserConfiguration are pure
document transformations. They validate scope, paths, supported types, stable
IDs, main classes, modules, and argument parsing, then return UTF-8 JSON in the
document field. The platform adapter selects the target project or local
file and performs the atomic write. These commands never write files. An empty
workingDirectory removes the layer's cwd override. Optional
mavenSkipTests writes extensions.maven.skipTests; omission removes the
override so the project Maven context is inherited, while explicit false
continues to run tests even when the project default skips them.
For project-scoped option updates, selected toolchain paths must resolve inside
root and are persisted with /-separated project-relative paths. Local-scoped
updates may carry host absolute paths. runConfig.updateOptions and
runConfig.inspect accept the same optional localDocument override.
When updateOptions carries a toolchain object (javaHomePath,
mavenExecutablePath, mavenJavaHomePath), it writes the document-level
global toolchain into the local layer instead of patching a configuration;
project scope rejects this payload because toolchain paths are machine-local.
runConfig.saveEditorChanges accepts the normal option-edit payload plus the
required toolchain object. In addition to Java and Maven paths, that object
may contain runtimeExecutablePaths, keyed by stable generic toolchain ID. It
applies the global toolchain and configuration override edits together,
returning localDocument, either a projectDocument string or null, and
either a toolchainDocument string or null. The latter updates
.lithe/toolchains/local.json, preserves unrelated toolchain IDs, and removes
an entry when its supplied executable path is empty. Local scope combines the
run-option edits in the local run document. Project scope returns the local
defaults and team options as separate fully prepared documents; the platform
adapter writes all returned documents as one transaction with rollback. Empty
per-configuration toolchain paths remove the
corresponding override keys while preserving unrelated extension fields.
Platform clients report the editor save as successful only after the written
documents resolve again. Failures identify whether preparation, document
writing, or post-save reload failed; a reload failure keeps the last usable UI
snapshot and states that the documents were already saved.
Automatic runtime discovery produces an effective executable path for the
current session. Platforms use that same path both to construct
toolchainCandidates and to resolve the launch command. An automatic path is
not a persisted user selection and is written to .lithe/toolchains/local.json
only after an explicit editor save.
On Windows, Node-backed commands resolve their package-manager shim from the
selected Node installation. They do not fall back to a PATH shim from another
installation, and Windows executable extensions take precedence over extensionless
shell scripts.
runConfig.createLaunchPlan accepts root, configurationId, optional
currentFile and classPath, optional debugPort, and optional
localDocument. Maven-backed Run and Debug callers may also supply the same
versioned mavenContext accepted by maven.launchPlan. Explicit profiles in
the resolved Run Configuration replace the context profiles; otherwise the
project profiles are inherited. Explicit extensions.maven.skipTests and
cwd values also replace the context values. Core applies the shared settings,
module, Skip Tests, and reactor-working-directory rules to the generated
framework or Java-main arguments without adding tool-window-only -am. It
returns a toolchain
reference, argument array, project-relative working directory, and structured
environment references. It does not return a shell command or platform
executable path. All project paths use /, reject absolute paths and ..
traversal, and remain relative to root. A java.main configuration without a
Maven toolchain launches through project-jdk and the configuration's Java
source path only when that source has no Maven ancestor. An older configuration
that omitted the Maven binding is rejected with an instruction to regenerate.
java.codeVision accepts a workspace root, a target Java path, and Java source
paths. It returns declaration locations and usage counts; Git blame attribution
is joined by the UI from the shared Git result. java.className accepts Java
source text and a file simple name and returns the fully qualified runtime class
name.
java.sourceDefinition accepts source, declarationName, and an optional
memberName, returning zero-based line and UTF-16 utf16Column or null
when no declaration is found.
java.structure accepts Java source and optional declarationSources. It
returns foldRegions, inlayHints, and
syntaxHighlights. Line numbers are zero-based because these values are editor
offsets; UTF-16 columns and hidden ranges match the native text editor coordinate
system. Syntax highlights contain document-relative utf16Start,
utf16Length, and a role from the shared editor syntax-theme contract. They
are sorted and non-overlapping, so native renderers can apply semantic colors
without maintaining another Java parser. The parser is platform-independent
and does not start a Java process or contact JDT.
spring.index accepts root, workspace-relative paths, optional trusted
absolute metadataRepositories (and the legacy singular metadataRepository),
optional textOverrides keyed by relative path, and
refreshDependencyMetadata. The command reads Spring configuration
metadata from workspace JSON files and dependency JARs, indexes application
configuration documents and Java source, and returns deterministically ordered
properties, values, propertyReferences, diagnostics, beans,
injections, and endpoints collections. Locations use relative paths and
one-based lines and columns.
properties include type, documentation, default value, and an optional Java
declaration. values include profile/override state and an optional declaration
target. propertyReferences represent Java @Value uses. Bean resolution
accounts for component names, @Bean aliases, interfaces, @Qualifier,
@Resource, @Primary, field injection, and constructor injection. Endpoint
entries expand multiple controller/method paths and retain the exact declared
HTTP method set.
Dependency metadata is cached in the Rust process. Project-open indexing sets
refreshDependencyMetadata to true; debounced unsaved-buffer indexing leaves
it false, so editing Java or configuration files does not repeatedly traverse
and open the local dependency repository. The repository path is selected by
the platform composition layer and is never persisted in shared results.