
Agentic Salesforce to Snowflake ELT: From One Prompt to a Governed Pipeline
How Bruin CLI, Bruin MCP, Bruin Cloud, and agent skills can build and maintain a Salesforce to Snowflake ELT pipeline across bronze, silver, and gold layers.
How we built a rich, interactive VS Code extension using Vue.js webviews, bridging Node.js extension code with a modern frontend through message passing.

Djamila Baroudi
Software Engineer

If you're a data engineer, your typical day involves constant context switching: writing code in VS Code, running pipelines from the terminal, checking logs in a CLI, and jumping to a browser to visualize lineage or job status. This fragmentation kills productivity and breaks your flow.
Bruin was founded to solve exactly this problem. We believe data teams deserve the same seamless, unified experience that software engineers enjoy, one place to think, build, run, and debug. That's why we created the Bruin VS Code Extension, which brings the full power of the Bruin CLI (asset management, pipeline execution, quality checks) directly into your editor.
Native VS Code UI components work fine for simple status bars or tree views. But for rich experiences like interactive data lineage graphs, live query previews, and asset editors, we needed more power. That's where Webviews powered by Vue.js come in.
A VS Code extension with Vue.js webviews means building two distinct applications that live side-by-side in one repository:
Node.js Extension webview-ui (Vue App)
├── extension.ts ├── package.json
├── commands/ (CLI bridge) ├── vite.config.js
└── panels/ (webview mgr) └── src/
├── components/
└── stores/
The Node.js extension runs in VS Code's host process. It handles workspace context, registers commands, manages panels, and acts as a secure bridge to the local Bruin CLI.
The Vue app runs inside a sandboxed webview (essentially a browser iframe). It renders everything the user sees: lineage graphs, asset details, connections and databases, query results, etc. The app is built with modern tooling: Vue 3, TypeScript, Vite, Pinia, and compiles to static assets that the extension serves.
Since webviews can't directly access Node.js APIs, all communication happens through structured JSON messages using VS Code's postMessage API.
// Webview side - simple intent
import { vscode } from "@/utilities/vscode";
const runPipeline = () => {
vscode.postMessage({
command: "runPipeline",
payload: messagePayload
});
};
// Extension side - handles the real work
webview.onDidReceiveMessage(async (message) => {
if (message.command === "runPipeline") {
const payload = message.payload;
// Execute Bruin CLI and send updates back to UI
const results = await executeBruinCLI(payload);
webview.postMessage({
command: "pipelineStatus",
payload: results
});
}
});
// Vue handles responses reactively
const handleMessage = (event) => {
const message = event.data;
if (message.command === "pipelineStatus") {
const payload = message.payload;
// Update the UI with the payload
}
};
Here's the full round trip when you click "Run Pipeline":
vscode.postMessage({ command: "bruin.runPipeline", payload })--start-date, --end-date, etc.)bruin run in VS Code's integrated terminalwebview.postMessage()
Challenge: Hot reloading during development requires careful setup.
Solution: We use Vite's dev server for the Vue app, but webviews need special handling. The extension serves built assets via webview.asWebviewUri(), so we run vite build --watch during development and manually refresh the webview panel. (more details in the next blog post)
# Development workflow
npm run dev:watch # Runs TypeScript compiler + Vite watch mode concurrently
Challenge: Debugging webview messages requires understanding where logs appear in each context.
Solution: VS Code provides different debugging tools for each part:
Challenge: Large message payloads make the UI feel laggy, and sending messages when webviews aren't ready wastes resources.
Solution: Be mindful about when and how often you send messages:
webview.visible). Don't send until components are mounted and ready to receive dataChallenge: Webview state is lost when panels are hidden or recreated.
Solution: We use Pinia stores for reactive state, and VS Code's webview.getState() / setState() for persistence:
✗ Before: VS Code → Terminal → Browser → Terminal → Browser...
✅ After: Everything happens inside VS Code
Data engineers can now:
Install from VS Code Marketplace
or from the Open VSX Marketplace
We are open to contributions! Please feel free to open an issue or a pull request.
GitHub Repository

How Bruin CLI, Bruin MCP, Bruin Cloud, and agent skills can build and maintain a Salesforce to Snowflake ELT pipeline across bronze, silver, and gold layers.

Most AI data analysts live in Slack or a browser. Bruin runs in WhatsApp too. Here is why field, sales, and ops teams prefer asking their data questions there, what it takes to make it actually work, and how to roll it out safely.
Can you just use ChatGPT, Claude, or a coding agent like Codex to analyze your company data? Here is the honest difference between a general AI model and a purpose-built AI data analyst, why a model alone is not enough, and what it takes to get trustworthy answers from live company data.