From 4204d9765473ee00512b32997fc86fd9bd5affb5 Mon Sep 17 00:00:00 2001 From: Michael Ramos Date: Tue, 24 Feb 2026 14:43:36 -0800 Subject: [PATCH 1/3] feat: add VS Code extension for inline annotations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simple extension that inserts comment markers in any file via right-click context menu or Cmd+Alt+A keyboard shortcut. No dialogs — inserts template and positions cursor for typing. Closes #91 Co-Authored-By: Claude Opus 4.6 --- apps/vscode-extension/.vscode/launch.json | 13 +++ apps/vscode-extension/.vscode/tasks.json | 12 +++ apps/vscode-extension/.vscodeignore | 5 + apps/vscode-extension/esbuild.config.mjs | 23 +++++ apps/vscode-extension/package.json | 65 ++++++++++++ apps/vscode-extension/src/extension.ts | 31 ++++++ apps/vscode-extension/tsconfig.json | 16 +++ bun.lock | 115 +++++++++++++++++----- package.json | 2 + 9 files changed, 255 insertions(+), 27 deletions(-) create mode 100644 apps/vscode-extension/.vscode/launch.json create mode 100644 apps/vscode-extension/.vscode/tasks.json create mode 100644 apps/vscode-extension/.vscodeignore create mode 100644 apps/vscode-extension/esbuild.config.mjs create mode 100644 apps/vscode-extension/package.json create mode 100644 apps/vscode-extension/src/extension.ts create mode 100644 apps/vscode-extension/tsconfig.json diff --git a/apps/vscode-extension/.vscode/launch.json b/apps/vscode-extension/.vscode/launch.json new file mode 100644 index 0000000..9ad3146 --- /dev/null +++ b/apps/vscode-extension/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "preLaunchTask": "build-extension" + } + ] +} diff --git a/apps/vscode-extension/.vscode/tasks.json b/apps/vscode-extension/.vscode/tasks.json new file mode 100644 index 0000000..07e5c8a --- /dev/null +++ b/apps/vscode-extension/.vscode/tasks.json @@ -0,0 +1,12 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build-extension", + "type": "shell", + "command": "node esbuild.config.mjs", + "group": "build", + "problemMatcher": ["$tsc"] + } + ] +} diff --git a/apps/vscode-extension/.vscodeignore b/apps/vscode-extension/.vscodeignore new file mode 100644 index 0000000..cab96d9 --- /dev/null +++ b/apps/vscode-extension/.vscodeignore @@ -0,0 +1,5 @@ +src/ +node_modules/ +tsconfig.json +esbuild.config.mjs +.vscode/ diff --git a/apps/vscode-extension/esbuild.config.mjs b/apps/vscode-extension/esbuild.config.mjs new file mode 100644 index 0000000..7ac135b --- /dev/null +++ b/apps/vscode-extension/esbuild.config.mjs @@ -0,0 +1,23 @@ +import * as esbuild from "esbuild"; + +const watch = process.argv.includes("--watch"); + +const config = { + entryPoints: ["src/extension.ts"], + bundle: true, + outfile: "dist/extension.js", + external: ["vscode"], + format: "cjs", + platform: "node", + target: "node18", + sourcemap: true, + minify: !watch, +}; + +if (watch) { + const ctx = await esbuild.context(config); + await ctx.watch(); + console.log("Watching for changes..."); +} else { + await esbuild.build(config); +} diff --git a/apps/vscode-extension/package.json b/apps/vscode-extension/package.json new file mode 100644 index 0000000..ba14a92 --- /dev/null +++ b/apps/vscode-extension/package.json @@ -0,0 +1,65 @@ +{ + "name": "plannotator", + "displayName": "Plannotator", + "description": "Add inline annotation markers to any file for plan review and code feedback", + "version": "0.0.1", + "publisher": "backnotprop", + "license": "MIT OR Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/backnotprop/plannotator.git", + "directory": "apps/vscode-extension" + }, + "engines": { + "vscode": "^1.80.0" + }, + "categories": ["Other"], + "keywords": ["annotation", "code-review", "plannotator"], + "main": "./dist/extension.js", + "activationEvents": [], + "contributes": { + "commands": [ + { + "command": "plannotator.addAnnotation", + "title": "Plannotator: Add Annotation" + } + ], + "menus": { + "editor/context": [ + { + "command": "plannotator.addAnnotation", + "group": "1_modification@99", + "when": "editorHasSelection" + } + ] + }, + "keybindings": [ + { + "command": "plannotator.addAnnotation", + "key": "ctrl+alt+a", + "mac": "cmd+alt+a", + "when": "editorTextFocus" + } + ], + "configuration": { + "title": "Plannotator", + "properties": { + "plannotator.annotationPrefix": { + "type": "string", + "default": "@plannotator", + "description": "Marker prefix for annotation comments" + } + } + } + }, + "scripts": { + "build": "node esbuild.config.mjs", + "watch": "node esbuild.config.mjs --watch", + "package": "npx @vscode/vsce package --no-dependencies" + }, + "devDependencies": { + "@types/vscode": "^1.80.0", + "esbuild": "^0.20.0", + "typescript": "~5.8.2" + } +} diff --git a/apps/vscode-extension/src/extension.ts b/apps/vscode-extension/src/extension.ts new file mode 100644 index 0000000..b709886 --- /dev/null +++ b/apps/vscode-extension/src/extension.ts @@ -0,0 +1,31 @@ +import * as vscode from "vscode"; + +export function activate(context: vscode.ExtensionContext) { + const cmd = vscode.commands.registerTextEditorCommand( + "plannotator.addAnnotation", + async (editor) => { + const config = vscode.workspace.getConfiguration("plannotator"); + const prefix = config.get("annotationPrefix", "@plannotator"); + + const selection = editor.selection; + const line = selection.start.line; + const indent = editor.document.lineAt(line).text.match(/^\s*/)![0]; + const template = `${indent}\n`; + + const inserted = await editor.edit((eb) => { + eb.insert(new vscode.Position(line, 0), template); + }); + + if (inserted) { + // Position cursor between ": " and " -->" + const cursorCol = indent.length + `\n`; + const startLine = selection.start.line; + const endLine = selection.end.line; + const multiLine = startLine !== endLine; + const indent = doc.lineAt(startLine).text.match(/^\s*/)![0]; + + // Auto-increment ID by scanning existing annotations + const idPattern = new RegExp(`${escapeRegex(prefix)}\\s+(\\d+)`); + let maxId = 0; + for (let i = 0; i < doc.lineCount; i++) { + const match = doc.lineAt(i).text.match(idPattern); + if (match) { + maxId = Math.max(maxId, parseInt(match[1])); + } + } + const nextId = String(maxId + 1).padStart(4, "0"); + + const startMarker = `${indent}\n`; const inserted = await editor.edit((eb) => { - eb.insert(new vscode.Position(line, 0), template); + if (multiLine) { + // Insert end marker first so startLine doesn't shift + const endInsertLine = endLine + 1; + const endMarker = `${indent}\n`; + eb.insert(new vscode.Position(endInsertLine, 0), endMarker); + } + eb.insert(new vscode.Position(startLine, 0), startMarker); }); if (inserted) { - // Position cursor between ": " and " -->" - const cursorCol = indent.length + ``, + ); + const endPattern = new RegExp( + `^\\s*`, + ); + const anyMarkerPattern = new RegExp( + `^\\s*