@deepseek-ai/dsh-tool-lsp 0.0.1-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +28 -0
- package/README.i18n.yaml +6 -0
- package/README.md +90 -0
- package/README.zh.md +90 -0
- package/lib/index.js +412 -0
- package/lib/invariant.js +23 -0
- package/lib/types/index.d.ts +40 -0
- package/lib/types/invariant.d.ts +16 -0
- package/lib/types/render.d.ts +74 -0
- package/lib/types/session-cwd.d.ts +16 -0
- package/package.json +61 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, DeepSeek
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.i18n.yaml
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# Bilingual-pair consistency record (docs/i18n/README.md): the git blob hash of each
|
|
2
|
+
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
|
+
# after editing either side, bring the other along and re-record with:
|
|
4
|
+
# pnpm run verify-translation-pairing --write packages/lsp/tool-lsp/README.md
|
|
5
|
+
README.md: 36fa3026cba3fc692ae5a86c7307acf77563cf53
|
|
6
|
+
README.zh.md: 1b0b1f4847cf711e7939b79ac2f8035f26cb73aa
|
package/README.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-tool-lsp
|
|
2
|
+
|
|
3
|
+
English | [中文](README.zh.md)
|
|
4
|
+
|
|
5
|
+
The model-facing **`lsp` tool** over `ctx.lsp`: one read-only tool with four operations for precise code navigation. It owns the model schema, prompt guidance, coordinate conversion, result limits and formatting, and UI presentation; it imports no provider.
|
|
6
|
+
|
|
7
|
+
Namespace plugin (`name` / `inject` / `Config` / `apply`, no default export). Injects `tools`, `lsp`, and `systemPrompt`.
|
|
8
|
+
|
|
9
|
+
## The tool
|
|
10
|
+
|
|
11
|
+
`lsp` accepts `operation` (`goToDefinition` | `findReferences` | `goToImplementation` | `hover`), `file_path`, `line`, and `character`. `line` and `character` are positive, one-based UTF-16 cursor coordinates; the tool converts them to the seam's zero-based positions and converts rendered locations back. `findReferences` includes declarations so impact analysis does not omit the defining site. Provider, language id, workspace root, limits, timeout, initialization, and executable stay outside model input.
|
|
12
|
+
|
|
13
|
+
The tool requires the workspace root from the session `header.cwd`, with no fallback: absence fails as `LSP_WORKSPACE_REQUIRED` before querying. Its canonical result is the complete normalized Service Definition union: `{ kind: "locations", locations, resolvedWorkspaceUri }` or `{ kind: "hover", hover }`; Code Mode can inspect every acquired location and zero-based range directly. Native rendering projects stable, file-grouped `path:line:character` entries against the provider's canonical workspace URI rather than applying host-platform path rules to the session cwd. A `file:` URI becomes a workspace-relative path inside that URI or a URI-derived absolute path outside it; malformed and non-`file:` URIs stay verbatim. Empty locations and `null` hover are successful no-result responses; malformed provider payloads remain structured errors.
|
|
14
|
+
|
|
15
|
+
## Configuration
|
|
16
|
+
|
|
17
|
+
| Key | Default | Meaning |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `maxLocations` | `100` | Largest number of rendered locations before an omission marker. |
|
|
20
|
+
| `maxResultChars` | `16000` | Largest complete rendered result, including truncation metadata. |
|
|
21
|
+
| `timeoutMs` | `60000` | Tool-call timeout budget, enforced by `dsh-timeout-policy`; covers the complete queued open/query/close lifecycle and is not model-configurable. |
|
|
22
|
+
|
|
23
|
+
## Model Experience
|
|
24
|
+
|
|
25
|
+
### System prompt
|
|
26
|
+
|
|
27
|
+
#### What the model sees
|
|
28
|
+
|
|
29
|
+
One system-prompt section (order 112) positions LSP as a precision aid with the following text:
|
|
30
|
+
|
|
31
|
+
##### Verbatim guidance
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
Use search/read for ordinary navigation. Use lsp when textual matches are ambiguous or before a change requires precise definitions, implementations, or references. Positions are one-based line and character (UTF-16) at the cursor; an off-symbol position may return no results. findReferences always includes the declaration.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Token effect
|
|
38
|
+
|
|
39
|
+
Fixed guidance cost on every request while the plugin is active.
|
|
40
|
+
|
|
41
|
+
#### KV Cache effect
|
|
42
|
+
|
|
43
|
+
Prefix-stable while the plugin scope and guidance text are unchanged; activation or disposal may invalidate reuse from this section.
|
|
44
|
+
|
|
45
|
+
### Tool schema
|
|
46
|
+
|
|
47
|
+
#### What the model sees
|
|
48
|
+
|
|
49
|
+
The model sees the generated [`lsp` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-lsp).
|
|
50
|
+
|
|
51
|
+
#### Token effect
|
|
52
|
+
|
|
53
|
+
Fixed schema cost on every request while enabled; the `timeoutMs` budget is never sent to the model.
|
|
54
|
+
|
|
55
|
+
#### KV Cache effect
|
|
56
|
+
|
|
57
|
+
Prefix-stable while the visible tool definition and order are unchanged; registration lifecycle or scoped restrictions may invalidate reuse from the first changed schema token.
|
|
58
|
+
|
|
59
|
+
### Results
|
|
60
|
+
|
|
61
|
+
#### What the model sees
|
|
62
|
+
|
|
63
|
+
File-grouped `path:line:character` location lines or normalized hover text, capped first by `maxLocations` and then by `maxResultChars`; omission and truncation markers are included inside the complete character cap. These caps affect only Native/model presentation, not the canonical value. Empty results use distinct `No results.` / `No hover information.` lines.
|
|
64
|
+
|
|
65
|
+
#### Token effect
|
|
66
|
+
|
|
67
|
+
Capped per tool result by `maxResultChars`, with `maxLocations` additionally bounding navigation item count.
|
|
68
|
+
|
|
69
|
+
#### KV Cache effect
|
|
70
|
+
|
|
71
|
+
Tool results append after the cached request prefix and do not directly invalidate it.
|
|
72
|
+
|
|
73
|
+
### UI presentation
|
|
74
|
+
|
|
75
|
+
#### What the model sees
|
|
76
|
+
|
|
77
|
+
Nothing. The client renders a generic search card — `{ card: 'generic', kind: 'search', title, locations: [{ path, line }] }` — whose args-derived title carries the operation and one-based cursor; follow-along focuses the queried line while the title preserves the column.
|
|
78
|
+
|
|
79
|
+
#### Token effect
|
|
80
|
+
|
|
81
|
+
Zero direct token effect because rendering is client-side only.
|
|
82
|
+
|
|
83
|
+
#### KV Cache effect
|
|
84
|
+
|
|
85
|
+
None; UI presentation is outside the model request.
|
|
86
|
+
|
|
87
|
+
## Known Limitations and Deferred Work
|
|
88
|
+
|
|
89
|
+
- **UTF-16 cursor coordinates** — columns are exact for the protocol but hard for a model to count around non-BMP characters; an off-symbol position may return empty results, so the prompt explains the convention without encouraging broad LSP use ([seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md)).
|
|
90
|
+
- **No cross-server completeness promise** — supported servers may return empty or partial results depending on indexing readiness; the tool promises no completeness across languages or servers.
|
package/README.zh.md
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# @deepseek-ai/dsh-tool-lsp
|
|
2
|
+
|
|
3
|
+
[English](README.md) | 中文
|
|
4
|
+
|
|
5
|
+
面向模型的 **`lsp` 工具**,基于 `ctx.lsp`:一个只读工具,通过四种操作执行精确代码导航。它拥有模型 schema、提示词指引、坐标转换、结果限制与格式化,以及 UI 呈现;不导入任何提供方。
|
|
6
|
+
|
|
7
|
+
Namespace 插件(`name`/`inject`/`Config`/`apply`,无默认导出)。注入 `tools`、`lsp` 和 `systemPrompt`。
|
|
8
|
+
|
|
9
|
+
## 工具
|
|
10
|
+
|
|
11
|
+
`lsp` 接受 `operation`(`goToDefinition` | `findReferences` | `goToImplementation` | `hover`)、`file_path`、`line` 和 `character`。`line` 与 `character` 是正的、从 1 开始的 UTF-16 光标坐标;工具将其转换为 seam 从零开始的位置,并把渲染位置转换回来。`findReferences` 包含声明,因此影响分析不会遗漏定义位置。提供方、language id、Workspace 根、限制、超时、初始化和可执行文件均不进入模型输入。
|
|
12
|
+
|
|
13
|
+
该工具要求从会话 `header.cwd` 取得 Workspace 根,没有回退值:缺失时会在查询前以 `LSP_WORKSPACE_REQUIRED` 失败。其规范结果是完整的已规范化 Service Definition 联合:`{ kind: "locations", locations, resolvedWorkspaceUri }` 或 `{ kind: "hover", hover }`;Code Mode 可以直接检查每个已取得的位置和从零开始的范围。原生渲染以提供方的规范工作区 URI 为基准,投影按文件稳定分组的 `path:line:character` 条目,而不对会话 cwd 应用宿主平台路径规则。`file:` URI 落在该工作区 URI 内时成为工作区相对路径,位于其外时成为从 URI 派生的绝对路径;格式错误的 URI 与非 `file:` URI 保持原样。空位置和 `null` hover 都是成功的无结果响应;格式错误的提供方载荷仍是结构化错误。
|
|
14
|
+
|
|
15
|
+
## 配置
|
|
16
|
+
|
|
17
|
+
| Key | 默认值 | 含义 |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| `maxLocations` | `100` | 出现省略标记前可渲染位置的最大数量。 |
|
|
20
|
+
| `maxResultChars` | `16000` | 完整渲染结果的最大长度,包括截断元数据。 |
|
|
21
|
+
| `timeoutMs` | `60000` | 由 `dsh-timeout-policy` 强制执行的工具调用超时预算;覆盖完整的排队打开/查询/关闭生命周期,且模型不可配置。 |
|
|
22
|
+
|
|
23
|
+
## 模型体验
|
|
24
|
+
|
|
25
|
+
### 系统提示词
|
|
26
|
+
|
|
27
|
+
#### 模型看到的内容
|
|
28
|
+
|
|
29
|
+
一个系统提示词区段(顺序 112)将 LSP 定位为精确辅助工具,文本如下:
|
|
30
|
+
|
|
31
|
+
##### 逐字指引
|
|
32
|
+
|
|
33
|
+
```markdown
|
|
34
|
+
Use search/read for ordinary navigation. Use lsp when textual matches are ambiguous or before a change requires precise definitions, implementations, or references. Positions are one-based line and character (UTF-16) at the cursor; an off-symbol position may return no results. findReferences always includes the declaration.
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
#### Token 影响
|
|
38
|
+
|
|
39
|
+
插件处于活跃状态时,每次请求承担固定指引成本。
|
|
40
|
+
|
|
41
|
+
#### KV Cache 影响
|
|
42
|
+
|
|
43
|
+
只要插件 scope 与指引文本不变,前缀就保持稳定;激活或释放可能使从该区段起的复用失效。
|
|
44
|
+
|
|
45
|
+
### 工具 schema
|
|
46
|
+
|
|
47
|
+
#### 模型看到的内容
|
|
48
|
+
|
|
49
|
+
模型会看到生成的 [`lsp` schema](../../../docs/tool-catalog.md#deepseek-aidsh-tool-lsp)。
|
|
50
|
+
|
|
51
|
+
#### Token 影响
|
|
52
|
+
|
|
53
|
+
启用期间,每次请求承担固定 schema 成本;`timeoutMs` 预算绝不会发给模型。
|
|
54
|
+
|
|
55
|
+
#### KV Cache 影响
|
|
56
|
+
|
|
57
|
+
只要可见工具定义与顺序不变,前缀就保持稳定;注册生命周期或 scope 限制可能使从第一个变化的 schema token 起的复用失效。
|
|
58
|
+
|
|
59
|
+
### 结果
|
|
60
|
+
|
|
61
|
+
#### 模型看到的内容
|
|
62
|
+
|
|
63
|
+
按文件分组的 `path:line:character` 位置行或规范化 hover 文本,先由 `maxLocations` 限制,再由 `maxResultChars` 限制;省略与截断标记计入完整字符上限。这些上限只影响原生/模型呈现,不影响规范值。空结果使用不同的 `No results.`/`No hover information.` 行。
|
|
64
|
+
|
|
65
|
+
#### Token 影响
|
|
66
|
+
|
|
67
|
+
每项工具结果以 `maxResultChars` 为上限,`maxLocations` 还会限制导航项数量。
|
|
68
|
+
|
|
69
|
+
#### KV Cache 影响
|
|
70
|
+
|
|
71
|
+
工具结果追加在已缓存请求前缀之后,不会直接使其失效。
|
|
72
|
+
|
|
73
|
+
### UI 呈现
|
|
74
|
+
|
|
75
|
+
#### 模型看到的内容
|
|
76
|
+
|
|
77
|
+
无。客户端渲染通用搜索卡片:`{ card: 'generic', kind: 'search', title, locations: [{ path, line }] }`;从 args 派生的标题携带操作与从 1 开始的光标,跟随焦点对准查询行,标题则保留列号。
|
|
78
|
+
|
|
79
|
+
#### Token 影响
|
|
80
|
+
|
|
81
|
+
直接 token 影响为零,因为渲染只发生在客户端。
|
|
82
|
+
|
|
83
|
+
#### KV Cache 影响
|
|
84
|
+
|
|
85
|
+
无;UI 呈现位于模型请求之外。
|
|
86
|
+
|
|
87
|
+
## 已知限制与暂缓事项
|
|
88
|
+
|
|
89
|
+
- **UTF-16 光标坐标**:列坐标与协议精确一致,但模型难以在非 BMP 字符周围计数;非 symbol 位置可能返回空结果,因此提示词解释了该约定,但不会鼓励宽泛使用 LSP(见 [seam Agent Note](../../../.agents/notes/implemented/architecture/2026-07-15-lsp-capability-seam.md))。
|
|
90
|
+
- **不承诺跨服务器完整性**:受支持的服务器仍可能根据索引就绪情况返回空或部分结果;该工具不承诺跨语言或服务器的完整性。
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,412 @@
|
|
|
1
|
+
import z from "@deepseek-ai/schemastery";
|
|
2
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
3
|
+
import { assertNever } from "@deepseek-ai/dsh-llm";
|
|
4
|
+
import { LspError } from "@deepseek-ai/dsh-lsp";
|
|
5
|
+
import { MAX_TIMER_DELAY_MS } from "@deepseek-ai/dsh-timeout";
|
|
6
|
+
import { posix, win32 } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
//#region lib/types/render.js
|
|
9
|
+
/**
|
|
10
|
+
* Pure formatting and coordinate conversion for the `lsp` tool: one-based↔zero-based UTF-16 cursor
|
|
11
|
+
* conversion, workspace-grouped location rendering with `file:`-URI resolution, complete-result
|
|
12
|
+
* capping, and UI presentation. No I/O — a UI may call the presenter on live streaming and on
|
|
13
|
+
* replay, so it depends only on the tool arguments.
|
|
14
|
+
* @module @deepseek-ai/dsh-tool-lsp/render
|
|
15
|
+
*/
|
|
16
|
+
/** The four operations the tool exposes, as a runtime tuple for schema enum + validation. */
|
|
17
|
+
const LSP_OPERATIONS = [
|
|
18
|
+
"goToDefinition",
|
|
19
|
+
"findReferences",
|
|
20
|
+
"goToImplementation",
|
|
21
|
+
"hover"
|
|
22
|
+
];
|
|
23
|
+
/** Default cap on rendered locations before an omission marker is appended. */
|
|
24
|
+
const DEFAULT_MAX_LOCATIONS = 100;
|
|
25
|
+
/** Default cap on the complete rendered tool result, including truncation metadata. */
|
|
26
|
+
const DEFAULT_MAX_RESULT_CHARS = 16e3;
|
|
27
|
+
/**
|
|
28
|
+
* Validate and convert model arguments: `operation` must be one of the four; `line`/`character` are
|
|
29
|
+
* positive one-based integers converted to the seam's zero-based position.
|
|
30
|
+
* @param args - the schema-validated raw arguments.
|
|
31
|
+
* @returns the validated input with a zero-based position.
|
|
32
|
+
* @throws Error when the operation is unknown or a coordinate is not a positive integer.
|
|
33
|
+
*/
|
|
34
|
+
function parseLspArgs(args) {
|
|
35
|
+
if (!isOperation(args.operation)) throw new Error(`operation must be one of ${LSP_OPERATIONS.join(", ")}`);
|
|
36
|
+
if (args.file_path.trim().length === 0) throw new Error("file_path must be a non-empty string");
|
|
37
|
+
const line = oneBased(args.line, "line");
|
|
38
|
+
const character = oneBased(args.character, "character");
|
|
39
|
+
return {
|
|
40
|
+
operation: args.operation,
|
|
41
|
+
filePath: args.file_path,
|
|
42
|
+
position: {
|
|
43
|
+
line: line - 1,
|
|
44
|
+
character: character - 1
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/** Whether a string is one of the four operations. */
|
|
49
|
+
function isOperation(value) {
|
|
50
|
+
return LSP_OPERATIONS.includes(value);
|
|
51
|
+
}
|
|
52
|
+
/** Validate a one-based coordinate is a positive integer. */
|
|
53
|
+
function oneBased(value, name) {
|
|
54
|
+
if (!Number.isInteger(value) || value < 1) throw new Error(`${name} must be a positive integer (one-based)`);
|
|
55
|
+
return value;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Render a locations result grouped by file, converting each zero-based location back to a one-based
|
|
59
|
+
* `path:line:character` entry. A `file:` URI inside the workspace becomes a workspace-relative path;
|
|
60
|
+
* outside it, a URI-derived absolute path; a non-`file:` URI is kept verbatim. Applies `maxLocations` and
|
|
61
|
+
* appends an omission marker when it truncates by count, then applies the complete result cap.
|
|
62
|
+
* @param locations - the seam's locations (possibly empty).
|
|
63
|
+
* @param workspaceUri - the provider's canonical workspace `file:` URI.
|
|
64
|
+
* @param maxLocations - the cap before truncation.
|
|
65
|
+
* @param maxResultChars - the complete rendered-text cap, including truncation metadata.
|
|
66
|
+
* @returns the rendered text; a distinct no-result line when there are none.
|
|
67
|
+
*/
|
|
68
|
+
function formatLocations(locations, workspaceUri, maxLocations, maxResultChars) {
|
|
69
|
+
if (locations.length === 0) return boundResult("No results.", maxResultChars, "locations");
|
|
70
|
+
const shown = locations.slice(0, maxLocations);
|
|
71
|
+
const omitted = locations.length - shown.length;
|
|
72
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
73
|
+
for (const location of shown) {
|
|
74
|
+
const path = renderUri(location.uri, workspaceUri);
|
|
75
|
+
const line = location.range.start.line + 1;
|
|
76
|
+
const character = location.range.start.character + 1;
|
|
77
|
+
const entries = grouped.get(path) ?? [];
|
|
78
|
+
entries.push(`${path}:${line}:${character}`);
|
|
79
|
+
grouped.set(path, entries);
|
|
80
|
+
}
|
|
81
|
+
const lines = [];
|
|
82
|
+
for (const entries of grouped.values()) lines.push(...entries);
|
|
83
|
+
if (omitted > 0) lines.push(`… ${omitted} more location${omitted === 1 ? "" : "s"} omitted (limit ${maxLocations}).`);
|
|
84
|
+
return boundResult(lines.join("\n"), maxResultChars, "locations");
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Render a hover result, applying `maxResultChars` last and keeping its marker within the cap.
|
|
88
|
+
* @param hover - the normalized hover, or `null` for no hover.
|
|
89
|
+
* @param maxResultChars - the complete rendered-text cap, including truncation metadata.
|
|
90
|
+
* @returns the rendered hover text; a distinct no-result line for `null`.
|
|
91
|
+
*/
|
|
92
|
+
function formatHover(hover, maxResultChars) {
|
|
93
|
+
return boundResult(hover === null ? "No hover information." : hover.contents, maxResultChars, "hover");
|
|
94
|
+
}
|
|
95
|
+
/** Bound a complete rendered result, including the truncation notice itself. */
|
|
96
|
+
function boundResult(text, maxChars, label) {
|
|
97
|
+
if (text.length <= maxChars) return text;
|
|
98
|
+
const notice = `\n… ${label} truncated (limit ${maxChars} characters).`;
|
|
99
|
+
if (notice.length >= maxChars) return notice.slice(0, maxChars);
|
|
100
|
+
return `${text.slice(0, maxChars - notice.length)}${notice}`;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve a location URI without applying the harness host's path rules. A valid `file:` URI becomes
|
|
104
|
+
* workspace-relative when it is under the provider's canonical workspace URI, or a URI-derived
|
|
105
|
+
* absolute path otherwise; malformed and non-`file:` URIs remain verbatim.
|
|
106
|
+
* @param uri - the target URI from the seam.
|
|
107
|
+
* @param workspaceUri - the provider's canonical workspace `file:` URI.
|
|
108
|
+
* @returns the display path or the verbatim URI.
|
|
109
|
+
*/
|
|
110
|
+
function renderUri(uri, workspaceUri) {
|
|
111
|
+
if (!uri.startsWith("file:")) return uri;
|
|
112
|
+
let target;
|
|
113
|
+
let workspace;
|
|
114
|
+
try {
|
|
115
|
+
target = new URL(uri);
|
|
116
|
+
workspace = new URL(workspaceUri);
|
|
117
|
+
} catch {
|
|
118
|
+
return uri;
|
|
119
|
+
}
|
|
120
|
+
if (workspace.protocol !== "file:") return uri;
|
|
121
|
+
const drivePath = /^\/[a-z](?::|%3A)/iu;
|
|
122
|
+
const windowsWorld = workspace.hostname.length > 0 || drivePath.test(workspace.pathname);
|
|
123
|
+
const targetWindowsWorld = windowsWorld && (target.hostname.length > 0 || drivePath.test(target.pathname));
|
|
124
|
+
const workspacePath = filePath(workspace, windowsWorld);
|
|
125
|
+
const targetPath = filePath(target, targetWindowsWorld);
|
|
126
|
+
if (workspacePath === void 0 || targetPath === void 0) return uri;
|
|
127
|
+
if (windowsWorld !== targetWindowsWorld) return targetPath;
|
|
128
|
+
const path = windowsWorld ? win32 : posix;
|
|
129
|
+
const relative = path.relative(workspacePath, targetPath);
|
|
130
|
+
const outside = relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative);
|
|
131
|
+
const rendered = relative === "" ? "." : outside ? targetPath : relative;
|
|
132
|
+
return windowsWorld ? rendered.replaceAll("\\", "/") : rendered;
|
|
133
|
+
}
|
|
134
|
+
/** Decode a file URL for its execution world while containing malformed URL failures. */
|
|
135
|
+
function filePath(url, windows) {
|
|
136
|
+
try {
|
|
137
|
+
const path = fileURLToPath(url, { windows });
|
|
138
|
+
return path.includes("\0") ? void 0 : path;
|
|
139
|
+
} catch {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* UI presentation for a pending `lsp` call. Uses a generic search card; the title carries the
|
|
145
|
+
* operation and one-based cursor, and `locations` focuses the queried line. The shared location
|
|
146
|
+
* shape has no character, so the title preserves the column.
|
|
147
|
+
* @param args - the raw tool arguments.
|
|
148
|
+
* @returns the generic call view.
|
|
149
|
+
*/
|
|
150
|
+
function presentLspCall(args) {
|
|
151
|
+
return {
|
|
152
|
+
card: "generic",
|
|
153
|
+
kind: "search",
|
|
154
|
+
title: `LSP ${args.operation} ${args.file_path}:${args.line}:${args.character}`,
|
|
155
|
+
locations: [{
|
|
156
|
+
path: args.file_path,
|
|
157
|
+
line: args.line
|
|
158
|
+
}]
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region lib/types/session-cwd.js
|
|
163
|
+
/**
|
|
164
|
+
* Derive the workspace root an `lsp` call resolves against: the calling agent's per-session
|
|
165
|
+
* workspace (`exec.agent.session.header.cwd`), mirroring how the filesystem tools resolve paths.
|
|
166
|
+
* Unlike those tools, LSP has NO provider fallback — a missing cwd fails the call as
|
|
167
|
+
* `LSP_WORKSPACE_REQUIRED`, because the local provider must canonicalize a real workspace before it
|
|
168
|
+
* can start a server.
|
|
169
|
+
* @module @deepseek-ai/dsh-tool-lsp/session-cwd
|
|
170
|
+
*/
|
|
171
|
+
/**
|
|
172
|
+
* The session workspace cwd for this call, or `undefined` when none applies.
|
|
173
|
+
* @param exec - the tool-execution context; only its optional `agent` is read.
|
|
174
|
+
* @returns the calling agent's session cwd, or undefined for a non-agent caller.
|
|
175
|
+
*/
|
|
176
|
+
function sessionCwd(exec) {
|
|
177
|
+
return exec.agent?.session.header.cwd;
|
|
178
|
+
}
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region lib/types/index.js
|
|
181
|
+
/**
|
|
182
|
+
* Model-facing `lsp` tool over `ctx.lsp`. One read-only tool with four operations
|
|
183
|
+
* (`goToDefinition`/`findReferences`/`goToImplementation`/`hover`); it converts one-based UTF-16
|
|
184
|
+
* cursor coordinates to the seam's zero-based positions, requires the session workspace with no
|
|
185
|
+
* fallback, caps and renders results, and attaches a configurable timeout budget for
|
|
186
|
+
* `dsh-timeout-policy` to enforce. It runtime-injects only `tools`, `lsp`, and `systemPrompt` and
|
|
187
|
+
* imports no provider.
|
|
188
|
+
*
|
|
189
|
+
* Namespace plugin (named exports, no default export).
|
|
190
|
+
* @module @deepseek-ai/dsh-tool-lsp
|
|
191
|
+
*/
|
|
192
|
+
/** Cordis plugin name for loader diagnostics. */
|
|
193
|
+
const name = "tool-lsp";
|
|
194
|
+
/** Services required by this plugin. */
|
|
195
|
+
const inject = [
|
|
196
|
+
"tools",
|
|
197
|
+
"lsp",
|
|
198
|
+
"systemPrompt"
|
|
199
|
+
];
|
|
200
|
+
/** Default tool-call timeout budget (ms), covering the queued open/query/close lifecycle. */
|
|
201
|
+
const DEFAULT_LSP_TOOL_TIMEOUT_MS = 6e4;
|
|
202
|
+
/** The stable system-prompt guidance positioning LSP as a precision aid. */
|
|
203
|
+
const LSP_PROMPT_TEXT = "Use search/read for ordinary navigation. Use lsp when textual matches are ambiguous or before a change requires precise definitions, implementations, or references. Positions are one-based line and character (UTF-16) at the cursor; an off-symbol position may return no results. findReferences always includes the declaration.";
|
|
204
|
+
const Config = z.object({
|
|
205
|
+
maxLocations: z.number().default(100),
|
|
206
|
+
maxResultChars: z.number().default(DEFAULT_MAX_RESULT_CHARS),
|
|
207
|
+
timeoutMs: z.number().max(MAX_TIMER_DELAY_MS).default(DEFAULT_LSP_TOOL_TIMEOUT_MS)
|
|
208
|
+
});
|
|
209
|
+
const LSP_POSITION_OUTPUT_SCHEMA = {
|
|
210
|
+
type: "object",
|
|
211
|
+
additionalProperties: false,
|
|
212
|
+
properties: {
|
|
213
|
+
line: {
|
|
214
|
+
type: "integer",
|
|
215
|
+
required: true
|
|
216
|
+
},
|
|
217
|
+
character: {
|
|
218
|
+
type: "integer",
|
|
219
|
+
required: true
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
const LSP_RANGE_OUTPUT_SCHEMA = {
|
|
224
|
+
type: "object",
|
|
225
|
+
additionalProperties: false,
|
|
226
|
+
properties: {
|
|
227
|
+
start: {
|
|
228
|
+
...LSP_POSITION_OUTPUT_SCHEMA,
|
|
229
|
+
required: true
|
|
230
|
+
},
|
|
231
|
+
end: {
|
|
232
|
+
...LSP_POSITION_OUTPUT_SCHEMA,
|
|
233
|
+
required: true
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
/**
|
|
238
|
+
* Register the `lsp` tool and its system-prompt guidance.
|
|
239
|
+
* @param ctx - the plugin context (must inject `tools`, `lsp`, `systemPrompt`).
|
|
240
|
+
* @param config - the resolved plugin configuration.
|
|
241
|
+
*/
|
|
242
|
+
function apply(ctx, config) {
|
|
243
|
+
const resolved = config;
|
|
244
|
+
assertPositiveInteger("maxLocations", resolved.maxLocations);
|
|
245
|
+
assertPositiveInteger("maxResultChars", resolved.maxResultChars);
|
|
246
|
+
assertTimer("timeoutMs", resolved.timeoutMs);
|
|
247
|
+
ctx.systemPrompt.section({
|
|
248
|
+
name: "tool:lsp",
|
|
249
|
+
order: 112,
|
|
250
|
+
text: LSP_PROMPT_TEXT
|
|
251
|
+
});
|
|
252
|
+
ctx.tools.register(defineTool({
|
|
253
|
+
name: "lsp",
|
|
254
|
+
description: "Query a language server for precise code navigation. operation is one of goToDefinition, findReferences, goToImplementation, hover. line and character are one-based UTF-16 cursor coordinates. findReferences includes the declaration.",
|
|
255
|
+
parameters: {
|
|
256
|
+
operation: {
|
|
257
|
+
type: "string",
|
|
258
|
+
required: true,
|
|
259
|
+
enum: [...LSP_OPERATIONS],
|
|
260
|
+
description: "goToDefinition, findReferences, goToImplementation, or hover."
|
|
261
|
+
},
|
|
262
|
+
file_path: {
|
|
263
|
+
type: "string",
|
|
264
|
+
required: true,
|
|
265
|
+
description: "The source file to query, relative to the workspace or absolute."
|
|
266
|
+
},
|
|
267
|
+
line: {
|
|
268
|
+
type: "number",
|
|
269
|
+
required: true,
|
|
270
|
+
description: "One-based line of the cursor."
|
|
271
|
+
},
|
|
272
|
+
character: {
|
|
273
|
+
type: "number",
|
|
274
|
+
required: true,
|
|
275
|
+
description: "One-based UTF-16 column of the cursor."
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
output: {
|
|
279
|
+
schema: { oneOf: [{
|
|
280
|
+
type: "object",
|
|
281
|
+
additionalProperties: false,
|
|
282
|
+
properties: {
|
|
283
|
+
kind: {
|
|
284
|
+
type: "string",
|
|
285
|
+
required: true,
|
|
286
|
+
const: "locations"
|
|
287
|
+
},
|
|
288
|
+
locations: {
|
|
289
|
+
type: "array",
|
|
290
|
+
required: true,
|
|
291
|
+
items: {
|
|
292
|
+
type: "object",
|
|
293
|
+
additionalProperties: false,
|
|
294
|
+
properties: {
|
|
295
|
+
uri: {
|
|
296
|
+
type: "string",
|
|
297
|
+
required: true
|
|
298
|
+
},
|
|
299
|
+
range: {
|
|
300
|
+
...LSP_RANGE_OUTPUT_SCHEMA,
|
|
301
|
+
required: true
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
resolvedWorkspaceUri: {
|
|
307
|
+
type: "string",
|
|
308
|
+
required: true
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}, {
|
|
312
|
+
type: "object",
|
|
313
|
+
additionalProperties: false,
|
|
314
|
+
properties: {
|
|
315
|
+
kind: {
|
|
316
|
+
type: "string",
|
|
317
|
+
required: true,
|
|
318
|
+
const: "hover"
|
|
319
|
+
},
|
|
320
|
+
hover: {
|
|
321
|
+
required: true,
|
|
322
|
+
oneOf: [{ type: "null" }, {
|
|
323
|
+
type: "object",
|
|
324
|
+
additionalProperties: false,
|
|
325
|
+
properties: {
|
|
326
|
+
contents: {
|
|
327
|
+
type: "string",
|
|
328
|
+
required: true
|
|
329
|
+
},
|
|
330
|
+
range: LSP_RANGE_OUTPUT_SCHEMA
|
|
331
|
+
}
|
|
332
|
+
}]
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}] },
|
|
336
|
+
render: (_args, value) => {
|
|
337
|
+
switch (value.kind) {
|
|
338
|
+
case "locations": return [{
|
|
339
|
+
type: "text",
|
|
340
|
+
text: formatLocations(value.locations, value.resolvedWorkspaceUri, resolved.maxLocations, resolved.maxResultChars)
|
|
341
|
+
}];
|
|
342
|
+
case "hover": return [{
|
|
343
|
+
type: "text",
|
|
344
|
+
text: formatHover(value.hover, resolved.maxResultChars)
|
|
345
|
+
}];
|
|
346
|
+
/* v8 ignore next -- exhaustive over the output schema's closed union; unreachable. */
|
|
347
|
+
default: return assertNever(value, "tool-lsp output");
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
timeoutMs: resolved.timeoutMs,
|
|
352
|
+
async execute(args, exec) {
|
|
353
|
+
const input = parseLspArgs(args);
|
|
354
|
+
const workspaceRoot = sessionCwd(exec);
|
|
355
|
+
if (workspaceRoot === void 0) throw new LspError("the lsp tool requires a session workspace cwd", "LSP_WORKSPACE_REQUIRED");
|
|
356
|
+
const result = await ctx.lsp.query({
|
|
357
|
+
operation: input.operation,
|
|
358
|
+
filePath: input.filePath,
|
|
359
|
+
position: input.position,
|
|
360
|
+
workspaceRoot
|
|
361
|
+
}, exec.signal);
|
|
362
|
+
switch (result.kind) {
|
|
363
|
+
case "locations": return {
|
|
364
|
+
kind: "locations",
|
|
365
|
+
locations: result.locations.map((location) => ({
|
|
366
|
+
uri: location.uri,
|
|
367
|
+
range: {
|
|
368
|
+
start: {
|
|
369
|
+
line: location.range.start.line,
|
|
370
|
+
character: location.range.start.character
|
|
371
|
+
},
|
|
372
|
+
end: {
|
|
373
|
+
line: location.range.end.line,
|
|
374
|
+
character: location.range.end.character
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
})),
|
|
378
|
+
resolvedWorkspaceUri: result.resolvedWorkspaceUri
|
|
379
|
+
};
|
|
380
|
+
case "hover": return {
|
|
381
|
+
kind: "hover",
|
|
382
|
+
hover: result.hover === null ? null : {
|
|
383
|
+
contents: result.hover.contents,
|
|
384
|
+
...result.hover.range === void 0 ? {} : { range: {
|
|
385
|
+
start: {
|
|
386
|
+
line: result.hover.range.start.line,
|
|
387
|
+
character: result.hover.range.start.character
|
|
388
|
+
},
|
|
389
|
+
end: {
|
|
390
|
+
line: result.hover.range.end.line,
|
|
391
|
+
character: result.hover.range.end.character
|
|
392
|
+
}
|
|
393
|
+
} }
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
/* v8 ignore next -- exhaustive over the closed LspQueryResult union; unreachable. */
|
|
397
|
+
default: return assertNever(result, "tool-lsp result");
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
presentCall: presentLspCall
|
|
401
|
+
}));
|
|
402
|
+
}
|
|
403
|
+
/** Reject a non-positive-integer config value at load, so misconfiguration fails loud. */
|
|
404
|
+
function assertPositiveInteger(name, value) {
|
|
405
|
+
if (!Number.isInteger(value) || value < 1) throw new Error(`tool-lsp: ${name} must be a positive integer`);
|
|
406
|
+
}
|
|
407
|
+
/** Reject a timer value Node would clamp instead of scheduling as configured. */
|
|
408
|
+
function assertTimer(name, value) {
|
|
409
|
+
if (!Number.isInteger(value) || value < 1 || value > MAX_TIMER_DELAY_MS) throw new Error(`tool-lsp: ${name} must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`);
|
|
410
|
+
}
|
|
411
|
+
//#endregion
|
|
412
|
+
export { Config, DEFAULT_LSP_TOOL_TIMEOUT_MS, DEFAULT_MAX_LOCATIONS, DEFAULT_MAX_RESULT_CHARS, LSP_OPERATIONS, LSP_PROMPT_TEXT, apply, formatHover, formatLocations, inject, name, parseLspArgs, presentLspCall, renderUri, sessionCwd };
|
package/lib/invariant.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region lib/types/invariant.js
|
|
2
|
+
/**
|
|
3
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-tool-lsp`.
|
|
4
|
+
* @module @deepseek-ai/dsh-tool-lsp/invariant
|
|
5
|
+
*/
|
|
6
|
+
const PACKAGE_NAME = "@deepseek-ai/dsh-tool-lsp";
|
|
7
|
+
/** Cordis companion plugin name. */
|
|
8
|
+
const name = "tool-lsp-invariant";
|
|
9
|
+
/** Service required before the companion can reserve package ownership. */
|
|
10
|
+
const inject = ["invariants"];
|
|
11
|
+
/**
|
|
12
|
+
* No runtime invariant: this stateless adapter contributes one tool and prompt section, while query
|
|
13
|
+
* lifecycle and result relations remain owned by the tool and LSP seams it composes.
|
|
14
|
+
*/
|
|
15
|
+
const install = () => {};
|
|
16
|
+
/**
|
|
17
|
+
* Register this package's invariant companion.
|
|
18
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
19
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
20
|
+
*/
|
|
21
|
+
const apply = (ctx) => Promise.resolve(ctx.invariants.register(PACKAGE_NAME, install));
|
|
22
|
+
//#endregion
|
|
23
|
+
export { apply, inject, name };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model-facing `lsp` tool over `ctx.lsp`. One read-only tool with four operations
|
|
3
|
+
* (`goToDefinition`/`findReferences`/`goToImplementation`/`hover`); it converts one-based UTF-16
|
|
4
|
+
* cursor coordinates to the seam's zero-based positions, requires the session workspace with no
|
|
5
|
+
* fallback, caps and renders results, and attaches a configurable timeout budget for
|
|
6
|
+
* `dsh-timeout-policy` to enforce. It runtime-injects only `tools`, `lsp`, and `systemPrompt` and
|
|
7
|
+
* imports no provider.
|
|
8
|
+
*
|
|
9
|
+
* Namespace plugin (named exports, no default export).
|
|
10
|
+
* @module @deepseek-ai/dsh-tool-lsp
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import z from '@deepseek-ai/schemastery';
|
|
14
|
+
export { DEFAULT_MAX_LOCATIONS, DEFAULT_MAX_RESULT_CHARS, formatHover, formatLocations, LSP_OPERATIONS, parseLspArgs, presentLspCall, renderUri, } from './render.ts';
|
|
15
|
+
export { sessionCwd } from './session-cwd.ts';
|
|
16
|
+
/** Cordis plugin name for loader diagnostics. */
|
|
17
|
+
export declare const name = "tool-lsp";
|
|
18
|
+
/** Services required by this plugin. */
|
|
19
|
+
export declare const inject: string[];
|
|
20
|
+
/** Default tool-call timeout budget (ms), covering the queued open/query/close lifecycle. */
|
|
21
|
+
export declare const DEFAULT_LSP_TOOL_TIMEOUT_MS = 60000;
|
|
22
|
+
/** The stable system-prompt guidance positioning LSP as a precision aid. */
|
|
23
|
+
export declare const LSP_PROMPT_TEXT = "Use search/read for ordinary navigation. Use lsp when textual matches are ambiguous or before a change requires precise definitions, implementations, or references. Positions are one-based line and character (UTF-16) at the cursor; an off-symbol position may return no results. findReferences always includes the declaration.";
|
|
24
|
+
/** Plugin configuration: result caps and the timeout budget. */
|
|
25
|
+
export interface Config {
|
|
26
|
+
/** Largest number of rendered locations before an omission marker (default 100). */
|
|
27
|
+
maxLocations?: number;
|
|
28
|
+
/** Largest complete rendered result in characters, including truncation metadata (default 16000). */
|
|
29
|
+
maxResultChars?: number;
|
|
30
|
+
/** Tool-call timeout budget in ms (default 60000). */
|
|
31
|
+
timeoutMs?: number;
|
|
32
|
+
}
|
|
33
|
+
export declare const Config: z<Config>;
|
|
34
|
+
/**
|
|
35
|
+
* Register the `lsp` tool and its system-prompt guidance.
|
|
36
|
+
* @param ctx - the plugin context (must inject `tools`, `lsp`, `systemPrompt`).
|
|
37
|
+
* @param config - the resolved plugin configuration.
|
|
38
|
+
*/
|
|
39
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package-owned invariant companion for `@deepseek-ai/dsh-tool-lsp`.
|
|
3
|
+
* @module @deepseek-ai/dsh-tool-lsp/invariant
|
|
4
|
+
*/
|
|
5
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
6
|
+
/** Cordis companion plugin name. */
|
|
7
|
+
export declare const name = "tool-lsp-invariant";
|
|
8
|
+
/** Service required before the companion can reserve package ownership. */
|
|
9
|
+
export declare const inject: string[];
|
|
10
|
+
/**
|
|
11
|
+
* Register this package's invariant companion.
|
|
12
|
+
* @param ctx - Cordis context carrying the invariant service.
|
|
13
|
+
* @returns the installed registration's disposer after setup succeeds.
|
|
14
|
+
*/
|
|
15
|
+
export declare const apply: (ctx: Context) => Promise<() => void>;
|
|
16
|
+
//# sourceMappingURL=invariant.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure formatting and coordinate conversion for the `lsp` tool: one-based↔zero-based UTF-16 cursor
|
|
3
|
+
* conversion, workspace-grouped location rendering with `file:`-URI resolution, complete-result
|
|
4
|
+
* capping, and UI presentation. No I/O — a UI may call the presenter on live streaming and on
|
|
5
|
+
* replay, so it depends only on the tool arguments.
|
|
6
|
+
* @module @deepseek-ai/dsh-tool-lsp/render
|
|
7
|
+
*/
|
|
8
|
+
import type { GenericCallView } from '@deepseek-ai/dsh-tools';
|
|
9
|
+
import type { LspHover, LspLocation, LspOperation, LspPosition } from '@deepseek-ai/dsh-lsp';
|
|
10
|
+
/** The four operations the tool exposes, as a runtime tuple for schema enum + validation. */
|
|
11
|
+
export declare const LSP_OPERATIONS: readonly LspOperation[];
|
|
12
|
+
/** Default cap on rendered locations before an omission marker is appended. */
|
|
13
|
+
export declare const DEFAULT_MAX_LOCATIONS = 100;
|
|
14
|
+
/** Default cap on the complete rendered tool result, including truncation metadata. */
|
|
15
|
+
export declare const DEFAULT_MAX_RESULT_CHARS = 16000;
|
|
16
|
+
/** Validated `lsp` arguments after coordinate checks. */
|
|
17
|
+
export interface LspToolInput {
|
|
18
|
+
readonly operation: LspOperation;
|
|
19
|
+
readonly filePath: string;
|
|
20
|
+
/** Zero-based UTF-16 position converted from the one-based model coordinates. */
|
|
21
|
+
readonly position: LspPosition;
|
|
22
|
+
}
|
|
23
|
+
/** The raw, schema-typed argument shape. */
|
|
24
|
+
export interface LspToolArgs {
|
|
25
|
+
readonly operation: string;
|
|
26
|
+
readonly file_path: string;
|
|
27
|
+
readonly line: number;
|
|
28
|
+
readonly character: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Validate and convert model arguments: `operation` must be one of the four; `line`/`character` are
|
|
32
|
+
* positive one-based integers converted to the seam's zero-based position.
|
|
33
|
+
* @param args - the schema-validated raw arguments.
|
|
34
|
+
* @returns the validated input with a zero-based position.
|
|
35
|
+
* @throws Error when the operation is unknown or a coordinate is not a positive integer.
|
|
36
|
+
*/
|
|
37
|
+
export declare function parseLspArgs(args: LspToolArgs): LspToolInput;
|
|
38
|
+
/**
|
|
39
|
+
* Render a locations result grouped by file, converting each zero-based location back to a one-based
|
|
40
|
+
* `path:line:character` entry. A `file:` URI inside the workspace becomes a workspace-relative path;
|
|
41
|
+
* outside it, a URI-derived absolute path; a non-`file:` URI is kept verbatim. Applies `maxLocations` and
|
|
42
|
+
* appends an omission marker when it truncates by count, then applies the complete result cap.
|
|
43
|
+
* @param locations - the seam's locations (possibly empty).
|
|
44
|
+
* @param workspaceUri - the provider's canonical workspace `file:` URI.
|
|
45
|
+
* @param maxLocations - the cap before truncation.
|
|
46
|
+
* @param maxResultChars - the complete rendered-text cap, including truncation metadata.
|
|
47
|
+
* @returns the rendered text; a distinct no-result line when there are none.
|
|
48
|
+
*/
|
|
49
|
+
export declare function formatLocations(locations: readonly LspLocation[], workspaceUri: string, maxLocations: number, maxResultChars: number): string;
|
|
50
|
+
/**
|
|
51
|
+
* Render a hover result, applying `maxResultChars` last and keeping its marker within the cap.
|
|
52
|
+
* @param hover - the normalized hover, or `null` for no hover.
|
|
53
|
+
* @param maxResultChars - the complete rendered-text cap, including truncation metadata.
|
|
54
|
+
* @returns the rendered hover text; a distinct no-result line for `null`.
|
|
55
|
+
*/
|
|
56
|
+
export declare function formatHover(hover: LspHover | null, maxResultChars: number): string;
|
|
57
|
+
/**
|
|
58
|
+
* Resolve a location URI without applying the harness host's path rules. A valid `file:` URI becomes
|
|
59
|
+
* workspace-relative when it is under the provider's canonical workspace URI, or a URI-derived
|
|
60
|
+
* absolute path otherwise; malformed and non-`file:` URIs remain verbatim.
|
|
61
|
+
* @param uri - the target URI from the seam.
|
|
62
|
+
* @param workspaceUri - the provider's canonical workspace `file:` URI.
|
|
63
|
+
* @returns the display path or the verbatim URI.
|
|
64
|
+
*/
|
|
65
|
+
export declare function renderUri(uri: string, workspaceUri: string): string;
|
|
66
|
+
/**
|
|
67
|
+
* UI presentation for a pending `lsp` call. Uses a generic search card; the title carries the
|
|
68
|
+
* operation and one-based cursor, and `locations` focuses the queried line. The shared location
|
|
69
|
+
* shape has no character, so the title preserves the column.
|
|
70
|
+
* @param args - the raw tool arguments.
|
|
71
|
+
* @returns the generic call view.
|
|
72
|
+
*/
|
|
73
|
+
export declare function presentLspCall(args: LspToolArgs): GenericCallView;
|
|
74
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive the workspace root an `lsp` call resolves against: the calling agent's per-session
|
|
3
|
+
* workspace (`exec.agent.session.header.cwd`), mirroring how the filesystem tools resolve paths.
|
|
4
|
+
* Unlike those tools, LSP has NO provider fallback — a missing cwd fails the call as
|
|
5
|
+
* `LSP_WORKSPACE_REQUIRED`, because the local provider must canonicalize a real workspace before it
|
|
6
|
+
* can start a server.
|
|
7
|
+
* @module @deepseek-ai/dsh-tool-lsp/session-cwd
|
|
8
|
+
*/
|
|
9
|
+
import type { ToolExecution } from '@deepseek-ai/dsh-tools';
|
|
10
|
+
/**
|
|
11
|
+
* The session workspace cwd for this call, or `undefined` when none applies.
|
|
12
|
+
* @param exec - the tool-execution context; only its optional `agent` is read.
|
|
13
|
+
* @returns the calling agent's session cwd, or undefined for a non-agent caller.
|
|
14
|
+
*/
|
|
15
|
+
export declare function sessionCwd(exec: ToolExecution): string | undefined;
|
|
16
|
+
//# sourceMappingURL=session-cwd.d.ts.map
|
package/package.json
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@deepseek-ai/dsh-tool-lsp",
|
|
3
|
+
"description": "Model-facing lsp tool over the DeepSeek Harness LSP capability seam (ctx.lsp) — one read-only tool with goToDefinition/findReferences/goToImplementation/hover operations, one-based UTF-16 cursor coordinates, bounded location rendering, and hover normalization",
|
|
4
|
+
"version": "0.0.1-rc.1",
|
|
5
|
+
"publishConfig": {
|
|
6
|
+
"access": "restricted"
|
|
7
|
+
},
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/deepseek-ai/deepseek-harness.git",
|
|
11
|
+
"directory": "packages/lsp/tool-lsp"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "lib/index.js",
|
|
15
|
+
"types": "lib/types/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./lib/types/index.d.ts",
|
|
19
|
+
"default": "./lib/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./invariant": {
|
|
22
|
+
"types": "./lib/types/invariant.d.ts",
|
|
23
|
+
"default": "./lib/invariant.js"
|
|
24
|
+
},
|
|
25
|
+
"./src/*": "./src/*",
|
|
26
|
+
"./package.json": "./package.json"
|
|
27
|
+
},
|
|
28
|
+
"files": [
|
|
29
|
+
"lib/index.js",
|
|
30
|
+
"lib/invariant.js",
|
|
31
|
+
"lib/types/**/*.d.ts"
|
|
32
|
+
],
|
|
33
|
+
"license": "BSD-3-Clause",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
36
|
+
"@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
|
|
37
|
+
"@deepseek-ai/dsh-lsp": "^0.0.1-rc.1",
|
|
38
|
+
"@deepseek-ai/dsh-system-prompt": "^0.0.1-rc.1",
|
|
39
|
+
"@deepseek-ai/dsh-timeout": "^0.0.1-rc.1",
|
|
40
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
41
|
+
"@deepseek-ai/dsh-tools": "^0.0.1-rc.1"
|
|
42
|
+
},
|
|
43
|
+
"dependencies": {
|
|
44
|
+
"@deepseek-ai/schemastery": "^3.18.1-rc.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@deepseek-ai/dsh-fs-local": "^0.0.1-rc.1",
|
|
48
|
+
"@deepseek-ai/dsh-agent": "^0.0.1-rc.1",
|
|
49
|
+
"@deepseek-ai/dsh-lsp": "^0.0.1-rc.1",
|
|
50
|
+
"@deepseek-ai/dsh-invariants": "^0.0.1-rc.1",
|
|
51
|
+
"@deepseek-ai/dsh-llm": "^0.0.1-rc.1",
|
|
52
|
+
"@deepseek-ai/dsh-lsp-local": "^0.0.1-rc.1",
|
|
53
|
+
"@deepseek-ai/dsh-session": "^0.0.1-rc.1",
|
|
54
|
+
"@deepseek-ai/dsh-timeout": "^0.0.1-rc.1",
|
|
55
|
+
"@deepseek-ai/dsh-system-prompt": "^0.0.1-rc.1",
|
|
56
|
+
"@deepseek-ai/dsh-tools": "^0.0.1-rc.1",
|
|
57
|
+
"@deepseek-ai/dsh-timeout-policy": "^0.0.1-rc.1",
|
|
58
|
+
"@deepseek-ai/cordis": "^4.0.1-rc.1",
|
|
59
|
+
"@deepseek-ai/dsh-subprocess-local": "^0.0.1-rc.1"
|
|
60
|
+
}
|
|
61
|
+
}
|