@nudojs/lsp 0.3.0 → 0.4.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/dist/server.js +832 -135
- package/package.json +15 -5
- package/CHANGELOG.md +0 -41
- package/agent-skill/SKILL.md +0 -83
- package/src/__tests__/definition.test.ts +0 -32
- package/src/__tests__/lsp-integration.test.ts +0 -822
- package/src/agent-tools.ts +0 -325
- package/src/semantic-tokens.ts +0 -45
- package/src/server.ts +0 -672
- package/src/symbols.ts +0 -109
- package/src/validation.ts +0 -197
- package/tsconfig.json +0 -12
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nudojs/lsp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Language Server Protocol implementation for the Nudo type inference engine",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,17 @@
|
|
|
11
11
|
"lsp",
|
|
12
12
|
"language-server"
|
|
13
13
|
],
|
|
14
|
-
"main": "
|
|
14
|
+
"main": "./dist/server.js",
|
|
15
|
+
"types": "./dist/server.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/server.d.ts",
|
|
19
|
+
"default": "./dist/server.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist"
|
|
24
|
+
],
|
|
15
25
|
"repository": {
|
|
16
26
|
"type": "git",
|
|
17
27
|
"url": "git+https://github.com/nudojs/nudo.git",
|
|
@@ -27,9 +37,9 @@
|
|
|
27
37
|
"dependencies": {
|
|
28
38
|
"vscode-languageserver": "^9.0.0",
|
|
29
39
|
"vscode-languageserver-textdocument": "^1.0.0",
|
|
30
|
-
"@nudojs/service": "0.
|
|
31
|
-
"@nudojs/core": "0.
|
|
32
|
-
"@nudojs/parser": "0.
|
|
40
|
+
"@nudojs/service": "0.3.1",
|
|
41
|
+
"@nudojs/core": "0.3.1",
|
|
42
|
+
"@nudojs/parser": "0.3.1"
|
|
33
43
|
},
|
|
34
44
|
"scripts": {
|
|
35
45
|
"build": "tsup src/server.ts --format esm --dts"
|
package/CHANGELOG.md
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
# @nudojs/lsp
|
|
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
|
-
|
|
15
|
-
## 0.2.0
|
|
16
|
-
|
|
17
|
-
### Minor Changes
|
|
18
|
-
|
|
19
|
-
- 6c38283: docs and ci
|
|
20
|
-
- 9f7f819: fix pkg info
|
|
21
|
-
- c175f71: version
|
|
22
|
-
|
|
23
|
-
### Patch Changes
|
|
24
|
-
|
|
25
|
-
- Updated dependencies [6c38283]
|
|
26
|
-
- Updated dependencies [9f7f819]
|
|
27
|
-
- Updated dependencies [c175f71]
|
|
28
|
-
- @nudojs/core@0.2.0
|
|
29
|
-
- @nudojs/service@0.2.0
|
|
30
|
-
|
|
31
|
-
## 0.1.0
|
|
32
|
-
|
|
33
|
-
### Minor Changes
|
|
34
|
-
|
|
35
|
-
- Conceptual design and basic implementation.
|
|
36
|
-
|
|
37
|
-
### Patch Changes
|
|
38
|
-
|
|
39
|
-
- Updated dependencies
|
|
40
|
-
- @nudojs/core@0.1.0
|
|
41
|
-
- @nudojs/service@0.1.0
|
package/agent-skill/SKILL.md
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
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.
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { parse } from "@nudojs/parser";
|
|
3
|
-
import { buildSymbolTable, findDefinition, findReferences } from "../symbols.ts";
|
|
4
|
-
|
|
5
|
-
describe("symbol table", () => {
|
|
6
|
-
it("finds function definition", () => {
|
|
7
|
-
const source = `function add(a, b) { return a + b; }`;
|
|
8
|
-
const ast = parse(source);
|
|
9
|
-
const table = buildSymbolTable(ast, "file:///test.js");
|
|
10
|
-
const def = findDefinition(table, "add");
|
|
11
|
-
expect(def).not.toBeNull();
|
|
12
|
-
expect(def!.kind).toBe("function");
|
|
13
|
-
expect(def!.loc.start.line).toBe(1);
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
it("finds variable definition", () => {
|
|
17
|
-
const source = `const x = 42;`;
|
|
18
|
-
const ast = parse(source);
|
|
19
|
-
const table = buildSymbolTable(ast, "file:///test.js");
|
|
20
|
-
const def = findDefinition(table, "x");
|
|
21
|
-
expect(def).not.toBeNull();
|
|
22
|
-
expect(def!.kind).toBe("variable");
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it("collects references", () => {
|
|
26
|
-
const source = `const x = 42; const y = x + 1;`;
|
|
27
|
-
const ast = parse(source);
|
|
28
|
-
const table = buildSymbolTable(ast, "file:///test.js");
|
|
29
|
-
const refs = table.references.filter((r) => r.name === "x");
|
|
30
|
-
expect(refs.length).toBeGreaterThan(0);
|
|
31
|
-
});
|
|
32
|
-
});
|