diff --git a/.gitignore b/.gitignore index b25b3fda4..306899526 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ **/venv **/.python-version **/node_modules +**/*.vsix \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/.vscode/launch.json b/examples/gemini/node/pipet-code-agent/.vscode/launch.json index 670d6e66c..e6046e515 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/launch.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/launch.json @@ -5,30 +5,35 @@ { "version": "0.2.0", "configurations": [ - { - "name": "Run Extension", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}" - ], - "outFiles": [ - "${workspaceFolder}/out/**/*.js" - ], - "preLaunchTask": "${defaultBuildTask}" - }, - { - "name": "Extension Tests", - "type": "extensionHost", - "request": "launch", - "args": [ - "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" - ], - "outFiles": [ - "${workspaceFolder}/out/test/**/*.js" - ], - "preLaunchTask": "${defaultBuildTask}" - } - ] + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/out/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": ["${workspaceFolder}/out/test/**/*.js"], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Package Extension", + "type": "node", + "console": "internalConsole", + "request": "launch", + "args": ["package", "--out", "build/PipetCodeAgent.vsix"], + "runtimeExecutable": "vsce", + "presentation": { + "reveal": "always" + } + } + ] } diff --git a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json index 3b17e53b6..078ff7e01 100644 --- a/examples/gemini/node/pipet-code-agent/.vscode/tasks.json +++ b/examples/gemini/node/pipet-code-agent/.vscode/tasks.json @@ -1,20 +1,20 @@ // See https://go.microsoft.com/fwlink/?LinkId=733558 // for the documentation about the tasks.json format { - "version": "2.0.0", - "tasks": [ - { - "type": "npm", - "script": "watch", - "problemMatcher": "$tsc-watch", - "isBackground": true, - "presentation": { - "reveal": "never" - }, - "group": { - "kind": "build", - "isDefault": true - } - } - ] + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] } diff --git a/examples/gemini/node/pipet-code-agent/img/Gemini.png b/examples/gemini/node/pipet-code-agent/img/Gemini.png new file mode 100644 index 000000000..8e5c560d5 Binary files /dev/null and b/examples/gemini/node/pipet-code-agent/img/Gemini.png differ diff --git a/examples/gemini/node/pipet-code-agent/media/main.css b/examples/gemini/node/pipet-code-agent/media/main.css new file mode 100644 index 000000000..9e5b5bbf8 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/main.css @@ -0,0 +1,84 @@ +/** + * Copyright 2024 Jason + */ + +body { + margin: 0; + padding: 0; + display: flex; + flex-direction: column; + min-height: 100vh; +} + +main { + flex: 1; +} + +#dialog { + border-radius: 5px; + padding: 10px; + margin-bottom: 10px; +} + +footer { + display: flex; + align-items: center; + padding: 10px; + /* position: fixed; + bottom: 0; + width: -webkit-fill-available; */ +} + +#userInput { + height: auto; + line-height: 1; + max-height: 300px; + resize: none; + background-color: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-foreground); + border-radius: 3px; + flex: 1; +} + +#sendMessage { + height: auto; + background-color: var(--vscode-input-background); + color: var(--vscode-input-foreground); + border: 1px solid var(--vscode-input-foreground); + margin-left: 5px; + transition: background-color 0.3s; + border-radius: 3px; +} + +#sendMessage:hover { + background-color: var(--vscode-input-foreground); + color: var(--vscode-input-background); +} + +.message-container { + position: relative; + background-color: var(--vscode-input-background); + color: var(--vscode-editor-foreground); +} + +.code-buttons-container { + background-color: #ffffff00; + border: none; + position: absolute; + right: 2px; + top: 2px; + z-index: 1; + width: 16px; + height: 16px; + background-image: url("data:image/svg+xml,%3Csvg width='16' height='16' xmlns='http://www.w3.org/2000/svg'%3E%3Cg id='Layer_1'%3E%3Ctitle%3ELayer 1%3C/title%3E%3Cpath stroke='null' id='svg_1' d='m11.17086,13.7471l-7.75098,0l0,-10.05744l-1.40928,0l0,10.05744c0,0.79023 0.63418,1.43678 1.40928,1.43678l7.75098,0l0,-1.43678zm2.81854,-2.87355l0,-8.62066c0,-0.79023 -0.63418,-1.43678 -1.40928,-1.43678l-6.34171,0c-0.7751,0 -1.40928,0.64655 -1.40928,1.43678l0,8.62066c0,0.79023 0.63418,1.43678 1.40928,1.43678l6.34171,0c0.7751,0 1.40928,-0.64655 1.40928,-1.43678zm-1.40928,0l-6.34171,0c0,-2.87355 0,-5.74711 0,-8.62066l6.34171,0l0,8.62066z' fill='%23bfbdb6'/%3E%3C/g%3E%3C/svg%3E"); + transition: background-color 0.3s; +} + +.code-buttons-container:hover { + background-color: #ffffff41; +} + +.pre { + position: relative; +} diff --git a/examples/gemini/node/pipet-code-agent/media/main.js b/examples/gemini/node/pipet-code-agent/media/main.js new file mode 100644 index 000000000..fc6015dfc --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/main.js @@ -0,0 +1,170 @@ +/** + * Copyright 2024 Google LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +document.addEventListener("DOMContentLoaded", function () { + const vscode = acquireVsCodeApi(); + const oldState = vscode.getState() || {}; + let oldHtml = oldState || "
"; + + const dialog = document.getElementById("dialog"); + const userInput = document.getElementById("userInput"); + const sendMessageButton = document.getElementById("sendMessage"); + let chatID; + dialog.innerHTML = oldHtml; + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); + + const allCodeButtons = Array.from( + dialog.getElementsByClassName("code-buttons-container") + ); + if (allCodeButtons) { + allCodeButtons.forEach((button) => { + button.addEventListener("click", () => { + const toCopy = + button.parentElement.querySelectorAll("pre code")[0].textContent; + navigator.clipboard.writeText(toCopy); + }); + }); + } + + const allChats = Array.from( + dialog.getElementsByClassName("message-container") + ); + if (allChats) { + allChats.forEach((chat) => { + switch (chat.firstChild.textContent) { + case "You: ": + vscode.postMessage({ + command: "updateHistory", + role: "user", + text: chat.textContent.replace("You:", "").trim(), + }); + break; + case "Gemini: ": + vscode.postMessage({ + command: "updateHistory", + role: "model", + text: chat.textContent.replace("model:", "").trim(), + }); + break; + } + }); + } + + function adjustTextareaHeight() { + userInput.style.height = "auto"; + userInput.style.height = userInput.scrollHeight + "px"; + sendMessageButton.disabled = !userInput.value; + } + userInput.addEventListener("input", adjustTextareaHeight); + userInput.addEventListener("keydown", handleKeyDown); + + function showAIMessage(text, chatID) { + const messageContainer = document.createElement("div"); + if (messageContainer) { + messageContainer.id = chatID; + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

