From 4f02650a34a6e8f288349785e5acb43bc43e3f36 Mon Sep 17 00:00:00 2001 From: David Souther Date: Sat, 4 May 2024 12:26:34 -0400 Subject: [PATCH 1/3] Use contents in context --- cli/fs.js | 1 - cli/index.js | 1 - core/src/engine/bedrock/bedrock.ts | 14 +++++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/cli/fs.js b/cli/fs.js index 67d6d1f..0c2e0c7 100644 --- a/cli/fs.js +++ b/cli/fs.js @@ -27,7 +27,6 @@ export async function loadFs(fs, args) { const root = resolve(args.values.root ?? '.'); fs.cd(root); - const settings = await ailly.Ailly.makePipelineSettings({ root, out: resolve(args.values.out ?? root), diff --git a/cli/index.js b/cli/index.js index 125d949..d82fe21 100755 --- a/cli/index.js +++ b/cli/index.js @@ -6,7 +6,6 @@ import * as ailly from "@ailly/core"; import { makeArgs, help } from "./args.js"; import { loadFs, LOGGER } from "./fs.js"; import { version } from "./version.js"; -import { promisify } from "node:util"; await main(); diff --git a/core/src/engine/bedrock/bedrock.ts b/core/src/engine/bedrock/bedrock.ts index 382d56d..ad8d674 100644 --- a/core/src/engine/bedrock/bedrock.ts +++ b/core/src/engine/bedrock/bedrock.ts @@ -40,6 +40,18 @@ export async function generate( if (c.context.edit) { const lang = c.context.edit.file.split(".").at(-1) ?? ""; fence = "```" + lang; + const { start, end } = c.context.edit as { start?: number; end?: number }; + if (start != undefined) { + if (messages.at(-1)?.role === "user") { + messages.at(-1)!.content += [ + "", + "You are replacing this section:", + "```", + c.meta?.text?.split("\n").slice(start, end), + "```", + ].join("\n"); + } + } messages.push({ role: "assistant", content: fence }); stopSequences = ["```"]; } @@ -176,7 +188,7 @@ export function getMessagesFolder( (content.context.folder ?? []) .map((c) => context[c]) .map( - (c) => `` + (c) => `` ) .join("\n") + "\n"; From 6dc0c81c19a6bd5f7e07e82612b0993826209f2e Mon Sep 17 00:00:00 2001 From: David Souther Date: Sat, 4 May 2024 12:29:13 -0400 Subject: [PATCH 2/3] Extension prompt runner and Ailly: Edit --- .vscode/launch.json | 4 +- core/index.ts | 14 ++-- core/src/actions/prompt_thread.ts | 4 +- core/src/engine/index.ts | 1 + core/src/engine/mistral/mistral.ts | 9 ++- core/src/engine/noop.ts | 4 +- core/src/engine/openai.ts | 8 +- extension/package.json | 41 +++++++++- extension/src/extension.cts | 57 -------------- extension/src/extension.ts | 118 +++++++++++++++++++++++++++++ extension/src/{fs.mts => fs.ts} | 4 + extension/src/generate.mts | 51 ------------- extension/src/generate.ts | 88 +++++++++++++++++++++ extension/src/settings.ts | 84 ++++++++++++++++++++ 14 files changed, 359 insertions(+), 128 deletions(-) delete mode 100644 extension/src/extension.cts create mode 100644 extension/src/extension.ts rename extension/src/{fs.mts => fs.ts} (93%) delete mode 100644 extension/src/generate.mts create mode 100644 extension/src/generate.ts create mode 100644 extension/src/settings.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index caec1bb..18dba6f 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -12,8 +12,8 @@ "--profile-temp", "--extensionDevelopmentPath=${workspaceFolder}/extension" ], - "outFiles": ["${workspaceFolder}/extension/out/**/*.js"], - "preLaunchTask": "${defaultBuildTask}" + "outFiles": ["${workspaceFolder}/extension/out/**/*.js"] + // "preLaunchTask": "${defaultBuildTask}" }, { "name": "Ailly Extension Tests", diff --git a/core/index.ts b/core/index.ts index a210ca8..a084acb 100644 --- a/core/index.ts +++ b/core/index.ts @@ -18,7 +18,7 @@ export const content = { }; export const Ailly = aillyModule; -export const version = getVersion(import.meta.url); +export const version = getVersion(import.meta?.url ?? "file://" + __filename); // TODO move this to jiffies import { execSync } from "node:child_process"; @@ -27,10 +27,14 @@ import { normalize, join } from "node:path"; import { readFileSync } from "node:fs"; export function getVersion(root: /*ImportMeta.URL*/ string) { - const cwd = normalize(join(fileURLToPath(root), "..")); - const packageJson = join(cwd, "./package.json"); - const pkg = JSON.parse(readFileSync(packageJson, { encoding: "utf8" })); - return pkg.version; + try { + const cwd = normalize(join(fileURLToPath(root), "..")); + const packageJson = join(cwd, "./package.json"); + const pkg = JSON.parse(readFileSync(packageJson, { encoding: "utf8" })); + return pkg.version; + } catch (_) { + return "unknown"; + } } export function getRevision(root: /* ImportMeta.URL */ string) { diff --git a/core/src/actions/prompt_thread.ts b/core/src/actions/prompt_thread.ts index cf416b6..f34dacd 100644 --- a/core/src/actions/prompt_thread.ts +++ b/core/src/actions/prompt_thread.ts @@ -192,12 +192,12 @@ async function generateOne( messages: meta?.messages ?.map((m) => ({ role: m.role, - content: m.content.replaceAll("\n", " ").substring(0, 150) + "...", + content: m.content.replace(/\n/g, " ").substring(0, 150) + "...", // tokens: m.tokens, })) // Skip the last `assistant` message .filter((m, i, a) => !(m.role == "assistant" && i === a.length - 1)) - .map(({ role, content }) => `${role}: ${content.replaceAll("\n", "\\n")}`) + .map(({ role, content }) => `${role}: ${content.replace(/\n/g, "\\n")}`) .join("\n\t"), }); const generated = await engine.generate(c, settings); diff --git a/core/src/engine/index.ts b/core/src/engine/index.ts index e9e3c20..b19620f 100644 --- a/core/src/engine/index.ts +++ b/core/src/engine/index.ts @@ -15,6 +15,7 @@ export interface Engine { ): Promise<{ debug: D; message: string }>; vector(s: string, parameters: ContentMeta): Promise; view?(): Promise; + models?(): string[]; } export interface Message { diff --git a/core/src/engine/mistral/mistral.ts b/core/src/engine/mistral/mistral.ts index 8ca722a..65ad57b 100644 --- a/core/src/engine/mistral/mistral.ts +++ b/core/src/engine/mistral/mistral.ts @@ -16,9 +16,10 @@ export async function generate( } let cwd = dirname( - import.meta.url - .replace(/^file:/, "") - .replace("ailly/core/dist", "ailly/core/src") + (import.meta?.url.replace(/^file:/, "") ?? __filename).replace( + "ailly/core/dist", + "ailly/core/src" + ) ); let command = join(cwd, normalize(".venv/bin/python3")); let args = [join(cwd, "mistral.py"), prompt]; @@ -35,7 +36,7 @@ export async function generate( child.on("disconnect", done); const error = (cause: unknown) => - reject(new Error("child_process had a problem", { cause })); + reject(new Error("child_process had a problem" /*, { cause }*/)); child.on("error", error); }); } diff --git a/core/src/engine/noop.ts b/core/src/engine/noop.ts index 7d7bc62..f2c60ec 100644 --- a/core/src/engine/noop.ts +++ b/core/src/engine/noop.ts @@ -1,8 +1,8 @@ import { getLogger } from "@davidsouther/jiffies/lib/esm/log.js"; import { Content } from "../content/content.js"; import { LOGGER as ROOT_LOGGER } from "../util.js"; -import type { PipelineSettings } from "../ailly"; -import type { Message } from "./index"; +import type { PipelineSettings } from "../ailly.js"; +import type { Message } from "./index.js"; const LOGGER = getLogger("@ailly/core:noop"); diff --git a/core/src/engine/openai.ts b/core/src/engine/openai.ts index b18e086..f204dbd 100644 --- a/core/src/engine/openai.ts +++ b/core/src/engine/openai.ts @@ -1,8 +1,8 @@ import { OpenAI, toFile } from "openai"; import { assertExists } from "@davidsouther/jiffies/lib/esm/assert.js"; -import type { Content } from "../content/content"; -import type { PipelineSettings } from "../ailly"; -import type { Message, Summary } from "./index"; +import type { Content } from "../content/content.js"; +import type { PipelineSettings } from "../ailly.js"; +import type { Message, Summary } from "./index.js"; import { LOGGER, isDefined } from "../util.js"; import { encode } from "../encoding.js"; @@ -85,7 +85,7 @@ async function callOpenAiWithRateLimit( } catch (e: any) { LOGGER.warn("Error calling openai", e.message); if (retry == 0) { - throw new Error("Failed 3 times to call openai", { cause: e }); + throw new Error("Failed 3 times to call openai" /*, { cause: e }*/); } if (e.error.code == "rate_limit_exceeded") { await new Promise((resolve) => { diff --git a/extension/package.json b/extension/package.json index 0ca4c6f..c9fcd60 100644 --- a/extension/package.json +++ b/extension/package.json @@ -22,6 +22,10 @@ { "command": "ailly.generate", "title": "Ailly: Generate" + }, + { + "command": "ailly.edit", + "title": "Ailly: Edit" } ], "menus": { @@ -36,6 +40,40 @@ { "title": "Ailly", "properties": { + "ailly.engine": { + "type": "string", + "default": "bedrock", + "enum": [ + "bedrock", + "openai" + ], + "description": "The Ailly engine to use when making LLM calls." + }, + "ailly.model": { + "type": [ + "string", + "null" + ], + "default": "haiku", + "description": "The default model to use when making LLM calls." + }, + "ailly.log-level": { + "type": "string", + "default": "info", + "enum": [ + "debug", + "info", + "warn", + "error", + "silent" + ], + "description": "The log level to record Ailly details." + }, + "ailly.log-pretty": { + "type": "boolean", + "default": false, + "description": "JSON (false) or pretty print (true) logs." + }, "ailly.openai-api-key": { "type": [ "string", @@ -50,11 +88,12 @@ }, "scripts": { "vscode:prepublish": "npm run esbuild-base -- --minify", - "esbuild-base": "esbuild ./src/extension.cts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", + "esbuild-base": "esbuild ./src/extension.ts --bundle --outfile=out/main.js --external:vscode --format=cjs --platform=node", "esbuild": "npm run esbuild-base -- --sourcemap", "esbuild-watch": "npm run esbuild-base -- --sourcemap --watch", "test-compile": "tsc -p ./", "compile": "tsc -p ./", + "build": "esbuild", "watch": "tsc -watch -p ./", "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", diff --git a/extension/src/extension.cts b/extension/src/extension.cts deleted file mode 100644 index 613ab55..0000000 --- a/extension/src/extension.cts +++ /dev/null @@ -1,57 +0,0 @@ -// The module 'vscode' contains the VS Code extensibility API -// Import the module and reference it with the alias vscode in your code below -import * as vscode from "vscode"; -import { basename } from "path"; - -// This method is called when your extension is activated -// Your extension is activated the very first time the command is executed -export function activate(context: vscode.ExtensionContext) { - // Use the console to output diagnostic information (console.log) and errors (console.error) - // This line of code will only be executed once when your extension is activated - console.log('Congratulations, your extension "ailly" is now active!'); - - // The command has been defined in the package.json file - // Now provide the implementation of the command with registerCommand - // The commandId parameter must match the command field in package.json - let disposable = vscode.commands.registerCommand( - "ailly.generate", - async (uri?: vscode.Uri, ..._args) => { - try { - let path: string; - if (uri) { - path = uri.path; - } else { - path = vscode.window.activeTextEditor?.document.uri.fsPath ?? ""; - if (!path) { - return; - } - } - - try { - const { generate } = await import("./generate.mjs"); - - vscode.window.showInformationMessage( - `Ailly generating ${basename(path)}` - ); - await generate(path); - vscode.window.showInformationMessage( - `Ailly generated ${basename(path)}` - ); - } catch (e) { - vscode.window.showWarningMessage( - `Ailly failed to generate ${basename(path)}: ${e}` - ); - - console.error(e); - } - } catch (e) { - console.error(e); - } - } - ); - - context.subscriptions.push(disposable); -} - -// This method is called when your extension is deactivated -export function deactivate() {} diff --git a/extension/src/extension.ts b/extension/src/extension.ts new file mode 100644 index 0000000..e77684c --- /dev/null +++ b/extension/src/extension.ts @@ -0,0 +1,118 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from "vscode"; +import { basename } from "path"; +import { generate } from "./generate.js"; +import { LOGGER, resetLogger } from "./settings"; + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed +export function activate(context: vscode.ExtensionContext) { + resetLogger(); + // Use the console to output diagnostic information (console.log) and errors (console.error) + // This line of code will only be executed once when your extension is activated + LOGGER.info('Congratulations, your extension "ailly" is now active!'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + context.subscriptions.push( + vscode.commands.registerCommand( + "ailly.generate", + async (uri?: vscode.Uri, ..._args) => { + try { + let path: string; + if (uri) { + path = uri.path; + } else { + path = vscode.window.activeTextEditor?.document.uri.fsPath ?? ""; + if (!path) { + return; + } + } + + try { + vscode.window.showInformationMessage( + `Ailly generating ${basename(path)}` + ); + await generate(path); + vscode.window.showInformationMessage( + `Ailly generated ${basename(path)}` + ); + } catch (err) { + vscode.window.showWarningMessage( + `Ailly failed to generate ${basename(path)}: ${err}` + ); + + LOGGER.error("Failed to generate", { err }); + } + } catch (err) { + LOGGER.error("Unknown failure", { err }); + } + } + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + "ailly.edit", + async (uri?: vscode.Uri, ..._args) => { + if (!vscode.window.activeTextEditor) return; + try { + let path: string; + if (uri) { + path = uri.path; + } else { + path = vscode.window.activeTextEditor.document.uri.fsPath ?? ""; + if (!path) { + return; + } + } + + const prompt = await vscode.window.showInputBox({ + title: "Prompt", + prompt: "What edits should Ailly make?", + }); + + if (!prompt) return; + + const start = vscode.window.activeTextEditor.selection.start.line; + const end = vscode.window.activeTextEditor.selection.end.line; + + try { + vscode.window.showInformationMessage( + `Ailly generating ${basename(path)}` + ); + await generate(path, { prompt, start, end }); + vscode.window.showInformationMessage( + `Ailly edited ${basename(path)}` + ); + } catch (err) { + vscode.window.showWarningMessage( + `Ailly failed to generate ${basename(path)}: ${err}` + ); + + LOGGER.error("Error doing edit", { err }); + } + } catch (err) { + LOGGER.error("Unknown error editing", { err }); + } + } + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand("ailly.set-engine", async () => { + vscode.window.showInformationMessage(`Ailly setting engine`); + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand("ailly.set-model", async () => { + vscode.window.showInformationMessage(`Ailly setting model`); + }) + ); +} + +// This method is called when your extension is deactivated +export function deactivate() {} diff --git a/extension/src/fs.mts b/extension/src/fs.ts similarity index 93% rename from extension/src/fs.mts rename to extension/src/fs.ts index a1ea2c9..20e360f 100644 --- a/extension/src/fs.mts +++ b/extension/src/fs.ts @@ -11,6 +11,10 @@ export class VSCodeFileSystemAdapter implements FileSystemAdapter { return Promise.resolve(); } + mkdir(path: string): Promise { + return Promise.resolve(workspace.fs.createDirectory(Uri.file(path))); + } + readFile(path: string): Promise { return Promise.resolve( workspace.fs diff --git a/extension/src/generate.mts b/extension/src/generate.mts deleted file mode 100644 index 34e0a14..0000000 --- a/extension/src/generate.mts +++ /dev/null @@ -1,51 +0,0 @@ -import vscode from "vscode"; - -import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; -import { VSCodeFileSystemAdapter } from "./fs.mjs"; -import * as Ailly from "@ailly/core"; - -export async function generate(path: string) { - // CommonJS <> ESM Shenanigans - console.log(`Generating for ${path}`); - const apiKey = await getOpenAIKey(); - if (!apiKey) { - return; - } - console.log(`apikey is ${apiKey}`); - - const fs = new FileSystem(new VSCodeFileSystemAdapter()); - // TODO: Work with multi-workspace editors - fs.cd(vscode.workspace.workspaceFolders?.[0]?.uri.fsPath ?? process.cwd()); - - // Load content - let content = await Ailly.content.load(fs as any); - content = content.filter((c) => c.path.startsWith(path)); - console.log(`Generating ${content.length} files`); - - // Generate - let generator = Ailly.Ailly.generate(content, { apiKey }); - generator.start(); - await generator.allSettled(); - - // Write - Ailly.content.write(fs as any, content); -} - -async function getOpenAIKey(): Promise { - if (process.env["OPENAI_API_KEY"]) { - return process.env["OPENAI_API_KEY"]; - } - let aillyConfig = vscode.workspace.getConfiguration("ailly"); - if (aillyConfig.has("openai-api-key")) { - const key = aillyConfig.get("openai-api-key"); - if (key) { - return key; - } - } - const key = await vscode.window.showInputBox({ - title: "Ailly: OpenAI API Key", - prompt: "API Key from OpenAI for requests", - }); - aillyConfig.update("openai-api-key", key); - return key; -} diff --git a/extension/src/generate.ts b/extension/src/generate.ts new file mode 100644 index 0000000..39eae04 --- /dev/null +++ b/extension/src/generate.ts @@ -0,0 +1,88 @@ +import vscode from "vscode"; + +import * as ailly from "@ailly/core"; +import { FileSystem } from "@davidsouther/jiffies/lib/esm/fs.js"; +import { VSCodeFileSystemAdapter } from "./fs.js"; +import { LOGGER, getAillyEngine, getAillyModel, resetLogger } from "./settings"; +import { AillyEdit } from "@ailly/core/src/content/content"; +import { dirname } from "path"; + +export async function generate( + path: string, + edit?: { prompt: string; start: number; end: number } +) { + resetLogger(); + // CommonJS <> ESM Shenanigans + LOGGER.info(`Generating for ${path}`); + + const fs = new FileSystem(new VSCodeFileSystemAdapter()); + // TODO: Work with multi-workspace editors + const root = vscode.workspace.workspaceFolders?.[0].uri.fsPath!; + fs.cd(root); + + const engine = await getAillyEngine(); + const model = await getAillyModel(engine); + + const settings = await ailly.Ailly.makePipelineSettings({ + root, + out: root, + context: "folder", + engine, + model, + }); + + // Load content + const context = await ailly.content.load(fs); + const content = Object.values(context).filter((c) => c.path.startsWith(path)); + if (content.length == 0) return; + if (edit) { + const editContext: AillyEdit = + edit.start === edit.end + ? { file: content[0].path, after: edit.start + 1 } + : { file: content[0].path, start: edit.start + 1, end: edit.end + 2 }; + content[0] = { + context: { + view: {}, + contents: content[0].context.contents, + folder: [content[0].path], + edit: editContext, + }, + path: "/dev/ailly", + name: "ailly", + outPath: "/dev/ailly", + prompt: edit.prompt, + }; + context[content[0].path] = content[0]; + LOGGER.info(`Editing ${content.length} files`); + } else { + LOGGER.info(`Generating ${content.length} files`); + } + + // Generate + let generator = await ailly.Ailly.GenerateManager.from( + content.map((c) => c.path), + context, + settings + ); + generator.start(); + await generator.allSettled(); + + if (content[0].meta?.debug?.finish! == "failed") { + throw new Error(content[0].meta.debug?.message!); + } + + // Write + if (edit && content[0].context.edit) { + vscode.window.activeTextEditor?.edit((builder) => { + builder.replace( + new vscode.Range( + new vscode.Position(edit.start, 0), + new vscode.Position(edit.end + 1, 0) + ), + (content[0].response ?? "") + "\n" + ); + }); + } else { + ailly.content.write(fs as any, content); + } +} diff --git a/extension/src/settings.ts b/extension/src/settings.ts new file mode 100644 index 0000000..0b0c520 --- /dev/null +++ b/extension/src/settings.ts @@ -0,0 +1,84 @@ +import * as ailly from "@ailly/core"; +import { DEFAULT_ENGINE, getEngine } from "@ailly/core/src/ailly"; +import { ENGINES } from "@ailly/core/src/engine"; +import { + basicLogFormatter, + getLogLevel, + getLogger, +} from "@davidsouther/jiffies/lib/esm/log"; +import vscode from "vscode"; + +export const LOGGER = getLogger("@ailly/extension"); + +export function resetLogger() { + ailly.Ailly.LOGGER.level = LOGGER.level = getLogLevel(getAillyLogLevel()); + ailly.Ailly.LOGGER.format = LOGGER.format = getAillyLogPretty() + ? basicLogFormatter + : JSON.stringify; +} + +export function getAillyLogLevel() { + return getConfig().get("log-level") ?? "info"; +} + +export function getAillyLogPretty() { + return getConfig().get("log-pretty") ?? false; +} + +export async function getOpenAIKey(): Promise { + if (process.env["OPENAI_API_KEY"]) { + return process.env["OPENAI_API_KEY"]; + } + let aillyConfig = getConfig(); + if (aillyConfig.has("openai-api-key")) { + const key = aillyConfig.get("openai-api-key"); + if (key) { + return key; + } + } + const key = await vscode.window.showInputBox({ + title: "Ailly: OpenAI API Key", + prompt: "API Key from OpenAI for requests", + }); + aillyConfig.update("openai-api-key", key); + return key; +} + +export async function getAillyEngine(): Promise { + const aillyConfig = getConfig(); + if (aillyConfig.has("engine")) { + const engine = aillyConfig.get("engine"); + if (engine) { + return engine; + } + } + const engine = await vscode.window.showQuickPick(Object.keys(ENGINES), { + title: "Ailly: Engine", + }); + aillyConfig.update("engine", engine); + return engine ?? DEFAULT_ENGINE; +} + +export async function getAillyModel( + engineName: string +): Promise { + const engine = await getEngine(engineName); + const aillyConfig = getConfig(); + if (aillyConfig.has("model")) { + const model = aillyConfig.get("model"); + if (model) { + return model; + } + } + const models = engine.models?.(); + if (!models) return; + const model = await vscode.window.showQuickPick(models, { + title: "Ailly: Model", + }); + aillyConfig.update("model", model); + return model; +} + +function getConfig() { + return vscode.workspace.getConfiguration("ailly"); +} From 2ac9ef5ea39f9da06fb49f999532af45affbae9e Mon Sep 17 00:00:00 2001 From: David Souther Date: Sat, 4 May 2024 12:13:23 -0400 Subject: [PATCH 3/3] Compile and publish extension --- .github/workflows/extension.yaml | 30 ++++++++++++++++++++++++++++++ extension/package.json | 3 ++- 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/extension.yaml diff --git a/.github/workflows/extension.yaml b/.github/workflows/extension.yaml new file mode 100644 index 0000000..1775931 --- /dev/null +++ b/.github/workflows/extension.yaml @@ -0,0 +1,30 @@ +name: Package Extension + +on: + push: + branches: [main] + workflow_dispatch: + +jobs: + npm-test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Use Node.js + uses: actions/setup-node@v4 + with: + node-version: "20.x" + + - name: Install dependencies + run: npm install && npx tsc -p core && npm link ./core -w cli + + - name: Package Extension + run: npm run --workspace extension + + - name: Publish extension artifact + uses: actions/upload-artifact + with: + name: extension + path: extension/ailly-*.vsix diff --git a/extension/package.json b/extension/package.json index c9fcd60..ccbdb06 100644 --- a/extension/package.json +++ b/extension/package.json @@ -5,7 +5,7 @@ "author": "David Souther ", "contributors": [], "repository": "http://github.com/davidsouther/ailly", - "version": "0.0.1", + "version": "0.1.0", "engines": { "vscode": "^1.83.0" }, @@ -98,6 +98,7 @@ "pretest": "npm run compile && npm run lint", "lint": "eslint src --ext ts", "test": "node ./out/test/runTest.js", + "prepackage": "npm run vscode:prepublish", "package": "vsce package" }, "devDependencies": {