From 5e24c7f9ad974f16c6cb5ce773fb03c870810a71 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:23:19 +0500 Subject: [PATCH 1/3] feat(extension): scaffold activate function and register status bar item --- src/extension/index.ts | 107 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 src/extension/index.ts diff --git a/src/extension/index.ts b/src/extension/index.ts new file mode 100644 index 0000000..007393c --- /dev/null +++ b/src/extension/index.ts @@ -0,0 +1,107 @@ +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import * as vscode from 'vscode'; +import { ProofCodeEngine } from '../engine/proofCodeEngine'; +import { ReportFormatter } from '../engine/reportFormatter'; +import { ProjectRulesEngine } from '../rules/projectRulesEngine'; +import { VerificationReport } from '../types'; +import { ProofCodeCodeActionProvider } from '../ui/codeActionProvider'; +import { ProofCodeCodeLensProvider } from '../ui/codeLensProvider'; +import { ProofCodeTreeDataProvider } from '../ui/treeDataProvider'; +import { ProofCodeWebviewPanel } from '../ui/webviewPanel'; + +let treeDataProvider: ProofCodeTreeDataProvider; +let statusBarItem: vscode.StatusBarItem; +let codeLensProvider: ProofCodeCodeLensProvider; +let diagnosticCollection: vscode.DiagnosticCollection; +let latestReport: VerificationReport | null = null; + +export function activate(context: vscode.ExtensionContext): void { + // 1. Sidebar Tree View + treeDataProvider = new ProofCodeTreeDataProvider(); + vscode.window.registerTreeDataProvider('proofcode.views.overview', treeDataProvider); + + // 2. Status Bar Item + statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100); + statusBarItem.command = 'proofcode.openDashboard'; + statusBarItem.text = '$(shield) ProofCode'; + statusBarItem.tooltip = 'ProofCode Change Verification: Click to open dashboard'; + statusBarItem.show(); + context.subscriptions.push(statusBarItem); + + // 3. Diagnostics, CodeLens & Quick Fix Code Actions + codeLensProvider = new ProofCodeCodeLensProvider(); + const codeLensDisposable = vscode.languages.registerCodeLensProvider( + [ + { language: 'typescript' }, + { language: 'javascript' }, + { language: 'typescriptreact' }, + { language: 'javascriptreact' } + ], + codeLensProvider + ); + diagnosticCollection = vscode.languages.createDiagnosticCollection('proofcode'); + + const codeActionProvider = new ProofCodeCodeActionProvider(); + const codeActionDisposable = vscode.languages.registerCodeActionsProvider( + [ + { language: 'typescript' }, + { language: 'javascript' }, + { language: 'typescriptreact' }, + { language: 'javascriptreact' } + ], + codeActionProvider, + { + providedCodeActionKinds: ProofCodeCodeActionProvider.providedCodeActionKinds + } + ); + + context.subscriptions.push(codeLensDisposable, diagnosticCollection, codeActionDisposable); + + // Helper to get workspace root + const getWorkspaceRoot = (): string | undefined => { + return vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0 + ? vscode.workspace.workspaceFolders[0].uri.fsPath + : undefined; + }; + + // Verification Runner + const runVerification = async (quiet = false): Promise => { + const root = getWorkspaceRoot(); + if (!root) { + if (!quiet) { + vscode.window.showWarningMessage('ProofCode requires an open workspace folder.'); + } + return null; + } + + const config = vscode.workspace.getConfiguration('proofcode'); + const runLiveTests = config.get('runTestsOnVerify', false); + const rulesFilePath = config.get('rulesFilePath', '.proofcode/rules.md'); + + return vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: 'ProofCode: Verifying change impact and risks...', + cancellable: false + }, + async () => { + try { + const engine = new ProofCodeEngine(root); + const report = await engine.verify({ + runLiveTests, + rulesFilePath + }); + + latestReport = report; + treeDataProvider.setReport(report); + + // Update Diagnostics in Problems panel + diagnosticCollection.clear(); + const fileDiagnosticsMap = new Map(); + + for (const risk of report.risks) { + const lineIdx = Math.max(0, risk.line - 1); + const colIdx = Math.max(0, (risk.column || 1) - 1); + const range = new vscode.Range(lineIdx, colIdx, lineIdx, colIdx + 40); + const severity = From caa86ec9fd583fb4ee6940dbfd30772131cafcd6 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:23:19 +0500 Subject: [PATCH 2/3] feat(extension): register sidebar tree view, commands, and webview panel --- src/extension/index.ts | 122 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/src/extension/index.ts b/src/extension/index.ts index 007393c..7470c1c 100644 --- a/src/extension/index.ts +++ b/src/extension/index.ts @@ -105,3 +105,125 @@ export function activate(context: vscode.ExtensionContext): void { const colIdx = Math.max(0, (risk.column || 1) - 1); const range = new vscode.Range(lineIdx, colIdx, lineIdx, colIdx + 40); const severity = + risk.severity === 'HIGH' + ? vscode.DiagnosticSeverity.Error + : risk.severity === 'MEDIUM' + ? vscode.DiagnosticSeverity.Warning + : vscode.DiagnosticSeverity.Information; + + const diag = new vscode.Diagnostic( + range, + `[${risk.ruleId}] ${risk.title}: ${risk.description}`, + severity + ); + diag.source = 'ProofCode'; + diag.code = risk.ruleId; + + const list = fileDiagnosticsMap.get(risk.file) || []; + list.push(diag); + fileDiagnosticsMap.set(risk.file, list); + } + + for (const rule of report.rules) { + if (rule.status === 'VIOLATION' && rule.file) { + const lineIdx = Math.max(0, (rule.line || 1) - 1); + const range = new vscode.Range(lineIdx, 0, lineIdx, 40); + const diag = new vscode.Diagnostic( + range, + `[${rule.rule.id}] Rule Violation: "${rule.rule.title}" - ${rule.detectedMessage || ''}`, + vscode.DiagnosticSeverity.Error + ); + diag.source = 'ProofCode'; + diag.code = rule.rule.id; + + const list = fileDiagnosticsMap.get(rule.file) || []; + list.push(diag); + fileDiagnosticsMap.set(rule.file, list); + } + } + + for (const [filePath, diags] of fileDiagnosticsMap.entries()) { + const fullPath = path.isAbsolute(filePath) ? filePath : path.resolve(root, filePath); + diagnosticCollection.set(vscode.Uri.file(fullPath), diags); + } + + // Update CodeLens annotations + codeLensProvider.setReport(report); + + // Update Status Bar + const score = report.score.total; + const risksCount = report.risks.length; + const icon = + report.verdict === 'READY_TO_SHIP' + ? '$(pass)' + : report.verdict === 'NEEDS_REVIEW' + ? '$(warning)' + : '$(error)'; + + statusBarItem.text = `${icon} ProofCode: ${score}% (${risksCount} risks)`; + statusBarItem.tooltip = `Verdict: ${report.verdict} | ${report.summary.filesChanged} files changed, ${report.summary.symbolsAffected} symbols affected.`; + + // If webview is open, refresh it + if (ProofCodeWebviewPanel.currentPanel) { + ProofCodeWebviewPanel.currentPanel.setReport(report); + } + + if (!quiet) { + if (report.verdict === 'READY_TO_SHIP') { + vscode.window.showInformationMessage( + `ProofCode: Verification Passed (${score}%). Ready to ship!` + ); + } else if (report.verdict === 'NEEDS_REVIEW') { + vscode.window.showWarningMessage( + `ProofCode: Score ${score}%. ${report.summary.mediumRiskCount} warning(s) need review.` + ); + } else { + vscode.window.showErrorMessage( + `ProofCode: BLOCKED (${score}%). ${report.summary.highRiskCount} high risk(s) or rule violations detected.` + ); + } + } + + return report; + } catch (err: unknown) { + vscode.window.showErrorMessage( + `ProofCode Verification Error: ${(err as Error).message}` + ); + return null; + } + } + ); + }; + + // Commands + const verifyCmd = vscode.commands.registerCommand('proofcode.verifyChange', async () => { + await runVerification(false); + }); + + const openDashboardCmd = vscode.commands.registerCommand('proofcode.openDashboard', async () => { + if (!latestReport) { + await runVerification(true); + } + + const root = getWorkspaceRoot(); + ProofCodeWebviewPanel.createOrShow( + context.extensionUri, + latestReport, + async () => { + await runVerification(false); + }, + async () => { + if (root) { + const rulesEngine = new ProjectRulesEngine(root); + const createdPath = await rulesEngine.initDefaultRulesFile(); + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(createdPath)); + await vscode.window.showTextDocument(doc); + } + } + ); + }); + + const initRulesCmd = vscode.commands.registerCommand('proofcode.initRules', async () => { + const root = getWorkspaceRoot(); + if (!root) return; + From bde13b6b0b49a16c418e5a01e1ac4532359ed1d6 Mon Sep 17 00:00:00 2001 From: waleedcodes <110061477+waleedcodes@users.noreply.github.com> Date: Fri, 9 Oct 2026 21:23:19 +0500 Subject: [PATCH 3/3] feat(extension): integrate diagnostic collection, codelens, code actions, and auto-verify --- src/extension/index.ts | 77 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) diff --git a/src/extension/index.ts b/src/extension/index.ts index 7470c1c..d6d1f7a 100644 --- a/src/extension/index.ts +++ b/src/extension/index.ts @@ -227,3 +227,80 @@ export function activate(context: vscode.ExtensionContext): void { const root = getWorkspaceRoot(); if (!root) return; + const rulesEngine = new ProjectRulesEngine(root); + const createdPath = await rulesEngine.initDefaultRulesFile(); + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(createdPath)); + await vscode.window.showTextDocument(doc); + vscode.window.showInformationMessage(`ProofCode: Initialized ${createdPath}`); + }); + + const openEvidenceCmd = vscode.commands.registerCommand( + 'proofcode.openEvidence', + async (filePath: string, line: number) => { + try { + const root = getWorkspaceRoot() || ''; + const targetPath = path.isAbsolute(filePath) ? filePath : path.resolve(root, filePath); + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(targetPath)); + const editor = await vscode.window.showTextDocument(doc); + const position = new vscode.Position(Math.max(0, line - 1), 0); + editor.selection = new vscode.Selection(position, position); + editor.revealRange( + new vscode.Range(position, position), + vscode.TextEditorRevealType.InCenter + ); + } catch (err: unknown) { + vscode.window.showErrorMessage(`Could not open file: ${(err as Error).message}`); + } + } + ); + + const exportReportCmd = vscode.commands.registerCommand('proofcode.exportReport', async () => { + const root = getWorkspaceRoot(); + if (!root || !latestReport) { + vscode.window.showWarningMessage('No report available to export. Run verification first.'); + return; + } + + const md = ReportFormatter.toMarkdown(latestReport); + const exportPath = path.join(root, '.proofcode', 'report.md'); + const dir = path.dirname(exportPath); + if (!fs.existsSync(dir)) { + fs.mkdirSync(dir, { recursive: true }); + } + + fs.writeFileSync(exportPath, md, 'utf8'); + const doc = await vscode.workspace.openTextDocument(vscode.Uri.file(exportPath)); + await vscode.window.showTextDocument(doc); + vscode.window.showInformationMessage(`ProofCode: Verification report exported to ${exportPath}`); + }); + + // Auto-verify on save if configured + const onSaveDisposable = vscode.workspace.onDidSaveTextDocument(async (doc) => { + if (doc.fileName.includes('.git') || doc.fileName.includes('node_modules')) return; + const config = vscode.workspace.getConfiguration('proofcode'); + if (config.get('autoVerifyOnSave', true)) { + await runVerification(true); + } + }); + + context.subscriptions.push( + verifyCmd, + openDashboardCmd, + initRulesCmd, + openEvidenceCmd, + exportReportCmd, + onSaveDisposable + ); + + // Initial silent run + runVerification(true).catch(() => {}); +} + +export function deactivate(): void { + if (statusBarItem) { + statusBarItem.dispose(); + } + if (diagnosticCollection) { + diagnosticCollection.dispose(); + } +}