@octocodeai/octocode-engine 16.6.2 → 17.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,13 +13,24 @@ export function configureSecurity(deps) {
13
13
  function createErrorResult(text) {
14
14
  return { content: [{ type: 'text', text }], isError: true };
15
15
  }
16
- function withToolTimeout(toolName, promise, signal, timeoutMs) {
16
+ // Combine an external caller-provided AbortSignal with an internal one so that
17
+ // aborting either cancels the tool. Returns the single source unchanged when
18
+ // only one (or none) is present, preserving existing behavior.
19
+ function mergeAbortSignals(external, internal) {
20
+ if (!external)
21
+ return internal;
22
+ if (!internal)
23
+ return external;
24
+ return AbortSignal.any([external, internal]);
25
+ }
26
+ function withToolTimeout(toolName, promise, signal, timeoutMs, onTimeout) {
17
27
  const timeout = getTimeoutMs(timeoutMs);
18
28
  if (signal?.aborted) {
19
29
  return Promise.resolve(createErrorResult(`Tool '${toolName}' was cancelled before execution.`));
20
30
  }
21
31
  return new Promise(resolve => {
22
32
  const timer = setTimeout(() => {
33
+ onTimeout?.();
23
34
  resolve(createErrorResult(`Tool '${toolName}' timed out after ${timeout / 1000}s. Try reducing query complexity or scope.`));
24
35
  }, timeout);
25
36
  const onAbort = () => {
@@ -50,7 +61,7 @@ function withToolTimeout(toolName, promise, signal, timeoutMs) {
50
61
  });
51
62
  }
52
63
  async function runSecure(opts) {
53
- const { toolName, handler, args, authInfo, sessionId, signal, timeoutMs } = opts;
64
+ const { toolName, handler, args, authInfo, sessionId, signal, timeoutMs, onTimeout, abortSignal, } = opts;
54
65
  try {
55
66
  const sanitizer = getSanitizer();
56
67
  const validation = sanitizer.validateInputParameters(args);
@@ -58,7 +69,8 @@ async function runSecure(opts) {
58
69
  return createErrorResult(`Security validation failed: ${validation.warnings.join('; ')}`);
59
70
  }
60
71
  const sanitizedParams = validation.sanitizedParams;
61
- const rawResult = await withToolTimeout(toolName, handler(sanitizedParams, authInfo, sessionId), signal, timeoutMs);
72
+ const mergedSignal = mergeAbortSignals(signal, abortSignal);
73
+ const rawResult = await withToolTimeout(toolName, handler(sanitizedParams, authInfo, sessionId), mergedSignal, timeoutMs, onTimeout);
62
74
  return rawResult;
63
75
  }
64
76
  catch (error) {
@@ -0,0 +1,258 @@
1
+ # LSP in Octocode — Lifecycle, Provisioning, and the No-Fallback Contract
2
+
3
+ Companion: `docs/context/LSP_GUIDE.md` (protocol primer + platformized resolution ladder).
4
+
5
+ ---
6
+
7
+ ## Two layers, two different jobs
8
+
9
+ | Layer | What it is | Speed | Answers |
10
+ |---|---|---|---|
11
+ | **Tree-sitter / OXC** | parser compiled into the engine | sub-ms/file | *"what does this code look like?"* — outlines, shapes, calls, imports (`structural/`, `signatures/`, `grammar.rs`) |
12
+ | **LSP** | a real language server, spawned over stdio | cold 1–120s, warm <100ms | *"what does this symbol mean?"* — cross-file definition identity, all references, call/type graph (`lsp/client.rs`, `manager.ts`, tools-core `semantic_content/execution.ts`) |
13
+
14
+ These are **not** interchangeable. Tree-sitter cannot resolve a symbol across files, infer a type, or follow an import — that needs a language server.
15
+
16
+ ## The no-fallback contract (the rule that matters)
17
+
18
+ When a semantic operation needs a language server and **no server is available**, octocode **throws** — it does *not* fabricate a syntactic or same-file approximation. A faked answer is worse than an honest failure, because the calling agent would trust it.
19
+
20
+ - The thrown error is the standard typed envelope: `status:"error"`, `errorCode:"lspServerUnavailable"`. In bulk it lands under `errors[]`.
21
+ - The message names the language, says no server is available, gives the install hint, and **directs the agent to `localSearchCode` (text/structural search) + `localGetFileContent`** instead.
22
+ - octocode never returns a same-file-only `references` result, or a tree-sitter guess, dressed up as a semantic answer.
23
+
24
+ **Throws when no server:** `definition`, `references`, `hover`, `callers`, `callees`, `callHierarchy`, `typeDefinition`, `implementation`, `workspaceSymbol`, `supertypes`, `subtypes`, `diagnostic`.
25
+
26
+ **Never throws (genuine tree-sitter features, server-free):** `documentSymbols` (native OXC for JS/TS, Markdown heading outline, or LSP when present) and structural/AST search via `localSearchCode`. These are real syntactic capabilities, not LSP stand-ins. `documentSymbols` only throws for a non-JS/TS language with no server *and* no outline.
27
+
28
+ > A server that *is* running but lacks a capability, or returns zero results, still yields an honest *empty* (`unsupportedOperation` / `noReferences` / …) — that is an accurate answer ("none"), not a missing-server failure.
29
+
30
+ ## Provisioning — three classes (how a server becomes available)
31
+
32
+ octocode maximizes the chance a real server answers via the resolution ladder (override → PATH → bundled → ecosystem discovery → managed cache; see `LSP_GUIDE.md` §13). Use `npx octocode lsp-server list` to see all servers and their current status, `npx octocode lsp-server status <file>` to check resolution for a specific file, and `npx octocode lsp-server install <name>` to trigger an auto-download. Servers fall into:
33
+
34
+ - **Bundled (npm dep, offline)** — pure-JS servers launched with the current Node; zero install. TS/JS, Python (pyright), Shell (bash-language-server), PHP (intelephense), YAML, JSON/HTML/CSS.
35
+ - **Auto-download (managed cache)** — portable single-binary servers fetched from a pinned release into `~/.octocode/lsp/<server>/<tag>/` (prompt-by-default, SHA-verified). rust-analyzer (all platforms), clangd (no linux-arm64 asset). Set `OCTOCODE_LSP_AUTO_INSTALL=auto` to skip the prompt or `=off` to disable downloads entirely.
36
+ - **Detect-and-instruct (host toolchain)** — need a runtime octocode won't auto-install: gopls (Go), jdtls (JDK 21+), sourcekit-lsp (Xcode/CLI tools on macOS), csharp-ls (.NET SDK). The status/hint tells you how to install; semantic ops throw until you do.
37
+
38
+ ## Supported language servers
39
+
40
+ Scope is the **main languages**. Niche/long-tail servers were intentionally removed from
41
+ LSP routing (their tree-sitter grammars remain for structural/AST search). A file type not
42
+ listed below has no server config: semantic ops return `lspServerUnavailable` and the agent
43
+ falls back to text search.
44
+
45
+ | Language | Extensions | Server | Provisioning |
46
+ |---|---|---|---|
47
+ | TypeScript / JS (+ TSX/JSX) | `.ts .mts .cts .tsx .js .mjs .cjs .jsx` | typescript-language-server (`tsgo`/override aware) | **bundled** |
48
+ | Python | `.py .pyi` | pyright (`pylsp` via override) | **bundled** |
49
+ | Shell | `.sh` | bash-language-server | **bundled** |
50
+ | PHP | `.php` | intelephense | **bundled** |
51
+ | YAML | `.yaml .yml` | yaml-language-server | **bundled** |
52
+ | JSON | `.json .jsonc` | vscode-json-language-server | **bundled** |
53
+ | HTML | `.html .htm` | vscode-html-language-server | **bundled** |
54
+ | CSS / SCSS / LESS | `.css .scss .less` | vscode-css-language-server | **bundled** |
55
+ | Rust | `.rs` | rust-analyzer | **auto-download** |
56
+ | C / C++ | `.c .h .cpp .cc .cxx .hpp` | clangd | **auto-download** (no linux-arm64 asset) |
57
+ | Go | `.go` | gopls | **detect-and-instruct** (needs Go toolchain) |
58
+ | Java | `.java` | jdtls | **detect-and-instruct** (needs JDK 21+) |
59
+ | Swift | `.swift` | sourcekit-lsp | **detect-and-instruct** (needs Xcode or `xcode-select --install`) |
60
+ | C# | `.cs` | csharp-ls | **detect-and-instruct** (needs .NET SDK + `dotnet tool install -g csharp-ls`) |
61
+ | SQL | `.sql` | sqls | **PATH / override only** |
62
+
63
+ Any built-in server can be overridden with `OCTOCODE_<LANG>_SERVER_PATH` or `.octocode/lsp-servers.json`.
64
+ PATH/override-only servers resolve only if already on `PATH` / in an ecosystem dir; otherwise
65
+ semantic ops throw with an install hint.
66
+
67
+ **Removed from LSP routing** (use text/structural search instead): TOML, Ruby, Kotlin,
68
+ Elixir, Terraform, Lua, Proto, OCaml, Zig, Julia, Erlang, R, GDScript. Their tree-sitter
69
+ grammars stay available for `localSearchCode` structural/AST queries.
70
+
71
+ ### Custom / bring-your-own LSP (any language)
72
+
73
+ A language with **no built-in spec** (e.g. Scala, Kotlin, Ruby) gets full semantic support by
74
+ registering a server in a JSON config — no rebuild, no code change. This is also how you swap a
75
+ built-in server for a different one. Resolution reads, in order (`config.rs::user_config_paths`):
76
+
77
+ 1. `$OCTOCODE_LSP_CONFIG` (explicit file path)
78
+ 2. `<workspace>/.octocode/lsp-servers.json` (per-project, checked in or local)
79
+ 3. `~/.octocode/lsp-servers.json` (per-user, all projects)
80
+
81
+ The file maps a **file extension** to a launch spec. A custom entry takes precedence over the
82
+ built-in spec for that extension:
83
+
84
+ ```jsonc
85
+ // .octocode/lsp-servers.json — register Scala (metals)
86
+ {
87
+ "languageServers": {
88
+ ".scala": { "command": "metals", "args": ["stdio"], "languageId": "scala" },
89
+ ".sc": { "command": "metals", "args": ["stdio"], "languageId": "scala" }
90
+ }
91
+ }
92
+ ```
93
+
94
+ | Field | Required | Meaning |
95
+ |---|---|---|
96
+ | `command` | yes | Executable name (resolved on `PATH`) or absolute path. Shell wrappers are rejected. |
97
+ | `languageId` | yes | LSP `languageId` sent on `textDocument/didOpen` (e.g. `scala`, `ruby`). |
98
+ | `args` | no | Launch args (default `[]`). |
99
+ | `initializationOptions` | no | Passed verbatim in the LSP `initialize` request. |
100
+
101
+ With the config present, semantic ops (`definition`, `references`, `hover`, call hierarchy, …)
102
+ work for that language exactly like a built-in one. **Without it, the extension stays unsupported:
103
+ the engine resolves no server and the no-fallback contract applies** — semantic ops throw
104
+ `lspServerUnavailable` and the agent falls back to `localSearchCode` + `localGetFileContent`.
105
+ Both halves of this contract are asserted by the benchmark (`benchmark/lsp/check-lsp.mjs`,
106
+ "Custom LSP — bring-your-own server (Scala / metals)"), and verified **live against a real
107
+ server** by `benchmark/lsp/check-custom-lsp.mjs` (`yarn lsp:custom`) — which registers
108
+ `bash-language-server` (a language with no built-in spec) and runs real `documentSymbols` /
109
+ `references` / `hover` through it.
110
+
111
+ ### Markup & docs: what's LSP vs minify
112
+
113
+ - **HTML / CSS / SCSS / LESS / JSON / YAML are LSP** — served by the bundled
114
+ `vscode-*-language-server` / `yaml-language-server` (markup/data, offline-ready). They're
115
+ not "code" languages but they do have real language servers.
116
+ - **Markdown / MDX are NOT LSP.** They are handled by the **minifier** using heading-section
117
+ heuristics (ATX `#`/`##` and setext headings → `minify/strategies/markdown.rs`; `md`,
118
+ `markdown`, `mdx` all map to the markdown strategy in `minify/config.rs`). There is no
119
+ markdown language server in octocode — `documentSymbols` on a `.md` file uses the native
120
+ heading-outline path, and structure/compression comes from the minifier, not a server.
121
+
122
+ ## Full format support matrix
123
+
124
+ Per-extension capabilities across all four axes — minify strategy, structural AST,
125
+ signature outline, and LSP server — machine-generated from the shipped napi binary.
126
+ The LSP column here is the per-extension view of the [Supported language servers](#supported-language-servers)
127
+ table above.
128
+
129
+ <!-- BEGIN GENERATED: support-matrix (yarn matrix:check --write) — do not edit between these markers -->
130
+
131
+ _Generated by `yarn matrix:check --write` (benchmark/check-matrix.mjs) — every cell probed live against the shipped napi binary. Do not edit between the markers; run `yarn matrix:check` to re-verify._
132
+
133
+ > **Note:** The matrix queries the native layer (`config.rs`) only. Two servers injected at the TS layer (`config.ts`) are not reflected: **bash-language-server** for `.sh` (languageId `shellscript`) and **intelephense** for `.php` (the native spec emits `intelephense` as command — both bundled and active). Their LSP cells show `—` in the matrix below but the servers are bundled and resolve correctly (`npx octocode lsp-server status <file>` will show `resolved: bundled`).
134
+
135
+ **151 extensions** known to the engine — 61 with structural AST, 47 with a signature outline, 32 with an LSP server, 90 minify-only.
136
+
137
+ ### Rich formats — AST + signature + LSP
138
+
139
+ Extensions with a wired tree-sitter grammar (and, where configured, a language server). The minify column is the configured strategy.
140
+
141
+ | Extension | Minify | Structural AST | Signature outline | LSP (server → language-id) |
142
+ |-----------|--------|:--------------:|-------------------|----------------------------|
143
+ | `.bash` | `conservative` | ✅ | ✅ tree-sitter | — |
144
+ | `.c` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `c` |
145
+ | `.cc` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `cpp` |
146
+ | `.cjs` | `terser` | ✅ | ✅ tree-sitter | `typescript-language-server` → `javascript` |
147
+ | `.cpp` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `cpp` |
148
+ | `.cs` | `conservative` | ✅ | ✅ tree-sitter | `csharp-ls` → `csharp` |
149
+ | `.css` | `aggressive` | ✅ | — | `vscode-css-language-server` → `css` |
150
+ | `.cts` | `conservative` | ✅ | ✅ tree-sitter | `typescript-language-server` → `typescript` |
151
+ | `.cxx` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `cpp` |
152
+ | `.erl` | `aggressive` | ✅ | ✅ tree-sitter | — |
153
+ | `.ex` | `aggressive` | ✅ | ✅ tree-sitter | — |
154
+ | `.exs` | `aggressive` | ✅ | ✅ tree-sitter | — |
155
+ | `.gemspec` | `conservative` | ✅ | ✅ tree-sitter | — |
156
+ | `.go` | `conservative` | ✅ | ✅ tree-sitter | `gopls` → `go` |
157
+ | `.h` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `c` |
158
+ | `.hcl` | `conservative` | ✅ | ✅ tree-sitter | — |
159
+ | `.hh` | `conservative` | ✅ | ✅ tree-sitter | — |
160
+ | `.hpp` | `conservative` | ✅ | ✅ tree-sitter | `clangd` → `cpp` |
161
+ | `.hrl` | `aggressive` | ✅ | ✅ tree-sitter | — |
162
+ | `.htm` | `aggressive` | ✅ | — | `vscode-html-language-server` → `html` |
163
+ | `.html` | `aggressive` | ✅ | — | `vscode-html-language-server` → `html` |
164
+ | `.hxx` | `conservative` | ✅ | ✅ tree-sitter | — |
165
+ | `.java` | `conservative` | ✅ | ✅ tree-sitter | `jdtls` → `java` |
166
+ | `.jl` | `conservative` | ✅ | — | — |
167
+ | `.js` | `terser` | ✅ | ✅ tree-sitter | `typescript-language-server` → `javascript` |
168
+ | `.json` | `json` | ✅ | — | `vscode-json-language-server` → `json` |
169
+ | `.jsonc` | `json` | ✅ | — | `vscode-json-language-server` → `json` |
170
+ | `.jsx` | `terser` | ✅ | ✅ tree-sitter | `typescript-language-server` → `javascriptreact` |
171
+ | `.kt` | `conservative` | ✅ | ✅ tree-sitter | — |
172
+ | `.kts` | `conservative` | ✅ | ✅ tree-sitter | — |
173
+ | `.less` | `aggressive` | ✅ | — | `vscode-css-language-server` → `less` |
174
+ | `.lua` | `aggressive` | ✅ | ✅ tree-sitter | — |
175
+ | `.mjs` | `terser` | ✅ | ✅ tree-sitter | `typescript-language-server` → `javascript` |
176
+ | `.ml` | `conservative` | ✅ | — | — |
177
+ | `.mli` | `conservative` | ✅ | — | — |
178
+ | `.mts` | `conservative` | ✅ | ✅ tree-sitter | `typescript-language-server` → `typescript` |
179
+ | `.php` | `conservative` | ✅ | ✅ tree-sitter | `intelephense` → `php` |
180
+ | `.proto` | `conservative` | ✅ | ✅ tree-sitter | — |
181
+ | `.py` | `conservative` | ✅ | ✅ tree-sitter | `pylsp` → `python` |
182
+ | `.pyi` | `conservative` | ✅ | ✅ tree-sitter | `pylsp` → `python` |
183
+ | `.r` | `aggressive` | ✅ | ✅ tree-sitter | — |
184
+ | `.rake` | `conservative` | ✅ | ✅ tree-sitter | — |
185
+ | `.rb` | `conservative` | ✅ | ✅ tree-sitter | — |
186
+ | `.rs` | `conservative` | ✅ | ✅ tree-sitter | `rust-analyzer` → `rust` |
187
+ | `.ru` | `conservative` | ✅ | ✅ tree-sitter | — |
188
+ | `.sbt` | — | ✅ | ✅ tree-sitter | — |
189
+ | `.sc` | — | ✅ | ✅ tree-sitter | — |
190
+ | `.scala` | `conservative` | ✅ | ✅ tree-sitter | — |
191
+ | `.scss` | `aggressive` | ✅ | — | `vscode-css-language-server` → `scss` |
192
+ | `.sh` | `conservative` | ✅ | ✅ tree-sitter | — |
193
+ | `.sql` | `conservative` | ✅ | — | `sqls` → `sql` |
194
+ | `.swift` | `conservative` | ✅ | ✅ tree-sitter | `sourcekit-lsp` → `swift` |
195
+ | `.tf` | `conservative` | ✅ | ✅ tree-sitter | — |
196
+ | `.tfvars` | `conservative` | ✅ | ✅ tree-sitter | — |
197
+ | `.toml` | `conservative` | ✅ | — | — |
198
+ | `.ts` | `conservative` | ✅ | ✅ tree-sitter | `typescript-language-server` → `typescript` |
199
+ | `.tsx` | `conservative` | ✅ | ✅ tree-sitter | `typescript-language-server` → `typescriptreact` |
200
+ | `.yaml` | `conservative` | ✅ | — | `yaml-language-server` → `yaml` |
201
+ | `.yml` | `conservative` | ✅ | — | `yaml-language-server` → `yaml` |
202
+ | `.zig` | `conservative` | ✅ | ✅ tree-sitter | — |
203
+ | `.zsh` | `conservative` | ✅ | ✅ tree-sitter | — |
204
+
205
+ Notes:
206
+ - **Signature outline is tree-sitter only** — markup/style/config grammars (HTML/CSS/SCSS/LESS/Scala/JSON/YAML/TOML) parse for structural `rule` queries but have no function body, so no skeleton. There is **no** regex/heuristic fallback.
207
+ - **`.jsx`** resolves the LSP server as `javascriptreact` (to enable JSX) even though its tree-sitter grammar registry id is `javascript` (shared JS grammar). `.tsx` has its own grammar, so both ids are `typescriptreact`.
208
+ - **`.hh` / `.hxx`** have the C++ grammar + signatures but **no clangd server config** (only `.cpp/.cc/.cxx/.hpp` are mapped).
209
+ - **C/C++**: structural `rule` queries (e.g. `kind: call_expression`) work fully; a bare call-shaped `pattern` can hit tree-sitter's declaration-vs-call ambiguity — prefer a `rule` with `kind`. JS/TS also have a native (oxc) symbol/in-file-reference path that needs **no server installed**.
210
+
211
+ ### Minify-only formats
212
+
213
+ Native comment/whitespace stripping; no AST/LSP. (90 extensions, grouped by strategy.)
214
+
215
+ **`aggressive`** (18): `clj` `cljs` `ejs` `erb` `handlebars` `hbs` `jinja` `jinja2` `mustache` `pl` `pm` `svelte` `svg` `twig` `vue` `xml` `xsl` `xslt`
216
+
217
+ **`conservative`** (66): `adb` `ads` `asm` `awk` `bzl` `cfg` `cmake` `coffee` `conf` `config` `csv` `dart` `dockerignore` `elm` `env` `f` `f03` `f08` `f90` `f95` `fish` `for` `fs` `fsx` `gitignore` `gql` `gradle` `graphql` `groovy` `haml` `hs` `ini` `jade` `kotlin` `lhs` `lisp` `lsp` `mm` `nasm` `nim` `nix` `pas` `perl` `plsql` `pp` `properties` `ps1` `psd1` `psm1` `pug` `rkt` `rst` `rust` `sass` `scm` `slim` `star` `styl` `tsql` `v` `vb` `vbs` `vhd` `vhdl` `wast` `wat`
218
+
219
+ **`general`** (2): `log` `txt`
220
+
221
+ **`json`** (1): `json5`
222
+
223
+ **`markdown`** (3): `markdown` `md` `mdx`
224
+
225
+ ### Verify
226
+
227
+ ```bash
228
+ yarn matrix:check # this matrix, live
229
+ yarn ast:check # structural search + signatures on real samples
230
+ yarn lsp:check # language-id + server resolution + native semantics
231
+ yarn lsp:live # spawn a real server, exercise every LSP operation type
232
+ yarn minify:check # minifier over every configured format
233
+ yarn benchmark # all of the above
234
+ ```
235
+
236
+ <!-- END GENERATED: support-matrix -->
237
+
238
+ ## Lifecycle — pool, cold start, indexing
239
+
240
+ - **Pool** (`lspClientPool.ts`): one warm `LSPClient` per (server × workspace), 60s idle timeout (`OCTOCODE_LSP_POOL_IDLE_MS`). A long-lived MCP session reuses warm servers across tool calls; one-shot CLI invocations don't share a pool.
241
+ - **Cold start / indexing**: a server reads the project and builds its model before answering correctly. Costs vary — typescript-language-server <1s, gopls 3–15s, rust-analyzer 5–60s (multiple `$/progress` waves), jdtls 30–120s.
242
+ - **Readiness** (`manager.ts` + `json_rpc.rs`): for servers that emit `$/progress` (go, rust, java, csharp, swift) the pool factory calls `waitForReady` with a per-language cap before the first query; servers without `$/progress` (TS/JS, Python, clangd, data formats) skip the wait to avoid a fixed 2s settle penalty.
243
+ - **Spawn gate**: every resolved command passes `validateLSPServerPath` (rejects shell wrappers / nonexistent / non-executable) in `LSPClient.start()` before the process is spawned.
244
+ - **Discovery caching** (`serverDiscovery.ts`): ecosystem-dir lookup results are memoised per `(command, workspaceRoot)` for the process lifetime. Ecosystem dirs are pre-filtered to existing ones once, cutting stat calls from ~15-per-server to ~5. Call `clearDiscoveryCache()` (or restart) after installing a server mid-session.
245
+
246
+ ## Open / future (non-blocking)
247
+
248
+ Progress streaming to the caller (`lsp.indexingStatus`), an opt-in `waitForIndexingMs`, tree-sitter symbol extraction for compiled-language `documentSymbols`, and a SCIP precomputed index — all deferred; none change the no-fallback contract above.
249
+
250
+ > **Known issue — cold `references` under-reports.** On a one-shot CLI invocation,
251
+ > `references` (and other project-wide ops) can return incomplete results
252
+ > *labelled* `complete=true`, because `typescript-language-server` emits no
253
+ > `$/progress` and the project isn't indexed yet when queried. This is the one
254
+ > behaviour that can make an agent wrong rather than just slow — analysis and the
255
+ > proposed fix (honest `complete=false` + cross-check hint, with an opt-in
256
+ > `waitForIndexingMs`) are in
257
+ > [`LSP_REFERENCES_INDEXING_RFC.md`](https://github.com/bgauryy/octocode/blob/main/docs/context/LSP_REFERENCES_INDEXING_RFC.md). Meanwhile,
258
+ > prefer `--op callers` or `localSearchCode` to confirm "who uses this".
package/index.d.ts CHANGED
@@ -4,7 +4,8 @@ export declare class NativeLspClient {
4
4
  constructor(config: JsLanguageServerConfig)
5
5
  start(): Promise<void>
6
6
  stop(): Promise<void>
7
- waitForReady(timeoutMs?: number | undefined | null): Promise<void>
7
+ /** Readiness after post-`initialized` indexing: `progressIdle` = a `$/progress` cycle drained to idle; `settledFallback` = no progress seen, only the settle window elapsed; `timeout` = progress still active at the deadline. */
8
+ waitForReady(timeoutMs?: number | undefined | null): Promise<'progressIdle' | 'settledFallback' | 'timeout'>
8
9
  hasCapability(capability: string): boolean
9
10
  /** Server-selected LSP `positionEncoding` (utf-16 unless the server is non-conformant); null if omitted/not started. */
10
11
  positionEncoding(): string | null
@@ -44,94 +45,6 @@ export declare function applyContentViewMinification(content: string, filePath:
44
45
  */
45
46
  export declare function applyMinification(content: string, filePath: string): string
46
47
 
47
- /**
48
- * Structural inspection of a binary (executable / object / archive) file.
49
- *
50
- * `format`/`arch`/`bits`/`endianness`/`stripped` are always populated (the
51
- * fields the old `identify` mode produced). The list fields are populated only
52
- * when the file is a recognized executable format `goblin` could parse; for an
53
- * unrecognized file they stay empty and `notes` explains why (e.g. "use
54
- * mode=list/decompress for archives").
55
- *
56
- * Every list is capped (see `crate::binary::inspect::LIST_CAP`); the `*_count`
57
- * fields carry the true totals, and `truncated` is set when any list was cut.
58
- */
59
- export interface BinaryInspectInfo {
60
- /** elf | macho | macho-fat | pe | coff | archive | wasm | unknown */
61
- format: string
62
- /** Human-readable one-line summary (drop-in for `file -b`). */
63
- description: string
64
- /** Space-separated hex of the leading bytes (drop-in for `xxd -p -l 32`). */
65
- magicHex: string
66
- arch?: string
67
- bits?: number
68
- /** "little" | "big" */
69
- endianness?: string
70
- stripped?: boolean
71
- /** Hex entry-point address, when the format has one. */
72
- entry?: string
73
- symbols: Array<string>
74
- imports: Array<string>
75
- exports: Array<string>
76
- sections: Array<string>
77
- /** Dynamic dependencies / needed shared libraries. */
78
- libraries: Array<string>
79
- symbolCount: number
80
- importCount: number
81
- exportCount: number
82
- /** True when any list was capped. */
83
- truncated: boolean
84
- /** Advisory notes (unrecognized format, parse degradation, size truncation). */
85
- notes: Array<string>
86
- }
87
-
88
- /** Result of a strings extraction pass over a binary buffer. */
89
- export interface BinaryStrings {
90
- /**
91
- * Printable runs, longest-first. ASCII **and** UTF-16 (LE/BE) — the win
92
- * over GNU `strings -a`, which misses wide strings. Each entry is prefixed
93
- * with its hex byte offset when offsets were requested.
94
- */
95
- strings: Array<string>
96
- /** Total runs found before any display capping. */
97
- totalFound: number
98
- /**
99
- * True when more of the file remains to scan beyond this window — follow
100
- * `next_scan_offset`. Lossless continuation cursor, **not** a data-loss
101
- * flag (the old fixed-cap meaning): every byte is reachable by paging.
102
- */
103
- truncated: boolean
104
- /**
105
- * Absolute byte offset to start the next scan window, or `None` at EOF.
106
- * Rewound to a safe break so no string is split across windows.
107
- */
108
- nextScanOffset?: number
109
- }
110
-
111
- /**
112
- * Native strings extraction. Recovers printable ASCII **and** UTF-16 (LE/BE)
113
- * runs of at least `min_length` from the scan window of `path` beginning at
114
- * `scan_offset`, longest-first, optionally hex offset-prefixed. Replaces the
115
- * `strings` shell-out and additionally surfaces the wide strings GNU
116
- * `strings -a` misses.
117
- *
118
- * Lossless pagination: the returned `nextScanOffset` (when set) is the absolute
119
- * byte offset of the next window, rewound to a safe break so no string is split
120
- * across windows. Pass `scanOffset = 0` for the first window.
121
- */
122
- export declare function extractBinaryStringsNative(path: string, minLength: number, includeOffsets: boolean, scanOffset: number): BinaryStrings
123
-
124
- /**
125
- * Native binary inspection (format lane). Parses `path` as an executable /
126
- * object / archive and returns its identity plus — for recognized executable
127
- * formats — symbols, imports, exports, sections and dynamic dependencies.
128
- *
129
- * Replaces the `file` + `xxd` shell-outs. Never throws on malformed input: a
130
- * parse failure degrades to magic-byte identity with an explanatory note. The
131
- * only `Err` cases are unreadable / oversized files.
132
- */
133
- export declare function inspectBinaryNative(path: string): BinaryInspectInfo
134
-
135
48
  /** Extract a byte-range substring from `content`. */
136
49
  export declare function byteSliceContent(content: string, byteStart: number, byteEnd: number): string
137
50
 
@@ -442,6 +355,19 @@ export interface FilterPatchOptions {
442
355
  contextLines?: number
443
356
  }
444
357
 
358
+ /**
359
+ * Myers line diff (`oldText` → `newText`). Returns a full edit script of
360
+ * `{ opType, line }` ops (`same` | `add` | `remove`). Prefer this over an
361
+ * O(N·M) LCS for agent edit previews on mid/large files.
362
+ */
363
+ export declare function computeLineDiff(oldText: string, newText: string): Array<LineDiffOp>
364
+
365
+ export interface LineDiffOp {
366
+ /** `"same"` | `"add"` | `"remove"` */
367
+ opType: string
368
+ line: string
369
+ }
370
+
445
371
  /** Convert a `file://` URI string back to an absolute filesystem path. */
446
372
  export declare function fromUri(uri: string): string
447
373
 
@@ -489,8 +415,10 @@ export declare const SUPPORTED_STRUCTURAL_EXTENSIONS: readonly string[]
489
415
  *
490
416
  * Char offsets match JavaScript `string.substring()` — pass them directly to
491
417
  * JavaScript string slicing without conversion.
418
+ *
419
+ * Runs on libuv's worker pool (tree-sitter parse) — returns a Promise.
492
420
  */
493
- export declare function getSemanticBoundaryOffsets(content: string, filePath: string): Array<number>
421
+ export declare function getSemanticBoundaryOffsets(content: string, filePath: string): Promise<Array<number>>
494
422
 
495
423
  /**
496
424
  * Returns all extensions that have signature extraction support
@@ -943,8 +871,13 @@ export interface StructuralQueryExplanation {
943
871
 
944
872
  export interface StructuralDetailedMatch extends StructuralMatch {
945
873
  id: string
874
+ /** tree-sitter `kind` of the matched node (e.g. `call_expression`). */
946
875
  nodeKind?: string
947
- confidence: 'exact-ast' | 'partial-ast' | 'fallback-text' | string
876
+ /**
877
+ * The octo matcher is a precise AST matcher, so every match is an exact
878
+ * tree-sitter node match — there is no partial/fallback tier.
879
+ */
880
+ confidence: 'exact-ast'
948
881
  }
949
882
 
950
883
  export interface StructuralSearchDetailedResult {
@@ -973,8 +906,10 @@ export interface StructuralSearchDetailedResult {
973
906
  * (exactly one). Returns node ranges (1-based lines, ready as `lineHint`s)
974
907
  * plus captured metavariables. Throws on unsupported extension, invalid
975
908
  * pattern/rule, or both/neither query supplied.
909
+ *
910
+ * Runs on libuv's worker pool (tree-sitter parse) — returns a Promise.
976
911
  */
977
- export declare function structuralSearch(content: string, filePath: string, pattern?: string | undefined | null, rule?: string | undefined | null): Array<StructuralMatch>
912
+ export declare function structuralSearch(content: string, filePath: string, pattern?: string | undefined | null, rule?: string | undefined | null): Promise<Array<StructuralMatch>>
978
913
 
979
914
  /**
980
915
  * Detailed structural search. Unsupported extensions and invalid queries return
@@ -987,7 +922,11 @@ export interface StructuralSearchFileResult {
987
922
  matches: Array<StructuralMatch>
988
923
  }
989
924
 
990
- export declare function structuralSearchFiles(options: StructuralSearchFilesOptions): StructuralSearchFilesResult
925
+ /**
926
+ * Runs on libuv's worker pool — the directory walk + per-file parse is
927
+ * CPU/IO-bound and can span thousands of files. Returns a Promise.
928
+ */
929
+ export declare function structuralSearchFiles(options: StructuralSearchFilesOptions): Promise<StructuralSearchFilesResult>
991
930
 
992
931
  export declare function structuralSearchFilesDetailed(options: StructuralSearchFilesOptions): StructuralSearchFilesDetailedResult
993
932
 
@@ -996,7 +935,25 @@ export interface StructuralSearchFilesOptions {
996
935
  pattern?: string
997
936
  rule?: string
998
937
  include?: Array<string>
938
+ /**
939
+ * File-path globs to skip (gitignore-style, e.g. `"*.min.js"`, `"src/gen/**"`).
940
+ * Mirrors `localSearchCode.exclude` so OQL `scope.exclude` is honored on the
941
+ * structural lane — previously silently dropped (typed-contract violation).
942
+ */
943
+ exclude?: Array<string>
999
944
  excludeDir?: Array<string>
945
+ /**
946
+ * Include hidden (dot) files. `None` preserves the default walker behavior
947
+ * (hidden ignored); `Some(true)` forces them in.
948
+ */
949
+ hidden?: boolean
950
+ /**
951
+ * Bypass `.gitignore`/`.ignore` rules. `None` preserves defaults; `Some(true)`
952
+ * searches files normally hidden by ignore files (mirrors `localSearchCode.noIgnore`).
953
+ */
954
+ noIgnore?: boolean
955
+ /** Maximum directory descent depth (0 = just the root). `None` = unbounded. */
956
+ maxDepth?: number
1000
957
  maxFiles?: number
1001
958
  maxFileBytes?: number
1002
959
  }
@@ -1006,6 +963,13 @@ export interface StructuralSearchFilesResult {
1006
963
  totalMatches: number
1007
964
  parsedFiles: number
1008
965
  skippedByPreFilter: number
966
+ /**
967
+ * Candidate files whose extension has no grammar — not evaluated, hence
968
+ * not proof of absence. Mirrors the detailed result's counter so the two
969
+ * shapes agree and the warning text can't collapse unevaluated into
970
+ * anchor-absent.
971
+ */
972
+ skippedUnsupported: number
1009
973
  skippedUnreadable: number
1010
974
  skippedLarge: number
1011
975
  warnings: Array<string>
package/index.js CHANGED
@@ -42,6 +42,7 @@ export const structuralSearchFiles = nativeBinding.structuralSearchFiles
42
42
  export const structuralSearchFilesDetailed = nativeBinding.structuralSearchFilesDetailed
43
43
  export const getSupportedStructuralExtensions = nativeBinding.getSupportedStructuralExtensions
44
44
  export const getSemanticBoundaryOffsets = nativeBinding.getSemanticBoundaryOffsets
45
+
45
46
  export const getSupportedSignatureExtensions = nativeBinding.getSupportedSignatureExtensions
46
47
  export const jsonToYamlString = nativeBinding.jsonToYamlString
47
48
  export const getMINIFY_CONFIG = nativeBinding.getMINIFY_CONFIG
@@ -59,9 +60,8 @@ export const byteSliceContent = nativeBinding.byteSliceContent
59
60
  export const sliceContent = nativeBinding.sliceContent
60
61
  export const extractMatchingLines = nativeBinding.extractMatchingLines
61
62
  export const filterPatch = nativeBinding.filterPatch
63
+ export const computeLineDiff = nativeBinding.computeLineDiff
62
64
  export const PatchLineType = nativeBinding.PatchLineType
63
- export const inspectBinaryNative = nativeBinding.inspectBinaryNative
64
- export const extractBinaryStringsNative = nativeBinding.extractBinaryStringsNative
65
65
  export const NativeLspClient = nativeBinding.NativeLspClient
66
66
  export const resolvePosition = nativeBinding.resolvePosition
67
67
  export const resolvePositionFromContent = nativeBinding.resolvePositionFromContent
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octocodeai/octocode-engine",
3
- "version": "16.6.2",
3
+ "version": "17.0.1",
4
4
  "description": "Rust native Octocode engine for context compression, structural search, and LSP semantic navigation",
5
5
  "type": "module",
6
6
  "main": "index.cjs",
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "https://github.com/bgauryy/octocode-mcp.git",
15
+ "url": "https://github.com/bgauryy/octocode.git",
16
16
  "directory": "packages/octocode-engine"
17
17
  },
18
18
  "exports": {
@@ -256,18 +256,19 @@
256
256
  },
257
257
  "files": [
258
258
  "dist",
259
+ "docs/**",
259
260
  "index.cjs",
260
261
  "index.js",
261
262
  "index.d.ts",
262
263
  "README.md"
263
264
  ],
264
265
  "optionalDependencies": {
265
- "@octocodeai/octocode-engine-darwin-arm64": "16.6.2",
266
- "@octocodeai/octocode-engine-darwin-x64": "16.6.2",
267
- "@octocodeai/octocode-engine-linux-arm64-gnu": "16.6.2",
268
- "@octocodeai/octocode-engine-linux-x64-gnu": "16.6.2",
269
- "@octocodeai/octocode-engine-linux-x64-musl": "16.6.2",
270
- "@octocodeai/octocode-engine-win32-x64-msvc": "16.6.2"
266
+ "@octocodeai/octocode-engine-darwin-arm64": "17.0.1",
267
+ "@octocodeai/octocode-engine-darwin-x64": "17.0.1",
268
+ "@octocodeai/octocode-engine-linux-arm64-gnu": "17.0.1",
269
+ "@octocodeai/octocode-engine-linux-x64-gnu": "17.0.1",
270
+ "@octocodeai/octocode-engine-linux-x64-musl": "17.0.1",
271
+ "@octocodeai/octocode-engine-win32-x64-msvc": "17.0.1"
271
272
  },
272
273
  "engines": {
273
274
  "node": ">=20.0.0"
@@ -302,8 +303,10 @@
302
303
  "platforms:check": "node scripts/check-platform-binaries.cjs",
303
304
  "version:check": "node scripts/check-version-consistency.cjs",
304
305
  "version:sync": "node scripts/sync-versions.cjs",
306
+ "manifest:gen": "node scripts/gen-server-manifest.mjs",
307
+ "verify:manifest": "node scripts/gen-server-manifest.mjs && git diff --exit-code src/lsp/serverManifestData.ts || (echo 'ERROR: src/lsp/serverManifestData.ts is out of sync with serverManifest.json — run yarn manifest:gen and commit the result' && exit 1)",
305
308
  "loader:check": "node scripts/check-loader-entries.cjs",
306
- "prepublish:verify": "yarn version:check && yarn loader:check && yarn pack:check && yarn platforms:check",
309
+ "prepublish:verify": "yarn version:check && yarn loader:check && yarn verify:manifest && yarn pack:check && yarn platforms:check",
307
310
  "binary-size:check": "node scripts/check-binary-size.cjs",
308
311
  "ast:check": "yarn workspace @octocodeai/octocode-benchmark ast:check",
309
312
  "lsp:check": "yarn workspace @octocodeai/octocode-benchmark lsp:check",
@@ -316,7 +319,7 @@
316
319
  "check:rust": "cargo check --all-targets --all-features",
317
320
  "fmt:rust": "cargo fmt --all",
318
321
  "fmt:rust:check": "cargo fmt --all -- --check",
319
- "test:rust": "cargo test --all-targets --all-features",
322
+ "test:rust": "cargo test --lib --tests --all-features && cargo build --benches --all-features",
320
323
  "test:node": "vitest run",
321
324
  "test:node:coverage": "vitest run --coverage",
322
325
  "test:watch": "vitest watch",
@@ -332,12 +335,13 @@
332
335
  "audit:rust": "cargo audit -D unmaintained -D unsound -D yanked",
333
336
  "audit": "yarn audit:rust",
334
337
  "verify:rust": "yarn check:rust && yarn fmt:rust:check && yarn lint:rust && yarn test:rust && yarn deps:rust && yarn udeps:rust && yarn audit:rust",
335
- "verify": "yarn verify:patterns && yarn typecheck && yarn verify:rust && yarn test && yarn benchmark && yarn pack:check && yarn version:check && yarn loader:check && yarn binary-size:check",
338
+ "verify": "yarn verify:patterns && yarn verify:manifest && yarn typecheck && yarn verify:rust && yarn test && yarn benchmark && yarn pack:check && yarn version:check && yarn loader:check && yarn binary-size:check",
336
339
  "readme:sync": "node ../../scripts/sync-package-readmes.mjs .",
337
340
  "prepack": "yarn readme:sync",
338
341
  "prepublishOnly": "yarn version:sync"
339
342
  },
340
343
  "dependencies": {
344
+ "@octocodeai/config": "17.0.0",
341
345
  "bash-language-server": "^5.6.0",
342
346
  "intelephense": "^1.14.1",
343
347
  "pyright": "^1.1.411",