Skip to content

Add a single dummy language tool. Add install-extension.sh to easily … #1832

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
node_modules
pnpm-lock.yaml
**/dist/*
.vscode-test
.vscode-test
llmTools.ts
**/ui/**
16 changes: 16 additions & 0 deletions packages/vscode/install-extension.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash

set -e

# Step 1: Package the extension
echo "📦 Packaging the extension..."
vsce package

# Find the latest .vsix file
VSIX_FILE=$(ls -t *.vsix | head -n 1)

# Step 2: Install it into main VS Code instance
echo "🛠 Installing $VSIX_FILE into VS Code..."
code --install-extension "$VSIX_FILE"

echo "✅ Extension installed. You can now test it with GitHub Copilot enabled."
19 changes: 9 additions & 10 deletions packages/vscode/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

47 changes: 44 additions & 3 deletions packages/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"license": "Apache-2.0",
"icon": "logo_white.png",
"engines": {
"vscode": "^1.85.0"
"vscode": "^1.99.1"
},
"publisher": "Prisma",
"categories": [
Expand Down Expand Up @@ -180,12 +180,53 @@
"title": "Generate",
"category": "Prisma"
}
]
],
"languageModelTools": [ {
"name": "get_prismas",
"displayName": "Get All Prismas",
"canBeReferencedInPrompt": true,
"toolReferenceName": "get_prismas",
"icon": "prisma_icon.svg",
"userDescription": "Get all Prisma schemas in the workspace.",
"modelDescription": "This tool gets all the Prismas. They are returned as a comma separated string. Call this whenever the user asks about Prisma.",
"inputSchema": {
"type": "object",
"properties": {
"context": {
"type": "string",
"description": "Any context the user provided or that might be relevant to the task at hand about Prisma.",
"default": "No additional context provided"
}
}
}
}],
"viewsContainers": {
"activitybar": [
{
"id": "Prisma-ViewContainer",
"title": "Prisma",
"icon": "./prisma_icon.svg"
}
]
},
"views": {
"Prisma-ViewContainer": [
{
"id": "Prisma-StudioView",
"name": "Studio"
},
{
"type": "webview",
"id": "prisma.databases",
"name": "Databases 2"
}
]
}
},
"devDependencies": {
"@types/glob": "8.1.0",
"@types/mocha": "10.0.10",
"@types/vscode": "1.85.0",
"@types/vscode": "1.99.1",
"@vscode/test-electron": "2.4.1",
"is-ci": "3.0.1",
"mocha": "10.8.2",
Expand Down
9 changes: 9 additions & 0 deletions packages/vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import { ExtensionContext } from 'vscode'
import plugins from './plugins'
import { registerChatTools } from './plugins/llmTools'
import * as studio from './ui/studio'
import * as databases from './ui/databases'

export function activate(context: ExtensionContext): void {
registerChatTools(context)

databases.activate(context)
studio.activate(context)

void plugins.map(async (plugin) => {
const enabled = await plugin.enabled()
if (enabled) {
Expand Down
39 changes: 39 additions & 0 deletions packages/vscode/src/plugins/llmTools.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as vscode from 'vscode'

export function registerChatTools(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.lm.registerTool('get_prismas', new PrismaTool()))
}

interface IGetPrismasParameters {
context: string
}

export class PrismaTool implements vscode.LanguageModelTool<IGetPrismasParameters> {
async invoke(
options: vscode.LanguageModelToolInvocationOptions<IGetPrismasParameters>,
_token: vscode.CancellationToken,
) {

return new vscode.LanguageModelToolResult([
new vscode.LanguageModelTextPart(`You provided this context while asking about Prismas: ${options.input.context}. Thank you for that!`),
])
}

async prepareInvocation(
options: vscode.LanguageModelToolInvocationPrepareOptions<IGetPrismasParameters>,
_token: vscode.CancellationToken,
) {
const confirmationMessages = {
title: 'Return Prismas',
message: new vscode.MarkdownString(
`Do you really want to get all Prismas?`),
}

return {
invocationMessage: 'Getting information about all Prismas',
confirmationMessages,
}
}
}


163 changes: 163 additions & 0 deletions packages/vscode/src/ui/databases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

const provider = new ColorsViewProvider(context.extensionUri);

context.subscriptions.push(
vscode.window.registerWebviewViewProvider(ColorsViewProvider.viewType, provider));

context.subscriptions.push(
vscode.commands.registerCommand('calicoColors.addColor', () => {
provider.addColor();
}));

context.subscriptions.push(
vscode.commands.registerCommand('calicoColors.clearColors', () => {
provider.clearColors();
}));
}

class ColorsViewProvider implements vscode.WebviewViewProvider {

public static readonly viewType = 'prisma.databases';

private _view?: vscode.WebviewView;

constructor(
private readonly _extensionUri: vscode.Uri,
) { }

public resolveWebviewView(
webviewView: vscode.WebviewView,
_context: vscode.WebviewViewResolveContext,
_token: vscode.CancellationToken,
) {
this._view = webviewView;

webviewView.webview.options = {
// Allow scripts in the webview
enableScripts: true,

localResourceRoots: [
this._extensionUri
]
};

webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);

webviewView.webview.onDidReceiveMessage(data => {
switch (data.type) {
case 'colorSelected':
{
vscode.window.activeTextEditor?.insertSnippet(new vscode.SnippetString(`#${data.value}`));
break;
}
}
});
}

public addColor() {
if (this._view) {
this._view.show?.(true); // `show` is not implemented in 1.49 but is for 1.50 insiders
this._view.webview.postMessage({ type: 'addColor' });
}
}

public clearColors() {
if (this._view) {
this._view.webview.postMessage({ type: 'clearColors' });
}
}

private _getHtmlForWebview(webview: vscode.Webview) {
// Get the local path to main script run in the webview, then convert it to a uri we can use in the webview.
// const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.js'));

// // Do the same for the stylesheet.
// const styleResetUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'reset.css'));
// const styleVSCodeUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'vscode.css'));
// const styleMainUri = webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'media', 'main.css'));

// Use a nonce to only allow a specific script to be run.
const nonce = getNonce();

const databases = ['Local: Dev', 'Local: Prod', 'Remote: Dev', 'Remote: Prod'];

return `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy"
content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}';">
<style>
body {
font-family: var(--vscode-font-family);
font-size: var(--vscode-font-size);
color: var(--vscode-foreground);
background-color: var(--vscode-sideBar-background);
padding: 0.5rem;
}

.list-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 12px;
margin-bottom: 6px;
border: 1px solid var(--vscode-sideBar-border);
border-radius: 4px;
background-color: var(--vscode-sideBarSectionHeader-background);
}

.item-label {
flex-grow: 1;
}

.button-group button {
margin-left: 6px;
background: var(--vscode-button-background);
color: var(--vscode-button-foreground);
border: none;
padding: 4px 8px;
border-radius: 3px;
cursor: pointer;
}

.button-group button:hover {
background: var(--vscode-button-hoverBackground);
}
</style>
</head>
<body>
${["Item A", "Item B", "Item C"].map(item => `
<div class="list-item">
<span class="item-label">${item}</span>
<div class="button-group">
<button onclick="send('edit', '${item}')">Edit</button>
<button onclick="send('delete', '${item}')">Delete</button>
</div>
</div>
`).join("")}

<script nonce="${nonce}">
const vscode = acquireVsCodeApi();
function send(command, item) {
vscode.postMessage({ command, item });
}
</script>
</body>
</html>
`;
}
}

function getNonce() {
let text = '';
const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}
Loading
Loading