Gemini: " + marked.parse(text) + "

"; + dialog.appendChild(messageContainer); + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); + } + } + + function updateAIMessage(newText, chatID) { + const messageContainer = document.getElementById(chatID); + if (messageContainer) { + (messageContainer.innerHTML = + "

Gemini: " + marked.parse(newText)), + +"

"; + addBottons(messageContainer); + sendMessageButton.innerHTML = "Chat"; + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); + } + } + + function onSelection(text) { + userInput.placeholder = text; + } + + function showUserMessage(text) { + const messageContainer = document.createElement("div"); + if (messageContainer) { + messageContainer.classList.add("message-container"); + messageContainer.innerHTML = + "

You: " + marked.parse(text) + "

"; + addBottons(messageContainer); + dialog.appendChild(messageContainer); + userInput.scrollIntoView({ behavior: "smooth", block: "end" }); + } + } + /** + * @param {HTMLDivElement} + */ + function addBottons(container) { + const codeElements = container.querySelectorAll("pre code"); + codeElements.forEach((codeElement) => { + const copyButton = document.createElement("button"); + copyButton.classList.add("code-buttons-container"); + copyButton.addEventListener("click", () => { + const textToCopy = codeElement.textContent; + navigator.clipboard.writeText(textToCopy); + }); + codeElement.parentElement.classList.add("pre"); + codeElement.parentElement.appendChild(copyButton); + Prism.highlightElement(codeElement); + }); + } + + function handleKeyDown(event) { + if (event.keyCode === 13) { + if (!event.shiftKey) { + event.preventDefault(); + sendMessageButton.click(); + } + } + } + + sendMessageButton.addEventListener("click", () => { + chatID = Math.random().toString().substring(2); + const userMessage = userInput.value; + vscode.postMessage({ command: "sendMessage", text: userMessage }); + userInput.value = ""; + userInput.style.height = "auto"; + sendMessageButton.disabled = true; + sendMessageButton.innerHTML = "Thinking..."; + }); + + window.addEventListener("message", (event) => { + const message = event.data; + switch (message.command) { + case "receiveMessage": + updateAIMessage(message.text, chatID); + break; + case "Message": + showUserMessage(message.text); + showAIMessage("Thinking ...", chatID); + break; + case "clearChat": + dialog.innerHTML = "
"; + break; + case "onSelection": + onSelection(message.text); + break; + default: + console.log("Unknown command: " + message.command); + } + vscode.setState(dialog.innerHTML); + }); +}); diff --git a/examples/gemini/node/pipet-code-agent/media/prism.css b/examples/gemini/node/pipet-code-agent/media/prism.css new file mode 100644 index 000000000..042fb4555 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/prism.css @@ -0,0 +1,146 @@ +/** + * Copyright 2024 + * prism.js default theme for JavaScript, CSS and HTML + * Based on dabblet (http://dabblet.com) + * @author Lea Verou + */ + +code[class*="language-"], +pre[class*="language-"] { + /* color: black; */ + background: none; + /* text-shadow: 0 1px white; */ + font-family: Consolas, Monaco, "Andale Mono", "Ubuntu Mono", monospace; + font-size: 1em; + text-align: left; + white-space: pre; + word-spacing: normal; + word-break: normal; + word-wrap: normal; + line-height: 1.5; + + -moz-tab-size: 4; + -o-tab-size: 4; + tab-size: 4; + + -webkit-hyphens: none; + -moz-hyphens: none; + -ms-hyphens: none; + hyphens: none; +} + +pre[class*="language-"]::-moz-selection, +pre[class*="language-"] ::-moz-selection, +code[class*="language-"]::-moz-selection, +code[class*="language-"] ::-moz-selection { + text-shadow: none; + background: #ffffff4b; +} + +pre[class*="language-"]::selection, +pre[class*="language-"] ::selection, +code[class*="language-"]::selection, +code[class*="language-"] ::selection { + text-shadow: none; + background: #ffffff4b; +} + +@media print { + code[class*="language-"], + pre[class*="language-"] { + text-shadow: none; + } +} + +/* Code blocks */ +pre[class*="language-"] { + padding: 1em; + margin: 0.5em 0; + overflow: auto; +} + +:not(pre) > code[class*="language-"], +pre[class*="language-"] { + background: #ffffff05; +} + +/* Inline code */ +:not(pre) > code[class*="language-"] { + padding: 0.1em; + border-radius: 0.3em; + white-space: normal; +} + +.token.comment, +.token.prolog, +.token.doctype, +.token.cdata { + color: slategray; +} + +.token.punctuation { + color: #999; +} + +.token.namespace { + opacity: 0.7; +} + +.token.property, +.token.tag, +.token.boolean, +.token.number, +.token.constant, +.token.symbol, +.token.deleted { + color: #905; +} + +.token.selector, +.token.attr-name, +.token.string, +.token.char, +.token.builtin, +.token.inserted { + color: #690; +} + +.token.operator, +.token.entity, +.token.url, +.language-css .token.string, +.style .token.string { + color: #9a6e3a; + /* This background color was intended by the author of this theme. */ + /*background: hsla(0, 0%, 100%, .5);*/ +} + +.token.atrule, +.token.attr-value, +.token.keyword { + color: #07a; +} + +.token.function, +.token.class-name { + color: #dd4a68; +} + +.token.regex, +.token.important, +.token.variable { + color: #e90; +} + +.token.important, +.token.bold { + font-weight: bold; +} + +.token.italic { + font-style: italic; +} + +.token.entity { + cursor: help; +} diff --git a/examples/gemini/node/pipet-code-agent/media/vscode.css b/examples/gemini/node/pipet-code-agent/media/vscode.css new file mode 100644 index 000000000..f9263424b --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/media/vscode.css @@ -0,0 +1,95 @@ +/** + * Copyright 2024 + */ + +:root { + --container-paddding: 20px; + --input-padding-vertical: 6px; + --input-padding-horizontal: 4px; + --input-margin-vertical: 4px; + --input-margin-horizontal: 0; +} + +body { + padding: 0 var(--container-paddding); + color: var(--vscode-foreground); + font-size: var(--vscode-font-size); + font-weight: var(--vscode-font-weight); + font-family: var(--vscode-font-family); + background-color: var(--vscode-editor-background); +} + +ol, +ul { + padding-left: var(--container-paddding); +} + +body > *, +form > * { + margin-block-start: var(--input-margin-vertical); + margin-block-end: var(--input-margin-vertical); +} + +*:focus { + outline-color: var(--vscode-focusBorder) !important; +} + +a { + color: var(--vscode-textLink-foreground); +} + +a:hover, +a:active { + color: var(--vscode-textLink-activeForeground); +} + +code { + font-size: var(--vscode-editor-font-size); + font-family: var(--vscode-editor-font-family); +} + +button { + border: none; + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + width: 100%; + text-align: center; + outline: 1px solid transparent; + outline-offset: 2px !important; + color: var(--vscode-button-foreground); + background: var(--vscode-button-background); +} + +button:hover { + cursor: pointer; + background: var(--vscode-button-hoverBackground); +} + +button:focus { + outline-color: var(--vscode-focusBorder); +} + +button.secondary { + color: var(--vscode-button-secondaryForeground); + background: var(--vscode-button-secondaryBackground); +} + +button.secondary:hover { + background: var(--vscode-button-secondaryHoverBackground); +} + +input:not([type='checkbox']), +textarea { + display: block; + width: 100%; + border: none; + font-family: var(--vscode-font-family); + padding: var(--input-padding-vertical) var(--input-padding-horizontal); + color: var(--vscode-input-foreground); + outline-color: var(--vscode-input-border); + background-color: var(--vscode-input-background); +} + +input::placeholder, +textarea::placeholder { + color: var(--vscode-input-placeholderForeground); +} diff --git a/examples/gemini/node/pipet-code-agent/package-lock.json b/examples/gemini/node/pipet-code-agent/package-lock.json index a65319f8e..04a877103 100644 --- a/examples/gemini/node/pipet-code-agent/package-lock.json +++ b/examples/gemini/node/pipet-code-agent/package-lock.json @@ -8,7 +8,7 @@ "name": "pipet-code-agent", "version": "0.0.1", "dependencies": { - "@google/generative-ai": "^0.3.0", + "@google/generative-ai": "^0.6.0", "dotenv": "^16.1.4" }, "devDependencies": { @@ -28,6 +28,15 @@ "vscode": "^1.78.0" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -44,23 +53,23 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", - "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", "dev": true, "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", - "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.5.2", + "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", @@ -76,30 +85,30 @@ } }, "node_modules/@eslint/js": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", - "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", + "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@google/generative-ai": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.3.0.tgz", - "integrity": "sha512-6xbaA/JPpwCoe+lfxE2RavVB8JI8F3P6mCse1Sbm586HhJkyuSevK7Opt4l2dbQZFej+M8ALhMMpfBiRW05Fag==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.6.0.tgz", + "integrity": "sha512-jkc7vBOW/yKhNtOkpQI1jYFtAybEc0kjf8I6CpSUBWVdrVh45jIikeTlYaYckH+KtZjteARUhg26mh5XnFT6Yg==", "engines": { "node": ">=18.0.0" } }, "node_modules/@humanwhocodes/config-array": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", - "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "version": "0.11.14", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", + "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", + "@humanwhocodes/object-schema": "^2.0.2", + "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { @@ -120,9 +129,9 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "dev": true }, "node_modules/@nodelib/fs.scandir": { @@ -180,9 +189,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.12", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", - "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/minimatch": { @@ -192,9 +201,9 @@ "dev": true }, "node_modules/@types/mocha": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.1.tgz", - "integrity": "sha512-/fvYntiO1GeICvqbQ3doGDIP97vWmvFt83GKguJ6prmQM2iXZfFcq6YE8KteFyRtX2/h5Hf91BYvPodJKFYv5Q==", + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", "dev": true }, "node_modules/@types/node": { @@ -204,29 +213,29 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", - "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", "dev": true }, "node_modules/@types/vscode": { - "version": "1.79.0", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.79.0.tgz", - "integrity": "sha512-Tfowu2rSW8hVGbqzQLSPlOEiIOYYryTkgJ+chMecpYiJcnw9n0essvSiclnK+Qh/TcSVJHgaK4EMrQDZjZJ/Sw==", + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz", + "integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.9.tgz", - "integrity": "sha512-4uQIBq1ffXd2YvF7MAvehWKW3zVv/w+mSfRAu+8cKbfj3nwzyqJLNcZJpQ/WZ1HLbJDiowwmQ6NO+63nCA+fqA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.4.0", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/type-utils": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", - "grapheme-splitter": "^1.0.4", + "graphemer": "^1.4.0", "ignore": "^5.2.0", "natural-compare-lite": "^1.4.0", "semver": "^7.3.7", @@ -250,14 +259,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.9.tgz", - "integrity": "sha512-FsPkRvBtcLQ/eVK1ivDiNYBjn3TGJdXy2fhXX+rc7czWl4ARwnpArwbihSOHI2Peg9WbtGHrbThfBUkZZGTtvQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "debug": "^4.3.4" }, "engines": { @@ -277,13 +286,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.9.tgz", - "integrity": "sha512-8RA+E+w78z1+2dzvK/tGZ2cpGigBZ58VMEHDZtpE1v+LLjzrYGc8mMaTONSxKyEkz3IuXFM0IqYiGHlCsmlZxQ==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9" + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -294,13 +303,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.9.tgz", - "integrity": "sha512-ksEsT0/mEHg9e3qZu98AlSrONAQtrSTljL3ow9CGej8eRo7pe+yaC/mvTjptp23Xo/xIf2mLZKC6KPv4Sji26Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.59.9", - "@typescript-eslint/utils": "5.59.9", + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -321,9 +330,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.9.tgz", - "integrity": "sha512-uW8H5NRgTVneSVTfiCVffBb8AbwWSKg7qcA4Ot3JI3MPCJGsB4Db4BhvAODIIYE5mNj7Q+VJkK7JxmRhk2Lyjw==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -334,13 +343,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.9.tgz", - "integrity": "sha512-pmM0/VQ7kUhd1QyIxgS+aRvMgw+ZljB3eDb+jYyp6d2bC0mQWLzUDF+DLwCTkQ3tlNyVsvZRXjFyV0LkU/aXjA==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/visitor-keys": "5.59.9", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -361,17 +370,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.9.tgz", - "integrity": "sha512-1PuMYsju/38I5Ggblaeb98TOoUvjhRvLpLa1DoTOFaLWqaXl/1iQ1eGurTXgBY58NUdtfTXKP5xBq7q9NDaLKg==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.59.9", - "@typescript-eslint/types": "5.59.9", - "@typescript-eslint/typescript-estree": "5.59.9", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", "eslint-scope": "^5.1.1", "semver": "^7.3.7" }, @@ -387,12 +396,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.9", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.9.tgz", - "integrity": "sha512-bT7s0td97KMaLwpEBckbzj/YohnvXtqbe2XgqNvTl6RJVakY5mvENOTPvw5u66nljfZxthESpDozs86U+oLY8Q==", + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.59.9", + "@typescript-eslint/types": "5.62.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -403,25 +412,31 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@vscode/test-electron": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.2.tgz", - "integrity": "sha512-CRfQIs5Wi5Ok5SUCC3PTvRRXa74LD43cSXHC8EuNlmHHEPaJa/AGrv76brcA1hVSxrdja9tiYwp95Lq8kwY0tw==", + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", "dev": true, "dependencies": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "jszip": "^3.10.1", - "semver": "^7.3.8" + "semver": "^7.5.2" }, "engines": { "node": ">=16" } }, "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -535,12 +550,15 @@ "dev": true }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { @@ -771,14 +789,14 @@ } }, "node_modules/dotenv": { - "version": "16.1.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.1.4.tgz", - "integrity": "sha512-m55RtE8AsPeJBpOIFKihEmqUcoVncQIwo7x9U8ZwLEZw9ZpXboz2c+rvog+jUaJvVrZ5kBOeYQBX5+8Aa/OZQw==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/motdotla/dotenv?sponsor=1" + "url": "https://dotenvx.com" } }, "node_modules/emoji-regex": { @@ -788,9 +806,9 @@ "dev": true }, "node_modules/escalade": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", - "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", "dev": true, "engines": { "node": ">=6" @@ -809,27 +827,28 @@ } }, "node_modules/eslint": { - "version": "8.42.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", - "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "version": "8.57.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", + "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.42.0", - "@humanwhocodes/config-array": "^0.11.10", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.0", + "@humanwhocodes/config-array": "^0.11.14", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -839,7 +858,6 @@ "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", - "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", @@ -849,9 +867,8 @@ "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", + "optionator": "^0.9.3", "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", "text-table": "^0.2.0" }, "bin": { @@ -878,9 +895,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", - "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -890,9 +907,9 @@ } }, "node_modules/eslint/node_modules/eslint-scope": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", - "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { "esrecurse": "^4.3.0", @@ -915,12 +932,12 @@ } }, "node_modules/espree": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", - "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, @@ -998,9 +1015,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -1038,9 +1055,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", - "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -1096,12 +1113,13 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { @@ -1109,9 +1127,9 @@ } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", "dev": true }, "node_modules/fs.realpath": { @@ -1121,9 +1139,9 @@ "dev": true }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, @@ -1196,9 +1214,9 @@ } }, "node_modules/globals": { - "version": "13.20.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", - "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1230,12 +1248,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", - "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", - "dev": true - }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -1288,9 +1300,9 @@ } }, "node_modules/ignore": { - "version": "5.2.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", - "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", + "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", "dev": true, "engines": { "node": ">= 4" @@ -1448,6 +1460,12 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -1472,6 +1490,15 @@ "setimmediate": "^1.0.5" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -1578,9 +1605,9 @@ } }, "node_modules/mocha": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", - "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", "dev": true, "dependencies": { "ansi-colors": "4.1.1", @@ -1590,13 +1617,12 @@ "diff": "5.0.0", "escape-string-regexp": "4.0.0", "find-up": "5.0.0", - "glob": "7.2.0", + "glob": "8.1.0", "he": "1.2.0", "js-yaml": "4.1.0", "log-symbols": "4.1.0", "minimatch": "5.0.1", "ms": "2.1.3", - "nanoid": "3.3.3", "serialize-javascript": "6.0.0", "strip-json-comments": "3.1.1", "supports-color": "8.1.1", @@ -1611,42 +1637,15 @@ }, "engines": { "node": ">= 14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" + "balanced-match": "^1.0.0" } }, "node_modules/mocha/node_modules/minimatch": { @@ -1661,15 +1660,6 @@ "node": ">=10" } }, - "node_modules/mocha/node_modules/minimatch/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/mocha/node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1697,18 +1687,6 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", "dev": true }, - "node_modules/nanoid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", - "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1740,17 +1718,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -1868,9 +1846,9 @@ "dev": true }, "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "engines": { "node": ">=6" @@ -2025,9 +2003,9 @@ "dev": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -2207,9 +2185,9 @@ } }, "node_modules/typescript": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.3.tgz", - "integrity": "sha512-XH627E9vkeqhlZFQuL+UsyAXEnibT0kWR2FWONlr4sTjvxyJYnyefgrkyECLzM5NenmKzRAy2rR/OlYLA1HkZw==", + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2249,15 +2227,6 @@ "node": ">= 8" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/workerpool": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", diff --git a/examples/gemini/node/pipet-code-agent/package.json b/examples/gemini/node/pipet-code-agent/package.json index a2d8462f7..01d33e9f5 100644 --- a/examples/gemini/node/pipet-code-agent/package.json +++ b/examples/gemini/node/pipet-code-agent/package.json @@ -1,18 +1,49 @@ { "name": "pipet-code-agent", "displayName": "Pipet Code Agent", - "description": "", + "description": "A AI CodeHelper", "version": "0.0.1", + "repository": "https://github.com/google/generative-ai-docs", "engines": { "vscode": "^1.78.0" }, "categories": [ - "Other" + "AI" ], "activationEvents": [], "main": "./out/extension.js", "contributes": { + "keybindings": [ + { + "command": "pipet-code-agent.generateCode", + "key": "ctrl+enter", + "mac": "cmd+enter", + "when": "editorTextFocus" + } + ], + "views": { + "chatView": [ + { + "id": "pipet-code-agent.chatView", + "name": "Gemini Chat", + "type": "webview" + } + ] + }, + "viewsContainers": { + "activitybar": [ + { + "id": "chatView", + "title": "Gemini Chat", + "icon": "./img/Gemini.png" + } + ] + }, "commands": [ + { + "command": "pipet-code-agent.generateCode", + "title": "Pipet: Generate Code." + }, { "command": "pipet-code-agent.commentCode", "title": "Pipet: Add a comment to selected code." @@ -20,25 +51,68 @@ { "command": "pipet-code-agent.reviewCode", "title": "Pipet: Review the selected code." + }, + { + "command": "pipet-code-agent.clearChat", + "title": "Pipet: Clear Chat.", + "icon": "$(clear-all)" + }, + { + "command": "pipet-code-agent.generateGitCommit", + "title": "Pipet: Generate Git Commit." } ], + "menus": { + "view/title": [ + { + "command": "pipet-code-agent.clearChat", + "group": "navigation", + "when": "view == pipet-code-agent.chatView" + } + ], + "editor/context": [ + { + "command": "pipet-code-agent.commentCode", + "group": "PipetCodeAgent" + }, + { + "command": "pipet-code-agent.reviewCode", + "group": "PipetCodeAgent" + }, + { + "command": "pipet-code-agent.generateCode", + "group": "PipetCodeAgent" + } + ], + "webview/context": [ + { + "command": "pipet-code-agent.clearChat", + "when": "webviewId == 'pipet-code-agent.chatView'" + } + ] + }, "configuration": [ { "title": "Pipet Code Agent: Google Gemini", "properties": { "google.gemini.apiKey": { - "type": [ - "string", - "null" - ], - "default": null, + "type": "string", + "default": "", "markdownDescription": "Enter your [API Key](https://aistudio.google.com/app/apikey) for Gemini." }, + "google.gemini.systemInstruction": { + "type": "string", + "default": "", + "editPresentation": "multilineText", + "markdownDeprecation": "Enter [System Instruction](https://ai.google.dev/docs/system_instructions) for Gemini." + }, "google.gemini.textModel": { - "type": [ - "string" + "type": "string", + "enum": [ + "models/gemini-1.5-pro-latest", + "models/gemini-pro" ], - "default": "models/gemini-1.0-pro-latest", + "default": "models/gemini-1.5-pro-latest", "markdownDescription": "Provide the name of the model you want to use. Choose from the [base models](https://ai.google.dev/models/gemini) or your own [tuned model](https://ai.google.dev/docs/model_tuning_guidance)." } } @@ -51,7 +125,8 @@ "watch": "tsc -watch -p ./", "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", - "test": "node ./out/test/runTest.js" + "test": "node ./out/test/runTest.js", + "build": "tsc -p ./" }, "devDependencies": { "@types/glob": "^8.1.0", @@ -67,7 +142,7 @@ "typescript": "^5.1.3" }, "dependencies": { - "@google/generative-ai": "^0.3.0", + "@google/generative-ai": "^0.6.0", "dotenv": "^16.1.4" } } diff --git a/examples/gemini/node/pipet-code-agent/src/chat.ts b/examples/gemini/node/pipet-code-agent/src/chat.ts new file mode 100644 index 000000000..e5e052d2d --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/chat.ts @@ -0,0 +1,62 @@ +/** + * Copyright 2024 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import { + ChatSession, + Content, + GoogleGenerativeAI, +} from "@google/generative-ai"; + +export function startchat(history: Content[]): ChatSession | void { + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); + + const systemInstruction = vscode.workspace + .getConfiguration() + .get("google.gemini.systemInstruction", "default"); + + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return undefined; + } + + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + const chat = model.startChat({ + systemInstruction: { + role: "system", + parts: [ + { + text: systemInstruction, + }, + ], + }, + history, + }); + return chat; +} \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/comments.ts b/examples/gemini/node/pipet-code-agent/src/comments.ts index 5dc517478..bcb3fb45f 100644 --- a/examples/gemini/node/pipet-code-agent/src/comments.ts +++ b/examples/gemini/node/pipet-code-agent/src/comments.ts @@ -12,99 +12,84 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ + */ -import * as vscode from 'vscode'; +import * as vscode from "vscode"; -import { GoogleGenerativeAI } from '@google/generative-ai'; - -// Provide instructions for the AI language model -// This approach uses a few-shot technique, providing a few examples. -const CODE_LABEL = 'Here is the code:'; -const COMMENT_LABEL = 'Here is a good comment:'; -const PROMPT = ` -A good code review comment describes the intent behind the code without -repeating information that's obvious from the code itself. Good comments -describe "why", explain any "magic" values and non-obvious behaviour. -Below are some examples of good code comments. - -${CODE_LABEL} -print(f" \\033[33m {msg}\\033[00m", file=sys.stderr) -${COMMENT_LABEL} -Use terminal codes to print color output to console. - -${CODE_LABEL} -to_delete = set(data.keys()) - frozenset(keep) -for key in to_delete: - del data[key] -${COMMENT_LABEL} -Modifies \`data\` to remove any entry not specified in the \`keep\` list. - -${CODE_LABEL} -lines[text_range.start_line - 1:text_range.end_line - 1] = [repl.new_content] -${COMMENT_LABEL} -Replace text from \`lines\` with \`new_content\`, noting that array indices -are offset 1 from line numbers. - -${CODE_LABEL} -api_key = os.getenv("GOOGLE_API_KEY") -${COMMENT_LABEL} -Attempt to load the API key from the environment.`; +import { GoogleGenerativeAI } from "@google/generative-ai"; +const SYSTEMINSTRUCTION = + "Comment code use doxygen style. Don't repeat Prompt. Only return the comments Content."; +/** + *@brief Generate comment for code selection. + * Gets the API key from user config and sends selected text to Gemini for comment generation. + * Inserts generated comment before the selected code. + */ export async function generateComment() { - vscode.window.showInformationMessage('Generating comment...'); - - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'models/gemini-1.0-pro-latest'); - - // Get API Key from local user configuration - const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); - if (!apiKey) { - vscode.window.showErrorMessage('API key not configured. Check your settings.'); - return; - } - - const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({model: modelName}); - - // Text selection - const editor = vscode.window.activeTextEditor; - if (!editor) { - console.debug('Abandon: no open text editor.'); - return; - } - - const selection = editor.selection; - const selectedCode = editor.document.getText(selection); - - // Build the full prompt using the template. - const fullPrompt = `${PROMPT} - -${CODE_LABEL} -${selectedCode} -${COMMENT_LABEL} -`; - - const result = await model.generateContent(fullPrompt); - const response = await result.response; - const comment = response.text(); - + vscode.window.showInformationMessage("Generating comment..."); + + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return; + } + + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + + // Text selection + const editor = vscode.window.activeTextEditor; + if (!editor) { + console.debug("Abandon: no open text editor."); + return; + } + + const selection = editor.selection; + const selectedCode = editor.document.getText(selection); + + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [ + { + text: + "```" + + editor.document.languageId + + "\n" + + selectedCode + + "\n```", + }, + ], + }, + ], + }); + const response = result.response; + const comment = response.text(); // Insert before selection. editor.edit((editBuilder) => { - // TODO(you!): Support other comment styles. - const commentPrefix = '# '; - - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); - - let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); - if (pyComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - pyComment += "\n"; - } - let commentIntro = padding + commentPrefix + "Code comment: (generated)\n"; - editBuilder.insert(selection.start, commentIntro); - editBuilder.insert(selection.start, pyComment); + editBuilder.insert(selection.start, comment); }); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } } diff --git a/examples/gemini/node/pipet-code-agent/src/extension.ts b/examples/gemini/node/pipet-code-agent/src/extension.ts index 67b967078..545b64640 100644 --- a/examples/gemini/node/pipet-code-agent/src/extension.ts +++ b/examples/gemini/node/pipet-code-agent/src/extension.ts @@ -12,17 +12,241 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - */ - -import * as vscode from 'vscode'; -import { generateComment } from './comments'; -import { generateReview } from './review'; + */ +import * as vscode from "vscode"; +import { generateComment } from "./comments"; +import { generateReview } from "./review"; +import { startchat } from "./chat"; +import { ChatSession, Content } from "@google/generative-ai"; +import { generateGitCommit } from "./gitCommit"; +import { performAction } from "./generateCode"; +import * as path from "path"; export function activate(context: vscode.ExtensionContext) { - vscode.commands.registerCommand('pipet-code-agent.commentCode', generateComment); - vscode.commands.registerCommand('pipet-code-agent.reviewCode', generateReview); + vscode.commands.registerCommand( + "pipet-code-agent.commentCode", + generateComment + ); + vscode.commands.registerCommand( + "pipet-code-agent.generateCode", + performAction + ); + vscode.commands.registerCommand( + "pipet-code-agent.reviewCode", + generateReview + ); + + vscode.commands.registerCommand( + "pipet-code-agent.generateGitCommit", + generateGitCommit + ); + + const provider = new ChatViewProvider(context.extensionUri); + context.subscriptions.push( + vscode.window.registerWebviewViewProvider( + ChatViewProvider.viewType, + provider + ) + ); + context.subscriptions.push( + vscode.commands.registerCommand("pipet-code-agent.clearChat", () => { + provider.clearChat(); + }) + ); } +let chat: void | ChatSession; +let history = [] as Content[]; +class ChatViewProvider implements vscode.WebviewViewProvider { + public static readonly viewType = "pipet-code-agent.chatView"; + 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); -export function deactivate() { } + vscode.window.onDidChangeTextEditorSelection((event) => { + const editor = event.textEditor; + if (!editor.selection.isEmpty) { + const startLine = editor.selection.start.line + 1; + const endLine = editor.selection.end.line + 1; + const name = path.basename(editor.document.fileName); + webviewView.webview.postMessage({ + command: "onSelection", + text: `${name}:${startLine}-${endLine}`, + }); + } else { + webviewView.webview.postMessage({ + command: "onSelection", + text: "Ask anything here", + }); + } + }); + + try { + chat = startchat(history); + webviewView.webview.onDidReceiveMessage(async (message) => { + if (message) { + switch (message.command) { + case "sendMessage": + const code = this.selectedCode(); + webviewView.webview.postMessage({ + command: "Message", + text: `${code}${message.text}`, + }); + if (chat) { + try { + const prompt = `${code}${message.text}`; + const result = await chat.sendMessageStream(prompt); + let chunktext = ""; + for await (const chunk of result.stream) { + chunktext += chunk.text(); + webviewView.webview.postMessage({ + command: "receiveMessage", + text: chunktext, + }); + } + } catch (error) { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } + } else { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: "Chat Start Failed.", + }); + } + break; + case "updateHistory": + this.updateHistory(message.role, message.text); + break; + } + } + }); + } catch (error) { + webviewView.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } + } + + public updateHistory(role: string, part: string) { + history.push({ + role: role, + parts: [{ text: part }], + }); + } + + public selectedCode(): string { + try { + const editor = vscode.window.activeTextEditor; + if (!editor) { + console.debug("Abandon: no open text editor."); + return ""; + } + + const code = editor.document.getText(editor.selection); + editor.selection = new vscode.Selection( + editor.selection.active, + editor.selection.active + ); + const prefix = "```"; + const codePrefix = `${prefix}${editor.document.languageId}`; + if (code) { + const result = `${codePrefix}\n${code}\n${prefix}\n`; + return result; + } else { + return ""; + } + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return ""; + } + } + + public clearChat() { + if (this._view) { + this._view.webview.postMessage({ + command: "clearChat", + }); + try { + history = []; + chat = startchat(history); + } catch (error) { + this._view.webview.postMessage({ + command: "receiveMessage", + text: `${error}`, + }); + } + } + } + private _getHtmlForWebview(webview: vscode.Webview) { + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.js") + ); + const styleMainUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "main.css") + ); + const styleCodeUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media", "prism.css") + ); + const mediaUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, "media") + ); + // Use a nonce to only allow a specific script to be run. + const nonce = getNonce(); + return ` + + + + + + + + Gemini Chat + + +
+
+
+
+ + +
+ + + + + + `; + } +} +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; +} +export function deactivate() {} diff --git a/examples/gemini/node/pipet-code-agent/src/generateCode.ts b/examples/gemini/node/pipet-code-agent/src/generateCode.ts new file mode 100644 index 000000000..e336a2e23 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/generateCode.ts @@ -0,0 +1,121 @@ +/** + * Copyright 2024 Google LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const SYSTEMINSTRUCTION = `Code Enhancement:\n目的: 将用户提供的注释替换为实际可用的代码,并在不重写整个函数/类/等的情况下,仅修改指定范围内的代码。\n输入:\nfileTextWithLineNumbers:包含行号的项目文件文本。\nlineNumber:用户想要修改的代码行号。\ncurrentLineText:当前行包含的注释文本。\nlanguage:项目使用的编程语言。\n输出:\n将 currentLineText 替换为实际代码的修改后的代码片段,不包含任何额外的行,也不要包裹在代码块中.\n示例:\n用户输入:\nlineNumber: 2\ncurrentLineText: TODO: Implement add_numbers function \nlanguage: python\n系统输出:\nreturn x + y\n注意: 输出仅包含替换后的代码,不包含任何额外的行,也不要包裹在代码块中(i.e., \`\`\`).`; + +function addLineNumbers(text: string): string { + const lines = text.split("\n"); + const numberedLines = lines.map((line, index) => `${index + 1}: ${line}`); + return numberedLines.join("\n"); +} + +export function performAction() { + // Get the active text editor + const editor = vscode.window.activeTextEditor; + if (editor) { + // Show loading notification + vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "Generating AI code...", + cancellable: false, + }, + async (progress) => { + try { + progress.report({ increment: 0 }); + + const language = editor.document.languageId; + const fileTextWithLineNumbers = addLineNumbers( + editor.document.getText() + ); + const currentLineText = editor.document + .lineAt(editor.selection.active.line) + .text.trim(); + const lineNumber = editor.selection.active.line + 1; + + // Generate the code + const generatedCode = await generateCode( + fileTextWithLineNumbers, + lineNumber, + currentLineText, + language + ); + + // Insert the generated code at the current cursor position + editor.insertSnippet( + new vscode.SnippetString("\n" + generatedCode), + editor.selection.active + ); + vscode.window.showInformationMessage("AI Code Generated!"); + } catch (error: any) { + vscode.window.showErrorMessage( + "Error generating code: " + error?.message + ); + } + } + ); + } +} + +async function generateCode( + fileTextWithLineNumbers: string, + lineNumber: number, + currentLineText: string, + language: string +): Promise { + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); + + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return ""; + } + + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + + const prompt = `fileTextWithLineNumbers:${fileTextWithLineNumbers} + lineNumber:${lineNumber} + currentLineText:${currentLineText} + language:${language}`; + + const result = await model.generateContent({ + systemInstruction: { role: "system", parts: [{ text: SYSTEMINSTRUCTION }] }, + contents: [ + { + role: "user", + parts: [ + { text: prompt }, + { text: "请不要将生成的代码包裹在代码块中。" }, + ], + }, + ], + }); + const response = result.response; + const text = response.text(); + return text; +} diff --git a/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts new file mode 100644 index 000000000..c5ac5d318 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/getCommentprefixes.ts @@ -0,0 +1,41 @@ + +/** + * Copyright 2024 Google LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + *@brief Returns comment prefixes for various file types. + *@param fileType The file type to get comment prefix for + *@return Comment prefix for the specified file type or `//` if the file type is unknown +*/ +export function getCommentprefixes(fileType: string): string { + switch (fileType) { + case "python": + return "# "; + case "javascript": + return "// "; + case "html": + return ""; + case "css": + return "/* */"; + case "cpp": + case "c": + case "h": // C/C++ header + case "java": + case "csharp": + return "// "; + default: + return "//"; // No comment prefix for unknown file types + } +} \ No newline at end of file diff --git a/examples/gemini/node/pipet-code-agent/src/gitCommit.ts b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts new file mode 100644 index 000000000..439573842 --- /dev/null +++ b/examples/gemini/node/pipet-code-agent/src/gitCommit.ts @@ -0,0 +1,87 @@ +/** + * Copyright 2024 Google LLC + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as vscode from "vscode"; +import { GoogleGenerativeAI } from "@google/generative-ai"; + +const SYSTEMINSTRUCTION = + "Generate Git Commit by Git Diff and last commit, Only return commit message."; + +export async function generateGitCommit() { + try { + const gitExtension = vscode.extensions.getExtension("vscode.git"); + const gitApi = gitExtension?.exports.getAPI(1); + if (!gitApi.repositories.length) { + vscode.window.showErrorMessage("no git repositories."); + return; + } + + vscode.window.showInformationMessage("Generate Git Diff..."); + const diff = await gitApi.repositories[0].diff(); + const logOptions = { maxEntries: 1 }; + const log = await gitApi.repositories[0].log(logOptions); + const lastCommit = log[0]; + const lastCommitMessage = `Last commit:\n\n**Hash:** ${lastCommit.hash}\n**Author:** ${lastCommit.author_name} <${lastCommit.author_email}>\n**Date:** ${lastCommit.date}\n**Message:** ${lastCommit.message}`; + await generateCommit(`${lastCommitMessage}\nDiff: ${diff}`); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } +} + +async function generateCommit(diff: string) { + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); + + // Get API Key from local user configuration + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); + if (!apiKey) { + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); + return; + } + const genai = new GoogleGenerativeAI(apiKey); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [{ text: "Git Diff:" + diff }], + }, + ], + }); + const response = result.response; + const commit = response.text(); + await vscode.env.clipboard.writeText(commit); + await vscode.commands.executeCommand("workbench.view.scm"); + vscode.window.showInformationMessage( + "Commit message copied to clipboard and can Past in the commit box." + ); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + } +} diff --git a/examples/gemini/node/pipet-code-agent/src/review.ts b/examples/gemini/node/pipet-code-agent/src/review.ts index d2fcc7bea..cfc6d7d2d 100644 --- a/examples/gemini/node/pipet-code-agent/src/review.ts +++ b/examples/gemini/node/pipet-code-agent/src/review.ts @@ -16,82 +16,93 @@ import * as vscode from 'vscode'; import { GoogleGenerativeAI } from '@google/generative-ai'; -const CODE_LABEL = 'Here is the code:'; -const REVIEW_LABEL = 'Here is the review:'; -const PROMPT = ` -Reviewing code involves finding bugs and increasing code quality. Examples of bugs are syntax +import { getCommentprefixes } from './getCommentprefixes'; + +const SYSTEMINSTRUCTION = `Reviewing code involves finding bugs and increasing code quality. Examples of bugs are syntax errors or typos, out of memory errors, and boundary value errors. Increasing code quality entails reducing complexity of code, eliminating duplicate code, and ensuring other developers -are able to understand the code. -${CODE_LABEL} -for i in x: - pint(f"Iteration {i} provides this {x**2}.") -${REVIEW_LABEL} -The command \`print\` is spelled incorrectly. -${CODE_LABEL} -height = [1, 2, 3, 4, 5] -w = [6, 7, 8, 9, 10] -${REVIEW_LABEL} -The variable name \`w\` seems vague. Did you mean \`width\` or \`weight\`? -${CODE_LABEL} -while i < 0: - thrice = i * 3 - thrice = i * 3 - twice = i * 2 -${REVIEW_LABEL} -There are duplicate lines of code in this control structure. -`; +are able to understand the code.`; export async function generateReview() { - vscode.window.showInformationMessage('Generating code review...'); - const modelName = vscode.workspace.getConfiguration().get('google.gemini.textModel', 'models/gemini-1.0-pro-latest'); + vscode.window.showInformationMessage("Generating code review..."); + const modelName = vscode.workspace + .getConfiguration() + .get("google.gemini.textModel", "default"); // Get API Key from local user configuration - const apiKey = vscode.workspace.getConfiguration().get('google.gemini.apiKey'); + const apiKey = vscode.workspace + .getConfiguration() + .get("google.gemini.apiKey", "default"); if (!apiKey) { - vscode.window.showErrorMessage('API key not configured. Check your settings.'); + vscode.window.showErrorMessage( + "API key not configured. Check your settings." + ); return; } const genai = new GoogleGenerativeAI(apiKey); - const model = genai.getGenerativeModel({model: modelName}); + const model = genai.getGenerativeModel( + { model: modelName }, + { apiVersion: "v1beta" } + ); // Text selection const editor = vscode.window.activeTextEditor; if (!editor) { - console.debug('Abandon: no open text editor.'); + console.debug("Abandon: no open text editor."); return; } const selection = editor.selection; const selectedCode = editor.document.getText(selection); + try { + const result = await model.generateContent({ + systemInstruction: { + role: "system", + parts: [{ text: SYSTEMINSTRUCTION }], + }, + contents: [ + { + role: "user", + parts: [ + { + text: + "```" + + editor.document.languageId + + "\n" + + selectedCode + + "\n```", + }, + ], + }, + ], + }); + const response = result.response; + const comment = response.text(); + // Insert before selection + editor.edit((editBuilder) => { + // Copy the indent from the first line of the selection. + const trimmed = selectedCode.trimStart(); + const padding = selectedCode.substring( + 0, + selectedCode.length - trimmed.length + ); - // Build the full prompt using the template. - const fullPrompt = `${PROMPT} - ${CODE_LABEL} - ${selectedCode} - ${REVIEW_LABEL} - `; - - const result = await model.generateContent(fullPrompt); - const response = await result.response; - const comment = response.text(); - - // Insert before selection - editor.edit((editBuilder) => { - // Copy the indent from the first line of the selection. - const trimmed = selectedCode.trimStart(); - const padding = selectedCode.substring(0, selectedCode.length - trimmed.length); - - // TODO(you!): Support other comment styles. - const commentPrefix = '# '; - let pyComment = comment.split('\n').map((l: string) => `${padding}${commentPrefix}${l}`).join('\n'); - if (pyComment.search(/\n$/) === -1) { - // Add a final newline if necessary. - pyComment += "\n"; - } - let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; - editBuilder.insert(selection.start, reviewIntro); - editBuilder.insert(selection.start, pyComment); - }); + const commentPrefix = getCommentprefixes(editor.document.languageId); + let pyComment = comment + .split("\n") + .map((l: string) => `${padding}${commentPrefix}${l}`) + .join("\n"); + if (pyComment.search(/\n$/) === -1) { + // Add a final newline if necessary. + pyComment += padding + "\n"; + } + let reviewIntro = padding + commentPrefix + "Code review: (generated)\n"; + editBuilder.insert(selection.start, reviewIntro); + editBuilder.insert(selection.start, pyComment); + }); + } catch (error) { + vscode.window.showErrorMessage(`${error}`); + return; + } }