opencodekit 0.9.0 → 0.9.2
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 +35 -43
- package/dist/index.js +29 -9
- package/dist/template/.opencode/AGENTS.md +64 -38
- package/dist/template/.opencode/README.md +13 -9
- package/dist/template/.opencode/agent/build.md +41 -3
- package/dist/template/.opencode/agent/explore.md +18 -4
- package/dist/template/.opencode/agent/review.md +16 -3
- package/dist/template/.opencode/agent/rush.md +23 -4
- package/dist/template/.opencode/opencode.json +514 -474
- package/dist/template/.opencode/package.json +1 -1
- package/dist/template/.opencode/plugin/compaction.ts +87 -4
- package/dist/template/.opencode/plugin/skill-mcp.ts +1 -1
- package/dist/template/.opencode/skill/chrome-devtools/SKILL.md +88 -0
- package/dist/template/.opencode/skill/polar/SKILL.md +92 -0
- package/dist/template/.opencode/tool/lsp.ts +454 -0
- package/package.json +1 -1
|
@@ -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;
|