├── .gitignore ├── .vscode └── launch.json ├── .vscodeignore ├── LICENSE ├── README.md ├── bun.lockb ├── demo ├── aphorisms.gif ├── github-api.gif └── terminal-game.gif ├── extension.js ├── icon.png ├── package.json └── webpack.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | *.vsix -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | // A launch configuration that compiles the extension and then opens it inside a new window 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | { 6 | "version": "0.2.0", 7 | "configurations": [ 8 | { 9 | "name": "Run Extension", 10 | "type": "extensionHost", 11 | "request": "launch", 12 | "args": [ 13 | "--extensionDevelopmentPath=${workspaceFolder}" 14 | ], 15 | "outFiles": [ 16 | "${workspaceFolder}/dist/*.js" 17 | ] 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | node_modules/** 2 | **/*.gif -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2024 Coder Technologies Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | Picopilot 4 |

5 | 6 | [![Visual Studio Marketplace](https://vsmarketplacebadges.dev/version/coder.picopilot.svg)](https://marketplace.visualstudio.com/items?itemName=coder.picopilot) 7 | 8 | [GitHub Copilot](https://marketplace.visualstudio.com/items?itemName=GitHub.copilot) in [70 lines of JavaScript](./extension.js). 9 | 10 | System Prompt: 11 | 12 | ```text 13 | You provide code completion results given a prefix and suffix. 14 | 15 | Respond with a JSON object with a key 'completion' containing the suggestion to place between the prefix and suffix. 16 | 17 | Follow existing code styles. Listen to comments at the end of the prefix. The language is "${document.languageId}". 18 | ``` 19 | 20 | Response Format: 21 | 22 | ```json 23 | { 24 | "completion": "" 25 | } 26 | ``` 27 | 28 | ## Demos 29 | 30 | _All demos are unedited._ 31 | 32 | ### Creating a terminal game 33 | 34 | ![Creating a terminal game](./demo/terminal-game.gif) 35 | 36 | ### Generating aphorisms 37 | 38 | ![Generating aphorisms](./demo/aphorisms.gif) 39 | 40 | ### Using the GitHub API 41 | 42 | ![GitHub API](./demo/github-api.gif) 43 | 44 | ## Install 45 | 46 | Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter. 47 | 48 | ```text 49 | ext install coder.picopilot 50 | ``` 51 | 52 | Feel free to configure a custom prompt in your settings. 53 | 54 | ## Development 55 | 56 | Clone the repository, run `bun install`, `bun watch`, open VS Code, and press F5 to launch the extension in development mode. 57 | 58 | Create your own AI extensions from this repo. It's remarkably simple! 59 | -------------------------------------------------------------------------------- /bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder/picopilot/f71c6ab4738d4159d18aa772b22f4b1d24c89899/bun.lockb -------------------------------------------------------------------------------- /demo/aphorisms.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder/picopilot/f71c6ab4738d4159d18aa772b22f4b1d24c89899/demo/aphorisms.gif -------------------------------------------------------------------------------- /demo/github-api.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder/picopilot/f71c6ab4738d4159d18aa772b22f4b1d24c89899/demo/github-api.gif -------------------------------------------------------------------------------- /demo/terminal-game.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder/picopilot/f71c6ab4738d4159d18aa772b22f4b1d24c89899/demo/terminal-game.gif -------------------------------------------------------------------------------- /extension.js: -------------------------------------------------------------------------------- 1 | const vscode = require("vscode"); 2 | const OpenAI = require("openai"); 3 | 4 | module.exports = { 5 | activate: (ctx) => { 6 | let api; 7 | const initAPI = async () => { 8 | api = new OpenAI({ apiKey: await ctx.secrets.get("openai-key") }); 9 | }; 10 | initAPI(); 11 | ctx.secrets.onDidChange((event) => { 12 | if (event.key === "openai-key") initAPI(); 13 | }); 14 | vscode.languages.registerInlineCompletionItemProvider( 15 | { pattern: "**" }, 16 | { 17 | provideInlineCompletionItems: async (document, position) => { 18 | if (!api) { 19 | const res = await vscode.window.showErrorMessage( 20 | "You must configure an OpenAI API Key!", 21 | "Set Key" 22 | ); 23 | if (res) 24 | await vscode.commands.executeCommand("picopilot.token"); 25 | return; 26 | } 27 | const prefix = document.getText(new vscode.Range(new vscode.Position(0, 0), position)); 28 | const suffix = document.getText(new vscode.Range(position, document.positionAt(document.getText().length))); 29 | const prompt = 30 | vscode.workspace.getConfiguration("picopilot").get("prompt") || `You provide code completion results given a prefix and suffix. 31 | Respond with a JSON object with the key 'completion' containing a suggestion to place between the prefix and suffix. 32 | Follow existing code styles. Listen to comments at the end of the prefix. The language is "{language}".`; 33 | 34 | const response = await api.chat.completions.create({ 35 | messages: [ 36 | { 37 | role: "system", 38 | content: prompt.replace("{language}", document.languageId), 39 | }, 40 | { role: "user", content: prefix }, 41 | { role: "user", content: suffix }, 42 | ], 43 | model: "gpt-4o", 44 | max_tokens: 500, 45 | response_format: { type: "json_object" }, 46 | }); 47 | const resp = JSON.parse(response.choices[0].message.content); 48 | return { 49 | items: [{ insertText: resp.completion.trim() }], 50 | }; 51 | }, 52 | } 53 | ); 54 | vscode.commands.registerCommand("picopilot.token", async () => { 55 | await vscode.env.openExternal( 56 | vscode.Uri.parse("https://platform.openai.com/api-keys") 57 | ); 58 | const res = await vscode.window.showInputBox({ 59 | title: "OpenAI API Key", 60 | prompt: "Generate an API Key and paste it in!", 61 | ignoreFocusOut: true, 62 | password: true, 63 | }); 64 | if (res) { 65 | await ctx.secrets.store("openai-key", res); 66 | vscode.window.showInformationMessage("PicoPilot is working!"); 67 | initAPI(); 68 | } 69 | }); 70 | }, 71 | }; 72 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/coder/picopilot/f71c6ab4738d4159d18aa772b22f4b1d24c89899/icon.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "picopilot", 3 | "publisher": "coder", 4 | "displayName": "Picopilot", 5 | "description": "GitHub Copilot in 70 lines of JavaScript.", 6 | "repository": { 7 | "url": "https://github.com/coder/picopilot" 8 | }, 9 | "version": "1.0.3", 10 | "engines": { 11 | "vscode": "^1.90.0" 12 | }, 13 | "categories": [ 14 | "Programming Languages", 15 | "Machine Learning", 16 | "AI" 17 | ], 18 | "activationEvents": [ 19 | "*" 20 | ], 21 | "icon": "./icon.png", 22 | "main": "./dist/extension.js", 23 | "contributes": { 24 | "commands": [ 25 | { 26 | "command": "picopilot.token", 27 | "title": "Picopilot: Set OpenAI API Key" 28 | } 29 | ], 30 | "configuration":[ 31 | { 32 | "title": "Picopilot", 33 | "properties": { 34 | "picopilot.prompt": { 35 | "type": "string", 36 | "default": "", 37 | "description": "The prompt to use to generate completions. '{language}' is replaced with the active document language." 38 | } 39 | } 40 | } 41 | ] 42 | }, 43 | "scripts": { 44 | "build": "webpack --mode production --devtool hidden-source-map", 45 | "watch": "bun run build -- --watch", 46 | "package": "vsce package --allow-star-activation" 47 | }, 48 | "devDependencies": { 49 | "@types/vscode": "^1.90.0", 50 | "webpack": "^5.92.1", 51 | "webpack-cli": "^5.1.4" 52 | }, 53 | "dependencies": { 54 | "openai": "^4.52.0" 55 | } 56 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | //@ts-check 2 | 3 | "use strict"; 4 | 5 | const path = require("path"); 6 | 7 | //@ts-check 8 | /** @typedef {import('webpack').Configuration} WebpackConfig **/ 9 | 10 | /** @type WebpackConfig */ 11 | const extensionConfig = { 12 | target: "node", // VS Code extensions run in a Node.js-context 📖 -> https://webpack.js.org/configuration/node/ 13 | mode: "none", // this leaves the source code as close as possible to the original (when packaging we set this to 'production') 14 | 15 | entry: "./extension.js", // the entry point of this extension, 📖 -> https://webpack.js.org/configuration/entry-context/ 16 | output: { 17 | // the bundle is stored in the 'dist' folder (check package.json), 📖 -> https://webpack.js.org/configuration/output/ 18 | path: path.resolve(__dirname, "dist"), 19 | filename: "extension.js", 20 | libraryTarget: "commonjs2", 21 | }, 22 | externals: { 23 | vscode: "commonjs vscode", 24 | }, 25 | resolve: { 26 | extensions: [".js"], 27 | }, 28 | devtool: "nosources-source-map", 29 | }; 30 | module.exports = [extensionConfig]; 31 | --------------------------------------------------------------------------------