@octocodeai/octocode-engine 16.6.2 → 16.7.0

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.
@@ -1,15 +1,33 @@
1
1
  import { normalizeCommandName } from './commandUtils.js';
2
- const REDOS_TIMEOUT_MS = 50;
3
- const REDOS_TEST_INPUT = 'a'.repeat(100);
2
+ const REDOS_TIMEOUT_MS = 200;
3
+ // Use multiple input lengths to catch ReDoS patterns that only blow up at larger sizes.
4
+ const REDOS_TEST_INPUTS = [
5
+ 'a'.repeat(50),
6
+ 'a'.repeat(500),
7
+ 'a'.repeat(2000),
8
+ ];
4
9
  function isReDoSSafe(regex) {
5
- const start = performance.now();
6
- try {
7
- regex.test(REDOS_TEST_INPUT);
8
- }
9
- catch {
10
- return false;
10
+ // Timing heuristic — catches obvious exponential backtracking but cannot provide
11
+ // structural guarantees. Tests multiple input sizes to catch patterns that only
12
+ // blow up at larger scales. A proper linear-time checker (e.g., re2) would be
13
+ // the gold standard but adds a native dependency.
14
+ for (const input of REDOS_TEST_INPUTS) {
15
+ regex.lastIndex = 0;
16
+ const start = performance.now();
17
+ try {
18
+ regex.test(input);
19
+ }
20
+ catch {
21
+ return false;
22
+ }
23
+ finally {
24
+ regex.lastIndex = 0;
25
+ }
26
+ if (performance.now() - start >= REDOS_TIMEOUT_MS) {
27
+ return false;
28
+ }
11
29
  }
12
- return performance.now() - start < REDOS_TIMEOUT_MS;
30
+ return true;
13
31
  }
14
32
  export class SecurityRegistry {
15
33
  _extraSecretPatterns = [];
@@ -1,3 +1,3 @@
1
- export declare const ALLOWED_COMMANDS: readonly ["rg", "ls", "find", "grep", "git"];
1
+ export declare const ALLOWED_COMMANDS: readonly ["rg", "ls", "find", "grep", "git", "file", "zcat", "gunzip", "bzcat", "xzcat", "zstdcat", "zstd", "lz4cat", "brotli", "lzfse", "tar", "unzip", "bsdtar", "7z", "7zz"];
2
2
  export declare const DANGEROUS_PATTERNS: readonly [RegExp, RegExp, RegExp];
3
3
  export declare const PATTERN_DANGEROUS_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp];
@@ -1,4 +1,25 @@
1
- export const ALLOWED_COMMANDS = ['rg', 'ls', 'find', 'grep', 'git'];
1
+ export const ALLOWED_COMMANDS = [
2
+ 'rg',
3
+ 'ls',
4
+ 'find',
5
+ 'grep',
6
+ 'git',
7
+ 'file',
8
+ 'zcat',
9
+ 'gunzip',
10
+ 'bzcat',
11
+ 'xzcat',
12
+ 'zstdcat',
13
+ 'zstd',
14
+ 'lz4cat',
15
+ 'brotli',
16
+ 'lzfse',
17
+ 'tar',
18
+ 'unzip',
19
+ 'bsdtar',
20
+ '7z',
21
+ '7zz',
22
+ ];
2
23
  export const DANGEROUS_PATTERNS = [
3
24
  /[;&|`$(){}[\]<>]/, // Shell metacharacters
4
25
  /\${/,
@@ -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
@@ -943,8 +944,13 @@ export interface StructuralQueryExplanation {
943
944
 
944
945
  export interface StructuralDetailedMatch extends StructuralMatch {
945
946
  id: string
947
+ /** tree-sitter `kind` of the matched node (e.g. `call_expression`). */
946
948
  nodeKind?: string
947
- confidence: 'exact-ast' | 'partial-ast' | 'fallback-text' | string
949
+ /**
950
+ * The octo matcher is a precise AST matcher, so every match is an exact
951
+ * tree-sitter node match — there is no partial/fallback tier.
952
+ */
953
+ confidence: 'exact-ast'
948
954
  }
949
955
 
950
956
  export interface StructuralSearchDetailedResult {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octocodeai/octocode-engine",
3
- "version": "16.6.2",
3
+ "version": "16.7.0",
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",
@@ -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": "16.7.0",
267
+ "@octocodeai/octocode-engine-darwin-x64": "16.7.0",
268
+ "@octocodeai/octocode-engine-linux-arm64-gnu": "16.7.0",
269
+ "@octocodeai/octocode-engine-linux-x64-gnu": "16.7.0",
270
+ "@octocodeai/octocode-engine-linux-x64-musl": "16.7.0",
271
+ "@octocodeai/octocode-engine-win32-x64-msvc": "16.7.0"
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",
@@ -332,7 +335,7 @@
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"