prinfer 0.5.3 → 0.6.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 +23 -12
- package/dist/cli.cjs +253 -94
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +253 -94
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +298 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +66 -1
- package/dist/index.d.ts +66 -1
- package/dist/index.js +294 -12
- package/dist/index.js.map +1 -1
- package/dist/mcp.cjs +232 -76
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +232 -76
- package/dist/mcp.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
**Typehints for your AI agent.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Give AI coding assistant the ability to inspect TypeScript's inferred types mimicking the IDE's hover behavior.
|
|
10
|
+
so they can write cleaner code without redundant type annotations.
|
|
10
11
|
|
|
11
12
|
## Why?
|
|
12
13
|
|
|
@@ -40,13 +41,15 @@ prinfer setup
|
|
|
40
41
|
|
|
41
42
|
### MCP Server (`prinfer-mcp`)
|
|
42
43
|
|
|
43
|
-
Your agent gets
|
|
44
|
+
Your agent gets a `hover` tool to check what TypeScript infers at any position:
|
|
44
45
|
|
|
45
46
|
```
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
hover(file: "src/utils.ts", line: 75, column: 10)
|
|
48
|
+
hover(file: "src/utils.ts", line: 75, column: 10, include_docs: true)
|
|
48
49
|
```
|
|
49
50
|
|
|
51
|
+
The position-based API matches IDE behavior and returns instantiated generic types at call sites.
|
|
52
|
+
|
|
50
53
|
### Claude Skill (`~/.claude/skills/prefer-infer.md`)
|
|
51
54
|
|
|
52
55
|
A coding guideline that encourages your agent to:
|
|
@@ -55,7 +58,7 @@ A coding guideline that encourages your agent to:
|
|
|
55
58
|
- Use prinfer to verify types before adding redundant hints
|
|
56
59
|
- Write idiomatic TypeScript
|
|
57
60
|
|
|
58
|
-
Plus a `/
|
|
61
|
+
Plus a `/hover` command for quick lookups.
|
|
59
62
|
|
|
60
63
|
## Manual Setup
|
|
61
64
|
|
|
@@ -74,8 +77,9 @@ claude mcp add prinfer node /path/to/node_modules/prinfer/dist/mcp.js
|
|
|
74
77
|
prinfer also works as a standalone CLI:
|
|
75
78
|
|
|
76
79
|
```bash
|
|
77
|
-
prinfer src/utils.ts
|
|
78
|
-
prinfer src/utils.ts:75
|
|
80
|
+
prinfer src/utils.ts:75:10
|
|
81
|
+
prinfer src/utils.ts:75:10 --docs
|
|
82
|
+
prinfer src/utils.ts:75:10 --project ./tsconfig.json
|
|
79
83
|
```
|
|
80
84
|
|
|
81
85
|
Output:
|
|
@@ -83,18 +87,25 @@ Output:
|
|
|
83
87
|
```
|
|
84
88
|
(x: number, y: string) => boolean
|
|
85
89
|
returns: boolean
|
|
90
|
+
name: myFunction
|
|
91
|
+
kind: function
|
|
92
|
+
docs: Adds two numbers together.
|
|
86
93
|
```
|
|
87
94
|
|
|
88
95
|
## Programmatic API
|
|
89
96
|
|
|
90
97
|
```typescript
|
|
91
|
-
import {
|
|
98
|
+
import { hover } from "prinfer";
|
|
99
|
+
|
|
100
|
+
const result = hover("./src/utils.ts", 75, 10);
|
|
101
|
+
// => { signature: "(x: number, y: string) => boolean", returnType: "boolean", line: 75, column: 10, kind: "function", name: "myFunction" }
|
|
92
102
|
|
|
93
|
-
|
|
94
|
-
|
|
103
|
+
// With documentation
|
|
104
|
+
const result2 = hover("./src/utils.ts", 75, 10, { include_docs: true });
|
|
105
|
+
// => { ..., documentation: "Adds two numbers together." }
|
|
95
106
|
|
|
96
|
-
// With
|
|
97
|
-
const
|
|
107
|
+
// With custom tsconfig
|
|
108
|
+
const result3 = hover("./src/utils.ts", 75, 10, { project: "./tsconfig.json" });
|
|
98
109
|
```
|
|
99
110
|
|
|
100
111
|
## Requirements
|
package/dist/cli.cjs
CHANGED
|
@@ -72,66 +72,203 @@ function loadProgram(entryFileAbs, project) {
|
|
|
72
72
|
function isArrowOrFnExpr(n) {
|
|
73
73
|
return !!n && (import_typescript.default.isArrowFunction(n) || import_typescript.default.isFunctionExpression(n));
|
|
74
74
|
}
|
|
75
|
-
function
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if ((import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
if (import_typescript.default.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
91
|
-
return isArrowOrFnExpr(node.initializer);
|
|
75
|
+
function findSmallestNodeAtPosition(sourceFile, position) {
|
|
76
|
+
let result;
|
|
77
|
+
function visit(node) {
|
|
78
|
+
const start = node.getStart(sourceFile);
|
|
79
|
+
const end = node.getEnd();
|
|
80
|
+
if (position < start || position >= end) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (!result || node.getWidth(sourceFile) <= result.getWidth(sourceFile)) {
|
|
84
|
+
result = node;
|
|
85
|
+
}
|
|
86
|
+
import_typescript.default.forEachChild(node, visit);
|
|
92
87
|
}
|
|
93
|
-
|
|
88
|
+
visit(sourceFile);
|
|
89
|
+
return result;
|
|
94
90
|
}
|
|
95
|
-
function
|
|
96
|
-
|
|
91
|
+
function findHoverableAncestor(sourceFile, position) {
|
|
92
|
+
const smallestNode = findSmallestNodeAtPosition(sourceFile, position);
|
|
93
|
+
if (!smallestNode) return void 0;
|
|
94
|
+
let bestMatch = smallestNode;
|
|
95
|
+
function visit(n) {
|
|
96
|
+
const start = n.getStart(sourceFile);
|
|
97
|
+
const end = n.getEnd();
|
|
98
|
+
if (position < start || position >= end) {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
if (import_typescript.default.isCallExpression(n)) {
|
|
102
|
+
const expr = n.expression;
|
|
103
|
+
if (import_typescript.default.isIdentifier(expr)) {
|
|
104
|
+
if (position >= expr.getStart(sourceFile) && position < expr.getEnd()) {
|
|
105
|
+
bestMatch = n;
|
|
106
|
+
}
|
|
107
|
+
} else if (import_typescript.default.isPropertyAccessExpression(expr)) {
|
|
108
|
+
if (position >= expr.name.getStart(sourceFile) && position < expr.name.getEnd()) {
|
|
109
|
+
bestMatch = n;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (import_typescript.default.isFunctionDeclaration(n) && n.name) {
|
|
114
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
115
|
+
bestMatch = n;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (import_typescript.default.isVariableDeclaration(n) && import_typescript.default.isIdentifier(n.name)) {
|
|
119
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
120
|
+
bestMatch = n;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
if ((import_typescript.default.isMethodDeclaration(n) || import_typescript.default.isMethodSignature(n)) && import_typescript.default.isIdentifier(n.name)) {
|
|
124
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
125
|
+
bestMatch = n;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (import_typescript.default.isClassDeclaration(n) && n.name) {
|
|
129
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
130
|
+
bestMatch = n;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (import_typescript.default.isInterfaceDeclaration(n) && n.name) {
|
|
134
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
135
|
+
bestMatch = n;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
import_typescript.default.forEachChild(n, visit);
|
|
97
139
|
return true;
|
|
98
140
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
function isNamedNode(node, name) {
|
|
102
|
-
return isFunctionLikeNamed(node, name) || isVariableNamed(node, name);
|
|
141
|
+
visit(sourceFile);
|
|
142
|
+
return bestMatch;
|
|
103
143
|
}
|
|
104
|
-
function
|
|
105
|
-
const
|
|
106
|
-
|
|
144
|
+
function findNodeAtPosition(sourceFile, line, column) {
|
|
145
|
+
const lineCount = sourceFile.getLineStarts().length;
|
|
146
|
+
if (line < 1 || line > lineCount) {
|
|
147
|
+
return void 0;
|
|
148
|
+
}
|
|
149
|
+
const lineStart = sourceFile.getLineStarts()[line - 1];
|
|
150
|
+
const lineEnd = line < lineCount ? sourceFile.getLineStarts()[line] : sourceFile.getEnd();
|
|
151
|
+
const lineLength = lineEnd - lineStart;
|
|
152
|
+
if (column < 1 || column > lineLength + 1) {
|
|
153
|
+
return void 0;
|
|
154
|
+
}
|
|
155
|
+
const position = sourceFile.getPositionOfLineAndCharacter(
|
|
156
|
+
line - 1,
|
|
157
|
+
column - 1
|
|
107
158
|
);
|
|
108
|
-
return
|
|
159
|
+
return findHoverableAncestor(sourceFile, position);
|
|
109
160
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
} else {
|
|
122
|
-
found = node;
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
161
|
+
function getSymbolKind(node) {
|
|
162
|
+
if (import_typescript.default.isFunctionDeclaration(node)) return "function";
|
|
163
|
+
if (import_typescript.default.isArrowFunction(node)) return "function";
|
|
164
|
+
if (import_typescript.default.isFunctionExpression(node)) return "function";
|
|
165
|
+
if (import_typescript.default.isMethodDeclaration(node)) return "method";
|
|
166
|
+
if (import_typescript.default.isMethodSignature(node)) return "method";
|
|
167
|
+
if (import_typescript.default.isVariableDeclaration(node)) {
|
|
168
|
+
const init = node.initializer;
|
|
169
|
+
if (init && (import_typescript.default.isArrowFunction(init) || import_typescript.default.isFunctionExpression(init))) {
|
|
170
|
+
return "function";
|
|
125
171
|
}
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return
|
|
172
|
+
return "variable";
|
|
173
|
+
}
|
|
174
|
+
if (import_typescript.default.isParameter(node)) return "parameter";
|
|
175
|
+
if (import_typescript.default.isPropertyDeclaration(node)) return "property";
|
|
176
|
+
if (import_typescript.default.isPropertySignature(node)) return "property";
|
|
177
|
+
if (import_typescript.default.isPropertyAccessExpression(node)) return "property";
|
|
178
|
+
if (import_typescript.default.isCallExpression(node)) return "call";
|
|
179
|
+
if (import_typescript.default.isTypeAliasDeclaration(node)) return "type";
|
|
180
|
+
if (import_typescript.default.isInterfaceDeclaration(node)) return "interface";
|
|
181
|
+
if (import_typescript.default.isClassDeclaration(node)) return "class";
|
|
182
|
+
if (import_typescript.default.isIdentifier(node)) return "identifier";
|
|
183
|
+
return "unknown";
|
|
184
|
+
}
|
|
185
|
+
function getNodeName(node) {
|
|
186
|
+
if (import_typescript.default.isFunctionDeclaration(node) && node.name) {
|
|
187
|
+
return node.name.text;
|
|
188
|
+
}
|
|
189
|
+
if (import_typescript.default.isVariableDeclaration(node) && import_typescript.default.isIdentifier(node.name)) {
|
|
190
|
+
return node.name.text;
|
|
191
|
+
}
|
|
192
|
+
if ((import_typescript.default.isMethodDeclaration(node) || import_typescript.default.isMethodSignature(node)) && import_typescript.default.isIdentifier(node.name)) {
|
|
193
|
+
return node.name.text;
|
|
194
|
+
}
|
|
195
|
+
if (import_typescript.default.isPropertyAccessExpression(node)) {
|
|
196
|
+
return node.name.text;
|
|
197
|
+
}
|
|
198
|
+
if (import_typescript.default.isCallExpression(node)) {
|
|
199
|
+
const expr = node.expression;
|
|
200
|
+
if (import_typescript.default.isIdentifier(expr)) return expr.text;
|
|
201
|
+
if (import_typescript.default.isPropertyAccessExpression(expr)) return expr.name.text;
|
|
202
|
+
}
|
|
203
|
+
if (import_typescript.default.isParameter(node) && import_typescript.default.isIdentifier(node.name)) {
|
|
204
|
+
return node.name.text;
|
|
205
|
+
}
|
|
206
|
+
if (import_typescript.default.isIdentifier(node)) {
|
|
207
|
+
return node.text;
|
|
208
|
+
}
|
|
209
|
+
if (import_typescript.default.isTypeAliasDeclaration(node) || import_typescript.default.isInterfaceDeclaration(node) || import_typescript.default.isClassDeclaration(node)) {
|
|
210
|
+
return node.name?.text;
|
|
211
|
+
}
|
|
212
|
+
return void 0;
|
|
130
213
|
}
|
|
131
|
-
function
|
|
214
|
+
function getDocumentation(checker, symbol) {
|
|
215
|
+
if (!symbol) return void 0;
|
|
216
|
+
const docs = symbol.getDocumentationComment(checker);
|
|
217
|
+
if (docs.length === 0) return void 0;
|
|
218
|
+
return import_typescript.default.displayPartsToString(docs);
|
|
219
|
+
}
|
|
220
|
+
function getHoverInfo(program, node, sourceFile, includeDocs) {
|
|
132
221
|
const checker = program.getTypeChecker();
|
|
133
|
-
const sf = sourceFile
|
|
134
|
-
const line =
|
|
222
|
+
const sf = sourceFile;
|
|
223
|
+
const { line, character } = sf.getLineAndCharacterOfPosition(
|
|
224
|
+
node.getStart(sf)
|
|
225
|
+
);
|
|
226
|
+
const flags = import_typescript.default.TypeFormatFlags.NoTruncation;
|
|
227
|
+
const kind = getSymbolKind(node);
|
|
228
|
+
const name = getNodeName(node);
|
|
229
|
+
let symbol;
|
|
230
|
+
if (import_typescript.default.isCallExpression(node)) {
|
|
231
|
+
const expr = node.expression;
|
|
232
|
+
if (import_typescript.default.isPropertyAccessExpression(expr)) {
|
|
233
|
+
symbol = checker.getSymbolAtLocation(expr.name);
|
|
234
|
+
} else {
|
|
235
|
+
symbol = checker.getSymbolAtLocation(expr);
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
const nodeWithName2 = node;
|
|
239
|
+
if (nodeWithName2.name) {
|
|
240
|
+
symbol = checker.getSymbolAtLocation(nodeWithName2.name);
|
|
241
|
+
} else {
|
|
242
|
+
symbol = checker.getSymbolAtLocation(node);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const documentation = includeDocs ? getDocumentation(checker, symbol) : void 0;
|
|
246
|
+
if (import_typescript.default.isCallExpression(node)) {
|
|
247
|
+
const sig2 = checker.getResolvedSignature(node);
|
|
248
|
+
if (sig2) {
|
|
249
|
+
const signature = checker.signatureToString(sig2, void 0, flags);
|
|
250
|
+
const ret = checker.getReturnTypeOfSignature(sig2);
|
|
251
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
252
|
+
return {
|
|
253
|
+
signature,
|
|
254
|
+
returnType,
|
|
255
|
+
line: line + 1,
|
|
256
|
+
column: character + 1,
|
|
257
|
+
documentation,
|
|
258
|
+
kind,
|
|
259
|
+
name
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
const t2 = checker.getTypeAtLocation(node);
|
|
263
|
+
return {
|
|
264
|
+
signature: checker.typeToString(t2, void 0, flags),
|
|
265
|
+
line: line + 1,
|
|
266
|
+
column: character + 1,
|
|
267
|
+
documentation,
|
|
268
|
+
kind,
|
|
269
|
+
name
|
|
270
|
+
};
|
|
271
|
+
}
|
|
135
272
|
let sig;
|
|
136
273
|
if (import_typescript.default.isFunctionDeclaration(node) || import_typescript.default.isMethodDeclaration(node)) {
|
|
137
274
|
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
@@ -146,26 +283,39 @@ function getTypeInfo(program, node, sourceFile) {
|
|
|
146
283
|
} else if (import_typescript.default.isMethodSignature(node)) {
|
|
147
284
|
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
148
285
|
}
|
|
149
|
-
const flags = import_typescript.default.TypeFormatFlags.NoTruncation;
|
|
150
286
|
if (sig) {
|
|
151
287
|
const signature = checker.signatureToString(sig, void 0, flags);
|
|
152
288
|
const ret = checker.getReturnTypeOfSignature(sig);
|
|
153
289
|
const returnType = checker.typeToString(ret, void 0, flags);
|
|
154
|
-
return {
|
|
290
|
+
return {
|
|
291
|
+
signature,
|
|
292
|
+
returnType,
|
|
293
|
+
line: line + 1,
|
|
294
|
+
column: character + 1,
|
|
295
|
+
documentation,
|
|
296
|
+
kind,
|
|
297
|
+
name
|
|
298
|
+
};
|
|
155
299
|
}
|
|
156
300
|
const nodeWithName = node;
|
|
157
|
-
let
|
|
301
|
+
let targetNode = node;
|
|
158
302
|
if (nodeWithName.name && import_typescript.default.isIdentifier(nodeWithName.name)) {
|
|
159
|
-
|
|
303
|
+
targetNode = nodeWithName.name;
|
|
160
304
|
}
|
|
161
|
-
const t = checker.getTypeAtLocation(
|
|
162
|
-
return {
|
|
305
|
+
const t = checker.getTypeAtLocation(targetNode);
|
|
306
|
+
return {
|
|
307
|
+
signature: checker.typeToString(t, void 0, flags),
|
|
308
|
+
line: line + 1,
|
|
309
|
+
column: character + 1,
|
|
310
|
+
documentation,
|
|
311
|
+
kind,
|
|
312
|
+
name
|
|
313
|
+
};
|
|
163
314
|
}
|
|
164
315
|
|
|
165
316
|
// src/index.ts
|
|
166
|
-
function
|
|
167
|
-
const
|
|
168
|
-
const { line, project } = opts;
|
|
317
|
+
function hover(file, line, column, options) {
|
|
318
|
+
const { project, include_docs = false } = options ?? {};
|
|
169
319
|
const entryFileAbs = import_node_path2.default.resolve(process.cwd(), file);
|
|
170
320
|
if (!import_node_fs.default.existsSync(entryFileAbs)) {
|
|
171
321
|
throw new Error(`File not found: ${entryFileAbs}`);
|
|
@@ -177,14 +327,11 @@ function inferType(file, name, options) {
|
|
|
177
327
|
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
178
328
|
);
|
|
179
329
|
}
|
|
180
|
-
const node =
|
|
330
|
+
const node = findNodeAtPosition(sourceFile, line, column);
|
|
181
331
|
if (!node) {
|
|
182
|
-
|
|
183
|
-
throw new Error(
|
|
184
|
-
`No symbol named "${name}"${lineInfo} found in ${entryFileAbs}`
|
|
185
|
-
);
|
|
332
|
+
throw new Error(`No symbol found at ${entryFileAbs}:${line}:${column}`);
|
|
186
333
|
}
|
|
187
|
-
return
|
|
334
|
+
return getHoverInfo(program, node, sourceFile, include_docs);
|
|
188
335
|
}
|
|
189
336
|
|
|
190
337
|
// src/cli.ts
|
|
@@ -193,24 +340,24 @@ var HELP = `
|
|
|
193
340
|
prinfer - TypeScript type inference inspection tool
|
|
194
341
|
|
|
195
342
|
Usage:
|
|
196
|
-
prinfer <file.ts>[
|
|
343
|
+
prinfer <file.ts>:<line>:<column> [--docs] [--project <tsconfig.json>]
|
|
197
344
|
prinfer setup
|
|
198
345
|
|
|
199
346
|
Commands:
|
|
200
347
|
setup Install MCP server and skill for Claude Code
|
|
201
348
|
|
|
202
349
|
Arguments:
|
|
203
|
-
file.ts
|
|
204
|
-
:line Optional line number to narrow search (e.g., file.ts:75)
|
|
205
|
-
name Name of the function/variable to inspect
|
|
350
|
+
file.ts:line:column Path to TypeScript file with 1-based line and column
|
|
206
351
|
|
|
207
352
|
Options:
|
|
353
|
+
--docs, -d Include JSDoc/TSDoc documentation
|
|
208
354
|
--project, -p Path to tsconfig.json (optional)
|
|
209
355
|
--help, -h Show this help message
|
|
210
356
|
|
|
211
357
|
Examples:
|
|
212
|
-
prinfer src/utils.ts
|
|
213
|
-
prinfer src/utils.ts:75
|
|
358
|
+
prinfer src/utils.ts:75:10
|
|
359
|
+
prinfer src/utils.ts:75:10 --docs
|
|
360
|
+
prinfer src/utils.ts:75:10 --project ./tsconfig.json
|
|
214
361
|
prinfer setup
|
|
215
362
|
`.trim();
|
|
216
363
|
var MANUAL_SETUP = `
|
|
@@ -256,26 +403,26 @@ When writing TypeScript code, prefer relying on type inference over explicit typ
|
|
|
256
403
|
- The type serves as documentation for complex structures
|
|
257
404
|
- You're defining a public API contract
|
|
258
405
|
|
|
259
|
-
Use the \`prinfer\` MCP tool (\`
|
|
406
|
+
Use the \`prinfer\` MCP tool (\`hover\`) to verify what TypeScript infers before adding explicit types.
|
|
260
407
|
|
|
261
408
|
## Commands
|
|
262
409
|
|
|
263
|
-
### /
|
|
410
|
+
### /hover
|
|
264
411
|
|
|
265
|
-
Check the inferred type
|
|
412
|
+
Check the inferred type at a specific position in a TypeScript file.
|
|
266
413
|
|
|
267
|
-
Usage: \`/
|
|
414
|
+
Usage: \`/hover <file>:<line>:<column>\`
|
|
268
415
|
|
|
269
416
|
Examples:
|
|
270
|
-
- \`/
|
|
271
|
-
- \`/
|
|
417
|
+
- \`/hover src/utils.ts:75:10\`
|
|
418
|
+
- \`/hover src/utils.ts:42:5\`
|
|
272
419
|
|
|
273
|
-
<command-name>
|
|
420
|
+
<command-name>hover</command-name>
|
|
274
421
|
|
|
275
|
-
Use the \`
|
|
276
|
-
1. Parse the arguments to extract file,
|
|
277
|
-
2. Call \`
|
|
278
|
-
3. Report the inferred signature
|
|
422
|
+
Use the \`hover\` MCP tool to check the type:
|
|
423
|
+
1. Parse the arguments to extract file, line, and column
|
|
424
|
+
2. Call \`hover(file, line, column, { include_docs: true })\`
|
|
425
|
+
3. Report the inferred signature, return type, and documentation
|
|
279
426
|
`;
|
|
280
427
|
function runSetup() {
|
|
281
428
|
const homeDir = import_node_os.default.homedir();
|
|
@@ -317,12 +464,16 @@ function runSetup() {
|
|
|
317
464
|
process.exit(1);
|
|
318
465
|
}
|
|
319
466
|
}
|
|
320
|
-
function
|
|
321
|
-
const match = arg.match(/^(.+):(\d+)$/);
|
|
467
|
+
function parsePositionArg(arg) {
|
|
468
|
+
const match = arg.match(/^(.+):(\d+):(\d+)$/);
|
|
322
469
|
if (match) {
|
|
323
|
-
return {
|
|
470
|
+
return {
|
|
471
|
+
file: match[1],
|
|
472
|
+
line: Number.parseInt(match[2], 10),
|
|
473
|
+
column: Number.parseInt(match[3], 10)
|
|
474
|
+
};
|
|
324
475
|
}
|
|
325
|
-
return
|
|
476
|
+
return null;
|
|
326
477
|
}
|
|
327
478
|
function parseArgs(argv) {
|
|
328
479
|
const args = argv.slice(2);
|
|
@@ -334,16 +485,17 @@ function parseArgs(argv) {
|
|
|
334
485
|
runSetup();
|
|
335
486
|
return null;
|
|
336
487
|
}
|
|
337
|
-
const
|
|
338
|
-
const
|
|
339
|
-
if (!
|
|
488
|
+
const positionArg = args[0];
|
|
489
|
+
const parsed = parsePositionArg(positionArg);
|
|
490
|
+
if (!parsed) {
|
|
340
491
|
console.error(
|
|
341
|
-
"Error:
|
|
492
|
+
"Error: Position argument must be in format <file>:<line>:<column>\n"
|
|
342
493
|
);
|
|
343
494
|
console.log(HELP);
|
|
344
495
|
process.exit(1);
|
|
345
496
|
}
|
|
346
|
-
const { file, line } =
|
|
497
|
+
const { file, line, column } = parsed;
|
|
498
|
+
const includeDocs = args.includes("--docs") || args.includes("-d");
|
|
347
499
|
let project;
|
|
348
500
|
const projectIdx = args.findIndex((a) => a === "--project" || a === "-p");
|
|
349
501
|
if (projectIdx >= 0) {
|
|
@@ -354,7 +506,7 @@ function parseArgs(argv) {
|
|
|
354
506
|
process.exit(1);
|
|
355
507
|
}
|
|
356
508
|
}
|
|
357
|
-
return { file,
|
|
509
|
+
return { file, line, column, includeDocs, project };
|
|
358
510
|
}
|
|
359
511
|
function main() {
|
|
360
512
|
const options = parseArgs(process.argv);
|
|
@@ -362,14 +514,21 @@ function main() {
|
|
|
362
514
|
process.exit(0);
|
|
363
515
|
}
|
|
364
516
|
try {
|
|
365
|
-
const result =
|
|
366
|
-
|
|
517
|
+
const result = hover(options.file, options.line, options.column, {
|
|
518
|
+
include_docs: options.includeDocs,
|
|
367
519
|
project: options.project
|
|
368
520
|
});
|
|
369
521
|
console.log(result.signature);
|
|
370
522
|
if (result.returnType) {
|
|
371
523
|
console.log("returns:", result.returnType);
|
|
372
524
|
}
|
|
525
|
+
if (result.name) {
|
|
526
|
+
console.log("name:", result.name);
|
|
527
|
+
}
|
|
528
|
+
console.log("kind:", result.kind);
|
|
529
|
+
if (result.documentation) {
|
|
530
|
+
console.log("docs:", result.documentation);
|
|
531
|
+
}
|
|
373
532
|
} catch (error) {
|
|
374
533
|
console.error(error.message);
|
|
375
534
|
process.exit(1);
|