prinfer 0.5.2 → 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 +26 -21
- package/dist/cli.cjs +279 -130
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +278 -130
- 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 +233 -85
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.js +233 -85
- package/dist/mcp.js.map +1 -1
- package/dist/postinstall.cjs +2 -25
- package/dist/postinstall.cjs.map +1 -1
- package/dist/postinstall.js +2 -25
- package/dist/postinstall.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
|
+
import { execSync } from "child_process";
|
|
4
5
|
import fs2 from "fs";
|
|
5
6
|
import os from "os";
|
|
6
7
|
import path3 from "path";
|
|
@@ -48,66 +49,203 @@ function loadProgram(entryFileAbs, project) {
|
|
|
48
49
|
function isArrowOrFnExpr(n) {
|
|
49
50
|
return !!n && (ts.isArrowFunction(n) || ts.isFunctionExpression(n));
|
|
50
51
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if ((ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) && nodeNameText(node.name) === name) {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
if (ts.isPropertyAssignment(node) && nodeNameText(node.name) === name) {
|
|
67
|
-
return isArrowOrFnExpr(node.initializer);
|
|
52
|
+
function findSmallestNodeAtPosition(sourceFile, position) {
|
|
53
|
+
let result;
|
|
54
|
+
function visit(node) {
|
|
55
|
+
const start = node.getStart(sourceFile);
|
|
56
|
+
const end = node.getEnd();
|
|
57
|
+
if (position < start || position >= end) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (!result || node.getWidth(sourceFile) <= result.getWidth(sourceFile)) {
|
|
61
|
+
result = node;
|
|
62
|
+
}
|
|
63
|
+
ts.forEachChild(node, visit);
|
|
68
64
|
}
|
|
69
|
-
|
|
65
|
+
visit(sourceFile);
|
|
66
|
+
return result;
|
|
70
67
|
}
|
|
71
|
-
function
|
|
72
|
-
|
|
68
|
+
function findHoverableAncestor(sourceFile, position) {
|
|
69
|
+
const smallestNode = findSmallestNodeAtPosition(sourceFile, position);
|
|
70
|
+
if (!smallestNode) return void 0;
|
|
71
|
+
let bestMatch = smallestNode;
|
|
72
|
+
function visit(n) {
|
|
73
|
+
const start = n.getStart(sourceFile);
|
|
74
|
+
const end = n.getEnd();
|
|
75
|
+
if (position < start || position >= end) {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
if (ts.isCallExpression(n)) {
|
|
79
|
+
const expr = n.expression;
|
|
80
|
+
if (ts.isIdentifier(expr)) {
|
|
81
|
+
if (position >= expr.getStart(sourceFile) && position < expr.getEnd()) {
|
|
82
|
+
bestMatch = n;
|
|
83
|
+
}
|
|
84
|
+
} else if (ts.isPropertyAccessExpression(expr)) {
|
|
85
|
+
if (position >= expr.name.getStart(sourceFile) && position < expr.name.getEnd()) {
|
|
86
|
+
bestMatch = n;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (ts.isFunctionDeclaration(n) && n.name) {
|
|
91
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
92
|
+
bestMatch = n;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
if (ts.isVariableDeclaration(n) && ts.isIdentifier(n.name)) {
|
|
96
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
97
|
+
bestMatch = n;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if ((ts.isMethodDeclaration(n) || ts.isMethodSignature(n)) && ts.isIdentifier(n.name)) {
|
|
101
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
102
|
+
bestMatch = n;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
if (ts.isClassDeclaration(n) && n.name) {
|
|
106
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
107
|
+
bestMatch = n;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ts.isInterfaceDeclaration(n) && n.name) {
|
|
111
|
+
if (position >= n.name.getStart(sourceFile) && position < n.name.getEnd()) {
|
|
112
|
+
bestMatch = n;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
ts.forEachChild(n, visit);
|
|
73
116
|
return true;
|
|
74
117
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
function isNamedNode(node, name) {
|
|
78
|
-
return isFunctionLikeNamed(node, name) || isVariableNamed(node, name);
|
|
118
|
+
visit(sourceFile);
|
|
119
|
+
return bestMatch;
|
|
79
120
|
}
|
|
80
|
-
function
|
|
81
|
-
const
|
|
82
|
-
|
|
121
|
+
function findNodeAtPosition(sourceFile, line, column) {
|
|
122
|
+
const lineCount = sourceFile.getLineStarts().length;
|
|
123
|
+
if (line < 1 || line > lineCount) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
const lineStart = sourceFile.getLineStarts()[line - 1];
|
|
127
|
+
const lineEnd = line < lineCount ? sourceFile.getLineStarts()[line] : sourceFile.getEnd();
|
|
128
|
+
const lineLength = lineEnd - lineStart;
|
|
129
|
+
if (column < 1 || column > lineLength + 1) {
|
|
130
|
+
return void 0;
|
|
131
|
+
}
|
|
132
|
+
const position = sourceFile.getPositionOfLineAndCharacter(
|
|
133
|
+
line - 1,
|
|
134
|
+
column - 1
|
|
83
135
|
);
|
|
84
|
-
return
|
|
136
|
+
return findHoverableAncestor(sourceFile, position);
|
|
85
137
|
}
|
|
86
|
-
function
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
} else {
|
|
98
|
-
found = node;
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
138
|
+
function getSymbolKind(node) {
|
|
139
|
+
if (ts.isFunctionDeclaration(node)) return "function";
|
|
140
|
+
if (ts.isArrowFunction(node)) return "function";
|
|
141
|
+
if (ts.isFunctionExpression(node)) return "function";
|
|
142
|
+
if (ts.isMethodDeclaration(node)) return "method";
|
|
143
|
+
if (ts.isMethodSignature(node)) return "method";
|
|
144
|
+
if (ts.isVariableDeclaration(node)) {
|
|
145
|
+
const init = node.initializer;
|
|
146
|
+
if (init && (ts.isArrowFunction(init) || ts.isFunctionExpression(init))) {
|
|
147
|
+
return "function";
|
|
101
148
|
}
|
|
102
|
-
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return
|
|
149
|
+
return "variable";
|
|
150
|
+
}
|
|
151
|
+
if (ts.isParameter(node)) return "parameter";
|
|
152
|
+
if (ts.isPropertyDeclaration(node)) return "property";
|
|
153
|
+
if (ts.isPropertySignature(node)) return "property";
|
|
154
|
+
if (ts.isPropertyAccessExpression(node)) return "property";
|
|
155
|
+
if (ts.isCallExpression(node)) return "call";
|
|
156
|
+
if (ts.isTypeAliasDeclaration(node)) return "type";
|
|
157
|
+
if (ts.isInterfaceDeclaration(node)) return "interface";
|
|
158
|
+
if (ts.isClassDeclaration(node)) return "class";
|
|
159
|
+
if (ts.isIdentifier(node)) return "identifier";
|
|
160
|
+
return "unknown";
|
|
106
161
|
}
|
|
107
|
-
function
|
|
162
|
+
function getNodeName(node) {
|
|
163
|
+
if (ts.isFunctionDeclaration(node) && node.name) {
|
|
164
|
+
return node.name.text;
|
|
165
|
+
}
|
|
166
|
+
if (ts.isVariableDeclaration(node) && ts.isIdentifier(node.name)) {
|
|
167
|
+
return node.name.text;
|
|
168
|
+
}
|
|
169
|
+
if ((ts.isMethodDeclaration(node) || ts.isMethodSignature(node)) && ts.isIdentifier(node.name)) {
|
|
170
|
+
return node.name.text;
|
|
171
|
+
}
|
|
172
|
+
if (ts.isPropertyAccessExpression(node)) {
|
|
173
|
+
return node.name.text;
|
|
174
|
+
}
|
|
175
|
+
if (ts.isCallExpression(node)) {
|
|
176
|
+
const expr = node.expression;
|
|
177
|
+
if (ts.isIdentifier(expr)) return expr.text;
|
|
178
|
+
if (ts.isPropertyAccessExpression(expr)) return expr.name.text;
|
|
179
|
+
}
|
|
180
|
+
if (ts.isParameter(node) && ts.isIdentifier(node.name)) {
|
|
181
|
+
return node.name.text;
|
|
182
|
+
}
|
|
183
|
+
if (ts.isIdentifier(node)) {
|
|
184
|
+
return node.text;
|
|
185
|
+
}
|
|
186
|
+
if (ts.isTypeAliasDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isClassDeclaration(node)) {
|
|
187
|
+
return node.name?.text;
|
|
188
|
+
}
|
|
189
|
+
return void 0;
|
|
190
|
+
}
|
|
191
|
+
function getDocumentation(checker, symbol) {
|
|
192
|
+
if (!symbol) return void 0;
|
|
193
|
+
const docs = symbol.getDocumentationComment(checker);
|
|
194
|
+
if (docs.length === 0) return void 0;
|
|
195
|
+
return ts.displayPartsToString(docs);
|
|
196
|
+
}
|
|
197
|
+
function getHoverInfo(program, node, sourceFile, includeDocs) {
|
|
108
198
|
const checker = program.getTypeChecker();
|
|
109
|
-
const sf = sourceFile
|
|
110
|
-
const line =
|
|
199
|
+
const sf = sourceFile;
|
|
200
|
+
const { line, character } = sf.getLineAndCharacterOfPosition(
|
|
201
|
+
node.getStart(sf)
|
|
202
|
+
);
|
|
203
|
+
const flags = ts.TypeFormatFlags.NoTruncation;
|
|
204
|
+
const kind = getSymbolKind(node);
|
|
205
|
+
const name = getNodeName(node);
|
|
206
|
+
let symbol;
|
|
207
|
+
if (ts.isCallExpression(node)) {
|
|
208
|
+
const expr = node.expression;
|
|
209
|
+
if (ts.isPropertyAccessExpression(expr)) {
|
|
210
|
+
symbol = checker.getSymbolAtLocation(expr.name);
|
|
211
|
+
} else {
|
|
212
|
+
symbol = checker.getSymbolAtLocation(expr);
|
|
213
|
+
}
|
|
214
|
+
} else {
|
|
215
|
+
const nodeWithName2 = node;
|
|
216
|
+
if (nodeWithName2.name) {
|
|
217
|
+
symbol = checker.getSymbolAtLocation(nodeWithName2.name);
|
|
218
|
+
} else {
|
|
219
|
+
symbol = checker.getSymbolAtLocation(node);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const documentation = includeDocs ? getDocumentation(checker, symbol) : void 0;
|
|
223
|
+
if (ts.isCallExpression(node)) {
|
|
224
|
+
const sig2 = checker.getResolvedSignature(node);
|
|
225
|
+
if (sig2) {
|
|
226
|
+
const signature = checker.signatureToString(sig2, void 0, flags);
|
|
227
|
+
const ret = checker.getReturnTypeOfSignature(sig2);
|
|
228
|
+
const returnType = checker.typeToString(ret, void 0, flags);
|
|
229
|
+
return {
|
|
230
|
+
signature,
|
|
231
|
+
returnType,
|
|
232
|
+
line: line + 1,
|
|
233
|
+
column: character + 1,
|
|
234
|
+
documentation,
|
|
235
|
+
kind,
|
|
236
|
+
name
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
const t2 = checker.getTypeAtLocation(node);
|
|
240
|
+
return {
|
|
241
|
+
signature: checker.typeToString(t2, void 0, flags),
|
|
242
|
+
line: line + 1,
|
|
243
|
+
column: character + 1,
|
|
244
|
+
documentation,
|
|
245
|
+
kind,
|
|
246
|
+
name
|
|
247
|
+
};
|
|
248
|
+
}
|
|
111
249
|
let sig;
|
|
112
250
|
if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) {
|
|
113
251
|
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
@@ -122,26 +260,39 @@ function getTypeInfo(program, node, sourceFile) {
|
|
|
122
260
|
} else if (ts.isMethodSignature(node)) {
|
|
123
261
|
sig = checker.getSignatureFromDeclaration(node) ?? void 0;
|
|
124
262
|
}
|
|
125
|
-
const flags = ts.TypeFormatFlags.NoTruncation;
|
|
126
263
|
if (sig) {
|
|
127
264
|
const signature = checker.signatureToString(sig, void 0, flags);
|
|
128
265
|
const ret = checker.getReturnTypeOfSignature(sig);
|
|
129
266
|
const returnType = checker.typeToString(ret, void 0, flags);
|
|
130
|
-
return {
|
|
267
|
+
return {
|
|
268
|
+
signature,
|
|
269
|
+
returnType,
|
|
270
|
+
line: line + 1,
|
|
271
|
+
column: character + 1,
|
|
272
|
+
documentation,
|
|
273
|
+
kind,
|
|
274
|
+
name
|
|
275
|
+
};
|
|
131
276
|
}
|
|
132
277
|
const nodeWithName = node;
|
|
133
|
-
let
|
|
278
|
+
let targetNode = node;
|
|
134
279
|
if (nodeWithName.name && ts.isIdentifier(nodeWithName.name)) {
|
|
135
|
-
|
|
280
|
+
targetNode = nodeWithName.name;
|
|
136
281
|
}
|
|
137
|
-
const t = checker.getTypeAtLocation(
|
|
138
|
-
return {
|
|
282
|
+
const t = checker.getTypeAtLocation(targetNode);
|
|
283
|
+
return {
|
|
284
|
+
signature: checker.typeToString(t, void 0, flags),
|
|
285
|
+
line: line + 1,
|
|
286
|
+
column: character + 1,
|
|
287
|
+
documentation,
|
|
288
|
+
kind,
|
|
289
|
+
name
|
|
290
|
+
};
|
|
139
291
|
}
|
|
140
292
|
|
|
141
293
|
// src/index.ts
|
|
142
|
-
function
|
|
143
|
-
const
|
|
144
|
-
const { line, project } = opts;
|
|
294
|
+
function hover(file, line, column, options) {
|
|
295
|
+
const { project, include_docs = false } = options ?? {};
|
|
145
296
|
const entryFileAbs = path2.resolve(process.cwd(), file);
|
|
146
297
|
if (!fs.existsSync(entryFileAbs)) {
|
|
147
298
|
throw new Error(`File not found: ${entryFileAbs}`);
|
|
@@ -153,14 +304,11 @@ function inferType(file, name, options) {
|
|
|
153
304
|
`Could not load source file into the program (check tsconfig include/exclude): ${entryFileAbs}`
|
|
154
305
|
);
|
|
155
306
|
}
|
|
156
|
-
const node =
|
|
307
|
+
const node = findNodeAtPosition(sourceFile, line, column);
|
|
157
308
|
if (!node) {
|
|
158
|
-
|
|
159
|
-
throw new Error(
|
|
160
|
-
`No symbol named "${name}"${lineInfo} found in ${entryFileAbs}`
|
|
161
|
-
);
|
|
309
|
+
throw new Error(`No symbol found at ${entryFileAbs}:${line}:${column}`);
|
|
162
310
|
}
|
|
163
|
-
return
|
|
311
|
+
return getHoverInfo(program, node, sourceFile, include_docs);
|
|
164
312
|
}
|
|
165
313
|
|
|
166
314
|
// src/cli.ts
|
|
@@ -168,43 +316,56 @@ var HELP = `
|
|
|
168
316
|
prinfer - TypeScript type inference inspection tool
|
|
169
317
|
|
|
170
318
|
Usage:
|
|
171
|
-
prinfer <file.ts>[
|
|
319
|
+
prinfer <file.ts>:<line>:<column> [--docs] [--project <tsconfig.json>]
|
|
172
320
|
prinfer setup
|
|
173
321
|
|
|
174
322
|
Commands:
|
|
175
323
|
setup Install MCP server and skill for Claude Code
|
|
176
324
|
|
|
177
325
|
Arguments:
|
|
178
|
-
file.ts
|
|
179
|
-
:line Optional line number to narrow search (e.g., file.ts:75)
|
|
180
|
-
name Name of the function/variable to inspect
|
|
326
|
+
file.ts:line:column Path to TypeScript file with 1-based line and column
|
|
181
327
|
|
|
182
328
|
Options:
|
|
329
|
+
--docs, -d Include JSDoc/TSDoc documentation
|
|
183
330
|
--project, -p Path to tsconfig.json (optional)
|
|
184
331
|
--help, -h Show this help message
|
|
185
332
|
|
|
186
333
|
Examples:
|
|
187
|
-
prinfer src/utils.ts
|
|
188
|
-
prinfer src/utils.ts:75
|
|
334
|
+
prinfer src/utils.ts:75:10
|
|
335
|
+
prinfer src/utils.ts:75:10 --docs
|
|
336
|
+
prinfer src/utils.ts:75:10 --project ./tsconfig.json
|
|
189
337
|
prinfer setup
|
|
190
338
|
`.trim();
|
|
191
339
|
var MANUAL_SETUP = `
|
|
192
340
|
Manual setup instructions:
|
|
193
341
|
|
|
194
|
-
1. Add MCP server
|
|
195
|
-
|
|
196
|
-
{
|
|
197
|
-
"mcpServers": {
|
|
198
|
-
"prinfer": {
|
|
199
|
-
"command": "prinfer-mcp"
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
}
|
|
342
|
+
1. Add MCP server:
|
|
343
|
+
Run: claude mcp add prinfer node /path/to/prinfer-mcp
|
|
203
344
|
|
|
204
345
|
2. Create skill file at ~/.claude/skills/prefer-infer.md:
|
|
205
|
-
|
|
206
346
|
See https://github.com/clockblocker/prinfer for skill content.
|
|
207
347
|
`.trim();
|
|
348
|
+
function getMcpBinaryPath() {
|
|
349
|
+
const thisScript = new URL(import.meta.url).pathname;
|
|
350
|
+
return path3.join(path3.dirname(thisScript), "mcp.js");
|
|
351
|
+
}
|
|
352
|
+
function installMcpServer() {
|
|
353
|
+
try {
|
|
354
|
+
const mcpPath = getMcpBinaryPath();
|
|
355
|
+
try {
|
|
356
|
+
execSync("claude mcp remove prinfer", { stdio: "pipe" });
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
execSync(`claude mcp add prinfer node ${mcpPath}`, {
|
|
360
|
+
stdio: "inherit"
|
|
361
|
+
});
|
|
362
|
+
console.log("[ok] Added prinfer MCP server");
|
|
363
|
+
return true;
|
|
364
|
+
} catch (err) {
|
|
365
|
+
console.error(`[error] MCP setup failed: ${err.message}`);
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
208
369
|
var SKILL_CONTENT = `# TypeScript Type Inference (prinfer)
|
|
209
370
|
|
|
210
371
|
## Coding Guideline
|
|
@@ -218,26 +379,26 @@ When writing TypeScript code, prefer relying on type inference over explicit typ
|
|
|
218
379
|
- The type serves as documentation for complex structures
|
|
219
380
|
- You're defining a public API contract
|
|
220
381
|
|
|
221
|
-
Use the \`prinfer\` MCP tool (\`
|
|
382
|
+
Use the \`prinfer\` MCP tool (\`hover\`) to verify what TypeScript infers before adding explicit types.
|
|
222
383
|
|
|
223
384
|
## Commands
|
|
224
385
|
|
|
225
|
-
### /
|
|
386
|
+
### /hover
|
|
226
387
|
|
|
227
|
-
Check the inferred type
|
|
388
|
+
Check the inferred type at a specific position in a TypeScript file.
|
|
228
389
|
|
|
229
|
-
Usage: \`/
|
|
390
|
+
Usage: \`/hover <file>:<line>:<column>\`
|
|
230
391
|
|
|
231
392
|
Examples:
|
|
232
|
-
- \`/
|
|
233
|
-
- \`/
|
|
393
|
+
- \`/hover src/utils.ts:75:10\`
|
|
394
|
+
- \`/hover src/utils.ts:42:5\`
|
|
234
395
|
|
|
235
|
-
<command-name>
|
|
396
|
+
<command-name>hover</command-name>
|
|
236
397
|
|
|
237
|
-
Use the \`
|
|
238
|
-
1. Parse the arguments to extract file,
|
|
239
|
-
2. Call \`
|
|
240
|
-
3. Report the inferred signature
|
|
398
|
+
Use the \`hover\` MCP tool to check the type:
|
|
399
|
+
1. Parse the arguments to extract file, line, and column
|
|
400
|
+
2. Call \`hover(file, line, column, { include_docs: true })\`
|
|
401
|
+
3. Report the inferred signature, return type, and documentation
|
|
241
402
|
`;
|
|
242
403
|
function runSetup() {
|
|
243
404
|
const homeDir = os.homedir();
|
|
@@ -248,33 +409,8 @@ function runSetup() {
|
|
|
248
409
|
console.error(MANUAL_SETUP);
|
|
249
410
|
process.exit(1);
|
|
250
411
|
}
|
|
251
|
-
|
|
412
|
+
const mcpOk = installMcpServer();
|
|
252
413
|
let skillOk = false;
|
|
253
|
-
const configFile = path3.join(claudeDir, "settings.json");
|
|
254
|
-
try {
|
|
255
|
-
let config = {};
|
|
256
|
-
if (fs2.existsSync(configFile)) {
|
|
257
|
-
config = JSON.parse(fs2.readFileSync(configFile, "utf-8"));
|
|
258
|
-
}
|
|
259
|
-
if (config.mcpServers?.prinfer) {
|
|
260
|
-
console.log("[ok] MCP server already configured");
|
|
261
|
-
mcpOk = true;
|
|
262
|
-
} else {
|
|
263
|
-
config.mcpServers = config.mcpServers || {};
|
|
264
|
-
config.mcpServers.prinfer = { command: "prinfer-mcp" };
|
|
265
|
-
fs2.writeFileSync(
|
|
266
|
-
configFile,
|
|
267
|
-
`${JSON.stringify(config, null, 2)}
|
|
268
|
-
`
|
|
269
|
-
);
|
|
270
|
-
console.log("[ok] Added MCP server to settings.json");
|
|
271
|
-
mcpOk = true;
|
|
272
|
-
}
|
|
273
|
-
} catch (err) {
|
|
274
|
-
console.error(
|
|
275
|
-
`[error] Failed to configure MCP server: ${err.message}`
|
|
276
|
-
);
|
|
277
|
-
}
|
|
278
414
|
const skillsDir = path3.join(claudeDir, "skills");
|
|
279
415
|
const skillFile = path3.join(skillsDir, "prefer-infer.md");
|
|
280
416
|
try {
|
|
@@ -304,12 +440,16 @@ function runSetup() {
|
|
|
304
440
|
process.exit(1);
|
|
305
441
|
}
|
|
306
442
|
}
|
|
307
|
-
function
|
|
308
|
-
const match = arg.match(/^(.+):(\d+)$/);
|
|
443
|
+
function parsePositionArg(arg) {
|
|
444
|
+
const match = arg.match(/^(.+):(\d+):(\d+)$/);
|
|
309
445
|
if (match) {
|
|
310
|
-
return {
|
|
446
|
+
return {
|
|
447
|
+
file: match[1],
|
|
448
|
+
line: Number.parseInt(match[2], 10),
|
|
449
|
+
column: Number.parseInt(match[3], 10)
|
|
450
|
+
};
|
|
311
451
|
}
|
|
312
|
-
return
|
|
452
|
+
return null;
|
|
313
453
|
}
|
|
314
454
|
function parseArgs(argv) {
|
|
315
455
|
const args = argv.slice(2);
|
|
@@ -321,16 +461,17 @@ function parseArgs(argv) {
|
|
|
321
461
|
runSetup();
|
|
322
462
|
return null;
|
|
323
463
|
}
|
|
324
|
-
const
|
|
325
|
-
const
|
|
326
|
-
if (!
|
|
464
|
+
const positionArg = args[0];
|
|
465
|
+
const parsed = parsePositionArg(positionArg);
|
|
466
|
+
if (!parsed) {
|
|
327
467
|
console.error(
|
|
328
|
-
"Error:
|
|
468
|
+
"Error: Position argument must be in format <file>:<line>:<column>\n"
|
|
329
469
|
);
|
|
330
470
|
console.log(HELP);
|
|
331
471
|
process.exit(1);
|
|
332
472
|
}
|
|
333
|
-
const { file, line } =
|
|
473
|
+
const { file, line, column } = parsed;
|
|
474
|
+
const includeDocs = args.includes("--docs") || args.includes("-d");
|
|
334
475
|
let project;
|
|
335
476
|
const projectIdx = args.findIndex((a) => a === "--project" || a === "-p");
|
|
336
477
|
if (projectIdx >= 0) {
|
|
@@ -341,7 +482,7 @@ function parseArgs(argv) {
|
|
|
341
482
|
process.exit(1);
|
|
342
483
|
}
|
|
343
484
|
}
|
|
344
|
-
return { file,
|
|
485
|
+
return { file, line, column, includeDocs, project };
|
|
345
486
|
}
|
|
346
487
|
function main() {
|
|
347
488
|
const options = parseArgs(process.argv);
|
|
@@ -349,14 +490,21 @@ function main() {
|
|
|
349
490
|
process.exit(0);
|
|
350
491
|
}
|
|
351
492
|
try {
|
|
352
|
-
const result =
|
|
353
|
-
|
|
493
|
+
const result = hover(options.file, options.line, options.column, {
|
|
494
|
+
include_docs: options.includeDocs,
|
|
354
495
|
project: options.project
|
|
355
496
|
});
|
|
356
497
|
console.log(result.signature);
|
|
357
498
|
if (result.returnType) {
|
|
358
499
|
console.log("returns:", result.returnType);
|
|
359
500
|
}
|
|
501
|
+
if (result.name) {
|
|
502
|
+
console.log("name:", result.name);
|
|
503
|
+
}
|
|
504
|
+
console.log("kind:", result.kind);
|
|
505
|
+
if (result.documentation) {
|
|
506
|
+
console.log("docs:", result.documentation);
|
|
507
|
+
}
|
|
360
508
|
} catch (error) {
|
|
361
509
|
console.error(error.message);
|
|
362
510
|
process.exit(1);
|