opencodekit 0.9.1 → 0.10.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 +2 -2
- package/dist/index.js +32 -24
- package/dist/template/.opencode/AGENTS.md +180 -85
- package/dist/template/.opencode/README.md +2 -2
- package/dist/template/.opencode/agent/build.md +16 -26
- package/dist/template/.opencode/agent/explore.md +18 -25
- package/dist/template/.opencode/agent/planner.md +38 -9
- package/dist/template/.opencode/agent/review.md +5 -13
- package/dist/template/.opencode/agent/rush.md +24 -55
- package/dist/template/.opencode/agent/scout.md +5 -21
- package/dist/template/.opencode/agent/vision.md +0 -14
- package/dist/template/.opencode/opencode.json +514 -474
- package/dist/template/.opencode/plugin/README.md +110 -98
- package/dist/template/.opencode/plugin/compactor.ts +95 -171
- package/dist/template/.opencode/plugin/enforcer.ts +177 -127
- package/dist/template/.opencode/plugin/injector.ts +150 -0
- package/dist/template/.opencode/plugin/lib/notify.ts +86 -0
- package/dist/template/.opencode/plugin/notification.ts +57 -123
- package/dist/template/.opencode/plugin/truncator.ts +60 -166
- package/dist/template/.opencode/skill/mqdh/SKILL.md +161 -0
- package/dist/template/.opencode/skill/v0/SKILL.md +154 -0
- package/dist/template/.opencode/tool/lsp.ts +454 -0
- package/package.json +51 -63
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: v0
|
|
3
|
+
description: AI-powered UI generation via v0 MCP. Create chats, generate React components, get design assistance. Use when building UI components, dashboards, or need AI design help.
|
|
4
|
+
mcp:
|
|
5
|
+
v0:
|
|
6
|
+
command: npx
|
|
7
|
+
args:
|
|
8
|
+
[
|
|
9
|
+
"mcp-remote",
|
|
10
|
+
"https://mcp.v0.dev",
|
|
11
|
+
"--header",
|
|
12
|
+
"Authorization: Bearer ${V0_API_KEY}",
|
|
13
|
+
]
|
|
14
|
+
env:
|
|
15
|
+
V0_API_KEY: "${V0_API_KEY}"
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# v0 AI Code Generation (MCP)
|
|
19
|
+
|
|
20
|
+
Access v0's AI-powered code generation via MCP. When this skill is loaded, the `v0` MCP server auto-starts and exposes tools for creating and managing v0 chats.
|
|
21
|
+
|
|
22
|
+
## Prerequisites
|
|
23
|
+
|
|
24
|
+
Set your v0 API key as an environment variable:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export V0_API_KEY="your-v0-api-key"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To get your v0 API key:
|
|
31
|
+
|
|
32
|
+
1. Go to [v0 account settings](https://v0.app/chat/settings/keys)
|
|
33
|
+
2. Create a new API key
|
|
34
|
+
3. Copy and set as environment variable
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
After loading this skill, list available tools:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
skill_mcp(skill_name="v0", list_tools=true)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Then invoke tools:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
skill_mcp(skill_name="v0", tool_name="create_chat", arguments='{"prompt": "Create a React dashboard component with a sidebar"}')
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Available Tools
|
|
51
|
+
|
|
52
|
+
### create_chat
|
|
53
|
+
|
|
54
|
+
Create a new v0 chat with a prompt.
|
|
55
|
+
|
|
56
|
+
| Parameter | Type | Required | Description |
|
|
57
|
+
| --------- | ------ | -------- | ---------------------------------- |
|
|
58
|
+
| `prompt` | string | Yes | The prompt for v0 to generate code |
|
|
59
|
+
|
|
60
|
+
**Example:**
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
skill_mcp(skill_name="v0", tool_name="create_chat", arguments='{"prompt": "Build a responsive navbar with dark mode toggle"}')
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### get_chat
|
|
67
|
+
|
|
68
|
+
Get details about an existing chat.
|
|
69
|
+
|
|
70
|
+
| Parameter | Type | Required | Description |
|
|
71
|
+
| --------- | ------ | -------- | -------------- |
|
|
72
|
+
| `chatId` | string | Yes | The v0 chat ID |
|
|
73
|
+
|
|
74
|
+
**Example:**
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
skill_mcp(skill_name="v0", tool_name="get_chat", arguments='{"chatId": "abc123"}')
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### find_chats
|
|
81
|
+
|
|
82
|
+
Search through your v0 chats.
|
|
83
|
+
|
|
84
|
+
| Parameter | Type | Required | Description |
|
|
85
|
+
| --------- | ------ | -------- | ------------ |
|
|
86
|
+
| `query` | string | No | Search query |
|
|
87
|
+
|
|
88
|
+
**Example:**
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
skill_mcp(skill_name="v0", tool_name="find_chats", arguments='{"query": "React components"}')
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### send_message
|
|
95
|
+
|
|
96
|
+
Continue a conversation in an existing chat.
|
|
97
|
+
|
|
98
|
+
| Parameter | Type | Required | Description |
|
|
99
|
+
| --------- | ------ | -------- | --------------- |
|
|
100
|
+
| `chatId` | string | Yes | The v0 chat ID |
|
|
101
|
+
| `message` | string | Yes | Message to send |
|
|
102
|
+
|
|
103
|
+
**Example:**
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
skill_mcp(skill_name="v0", tool_name="send_message", arguments='{"chatId": "abc123", "message": "Add dark mode support"}')
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Workflow
|
|
110
|
+
|
|
111
|
+
### 1. Generate a New Component
|
|
112
|
+
|
|
113
|
+
```
|
|
114
|
+
# Create a new chat with your requirements
|
|
115
|
+
skill_mcp(skill_name="v0", tool_name="create_chat", arguments='{"prompt": "Create a modern pricing table with 3 tiers using Tailwind CSS"}')
|
|
116
|
+
|
|
117
|
+
# The response includes a chat ID and generated code
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### 2. Iterate on Design
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
# Send follow-up messages to refine
|
|
124
|
+
skill_mcp(skill_name="v0", tool_name="send_message", arguments='{"chatId": "chat-id-here", "message": "Make the recommended tier more prominent with a gradient border"}')
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### 3. Search Previous Work
|
|
128
|
+
|
|
129
|
+
```
|
|
130
|
+
# Find relevant previous chats
|
|
131
|
+
skill_mcp(skill_name="v0", tool_name="find_chats", arguments='{"query": "dashboard"}')
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Use Cases
|
|
135
|
+
|
|
136
|
+
- **Component Generation**: Create React/Next.js components from descriptions
|
|
137
|
+
- **UI Prototyping**: Rapidly prototype UI ideas
|
|
138
|
+
- **Design Iteration**: Refine designs through conversation
|
|
139
|
+
- **Code Assistance**: Get help with Tailwind CSS, responsive design, etc.
|
|
140
|
+
|
|
141
|
+
## Tips
|
|
142
|
+
|
|
143
|
+
- **Be specific** in prompts - include framework (React, Next.js), styling (Tailwind, CSS), and functionality details
|
|
144
|
+
- **Iterate** using `send_message` rather than creating new chats for refinements
|
|
145
|
+
- **Search first** with `find_chats` to reuse previous work
|
|
146
|
+
- **Include context** about your existing design system or component patterns
|
|
147
|
+
|
|
148
|
+
## Troubleshooting
|
|
149
|
+
|
|
150
|
+
**"Invalid API key"**: Ensure `V0_API_KEY` environment variable is set correctly.
|
|
151
|
+
|
|
152
|
+
**"Rate limit exceeded"**: v0 has usage limits. Wait a few minutes or check your plan.
|
|
153
|
+
|
|
154
|
+
**"Chat not found"**: Verify the chat ID is correct. Use `find_chats` to list available chats.
|
|
@@ -14,7 +14,13 @@ import { lspManager } from "../lib/lsp/client";
|
|
|
14
14
|
import { resolveServer } from "../lib/lsp/config";
|
|
15
15
|
import type {
|
|
16
16
|
CodeAction,
|
|
17
|
+
Diagnostic,
|
|
18
|
+
DocumentSymbol,
|
|
19
|
+
HoverResult,
|
|
20
|
+
Location,
|
|
21
|
+
LocationLink,
|
|
17
22
|
PrepareRenameResult,
|
|
23
|
+
SymbolInfo,
|
|
18
24
|
WorkspaceEdit,
|
|
19
25
|
} from "../lib/lsp/types";
|
|
20
26
|
import {
|
|
@@ -328,5 +334,453 @@ Note: File was modified directly. Re-read before editing.`;
|
|
|
328
334
|
},
|
|
329
335
|
});
|
|
330
336
|
|
|
337
|
+
/**
|
|
338
|
+
* Get hover information (type/documentation) at a position
|
|
339
|
+
*/
|
|
340
|
+
export const lsp_hover = tool({
|
|
341
|
+
description: `Get type information and documentation for a symbol at a specific position.
|
|
342
|
+
|
|
343
|
+
Returns type signatures, JSDoc/docstrings, and other hover information the language server provides.
|
|
344
|
+
Useful for understanding what a variable/function is without reading its definition.`,
|
|
345
|
+
|
|
346
|
+
args: {
|
|
347
|
+
filePath: tool.schema.string().describe("Path to the file"),
|
|
348
|
+
line: tool.schema.number().describe("Line number (1-based)"),
|
|
349
|
+
character: tool.schema.number().describe("Character position (0-based)"),
|
|
350
|
+
},
|
|
351
|
+
|
|
352
|
+
async execute(args) {
|
|
353
|
+
const cwd = process.cwd();
|
|
354
|
+
const absPath = resolve(cwd, args.filePath);
|
|
355
|
+
const server = resolveServer(absPath);
|
|
356
|
+
|
|
357
|
+
if (!server) {
|
|
358
|
+
return `Error: No LSP server available for file type: ${absPath}`;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
try {
|
|
362
|
+
const client = await lspManager.getClient(cwd, server);
|
|
363
|
+
const result = (await client.hover(
|
|
364
|
+
absPath,
|
|
365
|
+
args.line,
|
|
366
|
+
args.character,
|
|
367
|
+
)) as HoverResult | null;
|
|
368
|
+
lspManager.releaseClient(cwd, server.id);
|
|
369
|
+
|
|
370
|
+
if (!result) {
|
|
371
|
+
return "No hover information available at this position.";
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return formatHoverResult(result);
|
|
375
|
+
} catch (error) {
|
|
376
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
377
|
+
}
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Go to definition of a symbol
|
|
383
|
+
*/
|
|
384
|
+
export const lsp_goto_definition = tool({
|
|
385
|
+
description: `Find the definition location of a symbol at a specific position.
|
|
386
|
+
|
|
387
|
+
Returns the file path and position where the symbol is defined.
|
|
388
|
+
Works for functions, variables, classes, types, imports, etc.`,
|
|
389
|
+
|
|
390
|
+
args: {
|
|
391
|
+
filePath: tool.schema.string().describe("Path to the file"),
|
|
392
|
+
line: tool.schema.number().describe("Line number (1-based)"),
|
|
393
|
+
character: tool.schema.number().describe("Character position (0-based)"),
|
|
394
|
+
},
|
|
395
|
+
|
|
396
|
+
async execute(args) {
|
|
397
|
+
const cwd = process.cwd();
|
|
398
|
+
const absPath = resolve(cwd, args.filePath);
|
|
399
|
+
const server = resolveServer(absPath);
|
|
400
|
+
|
|
401
|
+
if (!server) {
|
|
402
|
+
return `Error: No LSP server available for file type: ${absPath}`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const client = await lspManager.getClient(cwd, server);
|
|
407
|
+
const result = (await client.definition(
|
|
408
|
+
absPath,
|
|
409
|
+
args.line,
|
|
410
|
+
args.character,
|
|
411
|
+
)) as Location | Location[] | LocationLink[] | null;
|
|
412
|
+
lspManager.releaseClient(cwd, server.id);
|
|
413
|
+
|
|
414
|
+
if (!result) {
|
|
415
|
+
return "No definition found at this position.";
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return formatLocations(result);
|
|
419
|
+
} catch (error) {
|
|
420
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
421
|
+
}
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
/**
|
|
426
|
+
* Find all references to a symbol
|
|
427
|
+
*/
|
|
428
|
+
export const lsp_find_references = tool({
|
|
429
|
+
description: `Find all references to a symbol across the workspace.
|
|
430
|
+
|
|
431
|
+
Returns all locations where the symbol is used, including the definition.
|
|
432
|
+
Useful for understanding usage before refactoring.`,
|
|
433
|
+
|
|
434
|
+
args: {
|
|
435
|
+
filePath: tool.schema.string().describe("Path to the file"),
|
|
436
|
+
line: tool.schema.number().describe("Line number (1-based)"),
|
|
437
|
+
character: tool.schema.number().describe("Character position (0-based)"),
|
|
438
|
+
includeDeclaration: tool.schema
|
|
439
|
+
.boolean()
|
|
440
|
+
.optional()
|
|
441
|
+
.describe("Include the declaration itself (default: true)"),
|
|
442
|
+
},
|
|
443
|
+
|
|
444
|
+
async execute(args) {
|
|
445
|
+
const cwd = process.cwd();
|
|
446
|
+
const absPath = resolve(cwd, args.filePath);
|
|
447
|
+
const server = resolveServer(absPath);
|
|
448
|
+
|
|
449
|
+
if (!server) {
|
|
450
|
+
return `Error: No LSP server available for file type: ${absPath}`;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
try {
|
|
454
|
+
const client = await lspManager.getClient(cwd, server);
|
|
455
|
+
const result = (await client.references(
|
|
456
|
+
absPath,
|
|
457
|
+
args.line,
|
|
458
|
+
args.character,
|
|
459
|
+
args.includeDeclaration ?? true,
|
|
460
|
+
)) as Location[] | null;
|
|
461
|
+
lspManager.releaseClient(cwd, server.id);
|
|
462
|
+
|
|
463
|
+
if (!result || result.length === 0) {
|
|
464
|
+
return "No references found.";
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const locations = result.slice(0, 50); // Limit results
|
|
468
|
+
const formatted = locations.map((loc) => formatLocation(loc)).join("\n");
|
|
469
|
+
const suffix =
|
|
470
|
+
result.length > 50 ? `\n\n... and ${result.length - 50} more` : "";
|
|
471
|
+
|
|
472
|
+
return `Found ${result.length} reference(s):\n\n${formatted}${suffix}`;
|
|
473
|
+
} catch (error) {
|
|
474
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
475
|
+
}
|
|
476
|
+
},
|
|
477
|
+
});
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Get document symbols (outline) for a file
|
|
481
|
+
*/
|
|
482
|
+
export const lsp_document_symbols = tool({
|
|
483
|
+
description: `Get the symbol outline of a file (classes, functions, variables, etc.).
|
|
484
|
+
|
|
485
|
+
Returns a hierarchical structure of all symbols in the file.
|
|
486
|
+
Useful for understanding file structure quickly.`,
|
|
487
|
+
|
|
488
|
+
args: {
|
|
489
|
+
filePath: tool.schema.string().describe("Path to the file"),
|
|
490
|
+
},
|
|
491
|
+
|
|
492
|
+
async execute(args) {
|
|
493
|
+
const cwd = process.cwd();
|
|
494
|
+
const absPath = resolve(cwd, args.filePath);
|
|
495
|
+
const server = resolveServer(absPath);
|
|
496
|
+
|
|
497
|
+
if (!server) {
|
|
498
|
+
return `Error: No LSP server available for file type: ${absPath}`;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
const client = await lspManager.getClient(cwd, server);
|
|
503
|
+
const result = (await client.documentSymbols(absPath)) as
|
|
504
|
+
| DocumentSymbol[]
|
|
505
|
+
| SymbolInfo[]
|
|
506
|
+
| null;
|
|
507
|
+
lspManager.releaseClient(cwd, server.id);
|
|
508
|
+
|
|
509
|
+
if (!result || result.length === 0) {
|
|
510
|
+
return "No symbols found in this file.";
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return formatDocumentSymbols(result);
|
|
514
|
+
} catch (error) {
|
|
515
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Search for symbols across the workspace
|
|
522
|
+
*/
|
|
523
|
+
export const lsp_workspace_symbols = tool({
|
|
524
|
+
description: `Search for symbols by name across the entire workspace.
|
|
525
|
+
|
|
526
|
+
Performs fuzzy matching on symbol names (functions, classes, variables, etc.).
|
|
527
|
+
Useful for finding where something is defined without knowing the exact file.`,
|
|
528
|
+
|
|
529
|
+
args: {
|
|
530
|
+
query: tool.schema
|
|
531
|
+
.string()
|
|
532
|
+
.describe("Symbol name to search for (fuzzy match)"),
|
|
533
|
+
filePath: tool.schema
|
|
534
|
+
.string()
|
|
535
|
+
.optional()
|
|
536
|
+
.describe("Any file in the project (used to determine language server)"),
|
|
537
|
+
},
|
|
538
|
+
|
|
539
|
+
async execute(args) {
|
|
540
|
+
const cwd = process.cwd();
|
|
541
|
+
const absPath = args.filePath
|
|
542
|
+
? resolve(cwd, args.filePath)
|
|
543
|
+
: resolve(cwd, ".");
|
|
544
|
+
const server = resolveServer(absPath);
|
|
545
|
+
|
|
546
|
+
if (!server) {
|
|
547
|
+
return `Error: No LSP server available. Provide a filePath to help identify the language.`;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
const client = await lspManager.getClient(cwd, server);
|
|
552
|
+
const result = (await client.workspaceSymbols(args.query)) as
|
|
553
|
+
| SymbolInfo[]
|
|
554
|
+
| null;
|
|
555
|
+
lspManager.releaseClient(cwd, server.id);
|
|
556
|
+
|
|
557
|
+
if (!result || result.length === 0) {
|
|
558
|
+
return `No symbols found matching "${args.query}".`;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const symbols = result.slice(0, 30); // Limit results
|
|
562
|
+
const formatted = symbols
|
|
563
|
+
.map(
|
|
564
|
+
(sym) =>
|
|
565
|
+
`${getSymbolKindName(sym.kind)} ${sym.name}${sym.containerName ? ` (in ${sym.containerName})` : ""}\n ${formatUri(sym.location.uri)}:${sym.location.range.start.line + 1}`,
|
|
566
|
+
)
|
|
567
|
+
.join("\n\n");
|
|
568
|
+
const suffix =
|
|
569
|
+
result.length > 30 ? `\n\n... and ${result.length - 30} more` : "";
|
|
570
|
+
|
|
571
|
+
return `Found ${result.length} symbol(s):\n\n${formatted}${suffix}`;
|
|
572
|
+
} catch (error) {
|
|
573
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
574
|
+
}
|
|
575
|
+
},
|
|
576
|
+
});
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Get diagnostics (errors, warnings) for a file
|
|
580
|
+
*/
|
|
581
|
+
export const lsp_diagnostics = tool({
|
|
582
|
+
description: `Get diagnostics (errors, warnings, hints) for a file from the language server.
|
|
583
|
+
|
|
584
|
+
Returns type errors, lint warnings, and other issues detected by the language server.
|
|
585
|
+
Useful for checking if code has problems before running tests.`,
|
|
586
|
+
|
|
587
|
+
args: {
|
|
588
|
+
filePath: tool.schema.string().describe("Path to the file"),
|
|
589
|
+
severity: tool.schema
|
|
590
|
+
.string()
|
|
591
|
+
.optional()
|
|
592
|
+
.describe(
|
|
593
|
+
"Filter by severity: 'error', 'warning', 'info', 'hint', or 'all' (default: all)",
|
|
594
|
+
),
|
|
595
|
+
},
|
|
596
|
+
|
|
597
|
+
async execute(args) {
|
|
598
|
+
const cwd = process.cwd();
|
|
599
|
+
const absPath = resolve(cwd, args.filePath);
|
|
600
|
+
const server = resolveServer(absPath);
|
|
601
|
+
|
|
602
|
+
if (!server) {
|
|
603
|
+
return `Error: No LSP server available for file type: ${absPath}`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
try {
|
|
607
|
+
const client = await lspManager.getClient(cwd, server);
|
|
608
|
+
const result = await client.diagnostics(absPath);
|
|
609
|
+
lspManager.releaseClient(cwd, server.id);
|
|
610
|
+
|
|
611
|
+
let diagnostics = result.items || [];
|
|
612
|
+
|
|
613
|
+
// Filter by severity if specified
|
|
614
|
+
if (args.severity && args.severity !== "all") {
|
|
615
|
+
const severityMap: Record<string, number> = {
|
|
616
|
+
error: 1,
|
|
617
|
+
warning: 2,
|
|
618
|
+
info: 3,
|
|
619
|
+
hint: 4,
|
|
620
|
+
};
|
|
621
|
+
const targetSeverity = severityMap[args.severity.toLowerCase()];
|
|
622
|
+
if (targetSeverity) {
|
|
623
|
+
diagnostics = diagnostics.filter(
|
|
624
|
+
(d) => d.severity === targetSeverity,
|
|
625
|
+
);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
if (diagnostics.length === 0) {
|
|
630
|
+
return args.severity && args.severity !== "all"
|
|
631
|
+
? `No ${args.severity} diagnostics found.`
|
|
632
|
+
: "No diagnostics found. File appears clean.";
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
return formatDiagnostics(diagnostics, args.filePath);
|
|
636
|
+
} catch (error) {
|
|
637
|
+
return `Error: ${error instanceof Error ? error.message : String(error)}`;
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
});
|
|
641
|
+
|
|
642
|
+
// ============ Helper Functions ============
|
|
643
|
+
|
|
644
|
+
function formatHoverResult(hover: HoverResult): string {
|
|
645
|
+
const contents = hover.contents;
|
|
646
|
+
|
|
647
|
+
if (typeof contents === "string") {
|
|
648
|
+
return contents;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
if (Array.isArray(contents)) {
|
|
652
|
+
return contents
|
|
653
|
+
.map((c) => (typeof c === "string" ? c : c.value))
|
|
654
|
+
.join("\n\n");
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (typeof contents === "object" && "value" in contents) {
|
|
658
|
+
return contents.value;
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
return JSON.stringify(contents);
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
function formatUri(uri: string): string {
|
|
665
|
+
return uri.replace("file://", "").replace(process.cwd() + "/", "");
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
function formatLocation(loc: Location | LocationLink): string {
|
|
669
|
+
if ("targetUri" in loc) {
|
|
670
|
+
return `${formatUri(loc.targetUri)}:${loc.targetRange.start.line + 1}:${loc.targetRange.start.character}`;
|
|
671
|
+
}
|
|
672
|
+
return `${formatUri(loc.uri)}:${loc.range.start.line + 1}:${loc.range.start.character}`;
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function formatLocations(
|
|
676
|
+
result: Location | Location[] | LocationLink[],
|
|
677
|
+
): string {
|
|
678
|
+
const locations = Array.isArray(result) ? result : [result];
|
|
679
|
+
|
|
680
|
+
if (locations.length === 0) {
|
|
681
|
+
return "No locations found.";
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
if (locations.length === 1) {
|
|
685
|
+
return `Definition: ${formatLocation(locations[0] as Location | LocationLink)}`;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
return `Found ${locations.length} location(s):\n${locations.map((l) => ` ${formatLocation(l as Location | LocationLink)}`).join("\n")}`;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function formatDocumentSymbols(
|
|
692
|
+
symbols: DocumentSymbol[] | SymbolInfo[],
|
|
693
|
+
indent = 0,
|
|
694
|
+
): string {
|
|
695
|
+
const prefix = " ".repeat(indent);
|
|
696
|
+
const lines: string[] = [];
|
|
697
|
+
|
|
698
|
+
for (const sym of symbols) {
|
|
699
|
+
const kindName = getSymbolKindName(sym.kind);
|
|
700
|
+
const line =
|
|
701
|
+
"range" in sym
|
|
702
|
+
? sym.range.start.line + 1
|
|
703
|
+
: "location" in sym
|
|
704
|
+
? sym.location.range.start.line + 1
|
|
705
|
+
: "?";
|
|
706
|
+
|
|
707
|
+
lines.push(`${prefix}${kindName} ${sym.name} (line ${line})`);
|
|
708
|
+
|
|
709
|
+
if ("children" in sym && sym.children) {
|
|
710
|
+
lines.push(formatDocumentSymbols(sym.children, indent + 1));
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
return lines.join("\n");
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
function formatDiagnostics(
|
|
718
|
+
diagnostics: Diagnostic[],
|
|
719
|
+
filePath: string,
|
|
720
|
+
): string {
|
|
721
|
+
const severityNames = ["", "Error", "Warning", "Info", "Hint"];
|
|
722
|
+
|
|
723
|
+
const sorted = [...diagnostics].sort((a, b) => {
|
|
724
|
+
const sevA = a.severity ?? 4;
|
|
725
|
+
const sevB = b.severity ?? 4;
|
|
726
|
+
if (sevA !== sevB) return sevA - sevB;
|
|
727
|
+
return a.range.start.line - b.range.start.line;
|
|
728
|
+
});
|
|
729
|
+
|
|
730
|
+
const lines = sorted.map((d) => {
|
|
731
|
+
const sev = severityNames[d.severity ?? 4] || "Unknown";
|
|
732
|
+
const line = d.range.start.line + 1;
|
|
733
|
+
const col = d.range.start.character + 1;
|
|
734
|
+
const source = d.source ? `[${d.source}]` : "";
|
|
735
|
+
const code = d.code ? ` (${d.code})` : "";
|
|
736
|
+
return `${sev}: ${filePath}:${line}:${col}${code} ${source}\n ${d.message}`;
|
|
737
|
+
});
|
|
738
|
+
|
|
739
|
+
const errorCount = diagnostics.filter((d) => d.severity === 1).length;
|
|
740
|
+
const warnCount = diagnostics.filter((d) => d.severity === 2).length;
|
|
741
|
+
|
|
742
|
+
let summary = `Found ${diagnostics.length} diagnostic(s)`;
|
|
743
|
+
if (errorCount > 0 || warnCount > 0) {
|
|
744
|
+
const parts: string[] = [];
|
|
745
|
+
if (errorCount > 0) parts.push(`${errorCount} error(s)`);
|
|
746
|
+
if (warnCount > 0) parts.push(`${warnCount} warning(s)`);
|
|
747
|
+
summary += `: ${parts.join(", ")}`;
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
return `${summary}\n\n${lines.join("\n\n")}`;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
function getSymbolKindName(kind: number): string {
|
|
754
|
+
const kinds: Record<number, string> = {
|
|
755
|
+
1: "File",
|
|
756
|
+
2: "Module",
|
|
757
|
+
3: "Namespace",
|
|
758
|
+
4: "Package",
|
|
759
|
+
5: "Class",
|
|
760
|
+
6: "Method",
|
|
761
|
+
7: "Property",
|
|
762
|
+
8: "Field",
|
|
763
|
+
9: "Constructor",
|
|
764
|
+
10: "Enum",
|
|
765
|
+
11: "Interface",
|
|
766
|
+
12: "Function",
|
|
767
|
+
13: "Variable",
|
|
768
|
+
14: "Constant",
|
|
769
|
+
15: "String",
|
|
770
|
+
16: "Number",
|
|
771
|
+
17: "Boolean",
|
|
772
|
+
18: "Array",
|
|
773
|
+
19: "Object",
|
|
774
|
+
20: "Key",
|
|
775
|
+
21: "Null",
|
|
776
|
+
22: "EnumMember",
|
|
777
|
+
23: "Struct",
|
|
778
|
+
24: "Event",
|
|
779
|
+
25: "Operator",
|
|
780
|
+
26: "TypeParameter",
|
|
781
|
+
};
|
|
782
|
+
return kinds[kind] || `Kind(${kind})`;
|
|
783
|
+
}
|
|
784
|
+
|
|
331
785
|
// Default export for single-tool registration (uses rename as primary)
|
|
332
786
|
export default lsp_rename;
|