@opticlm/connector 2.1.0 → 2.3.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.
- package/README.md +11 -38
- package/dist/index.d.ts +65 -41
- package/dist/index.js +1 -6
- package/dist/{capabilities-CQHv2shL.d.ts → interfaces-B9NK_71f.d.ts} +67 -1
- package/dist/lsp/index.d.ts +4 -2
- package/dist/lsp/index.js +3 -3
- package/dist/mcp/index.d.ts +7 -51
- package/dist/mcp/index.js +8 -8
- package/dist/pipe/index.d.ts +43 -0
- package/dist/pipe/index.js +6 -0
- package/dist/schemas.d.ts +54 -0
- package/dist/schemas.js +2 -0
- package/package.json +6 -1
- package/dist/resolver-Cg62Av4_.d.ts +0 -129
package/README.md
CHANGED
|
@@ -115,6 +115,12 @@ Navigate to the definition of a symbol.
|
|
|
115
115
|
|
|
116
116
|
Find all references to a symbol.
|
|
117
117
|
|
|
118
|
+
### `find_file_references`
|
|
119
|
+
|
|
120
|
+
Find all references to a file across the workspace (e.g., all files that import or link to the given file).
|
|
121
|
+
|
|
122
|
+
Only registered when your `ReferencesProvider` implements the optional `provideFileReferences` method.
|
|
123
|
+
|
|
118
124
|
### `call_hierarchy`
|
|
119
125
|
|
|
120
126
|
Get call hierarchy for a function or method.
|
|
@@ -175,23 +181,6 @@ install(server, definitionProvider, {
|
|
|
175
181
|
})
|
|
176
182
|
```
|
|
177
183
|
|
|
178
|
-
### Callback reference
|
|
179
|
-
|
|
180
|
-
| Provider | Tool | Input callback | Output callback |
|
|
181
|
-
|----------|------|----------------|-----------------|
|
|
182
|
-
| `EditProvider` | `apply_edit` | `onEditInput` | `onEditOutput` |
|
|
183
|
-
| `DefinitionProvider` | `goto_definition` | `onDefinitionInput` | `onDefinitionOutput` |
|
|
184
|
-
| `DefinitionProvider` | `goto_type_definition` | `onTypeDefinitionInput` | `onTypeDefinitionOutput` |
|
|
185
|
-
| `ReferencesProvider` | `find_references` | `onReferencesInput` | `onReferencesOutput` |
|
|
186
|
-
| `HierarchyProvider` | `call_hierarchy` | `onCallHierarchyInput` | `onCallHierarchyOutput` |
|
|
187
|
-
| `GlobalFindProvider` | `global_find` | `onGlobalFindInput` | `onGlobalFindOutput` |
|
|
188
|
-
| `GraphProvider` | `get_link_structure` | — | `onLinkStructureOutput` |
|
|
189
|
-
| `GraphProvider` | `add_link` | `onAddLinkInput` | `onAddLinkOutput` |
|
|
190
|
-
| `FrontmatterProvider` | `get_frontmatter_structure` | `onFrontmatterStructureInput` | `onFrontmatterStructureOutput` |
|
|
191
|
-
| `FrontmatterProvider` | `set_frontmatter` | `onSetFrontmatterInput` | `onSetFrontmatterOutput` |
|
|
192
|
-
|
|
193
|
-
`get_link_structure` has no `onInput` since its input schema is empty. Resource-only providers (`FileAccessProvider`, `DiagnosticsProvider`, `OutlineProvider`) have no callbacks.
|
|
194
|
-
|
|
195
184
|
## MCP Resources
|
|
196
185
|
|
|
197
186
|
The SDK automatically registers resources based on which providers you install:
|
|
@@ -354,16 +343,13 @@ When the MCP server runs in a separate process from the IDE plugin (e.g., spawne
|
|
|
354
343
|
**IDE plugin side** — expose providers:
|
|
355
344
|
|
|
356
345
|
```typescript
|
|
357
|
-
import { servePipe } from '@opticlm/connector'
|
|
346
|
+
import { servePipe } from '@opticlm/connector/pipe'
|
|
358
347
|
|
|
359
348
|
const server = await servePipe({
|
|
360
349
|
pipeName: 'my-ide-lsp',
|
|
361
350
|
fileAccess: { /* ... */ },
|
|
362
351
|
definition: { /* ... */ },
|
|
363
|
-
|
|
364
|
-
provideDiagnostics: async (uri) => { /* ... */ },
|
|
365
|
-
onDiagnosticsChanged: (callback) => { /* ... */ },
|
|
366
|
-
},
|
|
352
|
+
// ...
|
|
367
353
|
// Add only the providers your IDE supports
|
|
368
354
|
})
|
|
369
355
|
// server.pipePath — the resolved pipe path
|
|
@@ -375,7 +361,7 @@ const server = await servePipe({
|
|
|
375
361
|
|
|
376
362
|
```typescript
|
|
377
363
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
378
|
-
import { connectPipe } from '@opticlm/connector'
|
|
364
|
+
import { connectPipe } from '@opticlm/connector/pipe'
|
|
379
365
|
import { install } from '@opticlm/connector/mcp'
|
|
380
366
|
|
|
381
367
|
const conn = await connectPipe({
|
|
@@ -392,8 +378,7 @@ const mcpServer = new McpServer({ name: 'my-mcp', version: '1.0.0' })
|
|
|
392
378
|
if (conn.fileAccess) install(mcpServer, conn.fileAccess)
|
|
393
379
|
if (conn.definition && conn.fileAccess)
|
|
394
380
|
install(mcpServer, conn.definition, { fileAccess: conn.fileAccess })
|
|
395
|
-
|
|
396
|
-
install(mcpServer, conn.diagnostics, { fileAccess: conn.fileAccess })
|
|
381
|
+
// ...
|
|
397
382
|
// Install whichever proxy providers are available
|
|
398
383
|
|
|
399
384
|
// When done:
|
|
@@ -434,13 +419,6 @@ const fileAccess = {
|
|
|
434
419
|
// Providers are automatically created based on server capabilities
|
|
435
420
|
install(server, fileAccess)
|
|
436
421
|
if (lsp.definition) install(server, lsp.definition, { fileAccess })
|
|
437
|
-
if (lsp.references) install(server, lsp.references, { fileAccess })
|
|
438
|
-
if (lsp.hierarchy) install(server, lsp.hierarchy, { fileAccess })
|
|
439
|
-
if (lsp.outline) install(server, lsp.outline, { fileAccess })
|
|
440
|
-
install(server, {
|
|
441
|
-
...lsp.diagnostics,
|
|
442
|
-
onDiagnosticsChanged: lsp.onDiagnosticsChanged,
|
|
443
|
-
}, { fileAccess })
|
|
444
422
|
|
|
445
423
|
const transport = new StdioServerTransport()
|
|
446
424
|
await server.connect(transport)
|
|
@@ -464,12 +442,7 @@ interface LspClientOptions {
|
|
|
464
442
|
### How It Works
|
|
465
443
|
|
|
466
444
|
1. `start()` spawns the LSP server process and performs the initialize/initialized handshake
|
|
467
|
-
2. The server's `ServerCapabilities` response determines which providers are created
|
|
468
|
-
- `definitionProvider` → `lsp.definition`
|
|
469
|
-
- `referencesProvider` → `lsp.references`
|
|
470
|
-
- `callHierarchyProvider` → `lsp.hierarchy`
|
|
471
|
-
- `documentSymbolProvider` → `lsp.outline`
|
|
472
|
-
- Diagnostics are always available (via `textDocument/publishDiagnostics` notifications)
|
|
445
|
+
2. The server's `ServerCapabilities` response determines which providers are created
|
|
473
446
|
3. Providers that the server does not support remain `undefined`
|
|
474
447
|
4. Documents are automatically opened/closed with the server on demand, with an idle timeout for cleanup
|
|
475
448
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,46 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { C as CodeSnippet,
|
|
3
|
-
import { F as FileAccessProvider, E as EditProvider } from './resolver-Cg62Av4_.js';
|
|
4
|
-
export { R as ResolverConfig, S as SymbolResolutionError, a as SymbolResolver } from './resolver-Cg62Av4_.js';
|
|
1
|
+
import { F as FileAccessProvider, U as UnifiedUri, l as FuzzyPosition, j as ExactPosition } from './interfaces-B9NK_71f.js';
|
|
2
|
+
export { C as CodeSnippet, D as DefinitionProvider, h as Diagnostic, g as DiagnosticSeverity, a as DiagnosticsProvider, k as DiskRange, i as DocumentSymbol, m as EditFailureReason, E as EditProvider, d as EditResult, n as Frontmatter, e as FrontmatterMatch, c as FrontmatterProvider, o as FrontmatterValue, f as GlobalFindMatch, p as GlobalFindOptions, G as GlobalFindProvider, b as GraphProvider, H as HierarchyProvider, L as Link, q as OnDiagnosticsChangedCallback, O as OutlineProvider, P as PendingEditOperation, R as ReferencesProvider, S as SymbolKind, T as TextEdit } from './interfaces-B9NK_71f.js';
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
readonly edit?: EditProvider;
|
|
13
|
-
readonly definition?: DefinitionProvider;
|
|
14
|
-
readonly references?: ReferencesProvider;
|
|
15
|
-
readonly hierarchy?: HierarchyProvider;
|
|
16
|
-
readonly diagnostics?: DiagnosticsProvider;
|
|
17
|
-
readonly outline?: OutlineProvider;
|
|
18
|
-
readonly globalFind?: GlobalFindProvider;
|
|
19
|
-
readonly graph?: GraphProvider;
|
|
20
|
-
readonly frontmatter?: FrontmatterProvider;
|
|
21
|
-
readonly availableMethods: string[];
|
|
22
|
-
disconnect(): void;
|
|
23
|
-
}
|
|
24
|
-
declare function connectPipe(options: connectPipeOptions): Promise<LspPipeConnection>;
|
|
4
|
+
/**
|
|
5
|
+
* Smart Resolver Logic for MCP LSP Driver SDK
|
|
6
|
+
*
|
|
7
|
+
* This is the internal engine of the SDK. It translates the LLM's imprecise
|
|
8
|
+
* instructions into precise coordinates.
|
|
9
|
+
*/
|
|
25
10
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Configuration options for the SymbolResolver.
|
|
13
|
+
*/
|
|
14
|
+
interface ResolverConfig {
|
|
15
|
+
/**
|
|
16
|
+
* Number of lines to scan above and below the lineHint if the symbol
|
|
17
|
+
* is not found at the exact line. Defaults to 2.
|
|
18
|
+
*/
|
|
19
|
+
lineSearchRadius?: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Error thrown when a symbol cannot be resolved.
|
|
23
|
+
*/
|
|
24
|
+
declare class SymbolResolutionError extends Error {
|
|
25
|
+
readonly symbolName: string;
|
|
26
|
+
readonly lineHint: number;
|
|
27
|
+
readonly reason: string;
|
|
28
|
+
constructor(symbolName: string, lineHint: number, reason: string);
|
|
38
29
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
30
|
+
/**
|
|
31
|
+
* The SymbolResolver translates fuzzy positions (as provided by an LLM)
|
|
32
|
+
* into exact positions that can be used by the IDE.
|
|
33
|
+
*
|
|
34
|
+
* Algorithm:
|
|
35
|
+
* 1. Read file content via FileAccessProvider.
|
|
36
|
+
* 2. Split content into lines.
|
|
37
|
+
* 3. Target the lineHint (converting 1-based to 0-based).
|
|
38
|
+
* 4. Search for symbolName in that line.
|
|
39
|
+
* - If orderHint is 0, find first occurrence.
|
|
40
|
+
* - If orderHint is N, find Nth occurrence.
|
|
41
|
+
* 5. Robustness Fallback:
|
|
42
|
+
* - If the line is empty or symbol not found at lineHint,
|
|
43
|
+
* scan +/- lineSearchRadius lines to handle minor line shifts.
|
|
44
|
+
* 6. Return ExactPosition (line, character start index).
|
|
45
|
+
*/
|
|
46
|
+
declare class SymbolResolver {
|
|
47
|
+
private readonly fs;
|
|
48
|
+
private readonly lineSearchRadius;
|
|
49
|
+
constructor(fs: FileAccessProvider, config?: ResolverConfig);
|
|
50
|
+
/**
|
|
51
|
+
* Resolves a fuzzy position to an exact position.
|
|
52
|
+
*
|
|
53
|
+
* @param uri - The URI of the file
|
|
54
|
+
* @param fuzzy - The fuzzy position provided by the LLM
|
|
55
|
+
* @returns The exact position in the file
|
|
56
|
+
* @throws SymbolResolutionError if the symbol cannot be found
|
|
57
|
+
*/
|
|
58
|
+
resolvePosition(uri: UnifiedUri, fuzzy: FuzzyPosition): Promise<ExactPosition>;
|
|
59
|
+
/**
|
|
60
|
+
* Finds the Nth occurrence of a symbol in a line.
|
|
61
|
+
*
|
|
62
|
+
* @param line - The line to search in (may be undefined if out of bounds)
|
|
63
|
+
* @param symbolName - The symbol to find
|
|
64
|
+
* @param orderHint - Which occurrence to find (0-based)
|
|
65
|
+
* @returns The character offset of the symbol, or null if not found
|
|
66
|
+
*/
|
|
67
|
+
private findSymbolInLine;
|
|
43
68
|
}
|
|
44
|
-
declare function servePipe(options: servePipeOptions): Promise<LspPipeServer>;
|
|
45
69
|
|
|
46
|
-
export {
|
|
70
|
+
export { ExactPosition, FileAccessProvider, FuzzyPosition, type ResolverConfig, SymbolResolutionError, SymbolResolver, UnifiedUri };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1 @@
|
|
|
1
|
-
|
|
2
|
-
`);})}sendNotification(r,e){if(this.destroyed)return;let i={type:"notification",method:r,params:e};this.socket.write(`${JSON.stringify(i)}
|
|
3
|
-
`);}destroy(){this.destroyed||(this.destroyed=true,this.rejectAll(new Error("Transport destroyed")),this.socket.destroy());}handleData(r){this.buffer+=r.toString();let e=this.buffer.split(`
|
|
4
|
-
`);this.buffer=e.pop()??"";for(let i of e){let o=i.replace(/\r$/,"");if(o)try{let n=JSON.parse(o);this.dispatch(n);}catch{}}}dispatch(r){switch(r.type){case "response":{let e=this.pending.get(r.id);e&&(this.pending.delete(r.id),r.error?e.reject(new Error(r.error.message)):e.resolve(r.result));break}case "request":{this.requestHandler&&this.requestHandler(r.method,r.params).then(e=>{if(this.destroyed)return;let i={type:"response",id:r.id,result:e};this.socket.write(`${JSON.stringify(i)}
|
|
5
|
-
`);}).catch(e=>{if(this.destroyed)return;let i={type:"response",id:r.id,error:{message:e instanceof Error?e.message:String(e)}};this.socket.write(`${JSON.stringify(i)}
|
|
6
|
-
`);});break}case "notification":{this.notificationHandler?.(r.method,r.params);break}}}rejectAll(r){for(let[,e]of this.pending)e.reject(r);this.pending.clear();}};function w(l){return process.platform==="win32"?`\\\\.\\pipe\\${l}`:`/tmp/${l}.sock`}var b=[{providerKey:"fileAccess",methods:["readFile","readDirectory"]},{providerKey:"edit",methods:["applyEdits","previewAndApplyEdits"]},{providerKey:"definition",methods:["provideDefinition"]},{providerKey:"references",methods:["provideReferences"]},{providerKey:"hierarchy",methods:["provideCallHierarchy"]},{providerKey:"diagnostics",methods:["provideDiagnostics","getWorkspaceDiagnostics"]},{providerKey:"outline",methods:["provideDocumentSymbols"]},{providerKey:"globalFind",methods:["globalFind"]},{providerKey:"graph",methods:["getLinkStructure","resolveOutlinks","resolveBacklinks","addLink"]},{providerKey:"frontmatter",methods:["getFrontmatterStructure","getFrontmatter","setFrontmatter"]}];function C(l){let{pipeName:r,connectTimeout:e=5e3}=l,i=w(r);return new Promise((o,n)=>{let a=connect(i),t=false,d=setTimeout(()=>{t||(t=true,a.destroy(),n(new Error(`Connection timeout after ${e}ms`)));},e);a.on("error",s=>{t||(t=true,clearTimeout(d),n(s));}),a.on("connect",()=>{if(clearTimeout(d),t)return;let s,p,c=new v(a,{onNotification:(u,m)=>{u==="onDiagnosticsChanged"&&s&&s(m[0]),u==="onFileChanged"&&p&&p(m[0]);}});c.sendRequest("_handshake",[]).then(u=>{if(t)return;let g=u.methods,f={};for(let{providerKey:P,methods:D}of b){let R=D.filter(h=>g.includes(`${P}.${h}`));if(R.length===0)continue;let y={};for(let h of R)y[h]=(...E)=>c.sendRequest(`${P}.${h}`,E);P==="diagnostics"&&g.includes("onDiagnosticsChanged")&&(y.onDiagnosticsChanged=h=>{s=h;}),P==="fileAccess"&&g.includes("onFileChanged")&&(y.onFileChanged=h=>{p=h;}),f[P]=y;}t=true,o({fileAccess:f.fileAccess,edit:f.edit,definition:f.definition,references:f.references,hierarchy:f.hierarchy,diagnostics:f.diagnostics,outline:f.outline,globalFind:f.globalFind,graph:f.graph,frontmatter:f.frontmatter,availableMethods:g,disconnect(){c.destroy();}});}).catch(u=>{t||(t=true,c.destroy(),n(u instanceof Error?u:new Error(String(u))));});});})}function A(l){let{pipeName:r}=l,e=w(r),i=new Map;for(let{providerKey:t,methods:d}of b){let s=l[t];if(s)for(let p of d){let c=s[p];if(typeof c=="function"){let u=`${t}.${p}`;i.set(u,(...m)=>c.apply(s,m));}}}let o=[...i.keys()];l.diagnostics?.onDiagnosticsChanged&&o.push("onDiagnosticsChanged"),l.fileAccess.onFileChanged&&o.push("onFileChanged");let n=new Set,a=createServer(t=>{let d=new v(t,{onRequest:async(s,p)=>{if(s==="_handshake")return {methods:o};let c=i.get(s);if(!c)throw new Error(`Unknown method: ${s}`);return c(...p)}});n.add(d),t.on("close",()=>n.delete(d));});return l.diagnostics?.onDiagnosticsChanged&&l.diagnostics.onDiagnosticsChanged(t=>{for(let d of n)d.sendNotification("onDiagnosticsChanged",[t]);}),l.fileAccess.onFileChanged&&l.fileAccess.onFileChanged(t=>{for(let d of n)d.sendNotification("onFileChanged",[t]);}),new Promise((t,d)=>{if(process.platform!=="win32")try{unlinkSync(e);}catch{}a.on("error",d),a.listen(e,()=>{a.removeListener("error",d),t({get pipePath(){return e},get connectionCount(){return n.size},async close(){for(let s of n)s.destroy();if(n.clear(),await new Promise((s,p)=>{a.close(c=>{c?p(c):s();});}),process.platform!=="win32")try{unlinkSync(e);}catch{}}});});})}var k=class extends Error{constructor(e,i,o){super(`Could not find symbol '${e}' at or near line ${i}. ${o}`);this.symbolName=e;this.lineHint=i;this.reason=o;this.name="SymbolResolutionError";}},F=class{constructor(r,e){this.fs=r;this.lineSearchRadius=e?.lineSearchRadius??2;}lineSearchRadius;async resolvePosition(r,e){let o=(await this.fs.readFile(r)).split(/\r?\n/),n=e.lineHint-1,a=e.orderHint??0,t=this.findSymbolInLine(o[n],e.symbolName,a);if(t!==null)return {line:n,character:t};for(let d=1;d<=this.lineSearchRadius;d++){let s=n-d;if(s>=0){let c=this.findSymbolInLine(o[s],e.symbolName,a);if(c!==null)return {line:s,character:c}}let p=n+d;if(p<o.length){let c=this.findSymbolInLine(o[p],e.symbolName,a);if(c!==null)return {line:p,character:c}}}throw new k(e.symbolName,e.lineHint,`Please verify the file content and try again. Searched lines ${Math.max(1,e.lineHint-this.lineSearchRadius)} to ${Math.min(o.length,e.lineHint+this.lineSearchRadius)}.`)}findSymbolInLine(r,e,i){if(r===void 0||r.length===0)return null;let o=0,n=0;for(;o<r.length;){let a=r.indexOf(e,o);if(a===-1)break;if(n===i)return a;n++,o=a+1;}return null}};export{k as SymbolResolutionError,F as SymbolResolver,C as connectPipe,A as servePipe};
|
|
1
|
+
var a=class extends Error{constructor(e,s,i){super(`Could not find symbol '${e}' at or near line ${s}. ${i}`);this.symbolName=e;this.lineHint=s;this.reason=i;this.name="SymbolResolutionError";}},u=class{constructor(n,e){this.fs=n;this.lineSearchRadius=e?.lineSearchRadius??2;}lineSearchRadius;async resolvePosition(n,e){let i=(await this.fs.readFile(n)).split(/\r?\n/),r=e.lineHint-1,t=e.orderHint??0,m=this.findSymbolInLine(i[r],e.symbolName,t);if(m!==null)return {line:r,character:m};for(let l=1;l<=this.lineSearchRadius;l++){let c=r-l;if(c>=0){let o=this.findSymbolInLine(i[c],e.symbolName,t);if(o!==null)return {line:c,character:o}}let d=r+l;if(d<i.length){let o=this.findSymbolInLine(i[d],e.symbolName,t);if(o!==null)return {line:d,character:o}}}throw new a(e.symbolName,e.lineHint,`Please verify the file content and try again. Searched lines ${Math.max(1,e.lineHint-this.lineSearchRadius)} to ${Math.min(i.length,e.lineHint+this.lineSearchRadius)}.`)}findSymbolInLine(n,e,s){if(n===void 0||n.length===0)return null;let i=0,r=0;for(;i<n.length;){let t=n.indexOf(e,i);if(t===-1)break;if(r===s)return t;r++,i=t+1;}return null}};export{a as SymbolResolutionError,u as SymbolResolver};
|
|
@@ -211,6 +211,12 @@ interface ReferencesProvider {
|
|
|
211
211
|
* @returns Array of code snippets representing reference locations
|
|
212
212
|
*/
|
|
213
213
|
provideReferences(uri: UnifiedUri, position: ExactPosition): Promise<CodeSnippet[]>;
|
|
214
|
+
/**
|
|
215
|
+
* Finds all references to the given file.
|
|
216
|
+
*
|
|
217
|
+
* @param uri - The URI of the file
|
|
218
|
+
*/
|
|
219
|
+
provideFileReferences?(uri: UnifiedUri): Promise<CodeSnippet[]>;
|
|
214
220
|
}
|
|
215
221
|
/**
|
|
216
222
|
* Provides call hierarchy functionality.
|
|
@@ -376,4 +382,64 @@ interface FrontmatterProvider {
|
|
|
376
382
|
*/
|
|
377
383
|
type OnDiagnosticsChangedCallback = (uri: UnifiedUri) => void;
|
|
378
384
|
|
|
379
|
-
|
|
385
|
+
/**
|
|
386
|
+
* Infrastructure Interfaces for MCP LSP Driver SDK
|
|
387
|
+
*
|
|
388
|
+
* The SDK consumer (Plugin Developer) must implement these interfaces
|
|
389
|
+
* to bridge the SDK to the specific IDE.
|
|
390
|
+
*/
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Provides access to the file system for reading files.
|
|
394
|
+
* Since the SDK is responsible for resolving FuzzyPosition to ExactPosition,
|
|
395
|
+
* it needs direct access to read files from the disk.
|
|
396
|
+
*/
|
|
397
|
+
interface FileAccessProvider {
|
|
398
|
+
/**
|
|
399
|
+
* Reads the content of a file from the disk (ignoring unsaved IDE buffers).
|
|
400
|
+
* Used for symbol resolution and context retrieval.
|
|
401
|
+
*
|
|
402
|
+
* @param uri - The URI of the file to read
|
|
403
|
+
* @returns The content of the file as a string
|
|
404
|
+
* @throws Error if the file cannot be read
|
|
405
|
+
*/
|
|
406
|
+
readFile(uri: UnifiedUri): Promise<string>;
|
|
407
|
+
/**
|
|
408
|
+
* Read children in a directory, exluding git-ignored files, similar to Unix `ls` command
|
|
409
|
+
* @param relativePath - The path to the folder to read
|
|
410
|
+
* @returns Array of file/folder paths in the directory
|
|
411
|
+
*/
|
|
412
|
+
readDirectory(relativePath: UnifiedUri): Promise<string[]>;
|
|
413
|
+
/**
|
|
414
|
+
* Called by the driver to register a callback for file changes.
|
|
415
|
+
* When provided, the files:// resources become subscribable.
|
|
416
|
+
* The plugin should call the registered callback whenever a file changes.
|
|
417
|
+
*
|
|
418
|
+
* @param callback - The callback to invoke when a file changes
|
|
419
|
+
*/
|
|
420
|
+
onFileChanged?(callback: (uri: UnifiedUri) => void): void;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Provides edit capabilities for applying changes to files.
|
|
424
|
+
* At least one of the methods must be implemented.
|
|
425
|
+
*/
|
|
426
|
+
interface EditProvider {
|
|
427
|
+
/**
|
|
428
|
+
* Applies edits directly without user interaction.
|
|
429
|
+
* Use this when user approval is handled elsewhere or not needed.
|
|
430
|
+
*
|
|
431
|
+
* @param operation - The pending edit operation to apply
|
|
432
|
+
* @returns true if applied successfully, false if failed
|
|
433
|
+
*/
|
|
434
|
+
applyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
435
|
+
/**
|
|
436
|
+
* Displays a diff view or a confirmation dialog in the IDE.
|
|
437
|
+
* The user decides whether to apply the edits or discard them.
|
|
438
|
+
*
|
|
439
|
+
* @param operation - The pending edit operation to preview
|
|
440
|
+
* @returns true if applied, false if rejected/cancelled
|
|
441
|
+
*/
|
|
442
|
+
previewAndApplyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export type { CodeSnippet as C, DefinitionProvider as D, EditProvider as E, FileAccessProvider as F, GlobalFindProvider as G, HierarchyProvider as H, Link as L, OutlineProvider as O, PendingEditOperation as P, ReferencesProvider as R, SymbolKind as S, TextEdit as T, UnifiedUri as U, DiagnosticsProvider as a, GraphProvider as b, FrontmatterProvider as c, EditResult as d, FrontmatterMatch as e, GlobalFindMatch as f, DiagnosticSeverity as g, Diagnostic as h, DocumentSymbol as i, ExactPosition as j, DiskRange as k, FuzzyPosition as l, EditFailureReason as m, Frontmatter as n, FrontmatterValue as o, GlobalFindOptions as p, OnDiagnosticsChangedCallback as q };
|
package/dist/lsp/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ChildProcess } from 'node:child_process';
|
|
2
2
|
import { ProtocolConnection } from 'vscode-languageserver-protocol/node.js';
|
|
3
|
-
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, O as OutlineProvider, a as DiagnosticsProvider,
|
|
3
|
+
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, O as OutlineProvider, a as DiagnosticsProvider, F as FileAccessProvider, g as DiagnosticSeverity, C as CodeSnippet, h as Diagnostic$1, i as DocumentSymbol$1, j as ExactPosition, k as DiskRange, S as SymbolKind } from '../interfaces-B9NK_71f.js';
|
|
4
4
|
import { Location, LocationLink, Diagnostic, DocumentSymbol, SymbolInformation } from 'vscode-languageserver-protocol';
|
|
5
5
|
|
|
6
6
|
interface LspClientOptions {
|
|
@@ -21,15 +21,16 @@ declare class LspClient {
|
|
|
21
21
|
private openDocuments;
|
|
22
22
|
private diagnosticsCache;
|
|
23
23
|
private diagnosticsListeners;
|
|
24
|
+
private fileChangedListeners;
|
|
24
25
|
readonly definition: DefinitionProvider | undefined;
|
|
25
26
|
readonly references: ReferencesProvider | undefined;
|
|
26
27
|
readonly hierarchy: HierarchyProvider | undefined;
|
|
27
28
|
readonly outline: OutlineProvider | undefined;
|
|
28
29
|
readonly diagnostics: DiagnosticsProvider | undefined;
|
|
30
|
+
readonly fileAccess: Pick<FileAccessProvider, 'onFileChanged'>;
|
|
29
31
|
private readonly options;
|
|
30
32
|
constructor(options: LspClientOptions);
|
|
31
33
|
getState(): LspClientState;
|
|
32
|
-
get onDiagnosticsChanged(): (callback: OnDiagnosticsChangedCallback) => void;
|
|
33
34
|
start(): Promise<void>;
|
|
34
35
|
stop(): Promise<void>;
|
|
35
36
|
notifyFileChanged(sdkPath: string): Promise<void>;
|
|
@@ -49,6 +50,7 @@ declare class LspClient {
|
|
|
49
50
|
private provideDefinition;
|
|
50
51
|
private provideTypeDefinition;
|
|
51
52
|
private provideReferences;
|
|
53
|
+
private provideFileReferences;
|
|
52
54
|
private provideCallHierarchy;
|
|
53
55
|
private provideDocumentSymbols;
|
|
54
56
|
private provideDiagnostics;
|
package/dist/lsp/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {spawn}from'child_process';import {InitializedNotification,ShutdownRequest,ExitNotification,StreamMessageReader,StreamMessageWriter,createProtocolConnection,InitializeRequest,CancellationTokenSource,DidOpenTextDocumentNotification,DidCloseTextDocumentNotification}from'vscode-languageserver-protocol/node.js';import D from'path';import {pathToFileURL,fileURLToPath}from'url';function c(o,t){let e=D.resolve(o,t);return pathToFileURL(e).toString()}function u(o,t){let e=fileURLToPath(t);return D.relative(o,e).replace(/\\/g,"/")}function p(o){return {start:{line:o.start.line,character:o.start.character},end:{line:o.end.line,character:o.end.character}}}function R(o){return {line:o.line,character:o.character}}var T={1:"error",2:"warning",3:"information",4:"hint"};function
|
|
2
|
-
`).slice(l.start.line,l.end.line+1);n.push({uri:m,range:l,content:
|
|
3
|
-
`)});}catch{n.push({uri:m,range:l,content:""});}}return n}var I={".ts":"typescript",".tsx":"typescriptreact",".js":"javascript",".jsx":"javascriptreact",".rs":"rust",".go":"go",".py":"python",".rb":"ruby",".java":"java",".kt":"kotlin",".kts":"kotlin",".c":"c",".h":"c",".cpp":"cpp",".cc":"cpp",".cxx":"cpp",".hpp":"cpp",".cs":"csharp",".swift":"swift",".lua":"lua",".zig":"zig",".json":"json",".yaml":"yaml",".yml":"yaml",".toml":"toml",".xml":"xml",".html":"html",".css":"css",".scss":"scss",".md":"markdown",".sql":"sql",".sh":"shellscript",".bash":"shellscript",".zsh":"shellscript",".ps1":"powershell",".dart":"dart",".ex":"elixir",".exs":"elixir",".erl":"erlang",".hs":"haskell",".ml":"ocaml",".mli":"ocaml",".scala":"scala",".r":"r",".R":"r",".php":"php",".pl":"perl",".vim":"vim"};function w(o){let t=D.extname(o);return I[t]??"plaintext"}var y=class{state="idle";process=null;connection=null;openDocuments=new Map;diagnosticsCache=new Map;diagnosticsListeners=[];definition;references;hierarchy;outline;diagnostics;options;constructor(t){this.options={documentIdleTimeout:3e4,requestTimeout:3e4,...t},this.definition=void 0,this.references=void 0,this.hierarchy=void 0,this.outline=void 0,this.diagnostics=void 0
|
|
1
|
+
import {spawn}from'child_process';import {InitializedNotification,ShutdownRequest,ExitNotification,StreamMessageReader,StreamMessageWriter,createProtocolConnection,InitializeRequest,CancellationTokenSource,DidOpenTextDocumentNotification,DidCloseTextDocumentNotification}from'vscode-languageserver-protocol/node.js';import D from'path';import {pathToFileURL,fileURLToPath}from'url';function c(o,t){let e=D.resolve(o,t);return pathToFileURL(e).toString()}function u(o,t){let e=fileURLToPath(t);return D.relative(o,e).replace(/\\/g,"/")}function p(o){return {start:{line:o.start.line,character:o.start.character},end:{line:o.end.line,character:o.end.character}}}function R(o){return {line:o.line,character:o.character}}var T={1:"error",2:"warning",3:"information",4:"hint"};function L(o){return T[o??1]??"error"}var U=["file","file","module","namespace","package","class","method","property","field","constructor","enum","interface","function","variable","constant","string","number","boolean","array","object","key","null","enumMember","struct","event","operator","typeParameter"];function P(o){return U[o]??"variable"}function g(o,t,e){return {uri:u(o,t),range:p(e.range),severity:L(e.severity),message:e.message,source:e.source,code:typeof e.code=="object"?String(e.code):e.code??void 0}}function f(o){return {name:o.name,detail:o.detail,kind:P(o.kind),range:p(o.range),selectionRange:p(o.selectionRange),children:o.children?.map(f)}}function C(o,t){return {name:t.name,kind:P(t.kind),range:p(t.location.range),selectionRange:p(t.location.range)}}async function h(o,t,e){if(!t)return [];let i=Array.isArray(t)?t:[t];if(i.length===0)return [];let n=[];for(let s of i){let r="targetUri"in s,a=r?s.targetUri:s.uri,d=r?s.targetRange:s.range,m=u(o,a),l=p(d);try{let b=(await e(m)).split(`
|
|
2
|
+
`).slice(l.start.line,l.end.line+1);n.push({uri:m,range:l,content:b.join(`
|
|
3
|
+
`)});}catch{n.push({uri:m,range:l,content:""});}}return n}var I={".ts":"typescript",".tsx":"typescriptreact",".js":"javascript",".jsx":"javascriptreact",".rs":"rust",".go":"go",".py":"python",".rb":"ruby",".java":"java",".kt":"kotlin",".kts":"kotlin",".c":"c",".h":"c",".cpp":"cpp",".cc":"cpp",".cxx":"cpp",".hpp":"cpp",".cs":"csharp",".swift":"swift",".lua":"lua",".zig":"zig",".json":"json",".yaml":"yaml",".yml":"yaml",".toml":"toml",".xml":"xml",".html":"html",".css":"css",".scss":"scss",".md":"markdown",".sql":"sql",".sh":"shellscript",".bash":"shellscript",".zsh":"shellscript",".ps1":"powershell",".dart":"dart",".ex":"elixir",".exs":"elixir",".erl":"erlang",".hs":"haskell",".ml":"ocaml",".mli":"ocaml",".scala":"scala",".r":"r",".R":"r",".php":"php",".pl":"perl",".vim":"vim"};function w(o){let t=D.extname(o);return I[t]??"plaintext"}var y=class{state="idle";process=null;connection=null;openDocuments=new Map;diagnosticsCache=new Map;diagnosticsListeners=[];fileChangedListeners=[];definition;references;hierarchy;outline;diagnostics;fileAccess;options;constructor(t){this.options={documentIdleTimeout:3e4,requestTimeout:3e4,...t},this.definition=void 0,this.references=void 0,this.hierarchy=void 0,this.outline=void 0,this.diagnostics=void 0,this.fileAccess={onFileChanged:e=>{this.fileChangedListeners.push(e);}};}getState(){return this.state}async start(){if(this.state!=="idle")throw new Error(`Cannot start: client is in "${this.state}" state`);this.state="starting";try{let t=this.spawnAndConnect();this.connection=t,t.onClose(()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();}),t.listen();let i=(await this.sendInitialize()).capabilities;this.setupProviders(i),t.onNotification("textDocument/publishDiagnostics",n=>{let s=u(this.options.workspacePath,n.uri),r=n.diagnostics.map(a=>g(this.options.workspacePath,n.uri,a));this.diagnosticsCache.set(s,r);for(let a of this.diagnosticsListeners)a(s);}),await t.sendNotification(InitializedNotification.type,{}),this.state="running";}catch(t){throw this.transitionToDead(),t}}async stop(){if(this.state!=="running")return;let t=[...this.openDocuments.keys()].map(e=>this.closeDocument(e));await Promise.all(t);try{await this.connection?.sendRequest(ShutdownRequest.type),await this.connection?.sendNotification(ExitNotification.type);}catch{}this.cleanup();}async notifyFileChanged(t){if(this.state!=="running")return;let e=c(this.options.workspacePath,t);if(this.openDocuments.get(e)){await this.closeDocument(e),await this.ensureDocumentOpen(e);for(let n of this.fileChangedListeners)n(t);}}startWithConnection(t,e){this.connection=t,this.process=e;}async initializeConnection(){let t=this.connection;if(!t)throw new Error("Connection not set");this.state="starting",t.onClose(()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();}),t.listen();let i=(await this.sendInitialize()).capabilities;this.setupProviders(i),t.onNotification("textDocument/publishDiagnostics",n=>{let s=u(this.options.workspacePath,n.uri),r=n.diagnostics.map(a=>g(this.options.workspacePath,n.uri,a));this.diagnosticsCache.set(s,r);for(let a of this.diagnosticsListeners)a(s);}),await t.sendNotification(InitializedNotification.type,{}),this.state="running";}spawnAndConnect(){let t=spawn(this.options.command,this.options.args??[],{stdio:["pipe","pipe","pipe"],env:this.options.env?{...process.env,...this.options.env}:process.env});this.process=t,t.on("exit",()=>{(this.state==="running"||this.state==="starting")&&this.transitionToDead();});let e=new StreamMessageReader(t.stdout),i=new StreamMessageWriter(t.stdin);return createProtocolConnection(e,i)}async sendInitialize(){if(!this.connection)throw new Error("No connection");let t={textDocument:{synchronization:{dynamicRegistration:false,didSave:true},definition:{dynamicRegistration:false},references:{dynamicRegistration:false},callHierarchy:{dynamicRegistration:false},documentSymbol:{dynamicRegistration:false,hierarchicalDocumentSymbolSupport:true},publishDiagnostics:{relatedInformation:true}}};return await this.connection.sendRequest(InitializeRequest.type,{processId:process.pid,capabilities:t,rootUri:c(this.options.workspacePath,"."),initializationOptions:this.options.initializationOptions??null})}setupProviders(t){t.definitionProvider&&(this.definition={provideDefinition:(e,i)=>this.provideDefinition(e,i),provideTypeDefinition:(e,i)=>this.provideTypeDefinition(e,i)}),t.referencesProvider&&(this.references={provideReferences:(e,i)=>this.provideReferences(e,i),provideFileReferences:e=>this.provideFileReferences(e)}),t.callHierarchyProvider&&(this.hierarchy={provideCallHierarchy:(e,i,n)=>this.provideCallHierarchy(e,i,n)}),t.documentSymbolProvider&&(this.outline={provideDocumentSymbols:e=>this.provideDocumentSymbols(e)}),this.diagnostics={provideDiagnostics:e=>this.provideDiagnostics(e),getWorkspaceDiagnostics:()=>this.getWorkspaceDiagnostics(),onDiagnosticsChanged:e=>{this.diagnosticsListeners.push(e);}};}assertRunning(){if(this.state!=="running")throw new Error(`Client is not running (state: "${this.state}")`);if(!this.connection)throw new Error("No connection");return this.connection}createTimeout(){let t=new CancellationTokenSource,e,i=new Promise((n,s)=>{e=setTimeout(()=>{t.cancel(),s(new Error("LSP request timed out"));},this.options.requestTimeout);});return {token:t,promise:i,clear:()=>{e&&clearTimeout(e);}}}async sendRequest(t,e){let i=this.assertRunning(),n=this.createTimeout();try{return await Promise.race([i.sendRequest(t,e,n.token.token),n.promise])}finally{n.clear();}}async ensureDocumentOpen(t){let e=this.openDocuments.get(t);if(e){e.closeTimer&&clearTimeout(e.closeTimer),e.closeTimer=this.scheduleDocumentClose(t);return}let i=u(this.options.workspacePath,t),n=await this.options.readFile(i),s=w(i),r={uri:t,version:1,closeTimer:this.scheduleDocumentClose(t)};this.openDocuments.set(t,r),await this.connection?.sendNotification(DidOpenTextDocumentNotification.type,{textDocument:{uri:t,languageId:s,version:r.version,text:n}});}scheduleDocumentClose(t){return setTimeout(()=>{this.closeDocument(t);},this.options.documentIdleTimeout)}async closeDocument(t){let e=this.openDocuments.get(t);if(e&&(e.closeTimer&&clearTimeout(e.closeTimer),this.openDocuments.delete(t),this.connection&&this.state==="running"))try{await this.connection.sendNotification(DidCloseTextDocumentNotification.type,{textDocument:{uri:t}});}catch{}}async provideDefinition(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/definition",{textDocument:{uri:i},position:{line:e.line,character:e.character}});return h(this.options.workspacePath,n,this.options.readFile)}async provideTypeDefinition(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/typeDefinition",{textDocument:{uri:i},position:{line:e.line,character:e.character}});return h(this.options.workspacePath,n,this.options.readFile)}async provideReferences(t,e){let i=c(this.options.workspacePath,t);await this.ensureDocumentOpen(i);let n=await this.sendRequest("textDocument/references",{textDocument:{uri:i},position:{line:e.line,character:e.character},context:{includeDeclaration:true}});return h(this.options.workspacePath,n,this.options.readFile)}async provideFileReferences(t){let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=await this.sendRequest("textDocument/references",{textDocument:{uri:e},position:{line:0,character:0},context:{includeDeclaration:false}});return h(this.options.workspacePath,i,this.options.readFile)}async provideCallHierarchy(t,e,i){let n=c(this.options.workspacePath,t);await this.ensureDocumentOpen(n);let s=await this.sendRequest("textDocument/prepareCallHierarchy",{textDocument:{uri:n},position:{line:e.line,character:e.character}});if(!s||s.length===0)return [];let r=s[0],a=i==="incoming"?"callHierarchy/incomingCalls":"callHierarchy/outgoingCalls",d=await this.sendRequest(a,{item:r});if(!d||d.length===0)return [];let m=d.map(l=>{let v="from"in l?l.from:l.to;return {uri:v.uri,range:v.selectionRange}});return h(this.options.workspacePath,m,this.options.readFile)}async provideDocumentSymbols(t){let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=await this.sendRequest("textDocument/documentSymbol",{textDocument:{uri:e}});if(!i||i.length===0)return [];let n=i[0];return n&&"range"in n&&"selectionRange"in n?i.map(f):i.map(s=>C(this.options.workspacePath,s))}async provideDiagnostics(t){this.assertRunning();let e=c(this.options.workspacePath,t);await this.ensureDocumentOpen(e);let i=this.diagnosticsCache.get(t);return i||(await new Promise(n=>setTimeout(n,200)),this.diagnosticsCache.get(t)??[])}async getWorkspaceDiagnostics(){this.assertRunning();let t=[];for(let e of this.diagnosticsCache.values())t.push(...e);return t}transitionToDead(){this.state="dead",this.cleanup();}cleanup(){for(let t of this.openDocuments.values())t.closeTimer&&clearTimeout(t.closeTimer);if(this.openDocuments.clear(),this.connection){try{this.connection.dispose();}catch{}this.connection=null;}if(this.process){try{this.process.kill();}catch{}this.process=null;}this.state="dead";}};function _(o){return new y(o)}export{y as LspClient,L as convertDiagnosticSeverity,h as convertLocationsToSnippets,g as convertLspDiagnostic,f as convertLspDocumentSymbol,R as convertLspPosition,p as convertLspRange,C as convertSymbolInformation,P as convertSymbolKind,_ as createLspClient,w as guessLanguageId,u as lspUriToPath,c as pathToLspUri};
|
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,56 +1,8 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider,
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Zod schemas for MCP tool inputs and outputs.
|
|
8
|
-
* @internal
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
declare const FuzzyPositionSchema: z.ZodObject<{
|
|
12
|
-
uri: z.ZodString;
|
|
13
|
-
symbol_name: z.ZodString;
|
|
14
|
-
line_hint: z.ZodNumber;
|
|
15
|
-
order_hint: z.ZodDefault<z.ZodNumber>;
|
|
16
|
-
}, z.core.$strip>;
|
|
17
|
-
declare const ApplyEditSchema: z.ZodObject<{
|
|
18
|
-
uri: z.ZodString;
|
|
19
|
-
start_hash: z.ZodString;
|
|
20
|
-
end_hash: z.ZodOptional<z.ZodString>;
|
|
21
|
-
replace_text: z.ZodString;
|
|
22
|
-
description: z.ZodString;
|
|
23
|
-
}, z.core.$strip>;
|
|
24
|
-
declare const CallHierarchySchema: z.ZodObject<{
|
|
25
|
-
uri: z.ZodString;
|
|
26
|
-
symbol_name: z.ZodString;
|
|
27
|
-
line_hint: z.ZodNumber;
|
|
28
|
-
order_hint: z.ZodDefault<z.ZodNumber>;
|
|
29
|
-
direction: z.ZodEnum<{
|
|
30
|
-
incoming: "incoming";
|
|
31
|
-
outgoing: "outgoing";
|
|
32
|
-
}>;
|
|
33
|
-
}, z.core.$strip>;
|
|
34
|
-
declare const GlobalFindSchema: z.ZodObject<{
|
|
35
|
-
query: z.ZodString;
|
|
36
|
-
case_sensitive: z.ZodDefault<z.ZodBoolean>;
|
|
37
|
-
exact_match: z.ZodDefault<z.ZodBoolean>;
|
|
38
|
-
regex_mode: z.ZodDefault<z.ZodBoolean>;
|
|
39
|
-
}, z.core.$strip>;
|
|
40
|
-
declare const AddLinkSchema: z.ZodObject<{
|
|
41
|
-
path: z.ZodString;
|
|
42
|
-
pattern: z.ZodString;
|
|
43
|
-
link_to: z.ZodString;
|
|
44
|
-
}, z.core.$strip>;
|
|
45
|
-
declare const GetFrontmatterStructureSchema: z.ZodObject<{
|
|
46
|
-
property: z.ZodString;
|
|
47
|
-
path: z.ZodOptional<z.ZodString>;
|
|
48
|
-
}, z.core.$strip>;
|
|
49
|
-
declare const SetFrontmatterSchema: z.ZodObject<{
|
|
50
|
-
path: z.ZodString;
|
|
51
|
-
property: z.ZodString;
|
|
52
|
-
value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodBoolean, z.ZodArray<z.ZodBoolean>, z.ZodNull]>;
|
|
53
|
-
}, z.core.$strip>;
|
|
3
|
+
import { F as FileAccessProvider, E as EditProvider, D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider, c as FrontmatterProvider, d as EditResult, e as FrontmatterMatch, f as GlobalFindMatch, L as Link } from '../interfaces-B9NK_71f.js';
|
|
4
|
+
import { ResolverConfig } from '../index.js';
|
|
5
|
+
import { FuzzyPositionSchema, ApplyEditSchema, GetFrontmatterStructureSchema, SetFrontmatterSchema, GlobalFindSchema, AddLinkSchema, CallHierarchySchema, FileReferencesSchema } from '../schemas.js';
|
|
54
6
|
|
|
55
7
|
/**
|
|
56
8
|
* MCP entry point for installing individual providers onto an MCP server.
|
|
@@ -104,6 +56,10 @@ interface ReferencesInstallOptions extends InstallOptions {
|
|
|
104
56
|
onReferencesInput?: (input: z.infer<typeof FuzzyPositionSchema>) => void;
|
|
105
57
|
/** Called with the result of `find_references`. */
|
|
106
58
|
onReferencesOutput?: (output: SnippetsOutput) => void;
|
|
59
|
+
/** Called with the raw tool input for `find_file_references`. */
|
|
60
|
+
onFileReferencesInput?: (input: z.infer<typeof FileReferencesSchema>) => void;
|
|
61
|
+
/** Called with the result of `find_file_references`. */
|
|
62
|
+
onFileReferencesOutput?: (output: SnippetsOutput) => void;
|
|
107
63
|
}
|
|
108
64
|
/** Options for installing a {@link HierarchyProvider}. */
|
|
109
65
|
interface HierarchyInstallOptions extends InstallOptions {
|
package/dist/mcp/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {z as z$1}from'zod';var g=class extends Error{constructor(i,
|
|
2
|
-
`)}function $(
|
|
3
|
-
${$(
|
|
4
|
-
`)}var
|
|
5
|
-
`)}function A(
|
|
6
|
-
${L(l)}`);return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:
|
|
1
|
+
import {ResourceTemplate}from'@modelcontextprotocol/sdk/server/mcp.js';import {z as z$1}from'zod';var g=class extends Error{constructor(i,r,t){super(`Could not find symbol '${i}' at or near line ${r}. ${t}`);this.symbolName=i;this.lineHint=r;this.reason=t;this.name="SymbolResolutionError";}},O=class{constructor(n,i){this.fs=n;this.lineSearchRadius=i?.lineSearchRadius??2;}lineSearchRadius;async resolvePosition(n,i){let t=(await this.fs.readFile(n)).split(/\r?\n/),e=i.lineHint-1,o=i.orderHint??0,s=this.findSymbolInLine(t[e],i.symbolName,o);if(s!==null)return {line:e,character:s};for(let c=1;c<=this.lineSearchRadius;c++){let l=e-c;if(l>=0){let h=this.findSymbolInLine(t[l],i.symbolName,o);if(h!==null)return {line:l,character:h}}let m=e+c;if(m<t.length){let h=this.findSymbolInLine(t[m],i.symbolName,o);if(h!==null)return {line:m,character:h}}}throw new g(i.symbolName,i.lineHint,`Please verify the file content and try again. Searched lines ${Math.max(1,i.lineHint-this.lineSearchRadius)} to ${Math.min(t.length,i.lineHint+this.lineSearchRadius)}.`)}findSymbolInLine(n,i,r){if(n===void 0||n.length===0)return null;let t=0,e=0;for(;t<n.length;){let o=n.indexOf(i,t);if(o===-1)break;if(e===r)return o;e++,t=o+1;}return null}};var f=a=>({content:[{type:"text",text:JSON.stringify(a??"")}],structuredContent:a});function d(a){if(a.includes(".."))throw new Error('URI could not include ".." operator');return a.replace(/\\/g,"/")}function C(){return `edit-${Date.now()}`}function L(a){return a.length===0?"No diagnostics found.":a.map(n=>{let i=n.range.start.line+1,r=n.severity.toUpperCase(),t=n.source?` [${n.source}]`:"",e=n.code!==void 0?` (${n.code})`:"";return `- **${r}**${t}${e} at line ${i}: ${n.message}`}).join(`
|
|
2
|
+
`)}function $(a,n=0){if(a.length===0&&n===0)return "No symbols found.";let i=" ".repeat(n);return a.map(r=>{let t=r.range.start.line+1,e=r.range.end.line+1,o=t===e?`line ${t}`:`lines ${t}-${e}`,s=r.detail?` - ${r.detail}`:"",c=`${i}- **${r.kind}** \`${r.name}\`${s} (${o})`;return r.children&&r.children.length>0?`${c}
|
|
3
|
+
${$(r.children,n+1)}`:c}).join(`
|
|
4
|
+
`)}var H=new Uint16Array(256);for(let a=0;a<256;a++){let n=a<<8;for(let i=0;i<8;i++)n=n<<1^(n&32768?4129:0);H[a]=n&65535;}function k(a){let n=65535;for(let i=0;i<a.length;i++)n=n<<8&65535^H[(n>>8^a.charCodeAt(i))&255];return (n&255).toString(16).padStart(2,"0")}function M(a){return a.split(/\r?\n/).map((n,i)=>({num:i+1,text:n}))}function j(a){return a.map(n=>`${n.num}:${k(n.text)}|${n.text}`).join(`
|
|
5
|
+
`)}function A(a){let n=a.match(/^(\d+):([0-9a-f]{2})$/);if(!n||!n[1]||!n[2])throw new Error(`Invalid hashline reference "${a}". Expected format: "<line>:<hash>" (e.g., "3:a1")`);return {line:parseInt(n[1],10),hash:n[2]}}var _=z$1.string().describe("The relative file path"),N=z$1.string().describe("The text of the symbol to find"),U=z$1.number().int().positive().describe("Approximate 1-based line number where the symbol is expected"),W=z$1.number().int().min(0).default(0).describe("0-based index of which occurrence to target if symbol appears multiple times"),R=z$1.object({uri:_,symbol_name:N,line_hint:U,order_hint:W}),v=z$1.object({uri:_,start_hash:z$1.string().describe('Start line reference from hashline output, format "<line>:<hash>" (e.g., "3:a1"). Copy this exactly from the files:// resource output.'),end_hash:z$1.string().optional().describe('End line reference for multi-line edits (e.g., "5:0e"). The range is inclusive. Omit for single-line edits (defaults to start_hash).'),replace_text:z$1.string().describe("The new text to replace the entire line range with"),description:z$1.string().describe("Rationale for the edit")}),J=z$1.object({uri:z$1.string().describe("The file URI or path"),symbol_name:N,line_hint:U,order_hint:W,direction:z$1.enum(["incoming","outgoing"]).describe("Direction of the call hierarchy")}),he=z$1.string().describe("The search query"),ge=z$1.boolean().default(false).describe("Whether the search is case-sensitive"),ye=z$1.boolean().default(false).describe("Whether to match exact words only"),ve=z$1.boolean().default(false).describe("Whether the query is a regular expression"),q=z$1.object({query:he,case_sensitive:ge,exact_match:ye,regex_mode:ve}),B=z$1.object({path:z$1.string().describe("The path to the document to modify"),pattern:z$1.string().describe("The text pattern to find and replace with a link"),link_to:z$1.string().describe("The target URI the link should point to")}),Q=z$1.object({uri:_}),K=z$1.object({property:z$1.string().describe("The frontmatter property name to search for"),path:z$1.string().optional().describe("Optional path to limit the search to a specific document. If not provided, searches all documents.")}),V=z$1.object({path:z$1.string().describe("The path to the document to modify"),property:z$1.string().describe("The frontmatter property name to set"),value:z$1.union([z$1.string(),z$1.array(z$1.string()),z$1.number(),z$1.array(z$1.number()),z$1.boolean(),z$1.array(z$1.boolean()),z$1.null()]).describe("The value to set. Can be a string, number, boolean, array of these types, or null to remove.")});function Se(a){if(!a)return null;let n=a.match(/^L(\d+)(?:-L(\d+))?$/);if(!n||!n[1])return null;let i=parseInt(n[1],10),r=n[2]?parseInt(n[2],10):i;return i<1||r<i?null:{start:i,end:r}}function X(a){let n=a.indexOf("?");return n===-1?{path:a,params:new URLSearchParams}:{path:a.slice(0,n),params:new URLSearchParams(a.slice(n+1))}}function z(a){return async n=>{let i=n.lastIndexOf("/"),r=i>=0?n.slice(0,i):"",t=i>=0?n.slice(i+1):n;try{let e=await a(r||"."),o=t.toLowerCase();return e.filter(s=>s.toLowerCase().startsWith(o)).map(s=>r?`${r}/${s}`:s)}catch{return []}}}function Y(a,n,i,r){a.registerTool("goto_definition",{description:"Navigate to the definition of a symbol.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideDefinition(e,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function Z(a,n,i,r){let t=n.provideTypeDefinition;t&&a.registerTool("goto_type_definition",{description:"Navigate to the type definition of a symbol.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async e=>{r?.onInput?.(e);try{let o=d(e.uri),s={symbolName:e.symbol_name,lineHint:e.line_hint,orderHint:e.order_hint},c=await i.resolvePosition(o,s),l=(await t(o,c)).map(m=>({uri:m.uri,startLine:m.range.start.line+1,endLine:m.range.end.line+1,content:m.content}));return r?.onOutput?.({snippets:l}),f({snippets:l})}catch(o){let s=o instanceof g?o.message:`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:s}],structuredContent:{error:s},isError:true}}});}function ee(a,n,i,r){a.registerTool("find_references",{description:"Find all references to a symbol. Returns a list of locations where the symbol is used.",inputSchema:R,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideReferences(e,s)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function te(a,n,i,r){a.registerTool("call_hierarchy",{description:"Get call hierarchy for a function or method. Shows incoming or outgoing calls.",inputSchema:J,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{r?.onInput?.(t);try{let e=d(t.uri),o={symbolName:t.symbol_name,lineHint:t.line_hint,orderHint:t.order_hint},s=await i.resolvePosition(e,o),c=(await n.provideCallHierarchy(e,s,t.direction)).map(l=>({uri:l.uri,startLine:l.range.start.line+1,endLine:l.range.end.line+1,content:l.content}));return r?.onOutput?.({snippets:c}),f({snippets:c})}catch(e){let o=e instanceof g?e.message:`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function re(a,n,i){let r=n.provideFileReferences;r&&a.registerTool("find_file_references",{description:"Find all references to a file across the workspace. Returns locations that import or link to the given file.",inputSchema:Q,outputSchema:{snippets:z$1.array(z$1.object({uri:z$1.string(),startLine:z$1.number(),endLine:z$1.number(),content:z$1.string()}))}},async t=>{i?.onInput?.(t);try{let e=d(t.uri),o=(await r(e)).map(s=>({uri:s.uri,startLine:s.range.start.line+1,endLine:s.range.end.line+1,content:s.content}));return i?.onOutput?.({snippets:o}),f({snippets:o})}catch(e){let o=`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:o}],structuredContent:{error:o},isError:true}}});}function ne(a,n,i){let r=new ResourceTemplate("diagnostics://{+path}",{list:void 0,complete:i?{path:i}:void 0});if(a.registerResource("diagnostics",r,{description:"Diagnostics (errors, warnings, hints) for a specific file. Use the file path after diagnostics://",mimeType:"text/markdown"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.provideDiagnostics(s),l=L(c);return {contents:[{uri:`diagnostics://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`diagnostics://${e.path}`,mimeType:"text/markdown",text:s}]}}}),n.getWorkspaceDiagnostics){let t=n.getWorkspaceDiagnostics.bind(n);a.registerResource("workspace-diagnostics","diagnostics://workspace",{description:"All diagnostics (errors, warnings, hints) across the entire workspace",mimeType:"text/markdown"},async()=>{try{let e=await t(),o=new Map;for(let c of e){let l=o.get(c.uri)??[];l.push(c),o.set(c.uri,l);}if(o.size===0)return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:"No diagnostics found in workspace."}]};let s=[];for(let[c,l]of o)s.push(`## ${c}
|
|
6
|
+
${L(l)}`);return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:s.join(`
|
|
7
7
|
|
|
8
|
-
`)}]}}catch(r){return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:`Error: ${r instanceof Error?r.message:String(r)}`}]}}});}n.onDiagnosticsChanged&&n.onDiagnosticsChanged(t=>{let r=m(t);s.server.sendResourceUpdated({uri:`diagnostics://${r}`}),n.getWorkspaceDiagnostics&&s.server.sendResourceUpdated({uri:"diagnostics://workspace"});});}function re(s,n,i){let e=new ResourceTemplate("outline://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("outline",e,{description:"Document outline (symbols like classes, functions, variables) for a specific file. Use the file path after outline://",mimeType:"text/markdown"},async(t,r)=>{try{let o=r.path,a=m(o),c=await n.provideDocumentSymbols(a),l=$(c);return {contents:[{uri:`outline://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`outline://${r.path}`,mimeType:"text/markdown",text:a}]}}});}function ne(s,n,i,e){let t=n.previewAndApplyEdits?.bind(n)??n.applyEdits?.bind(n);t&&s.registerTool("apply_edit",{description:'Apply a text edit to a file. WORKFLOW: First read the file via the files:// resource to get hashline-formatted content (e.g., "3:a1| return x"). Then reference lines by their "line:hash" to specify the edit range. The hash verifies the file has not changed since your read \u2014 if it has, the edit is rejected and you must re-read the file. For single-line edits, only start_hash is needed. For multi-line edits, provide both start_hash and end_hash. The edit replaces the entire line range (inclusive) with replace_text. The edit must be approved by the user before being applied.',inputSchema:{uri:v.shape.uri,start_hash:v.shape.start_hash,end_hash:v.shape.end_hash,replace_text:v.shape.replace_text,description:v.shape.description},outputSchema:{success:z$1.boolean(),message:z$1.string()}},async r=>{e?.onInput?.(r);try{let o=m(r.uri),a=A(r.start_hash),c=r.end_hash?A(r.end_hash):a,d=(await i(o)).split(/\r?\n/);if(a.line<1||a.line>d.length)throw new Error(`Start line ${a.line} is out of range (file has ${d.length} lines)`);if(c.line<a.line||c.line>d.length)throw new Error(`End line ${c.line} is out of range (file has ${d.length} lines)`);let f=d[a.line-1],x=k(f);if(x!==a.hash)throw new Error(`Hash mismatch at line ${a.line}: expected "${a.hash}", got "${x}". File has changed since last read.`);let P=d[c.line-1],F=k(P);if(F!==c.hash)throw new Error(`Hash mismatch at line ${c.line}: expected "${c.hash}", got "${F}". File has changed since last read.`);let I={start:{line:a.line-1,character:0},end:{line:c.line-1,character:P.length}},T={id:z(),uri:o,edits:[{range:I,newText:r.replace_text}],description:r.description},y=await t(T)?{success:!0,message:"Edit successfully applied and saved."}:{success:!1,message:"Edit rejected by user."};return e?.onOutput?.(y),h(y)}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:a}],structuredContent:{success:false,message:a},isError:true}}});}function ie(s,n){let{readFile:i,readDirectory:e}=n,t=new ResourceTemplate("files://{+path}",{list:void 0,complete:{path:_(e)}});s.registerResource("filesystem",t,{description:'Access filesystem resources. For directories: returns children as JSON (git-ignored files excluded). For files: returns content in hashline format where each line is prefixed with "<lineNumber>:<hash>|" (e.g., "1:a3|function hello() {"). The hash is a 2-char hex CRC16 digest of the line content. Use these line:hash references with the apply_edit tool to make edits. Supports line ranges with #L23 or #L23-L30 fragment. Supports regex filtering with ?pattern=<regex> query parameter (matches raw line text, not the hash prefix). Line numbers in the output are always the original file line numbers, even when filtering.'},async(r,o)=>{let a=r.toString();try{let c=o.path,l,d=c,f=c.indexOf("#");f!==-1&&(l=c.slice(f+1),d=c.slice(0,f));let{path:x,params:P}=V(d),{path:F,params:I}=V(l??"");l=l?F:void 0;let T=new URLSearchParams([...P.entries(),...I.entries()]),R=m(x),y=ye(l),G=T.get("pattern");try{let D=await i(R),b=H(D);if(y&&(b=b.filter(w=>w.num>=y.start&&w.num<=y.end)),G){let w=new RegExp(G);b=b.filter(de=>w.test(de.text));}return {contents:[{uri:a,mimeType:"text/plain",text:M(b)}]}}catch{let D=await e(R);return {contents:[{uri:a,mimeType:"application/json",text:JSON.stringify(D)}]}}}catch(c){let l=`Error: ${c instanceof Error?c.message:String(c)}`;return {contents:[{uri:a,mimeType:"text/plain",text:l}]}}}),n.onFileChanged&&n.onFileChanged(r=>{let o=m(r);s.server.sendResourceUpdated({uri:`files://${o}`});});}function oe(s,n,i){s.registerTool("global_find",{description:"Search for text across the entire workspace.",inputSchema:q,outputSchema:{matches:z$1.array(z$1.object({uri:z$1.string(),line:z$1.number(),column:z$1.number(),matchText:z$1.string(),context:z$1.string()})),count:z$1.number()}},async e=>{i?.onInput?.(e);try{let t=e.case_sensitive??!1,r=e.exact_match??!1,o=e.regex_mode??!1,a=await n.globalFind(e.query,{caseSensitive:t,exactMatch:r,regexMode:o});return i?.onOutput?.({count:a.length,matches:a}),h({count:a.length,matches:a})}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{error:r},isError:true}}});}function se(s,n,i){let e=new ResourceTemplate("outlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("outlinks",e,{description:"Outgoing links from a specific file. Use the file path after outlinks://",mimeType:"application/json"},async(r,o)=>{try{let a=o.path,c=m(a),l=await n.resolveOutlinks(c);return {contents:[{uri:`outlinks://${a}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(a){let c=`Error: ${a instanceof Error?a.message:String(a)}`;return {contents:[{uri:`outlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});let t=new ResourceTemplate("backlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("backlinks",t,{description:"Incoming links (backlinks) to a specific file. Use the file path after backlinks://",mimeType:"application/json"},async(r,o)=>{try{let a=o.path,c=m(a),l=await n.resolveBacklinks(c);return {contents:[{uri:`backlinks://${a}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(a){let c=`Error: ${a instanceof Error?a.message:String(a)}`;return {contents:[{uri:`backlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});}function ae(s,n,i){s.registerTool("get_link_structure",{description:"Get all links in the workspace, showing relationships between documents.",inputSchema:{},outputSchema:{links:z$1.array(z$1.object({sourceUri:z$1.string(),targetUri:z$1.string(),subpath:z$1.string().optional(),displayText:z$1.string().optional(),resolved:z$1.boolean(),line:z$1.number(),column:z$1.number()}))}},async()=>{try{let e=await n.getLinkStructure();return i?.onOutput?.({links:e}),h({links:e})}catch(e){let t=`Error: ${e instanceof Error?e.message:String(e)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function ce(s,n,i){s.registerTool("add_link",{description:"Add a link to a document by finding a text pattern and replacing it with a link to the target.",inputSchema:B,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async e=>{i?.onInput?.(e);try{let t=m(e.path),r=m(e.link_to);await n.addLink(t,e.pattern,r);let o={success:!0,message:"Link added successfully."};return i?.onOutput?.(o),h(o)}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{success:false,message:r},isError:true}}});}function ue(s,n,i){s.registerTool("get_frontmatter_structure",{description:"Get frontmatter property values across documents. If path is provided, searches only that document. Otherwise, searches all documents.",inputSchema:Q,outputSchema:{matches:z$1.array(z$1.object({path:z$1.string(),value:z$1.unknown()}))}},async e=>{i?.onInput?.(e);try{let t=e.path?m(e.path):void 0,r=await n.getFrontmatterStructure(e.property,t);return i?.onOutput?.({matches:r}),h({matches:r})}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{error:r},isError:true}}});}function le(s,n,i){s.registerTool("set_frontmatter",{description:"Set a frontmatter property on a document. Use null to remove the property.",inputSchema:K,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async e=>{i?.onInput?.(e);try{let t=m(e.path),r=e.value===null?void 0:e.value;await n.setFrontmatter(t,e.property,r);let o={success:!0,message:"Frontmatter updated successfully."};return i?.onOutput?.(o),h(o)}catch(t){let r=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:r}],structuredContent:{success:false,message:r},isError:true}}});}function pe(s,n,i){let e=new ResourceTemplate("frontmatter://{+path}",{list:void 0,complete:i?{path:i}:void 0});s.registerResource("frontmatter",e,{description:"Frontmatter metadata for a specific file. Use the file path after frontmatter://",mimeType:"application/json"},async(t,r)=>{try{let o=r.path,a=m(o),c=await n.getFrontmatter(a);return {contents:[{uri:`frontmatter://${o}`,mimeType:"application/json",text:JSON.stringify(c,null,2)}]}}catch(o){let a=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`frontmatter://${r.path}`,mimeType:"application/json",text:JSON.stringify({error:a})}]}}});}function ve(s){if(s.length<=0)throw new Error("providers array must not be empty");if(s.length===1&&s[0])return s[0];let n=s[0];if(!n)throw new Error("The first item of providers array must be defined");if("readFile"in n&&"readDirectory"in n){let i=s,e={readFile:t=>n.readFile(t),readDirectory:t=>n.readDirectory(t)};return i.some(t=>t.onFileChanged)&&(e.onFileChanged=t=>{for(let r of i)r.onFileChanged?.(t);}),e}if("applyEdits"in n||"previewAndApplyEdits"in n)return n;if("provideDefinition"in n){let i=s,e={async provideDefinition(t,r){return (await Promise.all(i.map(a=>a.provideDefinition(t,r)))).flat()}};return i.some(t=>t.provideTypeDefinition)&&(e.provideTypeDefinition=async(t,r)=>{let o=i.filter(c=>c.provideTypeDefinition);return (await Promise.all(o.map(c=>c.provideTypeDefinition(t,r)))).flat()}),e}if("provideReferences"in n){let i=s;return {async provideReferences(e,t){return (await Promise.all(i.map(o=>o.provideReferences(e,t)))).flat()}}}if("provideCallHierarchy"in n){let i=s;return {async provideCallHierarchy(e,t,r){return (await Promise.all(i.map(a=>a.provideCallHierarchy(e,t,r)))).flat()}}}if("provideDiagnostics"in n){let i=s,e={async provideDiagnostics(t){return (await Promise.all(i.map(o=>o.provideDiagnostics(t)))).flat()}};return i.some(t=>t.getWorkspaceDiagnostics)&&(e.getWorkspaceDiagnostics=async()=>{let t=i.filter(o=>o.getWorkspaceDiagnostics);return (await Promise.all(t.map(o=>o.getWorkspaceDiagnostics()))).flat()}),i.some(t=>t.onDiagnosticsChanged)&&(e.onDiagnosticsChanged=t=>{for(let r of i)r.onDiagnosticsChanged?.(t);}),e}if("provideDocumentSymbols"in n){let i=s;return {async provideDocumentSymbols(e){return (await Promise.all(i.map(r=>r.provideDocumentSymbols(e)))).flat()}}}if("globalFind"in n){let i=s;return {async globalFind(e,t){return (await Promise.all(i.map(o=>o.globalFind(e,t)))).flat()}}}if("getLinkStructure"in n){let i=s;return {async getLinkStructure(){return (await Promise.all(i.map(t=>t.getLinkStructure()))).flat()},async resolveOutlinks(e){return (await Promise.all(i.map(r=>r.resolveOutlinks(e)))).flat()},async resolveBacklinks(e){return (await Promise.all(i.map(r=>r.resolveBacklinks(e)))).flat()},async addLink(e,t,r){await Promise.all(i.map(o=>o.addLink(e,t,r)));}}}if("getFrontmatterStructure"in n){let i=s;return {async getFrontmatterStructure(e,t){return (await Promise.all(i.map(o=>o.getFrontmatterStructure(e,t)))).flat()},async getFrontmatter(e){let t=await Promise.all(i.map(r=>r.getFrontmatter(e)));return Object.assign({},...t)},async setFrontmatter(e,t,r){await Promise.all(i.map(o=>o.setFrontmatter(e,t,r)));}}}return n}function $e(s,n,i){let e=Array.isArray(n)?ve(n):n,t=i?.fileAccess,r=t?_(t.readDirectory):void 0,o=()=>{if(!t)throw new Error("fileAccess is required in options for providers that need symbol resolution");return new O(t,i?.resolverConfig)};if("readFile"in e&&"readDirectory"in e){ie(s,e);return}if("applyEdits"in e||"previewAndApplyEdits"in e){if(!t)throw new Error("fileAccess is required in options when installing an EditProvider");let a=i;ne(s,e,t.readFile,{onInput:a?.onEditInput,onOutput:a?.onEditOutput});return}if("provideDefinition"in e){let a=e,c=i,l=o();X(s,a,l,{onInput:c?.onDefinitionInput,onOutput:c?.onDefinitionOutput}),a.provideTypeDefinition&&Y(s,a,l,{onInput:c?.onTypeDefinitionInput,onOutput:c?.onTypeDefinitionOutput});return}if("provideReferences"in e){let a=i;Z(s,e,o(),{onInput:a?.onReferencesInput,onOutput:a?.onReferencesOutput});return}if("provideCallHierarchy"in e){let a=i;ee(s,e,o(),{onInput:a?.onCallHierarchyInput,onOutput:a?.onCallHierarchyOutput});return}if("provideDiagnostics"in e){te(s,e,r);return}if("provideDocumentSymbols"in e){re(s,e,r);return}if("globalFind"in e){let a=i;oe(s,e,{onInput:a?.onGlobalFindInput,onOutput:a?.onGlobalFindOutput});return}if("getLinkStructure"in e){let a=e,c=i;ae(s,a,{onOutput:c?.onLinkStructureOutput}),ce(s,a,{onInput:c?.onAddLinkInput,onOutput:c?.onAddLinkOutput}),se(s,a,r);return}if("getFrontmatterStructure"in e){let a=e,c=i;ue(s,a,{onInput:c?.onFrontmatterStructureInput,onOutput:c?.onFrontmatterStructureOutput}),le(s,a,{onInput:c?.onSetFrontmatterInput,onOutput:c?.onSetFrontmatterOutput}),pe(s,a,r);return}}
|
|
9
|
-
export{
|
|
8
|
+
`)}]}}catch(e){return {contents:[{uri:"diagnostics://workspace",mimeType:"text/markdown",text:`Error: ${e instanceof Error?e.message:String(e)}`}]}}});}n.onDiagnosticsChanged&&n.onDiagnosticsChanged(t=>{let e=d(t);a.server.sendResourceUpdated({uri:`diagnostics://${e}`}),n.getWorkspaceDiagnostics&&a.server.sendResourceUpdated({uri:"diagnostics://workspace"});});}function ie(a,n,i){let r=new ResourceTemplate("outline://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("outline",r,{description:"Document outline (symbols like classes, functions, variables) for a specific file. Use the file path after outline://",mimeType:"text/markdown"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.provideDocumentSymbols(s),l=$(c);return {contents:[{uri:`outline://${o}`,mimeType:"text/markdown",text:l}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`outline://${e.path}`,mimeType:"text/markdown",text:s}]}}});}function oe(a,n,i,r){let t=n.previewAndApplyEdits?.bind(n)??n.applyEdits?.bind(n);t&&a.registerTool("apply_edit",{description:'Apply a text edit to a file. WORKFLOW: First read the file via the files:// resource to get hashline-formatted content (e.g., "3:a1| return x"). Then reference lines by their "line:hash" to specify the edit range. The hash verifies the file has not changed since your read \u2014 if it has, the edit is rejected and you must re-read the file. For single-line edits, only start_hash is needed. For multi-line edits, provide both start_hash and end_hash. The edit replaces the entire line range (inclusive) with replace_text. The edit must be approved by the user before being applied.',inputSchema:{uri:v.shape.uri,start_hash:v.shape.start_hash,end_hash:v.shape.end_hash,replace_text:v.shape.replace_text,description:v.shape.description},outputSchema:{success:z$1.boolean(),message:z$1.string()}},async e=>{r?.onInput?.(e);try{let o=d(e.uri),s=A(e.start_hash),c=e.end_hash?A(e.end_hash):s,m=(await i(o)).split(/\r?\n/);if(s.line<1||s.line>m.length)throw new Error(`Start line ${s.line} is out of range (file has ${m.length} lines)`);if(c.line<s.line||c.line>m.length)throw new Error(`End line ${c.line} is out of range (file has ${m.length} lines)`);let h=m[s.line-1],b=k(h);if(b!==s.hash)throw new Error(`Hash mismatch at line ${s.line}: expected "${s.hash}", got "${b}". File has changed since last read.`);let P=m[c.line-1],F=k(P);if(F!==c.hash)throw new Error(`Hash mismatch at line ${c.line}: expected "${c.hash}", got "${F}". File has changed since last read.`);let E={start:{line:s.line-1,character:0},end:{line:c.line-1,character:P.length}},I={id:C(),uri:o,edits:[{range:E,newText:e.replace_text}],description:e.description},y=await t(I)?{success:!0,message:"Edit successfully applied and saved."}:{success:!1,message:"Edit rejected by user."};return r?.onOutput?.(y),f(y)}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {content:[{type:"text",text:s}],structuredContent:{success:false,message:s},isError:true}}});}function se(a,n){let{readFile:i,readDirectory:r}=n,t=new ResourceTemplate("files://{+path}",{list:void 0,complete:{path:z(r)}});a.registerResource("filesystem",t,{description:'Access filesystem resources. For directories: returns children as JSON (git-ignored files excluded). For files: returns content in hashline format where each line is prefixed with "<lineNumber>:<hash>|" (e.g., "1:a3|function hello() {"). The hash is a 2-char hex CRC16 digest of the line content. Use these line:hash references with the apply_edit tool to make edits. Supports line ranges with #L23 or #L23-L30 fragment. Supports regex filtering with ?pattern=<regex> query parameter (matches raw line text, not the hash prefix). Line numbers in the output are always the original file line numbers, even when filtering.'},async(e,o)=>{let s=e.toString();try{let c=o.path,l,m=c,h=c.indexOf("#");h!==-1&&(l=c.slice(h+1),m=c.slice(0,h));let{path:b,params:P}=X(m),{path:F,params:E}=X(l??"");l=l?F:void 0;let I=new URLSearchParams([...P.entries(),...E.entries()]),T=d(b),y=Se(l),G=I.get("pattern");try{let D=await i(T),x=M(D);if(y&&(x=x.filter(w=>w.num>=y.start&&w.num<=y.end)),G){let w=new RegExp(G);x=x.filter(fe=>w.test(fe.text));}return {contents:[{uri:s,mimeType:"text/plain",text:j(x)}]}}catch{let D=await r(T);return {contents:[{uri:s,mimeType:"application/json",text:JSON.stringify(D)}]}}}catch(c){let l=`Error: ${c instanceof Error?c.message:String(c)}`;return {contents:[{uri:s,mimeType:"text/plain",text:l}]}}}),n.onFileChanged&&n.onFileChanged(e=>{let o=d(e);a.server.sendResourceUpdated({uri:`files://${o}`});});}function ae(a,n,i){a.registerTool("global_find",{description:"Search for text across the entire workspace.",inputSchema:q,outputSchema:{matches:z$1.array(z$1.object({uri:z$1.string(),line:z$1.number(),column:z$1.number(),matchText:z$1.string(),context:z$1.string()})),count:z$1.number()}},async r=>{i?.onInput?.(r);try{let t=r.case_sensitive??!1,e=r.exact_match??!1,o=r.regex_mode??!1,s=await n.globalFind(r.query,{caseSensitive:t,exactMatch:e,regexMode:o});return i?.onOutput?.({count:s.length,matches:s}),f({count:s.length,matches:s})}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{error:e},isError:true}}});}function ce(a,n,i){let r=new ResourceTemplate("outlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("outlinks",r,{description:"Outgoing links from a specific file. Use the file path after outlinks://",mimeType:"application/json"},async(e,o)=>{try{let s=o.path,c=d(s),l=await n.resolveOutlinks(c);return {contents:[{uri:`outlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`outlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});let t=new ResourceTemplate("backlinks://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("backlinks",t,{description:"Incoming links (backlinks) to a specific file. Use the file path after backlinks://",mimeType:"application/json"},async(e,o)=>{try{let s=o.path,c=d(s),l=await n.resolveBacklinks(c);return {contents:[{uri:`backlinks://${s}`,mimeType:"application/json",text:JSON.stringify(l,null,2)}]}}catch(s){let c=`Error: ${s instanceof Error?s.message:String(s)}`;return {contents:[{uri:`backlinks://${o.path}`,mimeType:"application/json",text:JSON.stringify({error:c})}]}}});}function ue(a,n,i){a.registerTool("get_link_structure",{description:"Get all links in the workspace, showing relationships between documents.",inputSchema:{},outputSchema:{links:z$1.array(z$1.object({sourceUri:z$1.string(),targetUri:z$1.string(),subpath:z$1.string().optional(),displayText:z$1.string().optional(),resolved:z$1.boolean(),line:z$1.number(),column:z$1.number()}))}},async()=>{try{let r=await n.getLinkStructure();return i?.onOutput?.({links:r}),f({links:r})}catch(r){let t=`Error: ${r instanceof Error?r.message:String(r)}`;return {content:[{type:"text",text:t}],structuredContent:{error:t},isError:true}}});}function le(a,n,i){a.registerTool("add_link",{description:"Add a link to a document by finding a text pattern and replacing it with a link to the target.",inputSchema:B,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async r=>{i?.onInput?.(r);try{let t=d(r.path),e=d(r.link_to);await n.addLink(t,r.pattern,e);let o={success:!0,message:"Link added successfully."};return i?.onOutput?.(o),f(o)}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{success:false,message:e},isError:true}}});}function pe(a,n,i){a.registerTool("get_frontmatter_structure",{description:"Get frontmatter property values across documents. If path is provided, searches only that document. Otherwise, searches all documents.",inputSchema:K,outputSchema:{matches:z$1.array(z$1.object({path:z$1.string(),value:z$1.unknown()}))}},async r=>{i?.onInput?.(r);try{let t=r.path?d(r.path):void 0,e=await n.getFrontmatterStructure(r.property,t);return i?.onOutput?.({matches:e}),f({matches:e})}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{error:e},isError:true}}});}function de(a,n,i){a.registerTool("set_frontmatter",{description:"Set a frontmatter property on a document. Use null to remove the property.",inputSchema:V,outputSchema:{success:z$1.boolean(),message:z$1.string().optional()}},async r=>{i?.onInput?.(r);try{let t=d(r.path),e=r.value===null?void 0:r.value;await n.setFrontmatter(t,r.property,e);let o={success:!0,message:"Frontmatter updated successfully."};return i?.onOutput?.(o),f(o)}catch(t){let e=`Error: ${t instanceof Error?t.message:String(t)}`;return {content:[{type:"text",text:e}],structuredContent:{success:false,message:e},isError:true}}});}function me(a,n,i){let r=new ResourceTemplate("frontmatter://{+path}",{list:void 0,complete:i?{path:i}:void 0});a.registerResource("frontmatter",r,{description:"Frontmatter metadata for a specific file. Use the file path after frontmatter://",mimeType:"application/json"},async(t,e)=>{try{let o=e.path,s=d(o),c=await n.getFrontmatter(s);return {contents:[{uri:`frontmatter://${o}`,mimeType:"application/json",text:JSON.stringify(c,null,2)}]}}catch(o){let s=`Error: ${o instanceof Error?o.message:String(o)}`;return {contents:[{uri:`frontmatter://${e.path}`,mimeType:"application/json",text:JSON.stringify({error:s})}]}}});}function xe(a){if(a.length<=0)throw new Error("providers array must not be empty");if(a.length===1&&a[0])return a[0];let n=a[0];if(!n)throw new Error("The first item of providers array must be defined");if("readFile"in n&&"readDirectory"in n){let i=a,r={readFile:t=>n.readFile(t),readDirectory:t=>n.readDirectory(t)};return i.some(t=>t.onFileChanged)&&(r.onFileChanged=t=>{for(let e of i)e.onFileChanged?.(t);}),r}if("applyEdits"in n||"previewAndApplyEdits"in n)return n;if("provideDefinition"in n){let i=a,r={async provideDefinition(t,e){return (await Promise.all(i.map(s=>s.provideDefinition(t,e)))).flat()}};return i.some(t=>t.provideTypeDefinition)&&(r.provideTypeDefinition=async(t,e)=>{let o=i.filter(c=>c.provideTypeDefinition);return (await Promise.all(o.map(c=>c.provideTypeDefinition(t,e)))).flat()}),r}if("provideReferences"in n){let i=a,r={async provideReferences(t,e){return (await Promise.all(i.map(s=>s.provideReferences(t,e)))).flat()}};return i.some(t=>t.provideFileReferences)&&(r.provideFileReferences=async t=>{let e=i.filter(s=>s.provideFileReferences);return (await Promise.all(e.map(s=>s.provideFileReferences(t)))).flat()}),r}if("provideCallHierarchy"in n){let i=a;return {async provideCallHierarchy(r,t,e){return (await Promise.all(i.map(s=>s.provideCallHierarchy(r,t,e)))).flat()}}}if("provideDiagnostics"in n){let i=a,r={async provideDiagnostics(t){return (await Promise.all(i.map(o=>o.provideDiagnostics(t)))).flat()}};return i.some(t=>t.getWorkspaceDiagnostics)&&(r.getWorkspaceDiagnostics=async()=>{let t=i.filter(o=>o.getWorkspaceDiagnostics);return (await Promise.all(t.map(o=>o.getWorkspaceDiagnostics()))).flat()}),i.some(t=>t.onDiagnosticsChanged)&&(r.onDiagnosticsChanged=t=>{for(let e of i)e.onDiagnosticsChanged?.(t);}),r}if("provideDocumentSymbols"in n){let i=a;return {async provideDocumentSymbols(r){return (await Promise.all(i.map(e=>e.provideDocumentSymbols(r)))).flat()}}}if("globalFind"in n){let i=a;return {async globalFind(r,t){return (await Promise.all(i.map(o=>o.globalFind(r,t)))).flat()}}}if("getLinkStructure"in n){let i=a;return {async getLinkStructure(){return (await Promise.all(i.map(t=>t.getLinkStructure()))).flat()},async resolveOutlinks(r){return (await Promise.all(i.map(e=>e.resolveOutlinks(r)))).flat()},async resolveBacklinks(r){return (await Promise.all(i.map(e=>e.resolveBacklinks(r)))).flat()},async addLink(r,t,e){await Promise.all(i.map(o=>o.addLink(r,t,e)));}}}if("getFrontmatterStructure"in n){let i=a;return {async getFrontmatterStructure(r,t){return (await Promise.all(i.map(o=>o.getFrontmatterStructure(r,t)))).flat()},async getFrontmatter(r){let t=await Promise.all(i.map(e=>e.getFrontmatter(r)));return Object.assign({},...t)},async setFrontmatter(r,t,e){await Promise.all(i.map(o=>o.setFrontmatter(r,t,e)));}}}return n}function _e(a,n,i){let r=Array.isArray(n)?xe(n):n,t=i?.fileAccess,e=t?z(t.readDirectory):void 0,o=()=>{if(!t)throw new Error("fileAccess is required in options for providers that need symbol resolution");return new O(t,i?.resolverConfig)};if("readFile"in r&&"readDirectory"in r){se(a,r);return}if("applyEdits"in r||"previewAndApplyEdits"in r){if(!t)throw new Error("fileAccess is required in options when installing an EditProvider");let s=i;oe(a,r,t.readFile,{onInput:s?.onEditInput,onOutput:s?.onEditOutput});return}if("provideDefinition"in r){let s=r,c=i,l=o();Y(a,s,l,{onInput:c?.onDefinitionInput,onOutput:c?.onDefinitionOutput}),s.provideTypeDefinition&&Z(a,s,l,{onInput:c?.onTypeDefinitionInput,onOutput:c?.onTypeDefinitionOutput});return}if("provideReferences"in r){let s=r,c=i;ee(a,s,o(),{onInput:c?.onReferencesInput,onOutput:c?.onReferencesOutput}),re(a,s,{onInput:c?.onFileReferencesInput,onOutput:c?.onFileReferencesOutput});return}if("provideCallHierarchy"in r){let s=i;te(a,r,o(),{onInput:s?.onCallHierarchyInput,onOutput:s?.onCallHierarchyOutput});return}if("provideDiagnostics"in r){ne(a,r,e);return}if("provideDocumentSymbols"in r){ie(a,r,e);return}if("globalFind"in r){let s=i;ae(a,r,{onInput:s?.onGlobalFindInput,onOutput:s?.onGlobalFindOutput});return}if("getLinkStructure"in r){let s=r,c=i;ue(a,s,{onOutput:c?.onLinkStructureOutput}),le(a,s,{onInput:c?.onAddLinkInput,onOutput:c?.onAddLinkOutput}),ce(a,s,e);return}if("getFrontmatterStructure"in r){let s=r,c=i;pe(a,s,{onInput:c?.onFrontmatterStructureInput,onOutput:c?.onFrontmatterStructureOutput}),de(a,s,{onInput:c?.onSetFrontmatterInput,onOutput:c?.onSetFrontmatterOutput}),me(a,s,e);return}}
|
|
9
|
+
export{_e as install};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { F as FileAccessProvider, E as EditProvider, D as DefinitionProvider, R as ReferencesProvider, H as HierarchyProvider, a as DiagnosticsProvider, O as OutlineProvider, G as GlobalFindProvider, b as GraphProvider, c as FrontmatterProvider } from '../interfaces-B9NK_71f.js';
|
|
2
|
+
|
|
3
|
+
interface connectPipeOptions {
|
|
4
|
+
pipeName: string;
|
|
5
|
+
connectTimeout?: number;
|
|
6
|
+
}
|
|
7
|
+
interface LspPipeConnection {
|
|
8
|
+
readonly fileAccess?: FileAccessProvider;
|
|
9
|
+
readonly edit?: EditProvider;
|
|
10
|
+
readonly definition?: DefinitionProvider;
|
|
11
|
+
readonly references?: ReferencesProvider;
|
|
12
|
+
readonly hierarchy?: HierarchyProvider;
|
|
13
|
+
readonly diagnostics?: DiagnosticsProvider;
|
|
14
|
+
readonly outline?: OutlineProvider;
|
|
15
|
+
readonly globalFind?: GlobalFindProvider;
|
|
16
|
+
readonly graph?: GraphProvider;
|
|
17
|
+
readonly frontmatter?: FrontmatterProvider;
|
|
18
|
+
readonly availableMethods: string[];
|
|
19
|
+
disconnect(): void;
|
|
20
|
+
}
|
|
21
|
+
declare function connectPipe(options: connectPipeOptions): Promise<LspPipeConnection>;
|
|
22
|
+
|
|
23
|
+
interface servePipeOptions {
|
|
24
|
+
pipeName: string;
|
|
25
|
+
fileAccess: FileAccessProvider;
|
|
26
|
+
edit?: EditProvider;
|
|
27
|
+
definition?: DefinitionProvider;
|
|
28
|
+
references?: ReferencesProvider;
|
|
29
|
+
hierarchy?: HierarchyProvider;
|
|
30
|
+
diagnostics?: DiagnosticsProvider;
|
|
31
|
+
outline?: OutlineProvider;
|
|
32
|
+
globalFind?: GlobalFindProvider;
|
|
33
|
+
graph?: GraphProvider;
|
|
34
|
+
frontmatter?: FrontmatterProvider;
|
|
35
|
+
}
|
|
36
|
+
interface LspPipeServer {
|
|
37
|
+
readonly pipePath: string;
|
|
38
|
+
readonly connectionCount: number;
|
|
39
|
+
close(): Promise<void>;
|
|
40
|
+
}
|
|
41
|
+
declare function servePipe(options: servePipeOptions): Promise<LspPipeServer>;
|
|
42
|
+
|
|
43
|
+
export { type LspPipeConnection, type LspPipeServer, connectPipe, type connectPipeOptions, servePipe, type servePipeOptions };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import {connect,createServer}from'net';import {unlinkSync}from'fs';var v=class{socket;requestHandler;notificationHandler;nextId=1;pending=new Map;buffer="";destroyed=false;constructor(e,r){this.socket=e,this.requestHandler=r?.onRequest,this.notificationHandler=r?.onNotification,e.on("data",n=>this.handleData(n)),e.on("close",()=>this.rejectAll(new Error("Connection closed"))),e.on("error",n=>this.rejectAll(n));}sendRequest(e,r){return this.destroyed?Promise.reject(new Error("Transport destroyed")):new Promise((n,f)=>{let o=this.nextId++;this.pending.set(o,{resolve:n,reject:f});let c={type:"request",id:o,method:e,params:r};this.socket.write(`${JSON.stringify(c)}
|
|
2
|
+
`);})}sendNotification(e,r){if(this.destroyed)return;let n={type:"notification",method:e,params:r};this.socket.write(`${JSON.stringify(n)}
|
|
3
|
+
`);}destroy(){this.destroyed||(this.destroyed=true,this.rejectAll(new Error("Transport destroyed")),this.socket.destroy());}handleData(e){this.buffer+=e.toString();let r=this.buffer.split(`
|
|
4
|
+
`);this.buffer=r.pop()??"";for(let n of r){let f=n.replace(/\r$/,"");if(f)try{let o=JSON.parse(f);this.dispatch(o);}catch{}}}dispatch(e){switch(e.type){case "response":{let r=this.pending.get(e.id);r&&(this.pending.delete(e.id),e.error?r.reject(new Error(e.error.message)):r.resolve(e.result));break}case "request":{this.requestHandler&&this.requestHandler(e.method,e.params).then(r=>{if(this.destroyed)return;let n={type:"response",id:e.id,result:r};this.socket.write(`${JSON.stringify(n)}
|
|
5
|
+
`);}).catch(r=>{if(this.destroyed)return;let n={type:"response",id:e.id,error:{message:r instanceof Error?r.message:String(r)}};this.socket.write(`${JSON.stringify(n)}
|
|
6
|
+
`);});break}case "notification":{this.notificationHandler?.(e.method,e.params);break}}}rejectAll(e){for(let[,r]of this.pending)r.reject(e);this.pending.clear();}};function w(s){return process.platform==="win32"?`\\\\.\\pipe\\${s}`:`/tmp/${s}.sock`}var k=[{providerKey:"fileAccess",methods:["readFile","readDirectory"]},{providerKey:"edit",methods:["applyEdits","previewAndApplyEdits"]},{providerKey:"definition",methods:["provideDefinition"]},{providerKey:"references",methods:["provideReferences"]},{providerKey:"hierarchy",methods:["provideCallHierarchy"]},{providerKey:"diagnostics",methods:["provideDiagnostics","getWorkspaceDiagnostics"]},{providerKey:"outline",methods:["provideDocumentSymbols"]},{providerKey:"globalFind",methods:["globalFind"]},{providerKey:"graph",methods:["getLinkStructure","resolveOutlinks","resolveBacklinks","addLink"]},{providerKey:"frontmatter",methods:["getFrontmatterStructure","getFrontmatter","setFrontmatter"]}];function E(s){let{pipeName:e,connectTimeout:r=5e3}=s,n=w(e);return new Promise((f,o)=>{let c=connect(n),i=false,d=setTimeout(()=>{i||(i=true,c.destroy(),o(new Error(`Connection timeout after ${r}ms`)));},r);c.on("error",t=>{i||(i=true,clearTimeout(d),o(t));}),c.on("connect",()=>{if(clearTimeout(d),i)return;let t,l,a=new v(c,{onNotification:(h,P)=>{h==="onDiagnosticsChanged"&&t&&t(P[0]),h==="onFileChanged"&&l&&l(P[0]);}});a.sendRequest("_handshake",[]).then(h=>{if(i)return;let m=h.methods,p={};for(let{providerKey:g,methods:R}of k){let F=R.filter(u=>m.includes(`${g}.${u}`));if(F.length===0)continue;let y={};for(let u of F)y[u]=(...D)=>a.sendRequest(`${g}.${u}`,D);g==="diagnostics"&&m.includes("onDiagnosticsChanged")&&(y.onDiagnosticsChanged=u=>{t=u;}),g==="fileAccess"&&m.includes("onFileChanged")&&(y.onFileChanged=u=>{l=u;}),p[g]=y;}i=true,f({fileAccess:p.fileAccess,edit:p.edit,definition:p.definition,references:p.references,hierarchy:p.hierarchy,diagnostics:p.diagnostics,outline:p.outline,globalFind:p.globalFind,graph:p.graph,frontmatter:p.frontmatter,availableMethods:m,disconnect(){a.destroy();}});}).catch(h=>{i||(i=true,a.destroy(),o(h instanceof Error?h:new Error(String(h))));});});})}function S(s){let{pipeName:e}=s,r=w(e),n=new Map;for(let{providerKey:i,methods:d}of k){let t=s[i];if(t)for(let l of d){let a=t[l];if(typeof a=="function"){let h=`${i}.${l}`;n.set(h,(...P)=>a.apply(t,P));}}}let f=[...n.keys()];s.diagnostics?.onDiagnosticsChanged&&f.push("onDiagnosticsChanged"),s.fileAccess.onFileChanged&&f.push("onFileChanged");let o=new Set,c=createServer(i=>{let d=new v(i,{onRequest:async(t,l)=>{if(t==="_handshake")return {methods:f};let a=n.get(t);if(!a)throw new Error(`Unknown method: ${t}`);return a(...l)}});o.add(d),i.on("close",()=>o.delete(d));});return s.diagnostics?.onDiagnosticsChanged&&s.diagnostics.onDiagnosticsChanged(i=>{for(let d of o)d.sendNotification("onDiagnosticsChanged",[i]);}),s.fileAccess.onFileChanged&&s.fileAccess.onFileChanged(i=>{for(let d of o)d.sendNotification("onFileChanged",[i]);}),new Promise((i,d)=>{if(process.platform!=="win32")try{unlinkSync(r);}catch{}c.on("error",d),c.listen(r,()=>{c.removeListener("error",d),i({get pipePath(){return r},get connectionCount(){return o.size},async close(){for(let t of o)t.destroy();if(o.clear(),await new Promise((t,l)=>{c.close(a=>{a?l(a):t();});}),process.platform!=="win32")try{unlinkSync(r);}catch{}}});});})}export{E as connectPipe,S as servePipe};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Zod schemas for MCP tool inputs and outputs.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
declare const FuzzyPositionSchema: z.ZodObject<{
|
|
8
|
+
uri: z.ZodString;
|
|
9
|
+
symbol_name: z.ZodString;
|
|
10
|
+
line_hint: z.ZodNumber;
|
|
11
|
+
order_hint: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
declare const ApplyEditSchema: z.ZodObject<{
|
|
14
|
+
uri: z.ZodString;
|
|
15
|
+
start_hash: z.ZodString;
|
|
16
|
+
end_hash: z.ZodOptional<z.ZodString>;
|
|
17
|
+
replace_text: z.ZodString;
|
|
18
|
+
description: z.ZodString;
|
|
19
|
+
}, z.core.$strip>;
|
|
20
|
+
declare const CallHierarchySchema: z.ZodObject<{
|
|
21
|
+
uri: z.ZodString;
|
|
22
|
+
symbol_name: z.ZodString;
|
|
23
|
+
line_hint: z.ZodNumber;
|
|
24
|
+
order_hint: z.ZodDefault<z.ZodNumber>;
|
|
25
|
+
direction: z.ZodEnum<{
|
|
26
|
+
incoming: "incoming";
|
|
27
|
+
outgoing: "outgoing";
|
|
28
|
+
}>;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
declare const GlobalFindSchema: z.ZodObject<{
|
|
31
|
+
query: z.ZodString;
|
|
32
|
+
case_sensitive: z.ZodDefault<z.ZodBoolean>;
|
|
33
|
+
exact_match: z.ZodDefault<z.ZodBoolean>;
|
|
34
|
+
regex_mode: z.ZodDefault<z.ZodBoolean>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
declare const AddLinkSchema: z.ZodObject<{
|
|
37
|
+
path: z.ZodString;
|
|
38
|
+
pattern: z.ZodString;
|
|
39
|
+
link_to: z.ZodString;
|
|
40
|
+
}, z.core.$strip>;
|
|
41
|
+
declare const FileReferencesSchema: z.ZodObject<{
|
|
42
|
+
uri: z.ZodString;
|
|
43
|
+
}, z.core.$strip>;
|
|
44
|
+
declare const GetFrontmatterStructureSchema: z.ZodObject<{
|
|
45
|
+
property: z.ZodString;
|
|
46
|
+
path: z.ZodOptional<z.ZodString>;
|
|
47
|
+
}, z.core.$strip>;
|
|
48
|
+
declare const SetFrontmatterSchema: z.ZodObject<{
|
|
49
|
+
path: z.ZodString;
|
|
50
|
+
property: z.ZodString;
|
|
51
|
+
value: z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>, z.ZodNumber, z.ZodArray<z.ZodNumber>, z.ZodBoolean, z.ZodArray<z.ZodBoolean>, z.ZodNull]>;
|
|
52
|
+
}, z.core.$strip>;
|
|
53
|
+
|
|
54
|
+
export { AddLinkSchema, ApplyEditSchema, CallHierarchySchema, FileReferencesSchema, FuzzyPositionSchema, GetFrontmatterStructureSchema, GlobalFindSchema, SetFrontmatterSchema };
|
package/dist/schemas.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import {z}from'zod';var t=z.string().describe("The relative file path"),r=z.string().describe("The text of the symbol to find"),o=z.number().int().positive().describe("Approximate 1-based line number where the symbol is expected"),i=z.number().int().min(0).default(0).describe("0-based index of which occurrence to target if symbol appears multiple times"),l=z.object({uri:t,symbol_name:r,line_hint:o,order_hint:i}),d=z.object({uri:t,start_hash:z.string().describe('Start line reference from hashline output, format "<line>:<hash>" (e.g., "3:a1"). Copy this exactly from the files:// resource output.'),end_hash:z.string().optional().describe('End line reference for multi-line edits (e.g., "5:0e"). The range is inclusive. Omit for single-line edits (defaults to start_hash).'),replace_text:z.string().describe("The new text to replace the entire line range with"),description:z.string().describe("Rationale for the edit")}),p=z.object({uri:z.string().describe("The file URI or path"),symbol_name:r,line_hint:o,order_hint:i,direction:z.enum(["incoming","outgoing"]).describe("Direction of the call hierarchy")}),n=z.string().describe("The search query"),s=z.boolean().default(false).describe("Whether the search is case-sensitive"),a=z.boolean().default(false).describe("Whether to match exact words only"),c=z.boolean().default(false).describe("Whether the query is a regular expression"),b=z.object({query:n,case_sensitive:s,exact_match:a,regex_mode:c}),m=z.object({path:z.string().describe("The path to the document to modify"),pattern:z.string().describe("The text pattern to find and replace with a link"),link_to:z.string().describe("The target URI the link should point to")}),u=z.object({uri:t}),f=z.object({property:z.string().describe("The frontmatter property name to search for"),path:z.string().optional().describe("Optional path to limit the search to a specific document. If not provided, searches all documents.")}),g=z.object({path:z.string().describe("The path to the document to modify"),property:z.string().describe("The frontmatter property name to set"),value:z.union([z.string(),z.array(z.string()),z.number(),z.array(z.number()),z.boolean(),z.array(z.boolean()),z.null()]).describe("The value to set. Can be a string, number, boolean, array of these types, or null to remove.")});
|
|
2
|
+
export{m as AddLinkSchema,d as ApplyEditSchema,p as CallHierarchySchema,u as FileReferencesSchema,l as FuzzyPositionSchema,f as GetFrontmatterStructureSchema,b as GlobalFindSchema,g as SetFrontmatterSchema};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opticlm/connector",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"description": "Provides an abstract interface that allows LLMs to connect to fact sources such as LSPs, code diagnostics, symbol definitions/references, links, and frontmatter",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"./mcp": {
|
|
17
17
|
"types": "./dist/mcp/index.d.ts",
|
|
18
18
|
"default": "./dist/mcp/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./schemas": {
|
|
21
|
+
"types": "./dist/schemas.d.ts",
|
|
22
|
+
"default": "./dist/schemas.js"
|
|
19
23
|
}
|
|
20
24
|
},
|
|
21
25
|
"files": [
|
|
@@ -52,6 +56,7 @@
|
|
|
52
56
|
"@types/node": "^25",
|
|
53
57
|
"tsup": "^8.5.1",
|
|
54
58
|
"typescript": "^5.9",
|
|
59
|
+
"nanoid": "^5.1.7",
|
|
55
60
|
"vitest": "^4"
|
|
56
61
|
},
|
|
57
62
|
"engines": {
|
|
@@ -1,129 +0,0 @@
|
|
|
1
|
-
import { P as PendingEditOperation, U as UnifiedUri, l as FuzzyPosition, h as ExactPosition } from './capabilities-CQHv2shL.js';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Infrastructure Interfaces for MCP LSP Driver SDK
|
|
5
|
-
*
|
|
6
|
-
* The SDK consumer (Plugin Developer) must implement these interfaces
|
|
7
|
-
* to bridge the SDK to the specific IDE.
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Provides access to the file system for reading files.
|
|
12
|
-
* Since the SDK is responsible for resolving FuzzyPosition to ExactPosition,
|
|
13
|
-
* it needs direct access to read files from the disk.
|
|
14
|
-
*/
|
|
15
|
-
interface FileAccessProvider {
|
|
16
|
-
/**
|
|
17
|
-
* Reads the content of a file from the disk (ignoring unsaved IDE buffers).
|
|
18
|
-
* Used for symbol resolution and context retrieval.
|
|
19
|
-
*
|
|
20
|
-
* @param uri - The URI of the file to read
|
|
21
|
-
* @returns The content of the file as a string
|
|
22
|
-
* @throws Error if the file cannot be read
|
|
23
|
-
*/
|
|
24
|
-
readFile(uri: UnifiedUri): Promise<string>;
|
|
25
|
-
/**
|
|
26
|
-
* Read children in a directory, exluding git-ignored files, similar to Unix `ls` command
|
|
27
|
-
* @param relativePath - The path to the folder to read
|
|
28
|
-
* @returns Array of file/folder paths in the directory
|
|
29
|
-
*/
|
|
30
|
-
readDirectory(relativePath: UnifiedUri): Promise<string[]>;
|
|
31
|
-
/**
|
|
32
|
-
* Called by the driver to register a callback for file changes.
|
|
33
|
-
* When provided, the files:// resources become subscribable.
|
|
34
|
-
* The plugin should call the registered callback whenever a file changes.
|
|
35
|
-
*
|
|
36
|
-
* @param callback - The callback to invoke when a file changes
|
|
37
|
-
*/
|
|
38
|
-
onFileChanged?(callback: (uri: UnifiedUri) => void): void;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Provides edit capabilities for applying changes to files.
|
|
42
|
-
* At least one of the methods must be implemented.
|
|
43
|
-
*/
|
|
44
|
-
interface EditProvider {
|
|
45
|
-
/**
|
|
46
|
-
* Applies edits directly without user interaction.
|
|
47
|
-
* Use this when user approval is handled elsewhere or not needed.
|
|
48
|
-
*
|
|
49
|
-
* @param operation - The pending edit operation to apply
|
|
50
|
-
* @returns true if applied successfully, false if failed
|
|
51
|
-
*/
|
|
52
|
-
applyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
53
|
-
/**
|
|
54
|
-
* Displays a diff view or a confirmation dialog in the IDE.
|
|
55
|
-
* The user decides whether to apply the edits or discard them.
|
|
56
|
-
*
|
|
57
|
-
* @param operation - The pending edit operation to preview
|
|
58
|
-
* @returns true if applied, false if rejected/cancelled
|
|
59
|
-
*/
|
|
60
|
-
previewAndApplyEdits?(operation: PendingEditOperation): Promise<boolean>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Smart Resolver Logic for MCP LSP Driver SDK
|
|
65
|
-
*
|
|
66
|
-
* This is the internal engine of the SDK. It translates the LLM's imprecise
|
|
67
|
-
* instructions into precise coordinates.
|
|
68
|
-
*/
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Configuration options for the SymbolResolver.
|
|
72
|
-
*/
|
|
73
|
-
interface ResolverConfig {
|
|
74
|
-
/**
|
|
75
|
-
* Number of lines to scan above and below the lineHint if the symbol
|
|
76
|
-
* is not found at the exact line. Defaults to 2.
|
|
77
|
-
*/
|
|
78
|
-
lineSearchRadius?: number;
|
|
79
|
-
}
|
|
80
|
-
/**
|
|
81
|
-
* Error thrown when a symbol cannot be resolved.
|
|
82
|
-
*/
|
|
83
|
-
declare class SymbolResolutionError extends Error {
|
|
84
|
-
readonly symbolName: string;
|
|
85
|
-
readonly lineHint: number;
|
|
86
|
-
readonly reason: string;
|
|
87
|
-
constructor(symbolName: string, lineHint: number, reason: string);
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* The SymbolResolver translates fuzzy positions (as provided by an LLM)
|
|
91
|
-
* into exact positions that can be used by the IDE.
|
|
92
|
-
*
|
|
93
|
-
* Algorithm:
|
|
94
|
-
* 1. Read file content via FileAccessProvider.
|
|
95
|
-
* 2. Split content into lines.
|
|
96
|
-
* 3. Target the lineHint (converting 1-based to 0-based).
|
|
97
|
-
* 4. Search for symbolName in that line.
|
|
98
|
-
* - If orderHint is 0, find first occurrence.
|
|
99
|
-
* - If orderHint is N, find Nth occurrence.
|
|
100
|
-
* 5. Robustness Fallback:
|
|
101
|
-
* - If the line is empty or symbol not found at lineHint,
|
|
102
|
-
* scan +/- lineSearchRadius lines to handle minor line shifts.
|
|
103
|
-
* 6. Return ExactPosition (line, character start index).
|
|
104
|
-
*/
|
|
105
|
-
declare class SymbolResolver {
|
|
106
|
-
private readonly fs;
|
|
107
|
-
private readonly lineSearchRadius;
|
|
108
|
-
constructor(fs: FileAccessProvider, config?: ResolverConfig);
|
|
109
|
-
/**
|
|
110
|
-
* Resolves a fuzzy position to an exact position.
|
|
111
|
-
*
|
|
112
|
-
* @param uri - The URI of the file
|
|
113
|
-
* @param fuzzy - The fuzzy position provided by the LLM
|
|
114
|
-
* @returns The exact position in the file
|
|
115
|
-
* @throws SymbolResolutionError if the symbol cannot be found
|
|
116
|
-
*/
|
|
117
|
-
resolvePosition(uri: UnifiedUri, fuzzy: FuzzyPosition): Promise<ExactPosition>;
|
|
118
|
-
/**
|
|
119
|
-
* Finds the Nth occurrence of a symbol in a line.
|
|
120
|
-
*
|
|
121
|
-
* @param line - The line to search in (may be undefined if out of bounds)
|
|
122
|
-
* @param symbolName - The symbol to find
|
|
123
|
-
* @param orderHint - Which occurrence to find (0-based)
|
|
124
|
-
* @returns The character offset of the symbol, or null if not found
|
|
125
|
-
*/
|
|
126
|
-
private findSymbolInLine;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
export { type EditProvider as E, type FileAccessProvider as F, type ResolverConfig as R, SymbolResolutionError as S, SymbolResolver as a };
|