@nudojs/lsp 0.2.0 → 0.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/CHANGELOG.md +12 -0
- package/README.md +35 -0
- package/agent-skill/SKILL.md +83 -0
- package/dist/server.d.ts +8 -1
- package/dist/server.js +44156 -76
- package/package.json +15 -3
- package/src/__tests__/definition.test.ts +32 -0
- package/src/__tests__/lsp-integration.test.ts +822 -0
- package/src/agent-tools.ts +325 -0
- package/src/semantic-tokens.ts +45 -0
- package/src/server.ts +437 -67
- package/src/symbols.ts +109 -0
- package/src/validation.ts +197 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @nudojs/lsp
|
|
2
2
|
|
|
3
|
+
## 0.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0fd0718: Merge the three environment packages into one: `@nudojs/env-es`, `@nudojs/env-web`, `@nudojs/env-node` are replaced by a single `@nudojs/env` package with subpath exports `@nudojs/env/es`, `@nudojs/env/web`, `@nudojs/env/node`.
|
|
8
|
+
|
|
9
|
+
Move agent-facing tools from the standalone MCP server into the language server: `@nudojs/mcp` is removed. `@nudojs/lsp` now exposes `nudo.whatIf`, `nudo.suggestCase`, `nudo.trace`, `nudo.selectCase`, and `nudo.getActiveCases` via `workspace/executeCommand` (custom-request aliases `nudo/whatIf` etc. included), adds pull-mode diagnostics, and works on files that are not open in the editor (disk fallback). `nudo.whatIf` now actually applies the given type bindings — previously they were ignored. AI agents connect through any LSP↔MCP bridge (cclsp, mcpls, agent-lsp) or a native LSP client; an installable agent skill ships at `packages/lsp/agent-skill/SKILL.md`.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- @nudojs/service@0.2.1
|
|
14
|
+
|
|
3
15
|
## 0.2.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# @nudojs/lsp
|
|
2
|
+
|
|
3
|
+
Language Server Protocol implementation for the [Nudo](https://github.com/nudojs/nudo) type inference engine.
|
|
4
|
+
|
|
5
|
+
## What is Nudo?
|
|
6
|
+
|
|
7
|
+
Nudo is a type inference engine for JavaScript. Instead of a separate type system, it runs your code with symbolic type values via abstract interpretation — no TypeScript, no build step.
|
|
8
|
+
|
|
9
|
+
## This package
|
|
10
|
+
|
|
11
|
+
`@nudojs/lsp` implements an LSP server that provides Nudo-powered features to any editor:
|
|
12
|
+
|
|
13
|
+
- Hover type information
|
|
14
|
+
- Completions based on inferred types
|
|
15
|
+
- Diagnostics from abstract interpretation
|
|
16
|
+
- Case navigation for `@nudo:case` directives
|
|
17
|
+
- Go-to-Definition
|
|
18
|
+
- Find References
|
|
19
|
+
- Rename Symbol
|
|
20
|
+
- Signature Help (parameter hints)
|
|
21
|
+
- Code Actions / Quick Fixes
|
|
22
|
+
- Semantic Tokens (type-aware highlighting)
|
|
23
|
+
- Inlay Hints
|
|
24
|
+
|
|
25
|
+
Typically consumed by the [nudo-vscode](https://marketplace.visualstudio.com/items?itemName=wmzy.nudo-vscode) extension, but compatible with any LSP client.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm install @nudojs/lsp
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## License
|
|
34
|
+
|
|
35
|
+
[MIT](https://github.com/nudojs/nudo/blob/main/LICENSE)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nudo
|
|
3
|
+
description: Query precise JavaScript types by abstract interpretation — use when the project uses Nudo (@nudo: directives) and you need the inferred type of a variable or expression, a function's input→output type trace, what-if type hypotheses, case coverage, or type diagnostics for a .js file.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Nudo — type inference for JavaScript
|
|
7
|
+
|
|
8
|
+
Nudo is a comment-driven type inference engine for plain JavaScript. It derives types by **executing** code with symbolic type values (`T.number`, `T.string`) instead of requiring TypeScript annotations: functions marked with `@nudo:case` directives are run under abstract interpretation, and unmarked functions get cases synthesized from their call sites (whole-program inference). Ask Nudo instead of guessing what a refactor does to types.
|
|
9
|
+
|
|
10
|
+
## Install and connect
|
|
11
|
+
|
|
12
|
+
Nudo's agent face lives in its language server. Install it in the user's project (or globally):
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm i @nudojs/lsp # project-local; or: npm i -g @nudojs/lsp
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The server speaks LSP over stdio:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
node node_modules/@nudojs/lsp/src/server.ts # Node >= 22.18; on older Node: npx tsx <path>
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Three ways to connect (details in the [Agent Integration Guide](https://nudojs.github.io/nudo/docs/guides/mcp-server)):
|
|
25
|
+
|
|
26
|
+
1. **Generic LSP→MCP bridge** (cclsp, mcpls, agent-lsp) — registers Nudo as the language server for `.js` files; verify the bridge passes through `workspace/executeCommand`.
|
|
27
|
+
2. **Native LSP client** — spawn the server over stdio, `initialize`, then call `workspace/executeCommand` (or the custom request aliases below).
|
|
28
|
+
3. **VS Code / Cursor** — the `nudo-vscode` extension launches the server automatically.
|
|
29
|
+
|
|
30
|
+
## Command cheat sheet
|
|
31
|
+
|
|
32
|
+
All commands are available as `workspace/executeCommand` (dot form) and as custom LSP requests (slash form); agents use the `file` parameter everywhere (accepts a `file://` URI or a bare path). Editor extensions call the `nudo/selectCase` / `nudo/getActiveCases` requests with editor-style `uri` params instead — same handlers. Files that are not open in an editor are read from disk. `whatIf`, `suggestCase`, and `trace` return MCP-style text content — `{ content: [{ type: "text", text }] }`.
|
|
33
|
+
|
|
34
|
+
| Command (request alias) | Arguments (JSON) | Returns |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| `nudo.whatIf` (`nudo/whatIf`) | `{ "file": "src/app.js", "bindings": [{ "name": "x", "type": "string" }], "target": "y" }` | Text: the inferred type of `target` **under the assumed bindings** — e.g. `Type of "y": string \| number` |
|
|
37
|
+
| `nudo.trace` (`nudo/trace`) | `{ "file": "src/app.js", "functionName": "parse" }` | Text: one line per case, e.g. `Input: (T.string) => Output: number` |
|
|
38
|
+
| `nudo.suggestCase` (`nudo/suggestCase`) | `{ "file": "src/app.js", "functionName": "parse" }` | Text: paste-ready `@nudo:case` directives when every case is call-site synthesized, e.g. `Function "parse" has 2 synthesized case(s); suggested directives:`; otherwise the current case count, e.g. `Function "parse" already has 3 case(s)`, or a suggested `@nudo:case` directive |
|
|
39
|
+
| `nudo.selectCase` (`nudo/selectCase`) | `{ "file": "src/app.js", "functionName": "parse", "caseIndex": 1 }` | `{ "success": true }` — switches the active case (affects hover/diagnostics until changed back) |
|
|
40
|
+
| `nudo.getActiveCases` (`nudo/getActiveCases`) | `{ "file": "src/app.js" }` | `{ "parse": 1, "greet": 0 }` — active case index per function |
|
|
41
|
+
|
|
42
|
+
Diagnostics (failed `@nudo:returns` assertions, unreachable code, …) are available as LSP diagnostics — push (`textDocument/publishDiagnostics`) and pull (`textDocument/diagnostic`).
|
|
43
|
+
|
|
44
|
+
## What-if workflow
|
|
45
|
+
|
|
46
|
+
The signature move: hypothesize a type for one binding, observe what another binding becomes — without editing any source.
|
|
47
|
+
|
|
48
|
+
```js
|
|
49
|
+
// src/app.js
|
|
50
|
+
function normalize(x) {
|
|
51
|
+
const trimmed = x.trim();
|
|
52
|
+
const y = Number(trimmed);
|
|
53
|
+
return y;
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Assume `x` is a string, ask what `y` is:
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"command": "nudo.whatIf",
|
|
62
|
+
"arguments": [{
|
|
63
|
+
"file": "src/app.js",
|
|
64
|
+
"bindings": [{ "name": "x", "type": "string" }],
|
|
65
|
+
"target": "y"
|
|
66
|
+
}]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
→ `Type of "y": number`. Then flip the hypothesis (`"type": "string | null"`) and re-ask to see how the code's guard branches change the result. Use this to preview refactors, validate an API's return type before calling it, or check that a fix actually narrows a type.
|
|
71
|
+
|
|
72
|
+
## Type expression syntax
|
|
73
|
+
|
|
74
|
+
`bindings[].type` accepts a primitive or a `|`-separated union of primitives:
|
|
75
|
+
|
|
76
|
+
- `number`, `string`, `boolean`, `null`, `undefined`, `bigint`, `symbol`
|
|
77
|
+
- Unions: `string | null`, `number | string`
|
|
78
|
+
|
|
79
|
+
## Notes
|
|
80
|
+
|
|
81
|
+
- **Unopened files use disk state.** If the file is not open in a connected editor, analysis runs on the on-disk content; edits the user has not saved are invisible.
|
|
82
|
+
- Commands that report types reflect Nudo's inference, which follows runtime semantics (e.g. `Number("")` is `0`, not an error) — trust them over guesswork, but remember they describe the current code, not the user's intent.
|
|
83
|
+
- Whole-program inference means every function with inferable call sites already has cases. When all of them are call-site synthesized, `suggestCase` returns ready-to-paste `@nudo:case` directive text (paste it above the function); `already has N case(s)` (handwritten or entry-only cases) is the normal report for the rest, not an error.
|
package/dist/server.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* watched-files 删除事件监听器:接收「被删除且不在打开集」的 uri 列表。
|
|
3
|
+
* 缓存逐出等后续逻辑通过 registerWatchedFilesListener 挂到这里。
|
|
4
|
+
*/
|
|
5
|
+
declare const watchedFilesListeners: Array<(uris: string[]) => void>;
|
|
6
|
+
/** 注册 watched-files 监听器,返回注销函数。 */
|
|
7
|
+
declare function registerWatchedFilesListener(listener: (uris: string[]) => void): () => void;
|
|
1
8
|
|
|
2
|
-
export {
|
|
9
|
+
export { registerWatchedFilesListener, watchedFilesListeners };
|