# Changelog Source: https://docs.embedder.com/changelog/index Product updates and improvements ## Highlights This week’s updates focus on smoother, more predictable developer workflows and richer file support. You can expect more reliable chat behavior, clearer plan approval controls, faster interaction modes, and improved schematic handling across the CLI and VS Code. ## New features ### Plan execution and approval controls You can now choose how approved plans execute with a Manually or Automatically execution toggle in both the VS Code extension and the CLI TUI, giving you clear control over when actions run. An always-visible approval-mode pill in the composer footer lets you quickly switch between requiring per-action confirmations and auto-approving safety confirmations. ### Schematics and EDIF support Altium and EDIF uploads now surface synthesized KiCad schematics so the viewer shows accurate, geometry-preserving schematics across the CLI and VS Code. Altium files render more consistently, including compatibility with synthesized `.kicad_sch` companions. ### Background shell sessions and task logs Shell tasks can run in the background with output written to per-task log files so you can keep working while long-running commands complete. Task management and log access make it easier to track progress and inspect full outputs without blocking your main session. ## Miscellaneous * Fast mode and new model provider * MCP servers panel in the VS Code side-bar * Conversation experience and modal routing *** ## Highlights This week focused on smoother, more predictable workflows across the CLI and VS Code. Chat navigation and conversation continuity are clearer, TUI and editor rendering are more reliable, and capture workflows gain support for an additional logic analyzer. ## New features ### Chat navigation and conversation continuity You can recall previous prompts in the webview chat with the up/down arrows and confirm before starting a new conversation, so switching contexts is faster and intentional. Running turns are preserved when you navigate or open history, and a per-conversation activity indicator shows when an agent is working, giving clearer feedback while a turn is streaming. ### Saleae Original Logic analyzer support You can now use the budget 24 MHz / 8-channel Saleae-compatible logic analyzer in capture workflows. The device is recognized correctly and captures with appropriate device spec and parameter handling so it behaves like other supported logic analyzers. ## Miscellaneous * Chat, history, and UI layout polish * Terminal and editor client experience * Improved plan view layout to use more horizontal space * Improved startup caching to avoid redundant network fetches * CLI and TUI stability and rendering * Fixed static workspace switcher when there is one team *** ## Highlights This week focused on smoother, more reliable developer and ingestion workflows across the CLI, VS Code extension, and device toolchain. Work improved streaming and session stability, sped up and hardened document ingestion, and made AURIX build and tool discovery more robust for embedded developers. ## Miscellaneous * AURIX build generator improvements * Faster, more reliable PDF ingestion and embedding * Improved AURIX TC4D7 pinout with USB serial console details * Improved auto-selection of the newest installed AURIX Studio * CLI and VS Code stability and streaming improvements * Startup caching improvements *** ## Highlights This week focused on improving the end-to-end experience: faster, more reliable document ingestion for large uploads and smoother chat and editor interactions across the CLI and VS Code. Several reliability fixes also reduce cold-start and runtime interruptions so sessions stay responsive. ## New features ### Faster, more reliable document ingestion PDF uploads now process more quickly and robustly. Large documents are parsed faster, and scanned pages are recognized reliably, reducing latency and preventing silent page drops during parsing. ## Miscellaneous * Smoother chat and editor experience * Device login codes now remain valid for 30 minutes * Improved upload-schematics command visibility and VS Code launch * Improved CLI and VS Code reliability at startup and runtime * Fixed loading indicator on the API keys page * Fixed GitHub reconnect banner when access is revoked *** ## Highlights This week focused on making the IDE and web experiences feel faster and more reliable while improving hardware workflows. You will notice snappier chat navigation, clearer sign-in messaging, and smoother device and debug interactions. ## New features ### Chat and workspace navigation You can switch conversations and tabs instantly with immediate paint and per-tab scroll restore, so large histories no longer interrupt your flow. The history sidebar now hosts open-folder and sign-in flows, conversation-specific schematics open in the correct chat, and permission prompts no longer block other active chats. ### Redesigned subagent tooling Subagent tooling has a redesigned UI that surfaces live status and grouping more clearly, making delegated tasks easier to follow. ## Miscellaneous * Hardware debugging and device workflows * Sign-in and account states * Improved webview behavior and sticky-scroll handling * Improved jump-to-message from timeline chips * Fixed CLI TUI launch crash (spinner) * Fixed animated scroll sweep on chat tab switch * Fixed todo plan completion display for interrupted lists *** ## Highlights This week focuses on making interactive work smoother and more predictable: chat and composer flows are more consistent, and hardware workflows gain reliability and clearer monitoring. File previews, schematics, and markdown rendering received richer in-app viewing so you can inspect design files and documentation without leaving your workspace. ## New features ### Files, schematics, and in-app previews You can view KiCad and Altium schematics with upload and @-mention support, open large PDFs in an in-app viewer, and drag files into chat or mention them with a typeahead to attach file context to messages. Markdown files now render with Mermaid diagrams and syntax highlighting so documentation and diagrams display directly in your workspace. ## Miscellaneous * Chat composer and streaming improvements * Hardware monitoring, workflows, and connectivity * Privacy: require consent for unverifiable origins * Improved sidebar layout and theme options * Improved Oscilloscope tab handling * Fixed agent tool availability when switching modes mid-stream * Fixed nRF52-DK labeling on the hardware card *** ## Highlights This week focuses on polishing the user experience across chat, hardware workflows, and file viewing. Expect smoother live chat streaming and queue controls, richer hardware and monitor interactions, and clearer file previews and plotting when you need them. ## New features ### Hardware workflows and monitor experience You can manage on-hardware workflows with a dedicated hardware queue dock that supports drag-reorder and promote-to-front, and direct UI controls now coordinate with running hardware workflows so connect/disconnect actions behave reliably. Monitor UX is expanded with stacked monitor cards, richer popouts, dedicated tabs including Logic, Power, Debug, and Oscilloscope, and inline build/flash debug controls to streamline hardware iteration. ### File previews, diffs, and plotting Markdown files now render with Mermaid diagrams and syntax highlighting so diagrams and code blocks appear as you expect in previews. File and diff views now feature syntax-highlighted rendering and unified diffs, and plot cards open the full plotting workspace so interactive plots behave the same whether viewed in-card or expanded. ## Miscellaneous * Chat streaming, composer, and prompt queue * Onboarding and startup polish * Improved left sidebar library and theme options * Improved slash command popup keyboard navigation and scrolling * Improved stacked-file cards to show filenames and diff stats * Improved monitor popout cards with inline connect/disconnect controls * Improved composer behavior when editing and resending previous messages * Fixed timescale labeling on scope view when zoomed in *** ## Highlights This week focused on smoother first-time experiences and more reliable client workflows across the product. Improvements to installers and extension startup reduce friction, while updates to the web news feed, terminal tooling, and hardware workflows make everyday tasks faster and more predictable for users. ## New features ### Schematic visualizer and peripheral workflows You can explore schematics in the VS Code extension with a new visualizer and use bundled board pinout skills for several TC397 boards to get faster, contextual hardware information. The add-peripheral flow now validates file types and the custom-component add flow has been unified so adding peripherals and schematics is clearer and more consistent for all users. ## Miscellaneous * Installer and install route improvements * VS Code extension startup and chat performance * News feed reliability and date formatting * Updated YouTube social link * Updated live training link in VS Code extension * Improved onboarding gating on the standalone shell * Sign-in and authentication reliability * Terminal interface and clipboard reliability * Fixed mermaid parse protections in plan view * Fixed add\_peripheral file-type validation for schematic and PDF uploads *** ## Highlights This week focused on smoother product experiences across the VS Code extension, public news content, and hardware workflows. Improvements make onboarding flows more reliable, the extension easier to use for file-based workflows and tutorials, and project and device interactions more resilient while giving users clearer privacy and communications controls. ## New features ### VS Code extension and CLI experience Access step-by-step interactive tutorials and a corrected live‑training link directly from the extension, and follow a one-shot hint explaining Shift+drag file drops so file mentions work reliably. Dragged files resolve to workspace mentions when possible, custom-component uploads are unified into a single flow, and multi-line pastes into the chat preserve the full message content. ### Privacy controls and communications The app now respects Global Privacy Control signals from the browser and exposes a Communications toggle in Account > Profile so users can opt in or out of marketing messages. The unsubscribed state label has been clarified so account settings accurately reflect your choices. ## Miscellaneous * News page reliability and caching * Hardware workflows and project reliability * Improved Infineon board pinout skills integration * Fixed mermaid parse failures in plan view *** ## Highlights This week focused on smoothing developer workflows in the VS Code extension and improving hardware and plotting tools. Conversations, side questions, and todo flows are more interactive and discoverable, while plotting and device tooling now support more reliable RTT, oscilloscope discovery, and attach workflows. ## New features ### VS Code extension experience You can now ask side questions with a lightweight /btw aside that runs separately from the main turn and renders inline, so asides do not interrupt the active conversation. The message queue, todo tracker, history overlay, and chat rendering were all redesigned to make queued messages interactive, keep plans and todos in context across rewinds, and surface conversation history quickly. ### Plotting and hardware tooling Plotting gained math/derived channels so you can create computed data series for more powerful visual analysis, and charts now filter non-finite values so axes render consistently. Hardware tooling now auto-discovers supported oscilloscopes on the LAN, supports agent-driven RTT configuration, and adds an attach mode and run-to-main behavior for GDB helpers so connecting to live devices is faster and avoids unnecessary reflashes. ## Miscellaneous * Improved: tab close behavior is now optimistic for smoother UI * Updated: EMBEDDER.md context is now included automatically with each request * Fixed: math chart y-axis disappearing * Fixed: todo header line overflow * Fixed: defer RTT settings until after user confirmation * Fixed: show backend decline message on invite-accept *** ## Highlights This week focused on smoother, more responsive developer workflows across the VS Code extension, CLI, and hardware tooling. You will notice snappier chat interactions, clearer project UX, and improved debugging and device capture features that make everyday tasks faster and more reliable. ## New features ### Debugging and hardware tooling You can rely on a more robust debug experience: GDB lifecycles are now serialized and include hard-cleanup paths to avoid stuck sessions, and no-halt sessions gain explicit reset-halt handling for J-Link and OpenOCD. Hardware workflows also gained new visualization and capture features, including power plots for PPK2 devices and Joulescope support plus CSV export, making measurement inspection and sharing easier. ### Agent workflows and integrations Agent Teams is available as a first-class execution path with structured plans, deterministic launches, and runtime snapshots, so multi-agent scenarios run more predictably. You can also run the daemon in the background and manage its lifecycle from the CLI, and Slack now supports explicit agent request routing and a convenience /release-notes command to open the release hub. ## Miscellaneous * VS Code chat and UI improvements * Updated /release-notes slash command * Improved project access checks * Improved website messaging and SEO *** ## Highlights This week focused on making automated GitHub workflows more reliable and flexible, improving headless daemon workflows for remote agents, and expanding hardware tooling for embedded development. The result is smoother automation, clearer commits and follow-ups, and richer device tooling and capture visualization for daily developer tasks. ## New features ### GitHub work automation and follow-ups GitHub-triggered automation is more robust and conversational: runs can pause for follow-up questions and resume with preserved local context, support both PR-backed and issue-backed workflows, and continue follow-ups on the same PR instead of starting fresh. Completed work now publishes durable, versioned comments and more descriptive commit messages so automation produces clearer, repeatable results. ### Headless daemon and remote agent mode A new headless daemon mode plus a --daemon connection option let the CLI run autonomously and connect over WebSocket to remote agents. Remote runs behave like local sessions, enabling reliable background automation and remote agent workflows. ### Hardware tooling, flashing, and capture visualization New device workflows make flashing and trace inspection easier: a flash tool runs confirmed flash commands then automatically monitors UART/RTT output, logic analyzer captures are persisted and auto-published for viewing, and STM32 ITM/SWO tracing via OpenOCD is supported. These additions streamline firmware flashing, capture collection, and signal visualization for embedded debugging. ## Miscellaneous * Updated: Signal color scheme across the application * Updated: Website design to include product image * Improved: Schematic request handling in the CLI * Improved: Default auto-confirm configuration for CLI * Improved: Managed Git layout and environment handling * Fixed: preserve DISCONNECTED marker in serial output * Fixed: daemon authentication for GitHub work * Fixed: Python logic analyzer payload serialization *** ## Highlights This week focused on making background work and the developer experience more reliable and visible. You can now run and observe GitHub-linked work from remote agents more safely, and the monitor and editor experiences have been improved to make debugging and onboarding smoother. ## New features ### Daemon GitHub work and remote agent mode You can now run claimed GitHub PR work from a remote agent using the new daemon mode and have that work execute in isolated worktrees for safer checkouts and predictable tool behavior. Git integration and authentication are more reliable, and daemon error reporting now keeps the active work failure visible while retries are in progress, so you can see meaningful status when work is executing or retrying. ## Miscellaneous * Monitor and debug UI improvements * CLI and extension workflow enhancements * Improved Saleae USB detection on Windows * Improved standalone onboarding flow and auth completion * Fixed OpenOCD termination on Windows *** ## Highlights This week focused on making the developer and device experience smoother and more reliable across the VS Code console, the standalone shell, and the CLI. Work centered on faster, more interactive tooling—better streaming for diffs and multiplexed console panes—plus clearer error output and improved installer and onboarding behavior. ## New features ### Resizable multiplexed console tabs You can now arrange console tabs into resizable split panes and drag tabs to split or merge them. This makes it easier to view multiple device consoles or sessions side by side and quickly reorganize your workspace without losing tab state. ### Smooth streaming for file-edit tools File-edit diffs and tool output now stream more smoothly so you see incremental updates with reduced stutter and better ordering. This improves responsiveness when working with high-frequency streaming tools and helps follow changes as they arrive. ## Miscellaneous * Cleaner, more useful Python hardware tracebacks * Smoother install and local dev startup * Standalone shell and sidebar polish with session navigation * Improved terminal minimum-size handling * Improved serial console autoscroll and plotting reconnects * Updated CLI clipboard and paste handling * Improved new-developer onboarding by auto-running embed asset generation * Updated standalone shell to hide quick access chrome when appropriate * Improved CLI startup performance and developer QoL * Improved PPK2 hardware script validation feedback * Fixed VS Code chat tab creation behavior * Fixed Linux serial port detection and port listing * Fixed handling and rendering of serial/hardware script errors for agents *** ## Highlights This week focused on making the standalone app easier to navigate and improving hardware workflows for debugging and measurement. Session navigation and sidebar clarity were refined to help you move between workspaces and conversations quickly, while expanded hardware and telemetry support makes device debugging and measurement more reliable and informative. ## New features ### Standalone sidebar and session navigation You can now track recent cross-session activity from a bounded left sidebar and navigate back and forward through recently viewed sessions for faster context switching. Session list filters and sort controls plus a workspace session view make it easier to find and focus the session you need, and the sidebar has refined layout and typography for clearer scanning. ### Bring-your-own-key (BYOK) API Keys dashboard You can now manage provider API keys directly in the web app when your team has BYOK enabled. The new API Keys page lets you add, replace, and delete your provider API keys, with feature gating so the nav item only appears for enabled teams. ### Auto-Confirm toggle and persistence You can opt in to an Auto-Confirm mode that persists across engine restarts and client sessions, making repeated tool confirmations optional when you choose. The toggle is available end to end and now restores a user's prior setting after initialization so your preference is retained. ## Miscellaneous * Hardware I/O and real-time telemetry * Conversation resume and agent behavior improvements * Standalone layout and built-in extension * Improved debug output timestamps * Improved tool availability checks * Updated memory guidance for the agent * Fixed snapshot checkpointing to be non-blocking * Fixed unexpected logout on web search failures *** ## Highlights This week focused on making conversations feel more reliable and context-aware by improving local memory, resume behavior, and onboarding visibility. At the same time, VS Code chat received several usability refinements so indicators, controls, and input handling behave more predictably during interactive sessions. ## New features ### Conversation memory and context handling You can now keep richer, per-project memory on disk so the assistant retains useful knowledge across sessions while still honoring local overrides. Resume and context rehydration are more robust and faster, with consistent onboarding and startup behavior. ### Debug and hardware development tooling You can use a modern GDB backend for debugging workflows and benefit from SVD-aware register verification that checks register definitions after edits and writes. These additions help the agent make safer, more informed suggestions when working with hardware registers and debug sessions. ## Miscellaneous * VS Code chat UX and input experience * Improved skill discovery for bundled SKILL.md assets * Updated document search wording to report results * Improved product question guidance to use docs web search * Improved streaming tool-call UX and partial input handling * Fixed auto logout on websearch failures * Fixed message queue draining after compression *** ## Highlights This week focused on polishing the interactive experience across the VS Code extension and chat: clearer live-state feedback, more reliable recovery when panels move, and practical workflow improvements for attachments, queues, and serial tooling. The changes are aimed at reducing friction during live interactions and making tool execution and device workflows feel more predictable and responsive. ## New features ### VS Code extension: attachments, queue panel, and peripheral workflows You can attach files directly from the VS Code webview with mention chips and reliable compression handling, and manage queued messages from a new queue panel that lets you view, edit, and delete queued items. Peripheral management and serial monitor improvements make it easier to find and use connected devices, with auto-connect support, a serial output placeholder, and renamable serial tabs for clearer session organization. ## Miscellaneous * Composer and real-time chat indicators * Rewind, undo, and conversation compression resilience * Improved serial toolbar layout with flex-wrap * Updated high-contrast theme padding for better layout responsiveness * Updated /logs command description in the command menu * Improved copy functionality across authentication and error pages * Updated MCP install timeout modal keyboard handling and hints * Updated behavior to close auxiliary agent chat on extension activation * Restore in-flight state across panel moves and reconnects * Tool execution and streaming reliability * Fixed timestamps toggle visibility when hardware timestamps are not detected * Fixed tooltip flash and improved positioning logic * Fixed tooltip remaining visible after closing a tab * Fixed timestamp button scroll restoration to preserve position * Fixed space key selection in question mode on Mac terminals * Fixed VS Code tab naming for single-word messages *** ## Highlights This week focused on making the editor and device experience more resilient and easier to act on. Conversations and in-progress tool work now survive UI moves and reconnects, while device consoles and peripheral selection flows surface more context and open automatically so you can get to the right information faster. ## New features ### Peripheral management and selection Peripheral listing and selection are more discoverable: you can see already-attached peripherals during selection, search and create custom peripherals from the picker, and find the Add a peripheral option up front for faster setup. These changes make it easier to manage hardware attached to your project from both the CLI and VS Code. ## Miscellaneous * Serial monitor and device workflow improvements * Tool execution visibility and reliability * Improved streaming code block scroll behavior * Updated permission menu button colors to match tool status * Updated compression UI state to reflect server-side compression * Updated /logs command description in the webview * Updated /clear to re-sync EMBEDDER.md cache and upload updated context * VS Code chat and streaming recovery * Updated modal keyboard handling for MCP install timeouts * Fixed slash command toggle behavior in the composer * Fixed agent tab naming for short messages *** ## Highlights This week focused on making development and device workflows smoother and more predictable across VS Code and the CLI. Improvements to chat rendering and input keep conversations and code output easy to act on, while expanded debug tooling and serial console behavior make hardware workflows more resilient and faster to recover. New integrations make it easier to extend the agent with external servers and reusable skills. ## New features ### MCP integrations and skill loading You can manage MCP servers from the CLI and extension using the new marketplace UI and /mcp command, including OAuth flows and server catalog views to add and inspect external model context servers. A new skills discovery and loadSkill tool lets the agent load specialized workflows from local skill directories so you can extend agent behavior with companion files and predefined workflows on demand. Add an MCP Server dialog showing searchable list of available servers including Asana, Atlassian, Firecrawl, GitHub, Linear, and Notion ## Miscellaneous * Chat and VS Code extension improvements * Improved @ mention file search parity with TUI * Improved text handling and CSS for webview UI * Improved GDB parsing and gdbStateView integration * Improved terminal diff rendering and modal state handling * Updated composer popup panel background color * Fixed image paste and drag-and-drop bugs * Fixed bash command confirmation disappearing in VS Code * Fixed EPERM handling when creating .embedder on Windows *** ## Highlights This week focused on making developer workflows in the editor and on-device debugging more productive and reliable. Improvements to chat input, rendering, and model discovery help you move from conversation to code faster, while new debugger backends and parsing tools make hardware debug sessions more flexible and easier to understand. ## New features ### VS Code extension: chat input, rendering, and message handling You can queue messages while an agent is responding so your input is never lost and a pending badge shows queued counts. Code and markdown render more accurately with improved syntax highlighting and clickable file links so you can jump from chat output directly into the editor. Composer shortcuts for bash and serial modes plus layout tweaks keep input focused and readable. Embedder VS Code extension displaying a rendered markdown table with ESP32 specs and a queued message indicator ### GDB and probe backends for device debugging OpenOCD is available alongside J-Link as a first-class GDB server backend with automatic selection based on project metadata and probe availability, so your debug tooling picks the right backend for the device. Lifecycle management, startup detection, error handling, and RTT integration make sessions more resilient, and enhanced GDB parsing plus a new gdbStateView tool surface clearer debug state for faster troubleshooting. Embedder CLI running GDB debug tools to inspect processor registers, stack trace, and memory on an nRF52 device ### Mid-stream compression for long agent turns Large or long-running agent turns now compress and resume automatically when context thresholds are reached, allowing conversations to continue smoothly across extended reasoning. This reduces interruptions while preserving relevant context so long tasks remain responsive and actionable. ## Miscellaneous * Improved model information and search * Improved serial baud rate controls * Improved onboarding first-turn context * Fixed model response persistence for multi-step tools * Fixed bash command confirmation in VS Code * Fixed /init prompt visibility in VS Code * Fixed Windows serial DLL startup errors *** ## Highlights This week focused on polishing the developer experience in VS Code and making device workflows more reliable and discoverable. Improvements to message handling, command parity, and serial monitor interactions help you work faster and keep context when switching between editor and device tasks. ## New features ### Serial monitor and device workflows Serial monitor output streams inline with tool calls and the UI has been simplified for clearer device interaction, including a sensible maximum of serial tabs and updated naming so you can manage monitors more easily. [Bash](/core-concepts/common-workflows#bash-mode) (`!`) and [serial](/core-concepts/common-workflows#serial-send-mode) (`~`) input modes are now available from the Embedder console, letting you quickly switch between and send bash and serial commands. Embedder CLI in bash mode showing executed shell commands with output ## Miscellaneous * VS Code chat and command experience * Updated tab loading indicator placement * Improved models menu display in VS Code * Startup and I/O resilience * Fixed extra blank line before agent text * Fixed cancelled tool icon for aborted tool calls *** ## Highlights This week focused on smoothing interactive workflows and device interactions across the CLI and web experience. Improvements to input controls and serial monitoring make everyday tasks faster to perform and easier to verify. ## New features ### Interactive CLI history and controls You can now navigate previously sent prompts with the Up and Down arrow keys, making it faster to recall and reuse earlier commands. History deduplicates consecutive entries and resets on submit so navigation feels natural, and Ctrl+C now reliably exits from intermediate states for more predictable control. Conversation history panel listing past sessions with summaries, last active times, and thread counts ## Miscellaneous * Serial monitor and bash mode * Improved shell tool description for PowerShell on Windows * Improved line truncation and path display in shell tool * Improved toolchain path handling * Fixed CLI crash * Fixed 404 on fresh login *** ## Highlights This week focused on more efficient context handling and refinements to the serial monitor and bash mode, making device interaction smoother and long sessions more reliable. ## New features ### Improved context compression Conversation context now compresses more efficiently, reducing memory use and helping long sessions stay responsive. ## Miscellaneous * Improved shortcut discovery UI * Corrected baud rate detection * Improved agent response accuracy * Fixed GitHub sync issues * Resolved serial monitor artifacts * Fixed Control+W UI bugs *** ## Highlights This week focused on serial monitoring, sub-agents, and documentation context, plus refinements to the CLI and backend services to make device workflows smoother. ## New features ### Multi-device serial monitoring You can now monitor multiple serial ports at once, so you can watch a network of connected devices in real time from a single session. ### Sub-agents You can now delegate scoped tasks to specialized sub-agents, keeping the main conversation focused while work runs in parallel. ### Documentation and context enhancements You can now generate document summaries automatically and include them in context, making it easier to reference documentation within the agent. * Improved CLI handling of large multi-line prompts and scrolling * Updated platform support for Arduino, Raspberry Pi, Infineon, and Nordic * Improved error messages for better clarity * Improved UI across various components * Improved backend performance and efficiency * Fixed serial monitor stability and reliability issues # Add a Peripheral Source: https://docs.embedder.com/core-concepts/add-peripheral Select peripherals from the catalog or upload your own datasheets so Embedder can ground its answers and code generation in your hardware. Peripherals are the external components your project talks to — sensors, displays, flash chips, transceivers, and more. Adding them lets Embedder reference the right datasheets when answering questions and generating drivers. You'll see the peripheral picker during initial project setup, right after you pick a platform. You can reopen it anytime with the `/peripheral` command. ```txt embedder theme={"system"} /peripheral ``` The interface differs slightly between the terminal and the VS Code extension. Pick the one you're using: ## Select peripherals from the catalog In the chat panel, click the **Peripherals: None** button (or **Peripherals: \** if you've already added some) to open the picker. Animated demo of clicking the Peripherals: None button in the VS Code chat panel and the picker opening The picker appears as a card titled **Add peripherals**, and any peripherals you've added before are listed under `Existing:` at the top. Type a part number or name in the **Search peripherals…** input. The list filters as you type and shows the manufacturer alongside each result. Animated demo of typing esp32 into the search box and selecting the matching catalog entry Click a row to toggle the checkbox (☐ → ☑). Or use the keyboard: arrow keys to navigate, **Space** to toggle, **Esc** to cancel. You can select as many as you need. The hint bar at the bottom of the card shows: `↑↓ navigate · Space toggle · Enter confirm · Esc cancel`. Click the **Add** button (it shows the count, for example `Add (3 selected)`), or press **Enter**. The card closes and the peripherals are attached to your project. Animated demo of clicking the Add button on the peripheral picker card to confirm selections ## Add a custom peripheral If your part isn't in the catalog, upload its datasheet. Embedder indexes the document so the agent can cite it directly when you ask hardware questions. At the top of the picker list, click **+ Add peripheral**. The card switches to **Add custom peripheral**. Animated demo of clicking the + Add peripheral row at the top of the picker, switching the card to the Add custom peripheral form Type a name into the input — typically the part number, for example `BME280` or `W25Q128`. Click **+ Browse PDF files** and select one or more PDFs from your computer (datasheet, errata, application notes, schematic exports). Each file appears as a row with a `Click to remove` action if you change your mind. Upload everything you have. More documentation gives Embedder more grounded references when answering questions about that part. Click **Create** (or press +Enter / Ctrl+Enter). The card shows **Creating peripheral · Uploading documents, please wait…** while files upload, then closes once the peripheral is added. Click **Back** at any point to return to the picker without saving. Animated demo of typing a peripheral name, browsing for a PDF datasheet, and clicking Create to add the custom peripheral ## Select peripherals from the catalog SELECT PERIPHERALS dialog with search box showing components from Analog Devices, Nexperia, and Allegro MicroSystems Type a part number or name in the search box (for example, `BME280`, `W25Q128`, `SSD1306`). The list filters as you type, with the manufacturer shown alongside each result. Use the arrow keys to navigate and press **Space** to toggle a peripheral on or off. You can select as many as you need in one pass. The footer shows the active key hints: `Space to toggle · Enter to confirm · Esc to cancel`. Press **Enter** to add your selections to the project. You'll see a confirmation toast like `Added 3 peripheral(s)`. Anything you'd already added is skipped with an `Already added:` notice. ## Add a custom peripheral If your part isn't in the catalog, you can add it yourself by uploading its datasheet. Embedder indexes the document so the agent can cite it directly when you ask hardware questions. From the peripheral selector, navigate to the **Add a peripheral** action (shown as `Create a custom peripheral with documentation`) and press **Enter**. The **Add Custom Peripheral** screen opens. Enter a name in the **Peripheral Name** field — typically the part number (for example, `BME280`, `W25Q128`). Press **Enter** to advance to the file step, or **Tab** to switch fields manually. With the **Documentation & Schematics** field focused, press **Space** or **Enter** to open the file picker. Select one or more PDFs (datasheet, errata, application notes, schematic exports). Selected files appear in a numbered list. Press the corresponding number key (`1`–`9`) to remove a file you didn't mean to include, or press **Space** again to add more. Upload everything you have. More documentation gives Embedder more grounded references when answering questions about that part. When the footer reads `Press Enter to upload and add peripheral →`, press **Enter**. You'll see an upload progress indicator while files are processed. The custom peripheral is added to your project as soon as the upload completes. Custom peripheral support is available on Enterprise plans. If your account doesn't have access, the **Add a peripheral** / **+ Add peripheral** action will surface contact information instead of opening the upload form. Reach out to [sales@embedder.com](mailto:sales@embedder.com) for access. ## Update your peripherals later Reopen the picker any time to add or change components. Already-selected peripherals are filtered out of the catalog list so you only see what you can still add. Run the `/peripheral` command from either the CLI or the VS Code chat input: ```txt embedder theme={"system"} /peripheral ``` In VS Code, you can also click the **Peripherals: \** button in the chat panel — it fires the same command. ## Related * [Quickstart](/quickstart) — full first-session walkthrough * [Common workflows](/core-concepts/common-workflows) — broader project setup and hardware flows * [Slash commands reference](/core-concepts/slash-commands-reference) — every command, including `/peripheral` aliases # Best Practices Source: https://docs.embedder.com/core-concepts/best-practices This page covers proven patterns for getting the best results from Embedder, gathered from engineers using it across a wide range of environments. The most important factor is session length. As a session grows longer, Embedder accumulates conversation history, file contents, and command outputs. When there’s too much accumulated context, Embedder may lose track of earlier instructions, make more errors, or behave inconsistently. **Keeping sessions focused is the single most effective way to maintain accuracy.** Use this guide as a starting point. **Pay attention to what works and what doesn’t.** ## Help Embedder verify its work Include tests or expected outputs so Embedder can check itself. Embedder performs best when it can verify its own work. Without clear success criteria, it may generate solutions that appear correct but fail in practice. In those cases, you become the only source of feedback, and every mistake requires manual review and intervention. | Strategy | Before | After | | ------------------------------------- | -------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Provide verification criteria** | *"implement a function that reads a temperature sensor"* | *"implement read\_temperature(). When the sensor returns raw value 0x0190, the function should return 25.0°C. Include unit tests with mocked I²C reads and verify outputs for \[0x0000 -> 0.0°C, 0x0190 -> 25.0°C, 0x0320 -> 50.0°C]. Tests must pass."* | | **Address root causes, not symptoms** | *"the build is failing"* | *"the build fails with this error: \[paste error]. fix it and verify the build succeeds. address the root cause, don't suppress the error"* | Your verification can take many forms, such as a test suite, a linter, or a Bash command that validates output. Invest the time to make these checks reliable and thorough, since strong verification enables consistent, dependable results. ## Explore and plan, then code Separate research and planning from implementation to improve results. Letting Embedder jump straight to coding can produce code that solves the wrong problem. Use **Plan Mode** to separate research and planning from execution. The recommended workflow has four phases: Enter Plan Mode. Embedder reads files and answers questions without making changes. ```txt Embedder (Plan Mode) theme={"system"} Read /firmware/src and explain how GPIO, UART, and the main loop are initialized. Also check how the board clock and pin configuration are set up. ``` Ask Embedder to create a detailed implementation plan. ```txt Embedder (Plan Mode) theme={"system"} I want to add a feature that blinks an LED at 1 Hz and prints "alive" over UART every second. What files need to change? How should the timer interrupt be configured? Create a step-by-step implementation plan and list tests to verify it works on hardware. ``` When Embedder is done creating a plan, you can either approve it and continue with execution or give it feedback on what to change. You can manually edit the plan by going to `.embedder/plans` in your projects directory. Switch back to Act Mode and let Embedder code, verifying against its plan. ```txt Embedder (Act Mode) theme={"system"} Implement the LED blink + UART heartbeat from your plan. Configure a hardware timer interrupt. Add a small test or debug log to confirm the ISR fires every 1 second. Build the firmware and fix any compile errors. ``` Ask Embedder to commit with a descriptive message and create a PR. ```txt Embedder (Act Mode) theme={"system"} Commit with a descriptive message and open a PR. ``` Plan Mode can improve outcomes, but it is not always necessary and can slow down simple work. When the task is straightforward and limited in scope, such as correcting a small bug, adding a debug statement, or making a minor rename, it is usually faster to execute the change immediately. A dedicated planning step is most effective for more complex situations, especially when the solution is unclear, the update touches several parts of the codebase, or you are working with unfamiliar code. ## Provide specific context in prompts The more precise your instructions, the fewer corrections you'll need. Embedder can often infer your intent, but it does not have implicit knowledge of your goals or assumptions. Be explicit and precise in your instructions. Reference the exact files to modify, state any constraints or requirements, and link to relevant examples or patterns to follow. | Strategy | Before | After | | ------------------------------------------------------------------------------------------------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Scope the task.** Specify which file, what scenario, and testing preferences. | *"Add tests for uart.c"* | *"Write a hardware-level test for drivers/uart.c that verifies RX buffer overflow handling. Simulate 256+ bytes arriving back-to-back and assert no memory corruption. Avoid mocks; use existing HAL test harness."* | | **Point to sources.** Direct Embedder to the source that can answer a question. | *"Why is the SPI driver so complicated?"* | *"Read drivers/spi.c and its git history and summarize how the SPI driver has evolved. Explain any hardware constraints that influenced the design."* | | **Reference existing patterns.** Point Embedder to patterns in your codebase. | *"Add an I2C sensor driver"* | *"Look at how drivers/adc.c and drivers/uart.c structure init/config/read functions. Follow the same pattern to implement drivers/i2c\_temp\_sensor.c. Match naming, error handling, and ISR style. Don’t introduce new frameworks."* | | **Describe the symptom.** Provide the symptom, the likely location, and what "fixed" looks like. | *"Fix the LED bug"* | *"The status LED randomly stops blinking after \~10 minutes. Likely related to the timer ISR in src/timer.c. Reproduce with a long-running loop, add logging to confirm missed interrupts, then fix so the LED maintains a stable 1 Hz blink for 30+ minutes."* | Vague prompts can actually be quite useful when you are exploring or are open to suggestions. Asking "What bugs are in this file?" may surface things you wouldn't have otherwise found. ### Providing context You can provide rich data to Embedder in several ways: * **Reference files with @** instead of describing where code lives. * **Reference documentation** in your project workspace. * **Give URLs** for documentation and online references. ## Setup your environment A small amount of upfront setup can make Embedder far more effective in every session. Taking the time to configure your environment and provide the right context helps the agent work more accurately, consistently, and efficiently from the start. ### Writing an `EMBEDDER.md` file Run `/init` to generate a starter EMBEDDER.md file based on your current project structure. Embedder automatically reads an `EMBEDDER.md` file at the beginning of every session. Use this file to define important details such as common bash commands, coding conventions, and workflow guidelines. This provides persistent context that cannot be reliably inferred from the codebase alone. The `/init` command scans your project to identify build systems, test frameworks, and recurring patterns. This creates a strong starting point that you can then customize and refine. There is no strict structure required for `EMBEDDER.md`, but it should remain concise and easy to understand. Aim for clear, human readable instructions rather than lengthy documentation. ```markdown Embedder.md theme={"system"} # Code style - Follow the project’s C or C++ style guide and keep functions small and single purpose - Use consistent naming for peripherals and drivers such as uart_init, spi_write, adc_read - Avoid dynamic allocation in firmware. Prefer static or stack memory # Workflow - Always build the firmware after changes and fix all compiler warnings - Flash and verify on real hardware or simulation before marking tasks complete - Run targeted unit tests or hardware specific tests instead of the full suite for faster iteration ``` `EMBEDDER.md` is loaded at the start of every session, so only include information that applies broadly across your entire project. Keep the file short and focused. For each line, ask yourself, “If this were missing, would Embedder make mistakes?” If the answer is no, remove it. An oversized or cluttered `EMBEDDER.md` dilutes the important guidance and makes it more likely that Embedder overlooks your actual instructions. | Include | Exclude | | ------------------------------------------------ | ------------------------------------------------- | | Commands Embedder can't guess | Anything Embedder can see in code or docs | | Unique code style rules | Standard language conventions | | Testing instructions and preferred test runners | Detailed API documentation (link to docs instead) | | Repository etiquette | Information that changes frequently | | Developer environment quirks (required env vars) | File-by-file descriptions of the codebase | | Common gotchas or non-obvious behaviors | Self-evident practices like "write clean code" | If Embedder keeps doing something you’ve explicitly prohibited, your `EMBEDDER.md` is probably too long and the rule is getting buried. If it asks questions that are already answered in the file, the phrasing is likely ambiguous or unclear. Treat `EMBEDDER.md` like code: review it when behavior goes wrong, regularly prune unnecessary or redundant lines, and test changes to confirm that Embedder’s behavior actually shifts. You can also improve adherence by adding clear emphasis such as “IMPORTANT” or “YOU MUST.” Check the file into git so your team can contribute, since small improvements compound over time and make the document increasingly valuable. ### Provide documentation Run `/peripheral` to change what peripheral documentation is associated with your project Many platforms and peripherals have pre-indexed documentation. If you find Embedder is getting things wrong about your board or peripherals, consider uploading supplemental documentation via the web console. If you've added your own platform or peripheral, be sure to upload sufficient and up to date documentation. You can add more documentation for your platform or peripherals by using the `/console` command and uploading more documentation in the projects section of the web app. The `/console` command lets you upload additional documentation to existing platforms and peripherals in your project. ### Use CLI tools Tell Embedder to use tools like `gh`, `west`, or `openocd` when interacting with external services. The most efficient way to interact with external services is through CLI tools. If you use GitHub, install the `gh` CLI. Embedder knows how to use it for creating issues, opening pull requests, and reading comments. Without `gh`, Embedder can still use the GitHub API, but unauthenticated requests often hit rate limits. Embedder is also great at learning CLI tools it doesn't already know. Try prompts like `Use 'cli-tool --help' to learn about more about the cli tool, then use it to solve A, B, C.` ### Ask questions about your codebase Ask Embedder questions you'd ask a staff or senior engineer. When onboarding to a new codebase, use Embedder for learning and exploration. You can ask Embedder questions you would ask another engineer: * How does error logging work? * How do I read data from the sensor used in this project? * What does `handle_sample()` do on line 134 of `foo.c`? * What edge cases does `TIMER_CTRL` handle? * Why does this code call `foo1()` instead of `foo2()` on line 173? This can significantly speed up onboarding and reduce the load on other engineers. ## Manage your session Conversations are persistent, and the changes made during them can be reversed. ### Course-correct early and often Correct Embedder early and often. Embedder performs best with a tight feedback loop. Correcting Embedder quickly produces better solutions at a faster pace. * **`Esc`**: Stop Embedder mid-action with the `Esc` key. Context is preserved, so you can redirect. * `Ctrl + z (2x)` **or** `/rewind`: Press `Ctrl + z (2x)` or run `/rewind` to open the rewind menu and restore previous conversation and code state. * **`Ctrl + z or /undo`**: Press `Ctrl + z` or run `/undo` to have Embedder revert its most recent change. * **`/clear`**: Reset context between unrelated tasks. Long sessions with irrelevant context can reduce performance. If you've corrected Embedder more than twice on the same issue in one session, the context is cluttered with failed approaches. Run `/clear` and start fresh with a more specific prompt that incorporates what you learned. A clean session with a better prompt almost always outperforms a long session with accumulated corrections. ### Manage context aggressively Run `/clear` between unrelated tasks to reset context. Embedder automatically compresses conversation history during long sessions, preserving important code and decisions while freeing space. During longer sessions, Embedder's context window can fill with irrelevant information. This reduces performance and sometimes distracts Embedder. * Use `/clear` frequently between tasks to reset the context window entirely * When auto compression triggers, Embedder summarizes what matters most, including code patterns, file states, and key decisions * For more control, you can run `/compress` to choose when compression occurs * Customize compression behavior in `EMBEDDER.md` with instructions like `"When compressing, make sure to keep track of the full list of modified files"` to make sure any important context survives summarization ### Use subagents for investigation Delegate research with `"use subagents to investigate X"`. They explore in a separate context, keeping your main conversation clean for implementation. Subagents are one of the most effective tools for keeping sessions focused. When Embedder researches a codebase, it reads many files, which can slow performance in long sessions. Subagents complete the task in a separate context window and report back summaries: ``` Use subagents to investigate how our firmware initializes the CAN peripheral, handles message TX/RX interrupts, and whether we already have reusable CAN drivers I should build on instead of writing new code. ``` The subagent explores the codebase, reads relevant files, and reports back with findings, all without cluttering your main conversation. ### Rewind to previous checkpoints Every action Embedder makes creates a checkpoint. You can restore the conversation and code to a previous state. Embedder automatically checkpoints before changes. Run `/undo` to undo the last change or `/rewind` to open the checkpoint menu. Instead of carefully planning all your moves, you can tell Embedder to try something and if it doesn't work, rewind and try it another way. Checkpoints will *only* track changes made *by Embedder*. ### Resume conversations Run `/history` to pick up a conversation where you left off. Embedder keeps a copy of conversations locally. When a task spans multiple sessions (you start a feature, get interrupted, come back the next day) you don't have to re-explain the context. ## Avoid pitfalls These are some helpful tips to avoid common mistakes. * **Run** `/clear` **between unrelated tasks.** Otherwise, you may bloat your context window with lots of irrelevant information. * **Run** `/clear` **after failures.** If Embedder does something wrong, after two or three failed corrections run `/clear` and write a better initial prompt incorporating what you learned, as context will be polluted with failed approaches. * **Add additional documentation.** If Embedder seems to be misunderstanding aspects of your hardware. Consider uploading additional, or replacing current, documentation. * **Prune** `EMBEDDER.md` **often.** If `EMBEDDER.md` is too long, Embedder will ignore much of it as the important rules will get lost in the noise. The less guidelines in `EMBEDDER.md` the more focused Embedder will be on upholding those guidelines. * **Scope tasks and use subagents.** If you ask Embedder to "explore" something and don't scope it down, Embedder can end up filling its context rapidly. Scope tasks or use subagents to preserve context. # Common Workflows Source: https://docs.embedder.com/core-concepts/common-workflows Practical workflows for setting up projects, interacting with hardware, writing firmware, debugging, and managing sessions. Each section includes example prompts you can adapt. ## Start a new project ```txt embedder theme={"system"} /project ``` SELECT PROJECT dialog with search box showing create new project button and previous projects list Choose an existing project or create a new one. Embedder will prompt you to create a new project automatically if no project is associated with the directory you run it in, so note that running `/project` is not always needed. Select the platform you're using from our catalog, or add your own. SELECT PLATFORM dialog with search box showing nRF9xxx platforms from Nordic Semiconductor To add your own platform, type the name of the platform and press the `+ Add "[platform name]"` button. You will then be prompted to upload your documentation. SELECT PLATFORM dialog showing menu to upload documentation in pdf form See [Add a Peripheral](/core-concepts/add-peripheral) for the full walkthrough on selecting from the catalog and uploading custom peripherals with their datasheets. ```txt embedder theme={"system"} /init ``` Creates an `EMBEDDER.md` file with your projects context, configuration, and more. For devices not included in the catalogue, upload your own documentation or datasheets via `/peripheral` or through the web console (`/console`) to improve the agent's performance. ## Hardware interaction ### Use the serial monitor The serial terminal is integrated directly into Embedder. The AI reads serial output automatically. **Open the serial sidebar:** ```txt Embedder theme={"system"} /serial ``` Or use the keyboard shortcut: `` Ctrl+` `` (backtick) **Auto-connect behavior:** By default, Embedder auto-connects to your device after flashing To disable auto-connect, update your `EMBEDDER.md` or prompt: ```txt Embedder theme={"system"} > don't auto-connect to serial after flashing ``` **Filter serial output:** Click `[Filter]` in the serial toolbar to filter output by regex pattern. Useful for isolating specific log messages or errors from noisy output. Serial terminal filter showing regex pattern filtering output ## Firmware development ### Write peripheral drivers Embedder has context from your datasheets and documentation. Ask it to look up pinouts, register maps, and driver implementation details when developing drivers. ```txt Embedder theme={"system"} > write an I2C driver for the BME280 sensor on I2C1 (SDA=PB7, SCL=PB6). > include functions for reading temperature, humidity, and pressure. > use the existing HAL_I2C functions in our codebase. ``` ```txt Embedder theme={"system"} > create a UART driver for GPS module on UART2 at 9600 baud. > implement a ring buffer for RX, parse NMEA sentences, and extract lat/long coordinates. ``` ```txt Embedder theme={"system"} > implement SPI driver for the SD card module on SPI1. > include initialization, single block read/write, and proper CS pin handling on PA4. ``` ### Build and flash Embedder uses your project configuration to determine the required toolchains and dependencies. ```txt Embedder theme={"system"} > help me install the dependencies for this project ``` ```txt Embedder theme={"system"} > build the project ``` ```txt Embedder theme={"system"} > flash the firmware to the device ``` ```txt Embedder theme={"system"} > build and flash, then show me the serial output ``` ### Work with HALs and SDKs Ask about vendor-specific HALs and SDKs: ```txt Embedder theme={"system"} > how do I configure the STM32 HAL for I2C? ``` ```txt Embedder theme={"system"} > show me how to use the ESP-IDF GPIO driver ``` ```txt Embedder theme={"system"} > what's the correct way to initialize DMA on this chip? ``` ## Debug with serial output Embedder reads serial output and can help analyze issues in real time. ### Analyze errors ```txt Embedder theme={"system"} > what's causing this error in the serial output? ``` ```txt Embedder theme={"system"} > the device keeps resetting, analyze the crash dump ``` ```txt Embedder theme={"system"} > parse the debug logs and find the issue ``` ### Trace execution ```txt Embedder theme={"system"} > add debug prints to trace the initialization sequence ``` ```txt Embedder theme={"system"} > why isn't the interrupt handler being called? ``` ## Plan mode vs Act mode Embedder has two modes for different workflows. ### Act mode (default) Full tool access. Embedder can read, write, and execute. Use for: * Writing and modifying code * Building and flashing firmware * Making changes to your project ### Plan mode Read-only analysis. Embedder researches without making changes. Use for: * Planning complex refactors * Reviewing architecture before changes * Analyzing datasheets **Toggle between modes:** * Keyboard: `Shift+Tab` * Commands: `/plan` or `/act` **Start in plan mode:** ```txt Embedder theme={"system"} > /plan > analyze the power management implementation and suggest improvements ``` Plan mode is ideal for planning complex changes before implementation. ## Documentation and context ### Leverage uploaded documentation Platforms and peripherals chosen from our catalog already have documentation uploaded. You can upload more documents via the web console which can be accessed using `/console`. Web app showing a place to upload documentation for your project Embedder will use them automatically: ```txt Embedder theme={"system"} > what's the maximum I2C clock speed for this sensor? ``` ```txt Embedder theme={"system"} > show me the register map for the accelerometer ``` ```txt Embedder theme={"system"} > what are the power consumption specs in sleep mode? ``` ### Hardware-aware assistance Ask hardware-specific questions about your board: ```txt Embedder theme={"system"} > what switch position on the stm32n6570-dk enables flash mode? ``` ```txt Embedder theme={"system"} > what's the pin mapping for the Arduino header? ``` ```txt Embedder theme={"system"} > can I use DMA with this UART peripheral? ``` ## Bash mode Bypass the AI agent and execute terminal commands directly using the `!` prefix. ```bash theme={"system"} !ls -la ``` ```bash theme={"system"} !make clean ``` ```bash theme={"system"} !git status ``` This runs the command in your terminal without AI interpretation. Useful for quick commands when you don't need AI assistance. ## Serial send mode Send messages directly to your MCU over serial using the `~` prefix. ```txt embedder theme={"system"} ~help ``` ```txt embedder theme={"system"} ~reset ``` ```txt embedder theme={"system"} ~get status ``` This sends the text directly to your device's serial console. Use for: * Sending AT commands * Interacting with device CLI/REPL * Manual debugging commands ## Reference files and directories Use `@` to quickly include files or directories in your prompts. ```txt Embedder theme={"system"} > explain the logic in @src/main.c ``` ```txt Embedder theme={"system"} > what does @drivers/i2c.h do? ``` ```txt Embedder theme={"system"} > what's in @src/drivers/? ``` ```txt Embedder theme={"system"} > show me the structure of @lib/ ``` ```txt Embedder theme={"system"} > compare @src/old_driver.c and @src/new_driver.c ``` Hold `Shift` and drag any file from your workspace sidebar into the Embedder prompt to reference it automatically — no need to type the path. Dragging a file from the workspace sidebar into Embedder while holding Shift You can also drag files in from Finder the same way — just hold `Shift` while dropping them into the prompt. Dragging a file from Finder into Embedder while holding Shift File paths can be relative or absolute. Directory references show file listings, not contents. ## Session management ### Switch projects ```txt Embedder theme={"system"} /project # Switch to a different project ``` ### Resume previous sessions ```txt Embedder theme={"system"} /history # Browse and resume past conversations ``` ### Manage context For long conversations: ```txt Embedder theme={"system"} /compress # Compress conversation to save context ``` ## Quick reference | Workflow | Command/Shortcut | | ------------------- | ------------------------------------ | | New project setup | `/project` → `/peripheral` → `/init` | | Serial monitor | `/serial` or `` Ctrl+` `` | | Switch mode | `Shift+Tab` or `/plan` `/act` | | Add peripheral | `/peripheral` | | Reference file | `@filename` in prompt | | Bash mode | `!command` (e.g., `!ls`) | | Serial send | `~message` (e.g., `~help`) | | Undo last message | `/undo` or `Ctrl+Z` | | Rewind conversation | `/rewind` or `Ctrl+Z (2x)` | | Resume session | `/history` | | Compress context | `/compress` | | Web console | `/console` | | Get help | `/help` | # .embedderignore Source: https://docs.embedder.com/core-concepts/embedderignore Mark files and directories that Embedder can read but must not edit. Use `.embedderignore` to mark files and directories that Embedder is allowed to read but not allowed to edit. The agent can still inspect matched files for context (for example, vendored SDKs, generated headers, or upstream HALs), but any tool call that tries to write to a matched path is blocked, and the agent is told to ask for your permission before continuing. `.embedderignore` is an edit-restriction file, not a read- or visibility-restriction file. To hide a path from the agent entirely, keep it outside the project root. ## Configuring `.embedderignore` Create a `.embedderignore` file at the root of your project and list the paths and patterns you want to protect. ```text .embedderignore theme={"system"} # Vendor SDKs and HALs (read-only) vendor/ hal/ # Generated headers and build output generated/ build/ *.bin # Files we never want auto-edited .platformio/ config/secrets.* ``` You can also drop `.embedderignore` files into subdirectories. Embedder loads every `.embedderignore` from the project root down to the target file's parent directory, with closer files overriding rules from shallower ones (the same precedence as `.gitignore`). This is useful for monorepos where each package declares its own protected paths. ## Pattern syntax `.embedderignore` follows the same rules as `.gitignore`: | Pattern | Matches | | ------------- | ------------------------------------------------------------- | | `file.c` | A file with that name at any depth | | `dist/` | A directory and everything inside it | | `*.bin` | Files matching the glob in any directory | | `**/build` | A directory named `build` at any depth | | `src/**/*.h` | All `.h` files anywhere under `src/` | | `!app/main.c` | Re-includes a path that an earlier pattern would have matched | | `# comment` | A comment line, ignored | * `*` matches any sequence of characters except `/`. * `**` matches across directory boundaries. * `?` matches a single character. * `!` negates an earlier pattern (re-includes the path). * Trailing spaces are stripped unless escaped with `\`. * Matching is case-insensitive on macOS and Windows by default (matching the typical filesystem behavior) and case-sensitive on Linux. ## Examples ```text Firmware project theme={"system"} # Vendor code stays read-only vendor/ hal/ third_party/ # Generated outputs build/ generated/ *.elf *.bin *.hex ``` ```text Mixed project theme={"system"} # Vendored libraries node_modules/ third_party/ # Build artifacts dist/ build/ # Secrets config/secrets.* .env.local ``` ## What happens when a path is matched When the agent calls `writeFile` or `editFile` on a path that matches a pattern in `.embedderignore`: 1. The tool call returns a failure (no confirmation dialog is shown). 2. The agent receives a message naming the matched path and instructing it to stop and ask you for explicit permission before making any further edits in that scope. 3. You decide whether to grant permission for a one-off edit or remove the entry from `.embedderignore`. The agent-facing message looks like this: ```text theme={"system"} Editing `vendor/foo.c` is blocked by `.embedderignore`. Stop and ask the user for explicit permission before making any further edits to paths under this match. The user can either grant permission or remove the entry from `.embedderignore`. ``` ### What is and isn't blocked | Surface | Blocked? | | ---------------------------------------------------- | -------- | | `writeFile` | Yes | | `editFile` | Yes | | Shell writes detected in `bash` commands (see below) | Yes | | `readFile`, `grep`, `listDirectory`, `glob`, etc. | No | Read, search, and listing tools are intentionally unaffected so the agent can still use matched files for context. #### Shell write detection For `bash` commands, Embedder scans the command for known write patterns and gates each detected target against `.embedderignore`. Detected forms include: * Output redirection: `>` and `>>` (any file descriptor). * `tee `. * `sed -i ` (in-place edits). * `cp`, `mv`, `ln` (the destination argument). * `dd of=`. * `rm`, `rmdir`, `unlink`, `touch`, `mkdir`. If any target in a single command matches `.embedderignore`, the entire command is blocked and the agent gets a single failure message listing every blocked path. Shell-write detection is best-effort. Writes performed indirectly (custom scripts, `make` targets, language-level file I/O like `python -c "open(...)"`, or utilities not in the list above) can still hit ignored paths. Treat `.embedderignore` as a strong default for direct edits, not a sandbox. ## Reloading rules Embedder caches each parsed `.embedderignore` and reloads it automatically when the file's inode, size, or modification time changes. You don't need to restart the session after editing or adding `.embedderignore` files. ## Negation and overrides `!` re-includes a path that an earlier pattern would have matched, but with the same caveat as gitignore: if a parent directory is excluded with a directory pattern (`dir/`), files inside it cannot be re-included with `!` from the same file. Two reliable ways to selectively allow a child of an ignored directory: 1. **Use a glob parent pattern** so the directory itself isn't excluded: ```text theme={"system"} config/* !config/public.json ``` 2. **Drop a nested `.embedderignore`** in the protected directory and negate from there. The closer file overrides the parent. ```text .embedderignore (root) theme={"system"} secrets/ ``` ```text secrets/.embedderignore theme={"system"} !public.json ``` With both files, edits to `secrets/public.json` are allowed while `secrets/tokens.json` remains blocked. ## Troubleshooting * **Edits still go through on a path I expected to block.** Confirm the file is at or below the project root and the pattern matches. `git check-ignore -v ` is a quick way to verify gitignore-style matching. If the write came from a shell command, check whether it used a form covered by [shell write detection](#shell-write-detection); indirect writes (scripts, `make`, language-level I/O) aren't intercepted. * **A pattern inside an ignored directory has no effect.** Embedder doesn't descend into matched directories. Move the pattern to a parent location. * **I want to allow a one-off edit.** When the agent surfaces the blocked-edit message in chat, reply with explicit permission to proceed, or remove the entry from `.embedderignore`. # Keyboard Shortcuts Source: https://docs.embedder.com/core-concepts/keyboard-shortcuts All available keyboard shortcuts, organized by context. Run `/help` to see this list in Embedder. ## Global Shortcuts | Shortcut | Mac | Action | | ------------------- | ------------------- | ------------------------------ | | `Ctrl+R` | `Cmd+R` | Clear conversation / New chat | | ``Ctrl+` `` | ``Cmd+` `` | Toggle serial terminal sidebar | | `Ctrl+Alt+B` | `Cmd+Option+B` | Switch AI model | | `Shift+Tab` | `Shift+Tab` | Toggle plan/act mode | | `Ctrl+Z` | `Cmd+Z` | Undo last message | | `Ctrl+Z` (2x) | `Cmd+Z` (2x) | Open rewind interface | | `Ctrl+C` (2x) | `Ctrl+C` (2x) | Exit application | | `Ctrl+C` / `Escape` | `Ctrl+C` / `Escape` | Interrupt agent | | `/` | `/` | Open command suggestions | Use `Escape` to stop Embedder when you see it going in the wrong direction. Context is preserved, so you can immediately course-correct. ## Chat Input | Shortcut | Action | | ------------- | --------------------------------------- | | `Return` | Submit message | | `Ctrl+Return` | Insert newline | | `Up` / `Down` | Navigate suggestions | | `Tab` | Complete command / drill into directory | | `Escape` | Cancel suggestions | | `@` | Trigger file autocomplete | | `!` | Enter bash mode | | `~` | Enter send serial command mode | Use `@` to specifically cite files you'd like Embedder to interact with. ## Navigation (Emacs Mode) Emacs mode is the default navigation mode. | Shortcut | Action | | --------------------- | -------------- | | `Ctrl+N` / `Down` | Next item | | `Ctrl+P` / `Up` | Previous item | | `Ctrl+F` / `Right` | Forward | | `Ctrl+B` / `Left` | Back | | `Ctrl+G` / `Escape` | Cancel | | `Return` | Select item | | `Space` | Toggle item | | `Ctrl+V` / `PageDown` | Page down | | `Alt+V` / `PageUp` | Page up | | `Ctrl+A` / `Home` | Jump to top | | `Ctrl+E` / `End` | Jump to bottom | | `Tab` | Tab navigation | ## Navigation (Vim Mode) | Shortcut | Action | | --------------------- | -------------- | | `J` / `Down` | Next item | | `K` / `Up` | Previous item | | `L` / `Right` | Forward | | `H` / `Left` | Back | | `Escape` | Cancel | | `Return` | Select item | | `Space` | Toggle item | | `Ctrl+D` / `PageDown` | Page down | | `Ctrl+U` / `PageUp` | Page up | | `Home` | Jump to top | | `Shift+G` / `End` | Jump to bottom | | `Tab` | Tab navigation | ## Switching Keybinding Modes Switch between Emacs and Vim navigation modes using `/keybindings` or `/kb`. Preferences are saved to `~/.embedder/keybindings.json`. Keybinding changes take effect immediately without restarting Embedder. # Slash Commands Reference Source: https://docs.embedder.com/core-concepts/slash-commands-reference Slash commands are shortcuts that trigger specific Embedder behaviors. Type a command at the prompt and press `Enter` to execute it. Press `/` to open command suggestions. ## Session & Navigation | Command | Aliases | Description | | ----------- | --------------------------- | ------------------------------------- | | `/clear` | `/reset`, `/new` | Clear conversation history | | `/history` | `/resume`, `/conversations` | View conversation history | | `/rewind` | - | Rewind to a previous state | | `/undo` | - | Undo the last message | | `/compress` | `/summarize` | Compress conversation to save context | | `/exit` | `/quit`, `/q` | Exit the application | Run `/clear` frequently between tasks. A clean context almost always outperforms a cluttered one. ## Mode & Model | Command | Aliases | Description | | ------- | ------- | ------------------- | | `/plan` | - | Switch to plan mode | | `/act` | - | Switch to act mode | ## Project & Team | Command | Aliases | Description | | ------------- | --------------------------------- | ---------------------------------------- | | `/project` | `/switch-project` | Switch project | | `/peripheral` | `/peripherals`, `/add-peripheral` | Add peripherals to project | | `/init` | - | Create EMBEDDER.md with project contexts | | `/tasks` | `/todos`, `/plan` | View current session task list | Run `/init` in your new projects. ## Tools & Utilities | Command | Aliases | Description | | ---------- | ------- | ------------------------------ | | `/serial` | - | Toggle serial terminal sidebar | | `/console` | - | Open web console | | `/billing` | - | Open billing portal | | `/logs` | - | Open logs folder | ## Preferences & Help | Command | Aliases | Description | | -------------- | -------------- | ------------------------------------------------------------------ | | `/theme` | `/t` | Change color theme (opens selector, or pass a theme name directly) | | `/keybindings` | `/keys`, `/kb` | Change keybinding mode (emacs/vim) | | `/help` | `/h`, `/?` | Show documentation and commands | | `/welcome` | - | Show the welcome animation | You can choose separate light and dark themes. ## Account | Command | Aliases | Description | | -------------------- | ------------------- | ---------------------------- | | `/logout` | `/signout` | Sign out and return to login | | `/bug [description]` | `/report`, `/issue` | Report a bug | ## Usage Examples ```txt embedder theme={null} theme={"system"} /theme dracula # Set theme to dracula /bug App crashes on X # Report a bug with description /t nord # Short alias for theme ``` Most commands have shorter aliases. Use `/help` to see all available shortcuts. # Combined workflows Source: https://docs.embedder.com/debug-mode/combined-workflows Prompts that ask Embedder to use multiple instruments together Hardware bugs rarely respect tool boundaries. A current spike happens *because of* a firmware code path. An I²C NACK looks fine on the protocol decoder *until* you scope the rise time. In debug mode, Embedder can mix GDB, the logic analyzer, the scope, and the power analyzer in a single script — and you can ask for it in plain language. This page shows three example multi-tool prompts and what the agent does for each. ## Scenario 1: Sleep-current regression on a BLE board **Symptom:** sleep current rose from 8 µA to 380 µA after a recent commit. **Prompt:** ```txt embedder theme={"system"} > sleep current jumped from 8 µA to ~380 µA after my last commit. firmware drives GPIO0 high during sleep and logs "enter_sleep"/"wake" over the UART that's wired to the Joulescope's GPI1. trigger a capture on the GPI0 sleep window, decode the UART in parallel, and read NRF_RTC0 and the wakeup-source variable mid-capture without resetting the chip. ``` **What the agent does:** 1. Triggers a Joulescope capture only during the sleep window using the firmware's GPI0 marker. 2. Uses the JS220's hardware UART decoder on GPI1 to decode the DUT's log lines in parallel — `enter_sleep` / `wake` markers will line up with the current trace. 3. Connects GDB non-intrusively mid-capture so it can read the RTC peripheral and `g_last_wakeup_source` without disturbing the sleep state. 4. Reports avg sleep current, the decoded UART bytes, and the register values. **What to look for:** if the UART line shows `enter_sleep` *during* the high-current window, a peripheral didn't power down. If `g_last_wakeup_source` is unexpected, an interrupt is firing repeatedly. ## Scenario 2: Intermittent I²C NACK during sensor bring-up **Symptom:** the sensor returns valid data 95% of the time, NACKs the rest. Pure software state inspection didn't reveal anything. **Prompt:** ```txt embedder theme={"system"} > the BME280 NACKs about 5% of the time. start a manual I²C capture on SDA=ch0, SCL=ch1. set a GDB breakpoint on HAL_I2C_ErrorCallback. when the breakpoint hits, dump the I²C error register and the last few I²C frames so I can see which transaction failed. ``` **What the agent does:** 1. Connects GDB and arms the breakpoint. 2. Starts a manual logic-analyzer capture with an I²C decoder. 3. Continues execution and waits. 4. When the error fires, reads the error register, exports the analyzer table, and saves the capture file. 5. Reports the failing transaction byte and links to the saved capture. **What to look for:** the I²C frame just before the breakpoint hit. Address NACK at the start → wrong slave address. NACK mid-write → likely clock-stretch timeout; escalate to the [oscilloscope](/debug-mode/oscilloscope) to check rise time. ## Scenario 3: Brown-out under motor load **Symptom:** the MCU resets when the motor PWM kicks in. You suspect a rail droop, but it could also be a software fault. **Prompt:** ```txt embedder theme={"system"} > the chip resets when I enable the motor PWM. probe VDD on the SDS812X channel 1, trigger on a falling edge below 2.8V, capture for 500ms. capture PWM and Hall on Saleae channels 0–2 at the same time. after the reset, attach without halting and read SCB->CFSR and SCB->HFSR so I can tell if it was brown-out or a software fault. ``` **What the agent does:** 1. Arms a single-shot droop trigger on the Siglent and captures the rail. 2. Captures PWM and Hall sensor lines on the Saleae in parallel. 3. After the fault, connects GDB non-intrusively and reads `SCB->CFSR` (`0xE000ED28`) and `SCB->HFSR` (`0xE000ED2C`). 4. Reports the rail trace, the digital capture path, and the decoded fault registers. **What to look for:** if VDD drops below the MCU's BOR threshold during the PWM transition, it's a hardware brown-out — bigger bulk cap or stiffer rail. If VDD stays clean and `CFSR` shows a `UsageFault` (`UNDEFINSTR`, `UNALIGNED`), it's software — set a breakpoint on the fault handler and read the stacked PC. ## Tips for multi-tool prompts * **Mention every observable you want.** "Capture I²C and read the error register" is much clearer than "debug the I²C bus" — the agent doesn't have to guess what counts as success. * **Mention the trigger.** If you want the capture to start on a GPIO edge, a GDB breakpoint, or a timed window, say so. Otherwise the agent picks a default. * **Say "without resetting"** if you don't want the agent to flash or reset mid-investigation. The non-intrusive attach pattern keeps the chip running. * **Order matters when describing tools.** "Set a breakpoint, then start the capture" is sequential; "start the capture, then continue execution" sets up monitoring before the action. ## What makes multi-tool workflows possible Behind the scenes, debug mode wires a few primitives together that make it natural to chain instruments: * **One Python prelude per script** — every connected instrument's helpers are available without imports. * **Shared capture timeline** — Logic and Power tabs publish from one coordinator, so simultaneous captures share a timebase in the Console panel (VS Code extension and standalone app). * **Joulescope GPI triggers** (`gpi0`–`gpi3`) — capture only the window of interest by firmware-driven GPIO edges. * **JS220 hardware UART decoder** — DUT log lines decoded *in parallel* with current sampling. * **Digilent external triggers** (`external1`–`external4`, `t1`, `t2`) — slave a Digilent logic capture to an external GPIO edge. * **Non-intrusive GDB attach** — register snapshots without resetting a live target. You don't need to know which primitive applies — describing the symptom and the observables you want is enough. The agent picks the combination. ## When to ask for a combined workflow Reach for multi-tool prompts when: * You need temporal correlation across instruments (current event ↔ log line ↔ register value). * The bug is intermittent and you want one trigger to freeze every observable at once. * The hypothesis spans the hardware / software boundary — e.g. "is this a brown-out *or* a software fault?". Stay with a single tool when you only need one observable, or when the probe is fast enough that running tools serially is fine. ## Next steps Back to the OHPV loop. General tips for keeping sessions focused. # GDB Source: https://docs.embedder.com/debug-mode/gdb Ask Embedder to drive GDB over J-Link or OpenOCD to set breakpoints, inspect registers, and snapshot a running target In debug mode, Embedder can drive GDB on your behalf to set breakpoints, inspect registers, walk an RTOS task list, or snapshot a live target. You don't need to write `.gdbinit` files or pick a backend — describe the symptom and the agent picks the right approach. ## Prerequisites Run `/debug` or click the Debug button. GDB tools are unavailable in act and plan modes. Embedder supports J-Link (Segger) and OpenOCD (ST-Link, CMSIS-DAP, DAPLink, NXP LPC-Link2 / MCU-Link). Add this line to your project's `EMBEDDER.md`: ```md EMBEDDER.md theme={"system"} Debug Interface = jlink ``` Valid values: `jlink`, `st-link`, `cmsis-dap`, `serial`. If missing, Embedder uses whichever probe it detects first. Embedder needs an ELF file. Make sure your build is up to date — most prompts implicitly assume `build/firmware.elf` (or whatever path your `EMBEDDER.md` `build_command` produces). On first use, Embedder downloads a managed `arm-none-eabi-gdb-py3` toolchain to `~/.embedder/tools/gdb/`. If `hardware_status` reports `managedGdb.state` as `installing`, wait for it to finish before prompting for GDB. ## Example prompts **The device hangs and you don't know where:** ```txt embedder theme={"system"} > the device isn't logging anything — use gdb to figure out where it's hanging ``` The agent attaches non-intrusively, reads the program counter, walks the call stack, and tells you which function it stopped in. Typing a debug-mode prompt in the Embedder composer Embedder running a hardware script that attaches via GDB and reports the stopped frame **Inspect a specific function:** ```txt embedder theme={"system"} > set a breakpoint at uart_init, run, and dump the registers when it hits ``` **Diagnose a hard fault:** ```txt embedder theme={"system"} > the chip is hard-faulting on boot — read CFSR, HFSR, and the stacked PC ``` The agent reads the System Control Block fault registers and decodes which fault fired (UsageFault, BusFault, etc.) plus the offending instruction's address. **Walk an RTOS task list:** ```txt embedder theme={"system"} > list all FreeRTOS tasks and show me which one is blocked ``` Works for FreeRTOS and Zephyr — the agent surfaces task names and the function each one is currently in. **Snapshot a running target without resetting it:** ```txt embedder theme={"system"} > attach to the chip without resetting it, read g_uptime_ms and g_last_fault_code ``` This is the "non-intrusive attach" pattern — useful for inspecting a long-running device, motor state mid-run, or fault registers after a hang. The chip keeps running. **Read a memory-mapped peripheral register:** ```txt embedder theme={"system"} > read RCC->CR (0x40023800) and tell me which clock sources are running ``` ## What the agent does for you When you prompt for GDB in debug mode, Embedder writes a Python script under `.embedder/hardware/` that connects via J-Link or OpenOCD, performs the inspection, and returns structured results to the chat. You'll see the script in the tool call before it runs — you can stop or edit it if needed. By default it does **not** flash, reset, or erase the chip unless you explicitly ask. If you need a fresh start, say "flash the firmware first" or "reset the chip and break at main". ## Common gotchas Make sure J-Link software is installed (Embedder offers to install it on debug mode entry if missing). On macOS, plug the probe in *before* starting Embedder so USB enumeration completes. J-Link wants device names like `nRF52840_xxAA`. If the agent picks the wrong one, set it explicitly in your prompt: "use device nRF52840\_xxAA" or list it in `EMBEDDER.md` under `Target MCU`. Set `Target MCU = stm32f407vg` (or the closest match) in `EMBEDDER.md`. Embedder uses this to pick the OpenOCD target file. If your chip isn't a stock OpenOCD target, you may need to provide your own `target.cfg` and reference it in `EMBEDDER.md`. Build with `-Og` or `-O0` for the parts you're inspecting. With `-O2` / `-Os`, GDB often can't recover local variables. Say "attach without resetting" or "non-intrusive attach". Combine it with "no halt" if you want the chip to keep running while the agent reads state. ## Next steps Pair GDB with logic captures and power profiling. Back to the OHPV loop and other instruments. # Logic analyzer Source: https://docs.embedder.com/debug-mode/logic-analyzer Ask Embedder to capture and decode SPI, I²C, UART, CAN, USB, and 20 other digital protocols In debug mode, Embedder can drive a Saleae or Digilent logic analyzer to capture digital signals, decode them as protocol frames, and surface the results in the **Logic tab** of the Console panel (VS Code extension and standalone app). Describe what you want to see — "capture I²C", "show me the SPI frames" — and the agent handles the rest. Logic analyzer support is in **beta**. Both vendors are supported through one agnostic API; the prompts you write are the same either way. ## Prerequisites Run `/debug` or click the Debug button. Install Saleae Logic 2 and enable the Automation API: 1. Open Logic 2. 2. Click the **Automation** button at the bottom right. 3. In the popup, toggle **Automation Server** to **ON**. Toggling the Automation Server on in Logic 2 Embedder installs the `logic2-automation` Python package automatically on first use. Install Digilent WaveForms. Embedder installs `dwfpy` automatically on first use. The same Analog Discovery 3 also serves as an [oscilloscope](/debug-mode/oscilloscope). Connect digital channels to your DUT signals. Note which channel index goes to which signal — you'll mention them in your prompt. Match the analyzer's threshold to your DUT's logic level (Saleae supports `0.0`, `1.2`, `1.8`, `3.3` V; AD3 has a fixed threshold). 1.8 V signals at a 3.3 V threshold capture nothing. * **Saleae:** Logic 2 must be **running** with the Automation API enabled — Embedder talks to the live app over port 10430. Don't quit Logic 2 before prompting. * **Digilent:** WaveForms must be **closed**. The AD3 only accepts one process at a time; if WaveForms has the device open, Embedder's `dwfpy` will fail to claim it. ## Example prompts **Capture and decode I²C:** ```txt embedder theme={"system"} > capture 2 seconds of I²C on SDA=channel 0, SCL=channel 1, decode it, and show me the addresses ``` Embedder running a Saleae I²C capture and surfacing decoded frames **Capture SPI without setting it up:** ```txt embedder theme={"system"} > the SPI flash isn't responding — capture the bus and tell me what's happening ``` If the agent doesn't know your wiring, it'll ask. Include channel assignments in your prompt to skip the question. **UART at a non-standard baud:** ```txt embedder theme={"system"} > capture UART on channel 0 at 230400 baud for 5 seconds and decode it ``` **Trigger on an event (Digilent only):** ```txt embedder theme={"system"} > wait for a rising edge on channel 4, then capture I²C for 1 second ``` Saleae captures are timed only — for cross-tool triggers, see [Combined workflows](/debug-mode/combined-workflows). **Catch an intermittent bug:** ```txt embedder theme={"system"} > start a manual capture of CAN, I'll let it run while I reproduce the fault ``` The agent starts an open-ended capture and waits. When you say "stop the capture and decode it", it finalizes and exports. **Decode a less-common protocol:** ```txt embedder theme={"system"} > decode the LIN bus on channel 0 at 19200 baud, version 2.x ``` ## Supported protocols Saleae offers 23 native decoders; Digilent supports a subset of 11. Most common protocols work on both: | Protocol | Saleae | Digilent | Common options to mention | | ------------------------------------------------------------------------------------------------- | ------ | -------- | ----------------------------------------- | | UART | ✓ | ✓ | bit rate, parity, stop bits | | SPI | ✓ | ✓ | bits per transfer | | I²C | ✓ | ✓ | 7-bit / 10-bit address mode | | CAN | ✓ | ✓ | bit rate (default 500 kbit/s on Digilent) | | LIN | ✓ | ✓ | bit rate, LIN version | | 1-Wire | ✓ | ✓ | — | | I²S / PCM | ✓ | ✓ | — | | Manchester | ✓ | ✓ | bit rate, edge polarity | | JTAG | ✓ | ✓ | — | | Modbus | ✓ | ✓ | — | | SWD | ✓ | — | — | | USB | ✓ | — | — | | HD44780, MIDI, MDIO, SMBus, BiSS-C, DMX512, HDLC, HDMI-CEC, PS/2, Synchronous Parallel, Atmel SWI | ✓ | — | — | If your protocol's options aren't standard, mention them in the prompt: "decode UART at 9600 baud, 7 bits, even parity, 2 stop bits". ## What the agent does for you The agent writes a Python script that captures the requested channels, attaches a protocol decoder, and exports CSVs (decoded frames in hex) plus a native capture file. Captures auto-publish to the **Logic tab** of the Console panel (VS Code extension and standalone app) with waveform + decoded frames side by side, and persist under `.embedder/captures///` so you can come back to them later. TUI users get the same persisted files but no interactive chart. ## Common gotchas Check the threshold. Capturing 1.8 V signals at a 3.3 V threshold (default) gives a flat zero. On Saleae, set it explicitly: "use a 1.8V threshold". On AD3, use a level shifter. Wrong baud / clock / bits. UART defaults to 115200 8N1; SPI defaults to 8 bits per transfer. Mention the actual settings in your prompt. The capture helper auto-snaps unsupported rates to the nearest valid rate and warns. If you need more bandwidth, ask: "use the maximum supported sample rate for this device". For Saleae, Logic 2 must be running with the Automation Server toggled on (Automation button in the bottom right). For Digilent, the WaveForms desktop app must be closed — the AD3 only accepts one process at a time. ## Next steps Probe analog signal integrity when digital decode looks fine. A PicoScope MSO captures digital and analog on the same USB device. Trigger a logic capture from a GDB breakpoint or GPIO edge. # Oscilloscope Source: https://docs.embedder.com/debug-mode/oscilloscope Ask Embedder to capture analog signals — clock, reset, rails, comm lines — over LAN/SCPI on a Siglent SDS800X HD In debug mode, Embedder can drive a Siglent SDS800X HD bench scope over LAN to capture analog waveforms. Supported models: SDS802X HD, SDS804X HD, SDS812X HD, SDS814X HD, SDS822X HD, SDS824X HD. Captures auto-publish to the **Power tab** of the Console panel (VS Code extension and standalone app). Oscilloscope support is in **beta**. The SDS800X HD family is fully supported. Other LXI / SCPI scopes may work for some features but aren't fully verified — expect rough edges. ## What you can probe | Signal | What you're checking | | ---------------------- | ------------------------------------------------- | | Crystal / clock output | Oscillator startup, frequency, ringing | | `nRESET` | Reset pulse width, edge integrity | | VDD / 3V3 rail | Droop under load, ripple | | UART TX/RX | Idle level, edge slew, glitches | | SPI `SCK` / `CS` | Clock integrity at high rates | | I²C `SDA` / `SCL` | Pull-up strength, rise time | | PWM | Duty cycle, frequency, glitches | | GPIO debug pin | Interrupt latency (firmware toggles a pin in ISR) | ## Prerequisites Run `/debug`. Embedder installs `pyvisa` and `pyvisa-py` automatically the first time you run a scope script — approve the install when prompted. Connect the scope to the same LAN as your host (Ethernet or Wi-Fi). Embedder browses mDNS (`_lxi._tcp` / `_vxi-11._tcp` / `_scpi-raw._tcp`) on every non-internal IPv4 NIC, so any LXI-compliant scope shows up in `hardware_status` under `metadata.discoveredScopes` with its `vendor`, `model`, `serial`, `firmware`, `ip`, and `hostname`. If `discoveredScopes` is empty (corporate Wi-Fi, AP isolation, mDNS blocked), set the IP directly: ```bash theme={"system"} export EMBEDDER_SCOPE_HOST=192.168.1.20 ``` Read the scope's IP from the front panel under **Utility → Menu → I/O → LAN Config** (DHCP is on by default). Quick reachability check: open `http://` in a browser and you should see the scope's built-in LXI status page. Not sure which probe goes where? Ask Embedder: User asking Embedder how to wire the Siglent scope, agent walks through the channel and ground clip Connecting a scope probe tip and ground clip to a board test point ## Example prompts **Single-shot capture on an edge trigger:** ```txt embedder theme={"system"} > connect to the SDS812X HD on the LAN, set channel 1 to 500mV/div DC, timebase 1ms/div, trigger on rising C1 at 1.5V, single-shot capture, and tell me the rise time ``` The agent arms a single-shot trigger, blocks until it fires, and reads the frozen acquisition. Rise time comes back via the scope's built-in measurement. Embedder running a single-shot capture on the Siglent SDS812X HD and publishing the trace to the Power tab **Screenshot the scope display:** ```txt embedder theme={"system"} > grab a BMP screenshot of the current scope display and save it under /tmp/scope.bmp ``` **Check rail integrity under load:** ```txt embedder theme={"system"} > probe VDD while I enable the radio peripheral. is the rail clean? ``` The agent records a high-rate analog trace, looks for droop or ringing, and reports min / max / avg. **Measure interrupt latency:** ```txt embedder theme={"system"} > i toggle a GPIO at the start of EXTI0_IRQHandler. trigger on the rising edge of channel 1, capture both the trigger line and the GPIO, and tell me the latency. ``` You'll need a debug GPIO toggle in your ISR; the agent computes the time delta from the captured trace. **Verify SPI clock at high rate:** ```txt embedder theme={"system"} > capture SPI SCK on the SDS812X with the maximum memory depth — is the edge clean? are there reflections? ``` Use this when the [logic analyzer](/debug-mode/logic-analyzer) is decoding garbage — signal-integrity failures look fine in the digital decode but show up clearly on the scope. The 12-bit ADC and longer memory depth surface ringing and overshoot that lower-resolution captures miss. **Drop to raw SCPI:** ```txt embedder theme={"system"} > on the SDS812X, set memory depth to 100K with `:ACQ:MDEP 100K`, then run rise/fall measurements on channel 1 ``` Reach for raw SCPI when the helper layer doesn't cover what you need — the Siglent SDS Series Programming Guide (EN11G) is the full API reference. ## What the agent does for you The agent writes a Python script that opens a SCPI session over LAN, configures channels and timebase, arms the trigger, captures the waveform, and publishes the result to the Power tab. The chart is shared with power-analyzer traces, so you can correlate analog scope captures with current measurements on the same timeline. ## Common gotchas Check that the scope is on the same LAN as your host and that you can open `http://` in a browser. If mDNS is blocked (corporate Wi-Fi, AP isolation), set `EMBEDDER_SCOPE_HOST=` and re-run `hardware_status`. If `discoveredScopes` shows the scope but `reachable: false`, follow the `suggestedFix` (typically a subnet alias like `sudo ifconfig alias /`). A single-shot capture times out after 5 s by default. Check the trigger source, level, and coupling, or ask the agent to use a longer timeout: "wait up to 30 seconds for the trigger". V/div is too coarse for the signal — the trace runs into the rails. Ask the agent to "drop V/div to 200 mV" or whatever fits. The opposite (V/div too fine) shows a flat-looking line because the signal is way bigger than the visible window. Some helpers are tuned for the SDS800X HD's specifics. On other LXI / SCPI scopes those helpers may misparse data or fail — fall back to raw `scope_query` / `scope_write` calls and the vendor's programming guide, or stick to a verified SDS800X HD model for full coverage. ## Next steps Prefer a USB scope? PicoScope runs the same capture prompts over USB. Decode digital protocols with a Saleae or Digilent. Mix scope, logic, and GDB in one debug session. # Debug Mode Source: https://docs.embedder.com/debug-mode/overview Switch to a hardware-aware mode where Embedder can drive a debugger and bench instruments to diagnose your firmware Debug mode is a dedicated agent mode for diagnosing real hardware problems. You stay in plain language — describe what's wrong, ask for what you want to know — and Embedder uses GDB, logic analyzers, oscilloscopes, and power analyzers to investigate. Use it when you have a board on the bench and a problem you can't pin down from source alone: an MCU that hangs, an I²C bus that NACKs intermittently, sleep current that's 10× expected, a clock that won't lock. ## Enter debug mode ```txt embedder theme={"system"} /debug ``` Press `Shift+Tab` to cycle Act → Plan → Debug. Works in the terminal TUI, the VS Code extension, and the standalone app. Click the **Debug** option in the mode pill at the top of the composer. Embedder TUI cycling Act → Plan → Debug with Shift+Tab On entry, Embedder checks for a connected J-Link probe (and offers to install J-Link software if missing), runs `hardware_status` to list every connected instrument, and auto-loads the helper docs for each one. Python helpers for power analyzers (`ppk2-api`, `joulescope`) and logic analyzers (`logic2-automation`, `dwfpy`) are installed on first use, not at mode entry — you'll get a confirmation prompt the first time the agent tries to run a script for a given device. Always enter debug mode *before* asking the agent to probe. Outside of debug mode, the GDB and instrument tools are unavailable and the agent can't run them. ## How to prompt in debug mode Describe the symptom in natural language. Embedder picks the right instrument and writes the script for you. ```txt embedder theme={"system"} > the device isn't logging anything — use gdb to figure out where it's hanging ``` ```txt embedder theme={"system"} > capture 2 seconds of I²C on the BME280 and show me the addresses ``` ```txt embedder theme={"system"} > measure sleep current on this board at 3.3V for 5 seconds ``` ```txt embedder theme={"system"} > the chip resets when the motor PWM kicks in — find out why ``` You don't need to name the tool. If you say "is this clock signal clean?" the agent reaches for the scope; "capture UART" reaches for the logic analyzer. Naming the tool ("use gdb", "use the Joulescope") narrows the choice when there's ambiguity. ## What the agent does behind the scenes For every problem, Embedder follows a structured **Observe → Hypothesize → Probe → Verify** loop: Reads the `hardware_status` snapshot, recent serial / RTT history, current GDB state, and the source files involved. Lists candidate root causes and prioritizes them by likelihood and ease of verification. Writes and runs a Python script under `.embedder/hardware/` that drives the chosen instruments. Checks the probe results against each hypothesis. Either narrows the next probe or reports the root cause. ## Sub-guides Inspect register state, set breakpoints, walk RTOS task lists, snapshot a live target without resetting it. Capture and decode SPI, I²C, UART, CAN, USB, and 18 other digital protocols. Profile sleep current, boot energy, and active draw with PPK2 or Joulescope. Probe analog signals — clock, reset, power rails, comm lines. Drive a USB PicoScope as a scope, logic analyzer, and signal generator in one box. Worked scenarios that need multiple instruments — sleep-current regression, intermittent I²C NACK, brown-out under load. ## Tips * Re-prompt with "refresh hardware status" if you plug in a new instrument mid-session — the agent only auto-detects on entry. * The agent prefers non-destructive probes by default. If you want it to flash firmware, reset the chip, or erase memory, say so explicitly. * Sessions in debug mode block `submitWork` — you'll need to leave debug mode (or end the session) to mark the work as done. ## Availability Debug mode is enabled in standard Embedder builds. Some enterprise builds (e.g. the `infineon` distribution) ship with debug mode disabled — in those cases the `/debug` slash command is hidden and the agent will report "Debug mode unavailable" if asked to switch. # PicoScope Source: https://docs.embedder.com/debug-mode/picoscope Ask Embedder to drive a USB PicoScope as an oscilloscope, logic analyzer, and signal generator — one device, auto-detected over USB In debug mode, Embedder can drive a USB **PicoScope** — one device that acts as an oscilloscope, a mixed-signal logic analyzer, and a signal generator. The prompts you write are the same as for a bench scope or a Saleae: describe the measurement, and Embedder picks the right role. Analog captures publish to the **Power tab** and digital captures to the **Logic tab** of the Console panel (VS Code extension and standalone app). Reach for a PicoScope when you want scope-grade analog capture without a LAN bench instrument — it's USB-powered, and MSO models fold digital capture and an arbitrary waveform generator into the same box. ## Prerequisites Run `/debug` or click the Debug button. The instrument tools are unavailable in act and plan modes. Plug the scope into the host over USB — it's USB-powered, no bench supply or LAN needed. The PicoScope USB device is **single-client**. Quit the PicoScope desktop app before capturing, or Embedder can't claim the device. Install **PicoScope 7** or the **PicoSDK** from [picotech.com/downloads](https://www.picotech.com/downloads): * **macOS / Windows:** PicoScope 7 T\&M Instruments * **Linux:** the `libps*` packages from Pico's apt repo One install covers the whole PicoScope range. Everything else is automatic — when a PicoScope is detected, Embedder offers to finish the setup for you (*"A PicoScope is connected. Do you want to install PicoScope support now?"*) and confirms the drivers are in place. Run `hardware_status`. A ready PicoScope shows up under the `picoscope` provider with its model and series. * **Analog:** BNC scope probes to inputs A–D. Coupling is **DC or AC only** — PicoScope inputs have no GND coupling. * **Digital (MSO models):** the D0–D15 flying leads to your DUT signals. Note which channel goes where — you'll mention them in your prompt. * **Signal generator:** the AWG / Gen output to the node you want to stimulate. Full scale is **±4 × the per-division scale**. A 0–3.3 V logic line needs a 1 V/div scale (±4 V) — at 0.5 V/div (±2 V) it clips flat at the range limit. Using a **×10 probe**? Say so ("I've got a ×10 probe on channel A") — a PicoScope can't read the probe's switch position, so the agent scales the range and trigger levels for you. ## Supported models Every PicoScope series works, and the scope is auto-detected when you plug it in. MSO models add digital capture on top of analog. | PicoScope series | Logic capture (MSO) | Notes | | ------------------------ | ------------------- | --------------------------------------------------------------------------------------------------------- | | 2000A/B (e.g. 2205A MSO) | ✓ | | | 2000 / 3000 (legacy) | — | Name the series in your prompt — legacy scopes can't be auto-detected. No signal generator on legacy 3000 | | 3000A/B/D | ✓ | | | 3000E / 5000E | — | | | 4000 / 4000A | — | | | 5000 (legacy) | — | | | 5000A/B/D | ✓ | Flexible resolution, 8–16 bit | | 6000 / 6000C/D | — | | | 6000E | ✓ | | The **Logic capture (MSO)** column marks series that offer digital pods. Your specific unit still has to be an MSO variant with the pods fitted — a non-MSO scope in the same series captures analog only. ## Example prompts Describe the symptom or the measurement — Embedder picks the analog, digital, or generator path and writes the script. **Analog capture on an edge trigger:** ```txt embedder theme={"system"} > connect to the PicoScope, set channel A to 1V/div DC, timebase 1ms/div, trigger on a rising edge of A at 1.5V, capture, and tell me the rise time ``` The agent configures the channel and timebase (both snap to the scope's discrete ranges), arms the trigger, waits for it to fire, and reports the measurement. Because the range and rate are quantized, it reports the **achieved** sample rate, not the request. **Digital capture on an MSO model:** ```txt embedder theme={"system"} > capture D0, D1, and D2 for 5ms at 5 MSa/s with a 1.65V threshold and show me the transitions ``` Digital thresholds are set per 8-channel port (D0–D7, D8–D15), so a 3.3 V bus captures cleanly at a 1.65 V threshold. **Stimulus and response with the built-in generator:** ```txt embedder theme={"system"} > generate a 1kHz sine at 1V amplitude on the AWG output, then capture channel A and tell me the measured frequency ``` The generator runs independently of capture, so drive-then-measure is a single-device flow. Functions: sine, square, triangle, ramp up/down, and DC. Output limits vary by model (the 2205A MSO is ±2 V). **Connect a legacy scope:** ```txt embedder theme={"system"} > my scope is a legacy PicoScope 2000 — connect to it and capture channel A for 10ms ``` Legacy 2000 / 3000 scopes can't be auto-detected — name the series so the agent connects directly. **Fit a longer window into the buffer:** ```txt embedder theme={"system"} > capture channel A for 200ms — use whatever sample rate keeps it inside the buffer ``` The capture buffer is shared across every enabled channel and digital port (about 48 kS on a 2205A MSO), so long windows need a lower sample rate or fewer channels. The agent resolves the trade-off and tells you what it picked. ## How it differs from a bench oscilloscope If you've used Embedder's [LAN/SCPI oscilloscope](/debug-mode/oscilloscope), the same prompts work — with a few USB-specific differences: | | PicoScope (USB) | Bench scope (LAN/SCPI) | | ----------- | --------------------------------------- | -------------------------------- | | Connection | USB, auto-detected — no host or IP | LAN, needs the scope's IP / mDNS | | Power | USB-powered | Wall-powered | | Screenshot | — (no display) | ✓ (grab the scope screen) | | Coupling | DC / AC | DC / AC / GND | | Bonus roles | Logic analyzer (MSO) + signal generator | Analog only | There's no screenshot on a PicoScope because there's no front-panel display — every capture comes back as an interactive chart in the Console panel instead. ## What the agent does for you The agent writes a Python script under `.embedder/hardware/` that opens the PicoScope over USB, configures channels, timebase, and trigger (or the digital ports, or the signal generator), runs the capture, and publishes the result — analog traces to the **Power tab**, digital traces to the **Logic tab**. You'll see the script in the tool call before it runs, and you can stop or edit it if needed. ## Common gotchas Check the USB cable — the scope is USB-powered, so a charge-only cable won't enumerate it. Quit the PicoScope desktop app (the USB device is single-client), then re-run `hardware_status`. Pico's drivers probably aren't installed — the setup prompt reports this. Install PicoScope 7 or the PicoSDK from [picotech.com/downloads](https://www.picotech.com/downloads), then retry. The V/div scale is too small — full scale is only ±4 × scale. A 3.3 V line needs 1 V/div; at 0.5 V/div it runs into the range limit. Ask the agent to widen the range. A ×10 probe is switched to ×10 but the capture assumed ×1. Tell the agent "I've got a ×10 probe on channel A" so it corrects the range, reported volts, and trigger levels. The shared capture buffer clamped the window rather than failing. Lower the sample rate, shorten the window, or disable unused channels and ports to free buffer. Pico ships the legacy 3000 / 4000 / 5000 / 6000 series drivers as Intel-only. On Apple Silicon, use an Intel Mac, Windows, or Linux for these older scopes. ## Next steps Compare with the LAN/SCPI bench scope — screenshots and GND coupling. Protocol decoding with a Saleae or Digilent when you need decoded frames, not raw transitions. Mix the PicoScope with GDB and power profiling in one debug session. Back to the Observe → Hypothesize → Probe → Verify loop. # Power analyzer Source: https://docs.embedder.com/debug-mode/power-analyzer Ask Embedder to measure sleep current, boot energy, or active draw with a Nordic PPK2 or Jetperch Joulescope In debug mode, Embedder can drive a Nordic PPK2 or Jetperch Joulescope to measure DUT current, voltage, and energy. Captures auto-publish to the **Power tab** of the Console panel (VS Code extension and standalone app) with µA / mA / A and energy stats. The two devices cover different needs: | | PPK2 | Joulescope | | -------------------- | ----------------------------- | -------------------------------------------- | | Powers the DUT | ✓ (source meter, 800–5000 mV) | — (pass-through only, needs external supply) | | Cheap | ✓ | — | | Wider current range | — | ✓ (down to nA, up to 10 A) | | Higher sample rate | 100 kHz | up to 2 MS/s | | Hardware UART decode | — | ✓ (JS220 only) | | JLS recording | — | ✓ | ## Prerequisites Run `/debug`. Embedder's preflight detects PPK2 / Joulescope and walks you through Python / package install if needed. Embedder installs `ppk2-api` (GPL-2.0) or `joulescope` (Apache-2.0) automatically the first time you run a script for that device. Approve when prompted. Wiring depends on the device — see the per-device sections below. ## PPK2 wiring The PPK2 has a physical mode switch. Set it before connecting: | Position | Use when | | ---------------- | ----------------------------------------------------- | | **Source meter** | The PPK2 powers your DUT (800–5000 mV). | | **Ampere meter** | The DUT has its own supply; the PPK2 measures inline. | * PPK2 `VOUT+` → DUT VDD * PPK2 `GND` → DUT GND On Nordic DK boards, remove the **nRF CURRENT** jumper to disconnect the on-board supply, then connect PPK2 in its place. Plug the jumper back in before using serial, RTT, or GDB through the on-board debugger — the debug chip loses power without it. * External supply (+) → PPK2 `VIN` * PPK2 `VOUT+` → DUT VDD * GND common Same Nordic DK jumper warning applies. ## Joulescope wiring JS110 and JS220 use different binding posts. Check the front panel or ask Embedder ("what Joulescope model is connected?"): * Bench supply (+) → `IN+` * Bench supply (−) → `IN-` * `OUT+` → DUT VCC * `OUT-` → DUT GND * Bench supply (+) → `I+` **and** `V+` (tie these together with a short jumper — without it, voltage reads as floating) * Bench supply (−) → `V-` * `I-` → DUT VCC * DUT GND → `V-` Use this for low-impedance or high-current measurements where wire resistance matters. * Bench supply (+) → `I+` * `I-` → DUT VCC * `V+` → DUT VCC sense point (directly on the DUT) * `V-` → DUT GND sense point * Bench supply (−) → DUT GND See JS220 User's Guide §9.4 for diagrams. The FP02-BNA front panel relabels JS220 binding posts as JS110-style `IN+` / `IN-` / `OUT+` / `OUT-` — use the JS110 wiring above. The Joulescope is **pass-through only** — it cannot supply power. You always need an external bench supply. If you're not sure which posts go where, ask Embedder — it'll walk you through the wiring for your specific model. User asking Embedder for Joulescope wiring help, agent responds with the per-model wiring steps ## Example prompts **Measure sleep current with a PPK2:** ```txt embedder theme={"system"} > measure DUT current at 3.3V for 5 seconds in source meter mode ``` The agent reports avg / min / max in µA, plus a chart in the Power tab. **Profile boot with a Joulescope:** ```txt embedder theme={"system"} > profile the first 3 seconds of boot — i want to see where the current spikes ``` The chart shows the full transient, plus avg and peak current. Pass `>60s` durations and the agent will switch to streaming or JLS recording automatically. **Capture only a sleep window:** ```txt embedder theme={"system"} > the firmware drives GPI0 high while it sleeps. trigger a Joulescope capture on rising GPI0, end on falling, and report avg µA ``` This is the JS220 GPI-trigger pattern — only the sleep window is captured, ignoring boot and wake transitions. **Decode UART alongside the power trace (JS220 only):** ```txt embedder theme={"system"} > hook GPI1 to the DUT TX pin at 115200 baud. measure 5 seconds of current and tell me which log lines correspond to the high-current windows. ``` The JS220 FPGA decodes the UART in parallel with current sampling — perfect for correlating "enter\_sleep" and "wake" log lines with the actual transitions. **Multi-phase test:** ```txt embedder theme={"system"} > measure idle current for 2s, then trigger an LTE attach, measure another 5s. report charge for each phase separately. ``` **Long capture with raw samples:** ```txt embedder theme={"system"} > record 30 seconds of current at full sample rate to a JLS file so I can analyze it offline ``` JLS files open in the Joulescope desktop UI for advanced analysis. ## What the agent does for you The agent picks PPK2 or Joulescope helpers based on which device is connected, writes the script under `.embedder/hardware/`, and surfaces the result both as a structured JSON payload (avg / min / max / charge / energy) and as an interactive chart in the Power tab. The Power tab decimates with min / max windowing — peaks and transients are preserved, not averaged out. ## Common gotchas Source meter mode: did you ask the agent to power on the DUT? (`measure` does this for you.) Ampere meter mode: is the external supply actually on? On JS220 in 2-wire mode, did you tie `V+` to `I+`? Without that jumper, the voltage input floats. On Nordic DK boards in PPK2 ampere meter mode, the **nRF CURRENT** jumper must be plugged back in before using serial, RTT, or GDB through the on-board debugger. `joulescope_measure` buffers in RAM. For longer captures, ask for "streaming" (running stats only) or "JLS recording" (raw samples to disk). The 10 A range is rated for 3 A continuous; bursts up to 10 A are fine for under 50 ms. For higher continuous loads, use a different instrument. ## Next steps Snapshot register state during a power capture without resetting the chip. Sleep-current regression: power capture + UART decode + GDB attach. # Run the daemon Source: https://docs.embedder.com/headless/daemon Start, monitor, and manage the background daemon that executes GitHub and Slack work on your machine The daemon is a long-lived `embedder` process that connects out to the Embedder backend, claims queued [GitHub](/headless/github) and [Slack](/headless/slack) work items, and executes them on your machine. It is **organization-wide**: one daemon serves all of your organization's mapped repositories, regardless of which directory you start it from. Run it on your development machine, or on an always-on box with your target hardware attached — headless work can build, flash, and verify over serial just like an interactive session. ## Before you begin Make sure you have: * Embedder installed. If you have not installed it yet, start with [Quickstart](/quickstart). * An Embedder account that is a **member of the teams** whose repositories the daemon should serve — the daemon can only claim work for teams you belong to. * A machine that can stay online while the daemon is running. For the smoothest setup, sign in with the normal `embedder` command first. `embedder start daemon` can then create and store a daemon API key from your login automatically. ## Start the daemon From any directory, run: ```bash theme={"system"} embedder start daemon ``` The command starts a detached daemon in the background — you can close the terminal afterwards. ```text theme={"system"} Started Embedder daemon in the background. PID: 21004 Scope: all linked repositories Run `embedder monitor` to inspect it. ``` If a daemon is already running, Embedder prints the active PID instead of starting a duplicate. ### Credentials `embedder start daemon` resolves an API key in this order: 1. **`EMBEDDER_API_KEY`**, if set in the environment. 2. **A stored daemon API key** from a previous start. 3. **A key created from your current login** — if you are signed in, one is created and stored automatically. 4. **A prompt to paste a key** if none of the above are available. For non-interactive machines, create a token in [the dashboard](https://app.embedder.com/account) under **Account** → **Tokens** (tokens are shown once — copy them when created), then start the daemon with the key set: ```bash theme={"system"} export EMBEDDER_API_KEY=your_key_here embedder start daemon ``` ```powershell theme={"system"} $env:EMBEDDER_API_KEY="your_key_here" embedder start daemon ``` Store API keys safely. Anyone with the key can run daemon work as your account. ### Pin the daemon to one team and project By default the daemon claims work for **all** mapped repositories in teams you belong to. To restrict it, pass both `--team` and `--project`: ```bash theme={"system"} embedder start daemon --team "Firmware Team" --project "sensor-hub" ``` You can also use environment variables: ```bash theme={"system"} EMBEDDER_TEAM="Firmware Team" EMBEDDER_PROJECT="sensor-hub" embedder start daemon ``` Names are matched case-insensitively and must be exact. `--team` and `--project` must be provided together. Passing only one of them exits with a usage error. ## Monitor the daemon ```bash theme={"system"} embedder monitor ``` The monitor is a live dashboard that refreshes every second. It shows the daemon's connection state, current activity, GitHub and Slack work phases, claimed team and project, connected hardware (serial ports, J-Link probes), and a `[ Stop ]` control. Press m to change the model the daemon uses. Daemon monitor showing a connected organization-wide daemon with its model, session status, GitHub and Slack work state, worker slots, and hardware including serial ports and J-Link Typical phases you'll see: `Polling GitHub work queue`, `Executing GitHub work for `, `Executing Slack work for `, `Waiting for GitHub reply on `, and `Idle`. ## Stop the daemon Select `[ Stop ]` in the monitor, or run: ```bash theme={"system"} embedder stop daemon ``` If more than one daemon is active, pass the PID shown by `embedder monitor`: ```bash theme={"system"} embedder stop daemon --pid 21004 ``` ## How the daemon executes work Understanding the execution model helps when you're deciding where to run the daemon: * **One work item at a time.** The daemon claims a single GitHub or Slack item, finishes it (or pauses it on a follow-up question), then claims the next. The monitor shows `Waiting for local capacity` when items queue behind a running one. * **Isolated worktrees.** Each work item runs in a fresh git worktree under `~/.embedder`, on a branch named `embedder/work/`. Your local checkouts are never touched, and no pre-existing clone is needed — the repository is fetched using a short-lived token from the backend. Embedder ships its own `git`, and the `gh` CLI is not required. * **Auto-approved tools.** There is no human in the loop, so tool confirmations are auto-approved and the agent cannot ask interactive questions mid-run — it instead pauses the work item and asks in the originating GitHub or Slack thread. * **The backend does the talking.** The daemon commits (as `Embedder Agent `) and pushes the branch; the backend opens or updates the pull request and posts every comment and Slack reply. The daemon machine never holds GitHub credentials. * **Runs are capped at 30 minutes.** A run that exceeds the ceiling is aborted and reported as failed. * **Follow-up questions free the daemon.** When the agent asks a question, the work item's state — conversation, worktree, todo list — is saved locally. The daemon takes other work, and when the requester replies, the item resumes from the saved state. * **Failed runs keep their worktree** for seven days so you can inspect what happened; cleanup is automatic after that. ## Troubleshooting The backend rejected the daemon's API key — it was revoked, expired, or belongs to a different backend. Run `embedder start daemon` again to set up a fresh key, or create one in [the dashboard](https://app.embedder.com/account) under **Account** → **Tokens** and export `EMBEDDER_API_KEY`. `--team`/`--project` names must match exactly (case-insensitive). The error lists the available names — copy the right one, and quote names containing spaces. If a name is ambiguous, use a more specific one. * The daemon's account must be a **member of the team** the repository is mapped to. * If the daemon is pinned with `--team`/`--project`, it only claims work for that project — restart without the pin to serve everything. * Check the repository is actually mapped to a project in [the GitHub integration](/headless/github). Claimed work whose daemon goes quiet is requeued by the backend after 30 minutes and picked up by the next available daemon. Keep the daemon on a machine that doesn't sleep for uninterrupted service. The monitor only sees daemons on the same machine. Start one with `embedder start daemon`, or check the machine you started it on. `embedder monitor` lists each daemon with its PID. Stop a specific one with `embedder stop daemon --pid `. ## Next steps Map repositories to projects and trigger work with @embedder. Start headless work from a Slack thread. # GitHub integration Source: https://docs.embedder.com/headless/github Install the Embedder GitHub App, map repositories to projects, and trigger work with @embedder mentions Once the GitHub App is installed and a repository is mapped to an Embedder project, anyone with access to the repository can hand Embedder work by mentioning `@embedder` on an issue or pull request. A [daemon](/headless/daemon) on your machine executes the work and Embedder posts the result back — usually as a pull request. ## Before you begin Make sure you have: * **Owner or admin access** to your Embedder organization. Members can view the integration but not change it. * **GitHub linked as a sign-in method** on your Embedder account. Open [the dashboard](https://app.embedder.com/account) → **Account** → **Profile** and link GitHub before installing — Embedder uses it to match the installation back to your organization. * **An Embedder project for each repository** you want to connect. Create projects from the CLI or the VS Code extension first; the mapping step links each repository to an existing project. ## Install and connect In [the dashboard](https://app.embedder.com), select your team in the sidebar, then open **GitHub**. Select **Connect GitHub**. You are handed off to GitHub's own App installation screen, where you choose the organization or account to install into and whether to grant access to **all repositories** or **only selected repositories**. After you approve, GitHub redirects you back to the dashboard. The dashboard confirms with **GitHub installation linked**, and the installation appears with a **Connected** badge. One GitHub installation links to one Embedder organization. If a colleague installed the App but didn't finish, the original installer sees a **Finish linking** button instead. Expand the installation with **Show repositories**. Each repository row has a **Project** dropdown — select the Embedder project that repository should run under. Mapping rules: * **A project backs exactly one repository.** Projects already linked elsewhere appear disabled in the dropdown. * **Mappings are per team.** The same repository can be mapped in more than one team of your organization. * Selecting a project saves immediately (**Repository connected**); choosing **Not connected** removes the mapping. Mentions on an **unmapped repository are silently ignored** — no reaction, no reply. Map every repository you want Embedder to work on. Headless work only executes while a daemon is online. Follow [Run the daemon](/headless/daemon): ```bash theme={"system"} embedder start daemon ``` Comment on any issue or pull request in a mapped repository: ```text theme={"system"} @embedder please fix the flaky I2C init and open a PR ``` When a daemon claims the work, the comment gets a 🚀 reaction. When the work finishes, Embedder posts a completion comment with a link to the pull request it opened or updated. ## Where mentions work Embedder reacts to `@embedder` (case-insensitive) in: * **Issue comments** and **pull request comments** * **Pull request review comments** — mention it on a specific diff line and it replies in that review thread * **Issue bodies** — opening (or editing in) an issue that contains `@embedder` queues work for it The request is everything after the mention up to the first blank line. Keep the whole instruction in one paragraph directly after `@embedder`. For work triggered from an issue, Embedder creates a branch and opens a pull request titled `Fix #: `. For work triggered from a pull request, it pushes to the PR's existing branch. ## Follow-up questions If the agent needs input, it posts a comment asking for clarification and pauses the work item — freeing your daemon for other work. The comment includes the exact reply command to use, in the form: ```text theme={"system"} @embedder reply <work-id> <your answer> ``` Reply from the **same GitHub account** that made the original request. The work item resumes from where it left off, with the same working state. ## Automatic PR reviews The GitHub App can also review every pull request automatically. Reviews are read-only: the agent checks out the PR, reads the diff, and reports findings as a check run named **Embedder Review** plus inline comments — it never modifies code unless you ask. <Frame> <img alt="Pull request review by embedder-bot showing a severity table with one low issue and an inline diff comment explaining the problem with a suggested change" /> </Frame> Automatic review is **off by default**. On the GitHub integration page, each installation has an **Automatic pull request review** section: * **Review: On / Off** — enable review per repository. The repository must be mapped to a project first. * **Default: review all repos / review off** — the default for repositories without a per-repo setting. * **Stop all reviews** — an organization-wide kill switch. * **Automatic reviews per month** — an optional cap. Manually requested reviews don't count toward it. You can also request a one-off review on any PR by commenting exactly: ```text theme={"system"} @embedder review ``` To have Embedder fix what a review found, comment `@embedder fix review issues` on the PR. <Note> Reviews run on your own daemon too. A repository with review enabled is only reviewed while a daemon is online for its mapped team and project. Draft PRs are skipped, and very large diffs (over 400 KB) are skipped with a note. </Note> To give the reviewer repository-specific instructions, add a `.embedder/REVIEW.md` file to the repository — its contents are included in every review of that repository. ## Troubleshooting <AccordionGroup> <Accordion title="Nothing happens when I mention @embedder"> Silence means the request never became claimable work: * **The repository isn't mapped.** Unmapped repositories are ignored without any error. Check the **Project** dropdown for the repository on the GitHub integration page. * **The GitHub App doesn't cover the repository.** If you installed with "only selected repositories", add this one to the installation on GitHub. </Accordion> <Accordion title="The comment got a 🚀 reaction but no reply yet"> The 🚀 means a daemon claimed the work and is executing it. Watch progress with `embedder monitor` on the daemon machine. A single run can take a while but is capped at 30 minutes. </Accordion> <Accordion title="No 🚀 reaction appears"> The work is queued but no daemon has claimed it. Check that a daemon is running (`embedder monitor`), that its user is a member of the team the repository is mapped to, and that it isn't pinned to a different team or project with `--team`/`--project`. </Accordion> <Accordion title="The Embedder Review check stays queued"> No daemon was online to run the review. The check closes as neutral after 6 hours; push to the pull request to trigger a fresh review once a daemon is running. </Accordion> <Accordion title="Editing my comment doesn't re-run the work"> Each comment triggers work once. Embedder replies that the comment already triggered GitHub work — post a new comment to run again. </Accordion> <Accordion title="My reply to a follow-up question is ignored"> Use the exact `@embedder reply …` command from the bot's comment, in the same thread, from the same GitHub account that made the original request. </Accordion> </AccordionGroup> ## Next steps <CardGroup> <Card title="Run the daemon" icon="server" href="/headless/daemon"> Start, monitor, and manage the daemon that executes GitHub work. </Card> <Card title="Slack integration" icon="slack" href="/headless/slack"> Start the same headless work from a Slack thread. </Card> </CardGroup> # Headless overview Source: https://docs.embedder.com/headless/overview Trigger Embedder from GitHub and Slack and let a daemon on your own machine execute the work autonomously Headless mode lets your team hand work to Embedder without opening a terminal. Mention `@embedder` on a GitHub issue or pull request, or message `@Embedder` in Slack, and a background daemon running on your own hardware picks up the request, executes it, and reports back — as a pull request, a GitHub comment, or a Slack reply. ## The three pieces | Piece | What it does | Where you set it up | | -------------- | --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | **GitHub App** | Turns `@embedder` mentions on issues and PRs into work items, posts results back, and runs automatic PR reviews | [Dashboard](https://app.embedder.com) → **GitHub**, see [GitHub integration](/headless/github) | | **Slack app** | Turns `@Embedder` messages into work items and streams progress into the thread | [Dashboard](https://app.embedder.com) → **Slack**, see [Slack integration](/headless/slack) | | **Daemon** | A background `embedder` process on your machine that claims queued work and executes it | Your terminal, see [Run the daemon](/headless/daemon) | The GitHub App is the foundation: Slack requests also run against repositories you connect through it, so set up GitHub first. ## How a request becomes a result <Steps> <Step title="Someone triggers work"> A teammate comments `@embedder fix the flaky I2C init` on a pull request, or asks the same in a Slack channel. </Step> <Step title="The backend queues a work item"> Embedder resolves which repository the request targets and which project is mapped to it, then queues a work item for that repository. Repositories are mapped to Embedder projects in the dashboard — unmapped repositories are ignored. </Step> <Step title="Your daemon claims it"> Any online daemon whose user has access to the mapped team claims the item. On GitHub, the triggering comment gets a 🚀 reaction; in Slack, the thread shows **Working on it**. </Step> <Step title="The daemon executes on your machine"> The daemon fetches the repository into an isolated git worktree, selects the mapped team and project (so the agent has your hardware context — datasheets, peripherals, board configuration), and runs the agent with tool confirmations auto-approved. Slack requests stream progress into the thread as the agent works. </Step> <Step title="Results flow back"> When the agent finishes, the daemon commits and pushes a branch, and the backend opens or updates the pull request and posts the completion comment or Slack reply. If the agent needs input instead, it asks a follow-up question in the same GitHub thread or Slack thread, and resumes when you reply. </Step> </Steps> ## What runs where Headless work executes on **your machine**, not on Embedder's servers: * The daemon makes **outbound connections only** — a WebSocket to the Embedder backend plus HTTPS polling. It listens on no ports. * Repositories are fetched with **short-lived, per-work-item GitHub tokens** minted by the backend. The daemon machine needs no GitHub credentials, no `gh` CLI, and no pre-existing clone of the repository. * Each work item runs in a **fresh, isolated git worktree** under `~/.embedder` — your local checkouts are never touched. * Because the daemon runs on your hardware, headless work has access to your **connected devices**: it can build, flash, and verify firmware over serial while executing a GitHub issue or Slack request. Commits made by headless work are authored as `Embedder Agent <bot@embedder.dev>`. ## Set it up <CardGroup> <Card title="GitHub integration" icon="github" href="/headless/github"> Install the GitHub App and map repositories to Embedder projects. </Card> <Card title="Slack integration" icon="slack" href="/headless/slack"> Connect your Slack workspace so anyone can start work from a thread. </Card> <Card title="Run the daemon" icon="server" href="/headless/daemon"> Start and monitor the daemon that executes the work. </Card> <Card title="Headless server mode" icon="plug" href="/integrations/headless-server"> Drive Embedder programmatically over JSON-RPC instead. </Card> </CardGroup> # Slack integration Source: https://docs.embedder.com/headless/slack Connect your Slack workspace so anyone on your team can start Embedder work by messaging @Embedder With the Slack integration connected, anyone in your Slack workspace can hand Embedder work by mentioning `@Embedder` in a channel or sending it a direct message. The request runs on your [daemon](/headless/daemon) against one of your organization's linked GitHub repositories, streams progress into the Slack thread, and links the resulting pull request when it pushes changes. ## Before you begin Make sure you have: * **Owner or admin access** to your Embedder organization to install the app. * **The [GitHub integration](/headless/github) set up with mapped repositories.** Slack requests run against the repositories your organization has connected and mapped — without them, Slack work has nowhere to run. * **A running [daemon](/headless/daemon)** to execute the work. ## Connect the workspace <Steps> <Step title="Add Embedder to Slack"> In [the dashboard](https://app.embedder.com), select your team in the sidebar, then open **Slack** and select **Add to Slack**. Approve the installation on Slack's consent screen. <Check> The dashboard confirms with **Slack workspace connected**, and the workspace appears under **Slack workspaces** with a **Connected** badge. </Check> <Note> The connection is **workspace ↔ organization**: everyone in the connected Slack workspace can start work across all of the organization's linked repositories. A Slack workspace can belong to only one Embedder organization, and Enterprise Grid installs are not supported yet. </Note> </Step> <Step title="Invite the bot to a channel"> In any channel where people should be able to use Embedder: ```text theme={"system"} /invite @Embedder ``` Direct messages to the bot work without an invite. </Step> <Step title="Link your Slack identity"> The first time you send Embedder a request, it asks you to connect your Slack account to your Embedder account and replies with a link. Select **Connect Slack**, then return to Slack and send the request again. This is a one-time step per user. </Step> <Step title="Make a request"> Mention the bot and name the repository you want it to work in: ```text theme={"system"} @Embedder in firmware-nrf9160, add a retry with backoff to the MQTT reconnect logic ``` The thread shows **Working on it** once a daemon claims the request, streams progress while the agent works, and finishes with **Done** and a pull request link if the work pushed changes. <Frame> <img alt="Slack thread where a user asks Embedder for a live temperature plot, the bot picks the session back up, and replies Done with a repository link, a summary of changes, and serial output verified on hardware" /> </Frame> </Step> </Steps> ## How requests are routed to a repository Embedder picks the target repository from your message: 1. **An explicit name always wins.** Write `owner/repo` or the repository's name anywhere in the request. 2. **A close match works too** — "the sensor-hub repo" matches a linked repository named `sensor-hub`. 3. **If your organization has exactly one linked repository**, it is used by default and you don't need to name it. 4. **If the repository is ambiguous or missing**, the bot asks in the thread and lists the candidates. Reply with the name, and it picks up the request from there. To work on a specific pull request, reference it in the message — `pr 13` or the PR's URL — and Embedder checks out that PR's branch instead of the default branch. ## Working in a thread A Slack thread is a **session**: it keeps its repository checkout and conversation state, so you can iterate. * **Reply in the thread to continue.** After a task completes, a reply picks the same session back up in the same working state — "now also update the changelog" continues where the last run finished. * **Replies while it's working are folded in.** If you add a message mid-run, the bot acknowledges it and incorporates it into the run in progress. * **The agent can ask questions.** When it needs input it posts **One question before I continue** and pauses — reply in the thread to resume. While paused, your daemon is free to pick up other work. * **Not every request needs code.** Questions like "how does the OTA update flow work in firmware-nrf9160?" complete with an answer in the thread and no pull request. * **Sessions expire.** A session closes after 24 hours of inactivity; send a new root message to start a fresh one. ## If no daemon is online Slack requests queue until a daemon claims them. If nothing picks the request up within about 45 seconds, the thread shows **Waiting for daemon** with a hint to start one. The request stays queued and runs as soon as a daemon comes online — or expires with the session after 24 hours. ## Troubleshooting <AccordionGroup> <Accordion title="The bot doesn't respond in a channel"> Invite it with `/invite @Embedder`. If it isn't in the workspace at all, an organization owner or admin needs to connect the workspace from the dashboard's **Slack** page. </Accordion> <Accordion title=""… is not a linked repository in this organization""> The repository you named isn't connected, or isn't mapped to a project. Connect and map it on the dashboard's **GitHub** page — see [GitHub integration](/headless/github). </Accordion> <Accordion title=""Connect and map a GitHub repository before starting Slack agent work""> Your organization has no linked repositories yet. Set up the [GitHub integration](/headless/github) first. </Accordion> <Accordion title="It picked the wrong repository"> Name the repository explicitly as `owner/repo` in your request — an explicit name always takes priority over fuzzy matching. </Accordion> <Accordion title=""This Slack workspace is linked elsewhere""> A Slack workspace can belong to only one Embedder organization. Disconnect it from the other organization first. </Accordion> <Accordion title=""This session has ended""> The session completed, failed, or expired after 24 hours of inactivity. Send a new root message (not a thread reply) to start a new session. </Accordion> </AccordionGroup> ## Next steps <CardGroup> <Card title="Run the daemon" icon="server" href="/headless/daemon"> Start the daemon that executes Slack requests. </Card> <Card title="GitHub integration" icon="github" href="/headless/github"> Connect and map the repositories Slack work runs against. </Card> </CardGroup> # Headless server mode Source: https://docs.embedder.com/integrations/headless-server Drive Embedder programmatically over JSON-RPC to embed the agent in your own editor, automation, or test harness Embedder can run as a long-lived headless server that speaks JSON-RPC 2.0 over stdio. It is the same interface the [VS Code extension](/integrations/vscode-extension) uses, and it lets you embed Embedder's agent in your own editor, automation, or test harness. ```bash theme={"system"} embedder --server ``` <Note> There are two headless modes. `embedder --server` (this guide) is **client-driven**: your client sends requests and the server responds and streams events. `embedder --daemon` is **autonomous**: it connects out to the Embedder backend and executes queued GitHub and Slack work with no human in the loop. See [Daemon mode](#daemon-mode-autonomous) below and the [Headless overview](/headless/overview). </Note> ## When to use it Use headless server mode when you want to: * **Embed the agent in an editor or IDE.** The VS Code extension is exactly this. * **Automate Embedder** from a script, CI job, or your own tool. * **Build a custom UI** on top of Embedder's chat and hardware capabilities. If you just want an interactive terminal session, run `embedder` with no flags instead. ## Before you begin Make sure you have: 1. **`embedder` on your `PATH`.** Verify with: ```bash theme={"system"} embedder --version ``` 2. **A logged-in account.** The server uses your stored credentials. Verify with: ```bash theme={"system"} embedder status ``` If this prints your account details, you are authenticated. If not, run the interactive CLI once (`embedder`) and complete login, or supply a token via the `EMBEDDER_AUTH_TOKEN` environment variable (see [Authentication](#authentication)). 3. **A JSON-RPC client.** Any language works — you only need to read and write `Content-Length`-framed JSON over the child process's stdio. A complete, dependency-free reference client is included [below](#complete-example-client). ## Start the server The server reads JSON-RPC **requests** on **stdin** and writes **responses** and **notifications** on **stdout**. Typically you don't run it in a terminal yourself — you spawn it from your client and talk to it over the pipes: ```js theme={"system"} import { spawn } from "node:child_process"; const server = spawn("embedder", ["--server"], { cwd: "/path/to/your/project", // the workspace env: process.env, stdio: ["pipe", "pipe", "pipe"], // [stdin, stdout, stderr] }); ``` <Warning> **stdout carries protocol frames only** — the server never logs to stdout. Diagnostic logs go to a rotating file under the Embedder log directory; in dev builds they are additionally mirrored to stderr. Never parse stderr as protocol, and don't let anything else write to the server's stdout. </Warning> <Note> **One workspace per process.** The server operates on a single project directory. Launch it with the project as the working directory; you also pass that path explicitly in `initialize`. </Note> ## The wire protocol Headless mode speaks **JSON-RPC 2.0** with **LSP-style framing**: each message is a `Content-Length` header, a blank line, then a UTF-8 JSON body. ```text theme={"system"} Content-Length: 123\r\n \r\n {"jsonrpc":"2.0","id":1,"method":"initialize","params":{ ... }} ``` There are three message shapes: | Shape | Has `id`? | Has `method`? | Direction | | ---------------- | --------- | ------------------------ | -------------------------------------------------------- | | **Request** | yes | yes | client → server | | **Response** | yes | no (`result` or `error`) | server → client | | **Notification** | no | yes | server → client (events: text deltas, tool calls, state) | You don't need any Embedder package or SDK to talk to the server — the wire format is plain JSON-RPC 2.0. The request, response, and notification shapes in this guide are all you need, and a client in any language works. ## Authentication The server resolves an auth token in this order: 1. **`EMBEDDER_AUTH_TOKEN`** environment variable, if set. 2. **Stored credentials** written by a prior interactive login. ```js theme={"system"} const server = spawn("embedder", ["--server"], { cwd: workspace, env: { ...process.env, EMBEDDER_AUTH_TOKEN: process.env.MY_TOKEN }, // optional stdio: ["pipe", "pipe", "pipe"], }); ``` If no token is available, auth-dependent calls fail with `-31008 NotAuthenticated`. <Warning> Authentication happens **asynchronously after** `initialize` returns — a `-31008` right after the handshake usually means you need to wait, not that your credentials are wrong. See [Wait for readiness](#wait-for-readiness). </Warning> ## Connection lifecycle The normal startup sequence: <Steps> <Step title="Spawn the server"> Launch `embedder --server` with the project as the working directory, and wire up `Content-Length` framing on stdin/stdout. </Step> <Step title="Initialize"> Send `initialize` — the handshake that exchanges protocol version and client identity. See the parameter tables below. </Step> <Step title="Wait for readiness"> Auth and model loading complete **after** the handshake. Wait for them before calling anything that needs your identity or a model. See [Wait for readiness](#wait-for-readiness). </Step> <Step title="Select a team and project"> Call `team/select` then `project/select`. This is required to chat — the model runs through a backend proxy that needs the project context. See [the note on teams and projects](#select-a-team-and-project-before-chatting). </Step> <Step title="Configure"> Set `autoConfirm/set`, `model/set`, and `mode/set` as desired. </Step> <Step title="Chat"> Send `chat/send`, then consume the notification stream until the turn completes. </Step> <Step title="Shut down"> End the child's stdin (the server exits on EOF), or send `SIGTERM`. </Step> </Steps> ### `initialize` Request params: | Field | Type | Notes | | ----------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | `protocolVersion` | string | The protocol version your client targets (for example `"0.7.0"`). On incompatibility the server returns `-31000` with both versions in `error.data`. | | `workspacePath` | string | Absolute path to the project directory (match the spawn `cwd`). | | `clientName` | string | Free-form identifier for your client. | | `clientVersion` | string | Free-form version string. | Result: | Field | Type | Notes | | ----------------------------- | -------------- | ------------------------------------------------------ | | `protocolVersion` | string | The server's protocol version. | | `serverName` | string | `"embedder-cli"`. | | `serverVersion` | string | The CLI version. | | `capabilities.supportedModes` | string\[] | Usually `["act","plan","debug"]`. | | `sessionId` | string | The default session's id. | | `activeInstanceCount` | number | How many server instances are live for this workspace. | | `checkpointsEnabled` | boolean | Whether snapshot/rewind is available. | | `checkpointsDisabledReason` | string \| null | Why checkpoints are off, if applicable. | <Warning> `initialize` resolving **does not** mean the server is ready to chat. It only completes the handshake. Read the next section. </Warning> ## Wait for readiness When `initialize` returns, **authentication and model loading have not happened yet** — they run as background work scheduled after the handshake resolves. If you immediately call `team/list`, `chat/send`, or anything else that needs your identity or a model, you get: ```text theme={"system"} -31008 "Not authenticated. Wait for initialization to complete." ``` This is **by design**, not an error in your client — the handshake is intentionally fast so a UI can render immediately, and the rest streams in. The error is marked recoverable; you are expected to wait. There are two ways to know the server is ready: ### Option A: watch `state/update` (push, recommended for UIs) The server emits `state/update` notifications as it boots. Watch the `sessionStatus` field. It transitions out of `"loading"` once auth resolves: | `sessionStatus` | Meaning | | --------------------------------------------------------------------- | --------------------------------------------------------- | | `loading` | Still authenticating / loading models. **Wait.** | | `unauthenticated` | No valid credentials. **Fatal** — log in. | | `no_plan`, `subscription_past_due`, `evaluation_expired`, `suspended` | Account issue. **Fatal** for chat. | | `select_team` | Authenticated; no team selected yet. | | `select_project` | Team selected; no project selected yet. | | `create_project` | Team has no projects. | | `ready` | Fully ready (team and project selected, model available). | | `error` | Bootstrap failed. | Auth and model readiness (`currentModel` set, `sessionStatus` past `loading`) is **necessary but not sufficient** to chat: the model runs through a backend proxy that also requires a **selected project**. In practice, wait for auth and model, then select a team and project (reaching `sessionStatus: ready`) before the first `chat/send`. ### Option B: poll `session/status` (pull, simplest for scripts) `session/status` returns the full state snapshot at any time. Poll it until a model is available: ```js theme={"system"} async function waitForReady(rpc, timeoutMs = 60_000) { const fatal = new Set([ "unauthenticated", "error", "no_plan", "subscription_past_due", "evaluation_expired", "suspended", ]); const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const st = await rpc.request("session/status", {}); if (fatal.has(st.sessionStatus)) throw new Error(`not usable: ${st.sessionStatus}`); if (st.currentModel || st.sessionStatus === "ready") return st; await new Promise((r) => setTimeout(r, 700)); } throw new Error("timed out waiting for readiness"); } ``` ### Select a team and project before chatting There's a subtlety worth calling out: `chat/send` **accepts** your message even when no project is selected — but the **turn then fails**, because the model runs through a backend proxy that requires project context. Without a selected project the turn fails with `Invalid headers: Invalid UUID …`. This surfaces as a *failed turn*, not as an error on the `chat/send` call itself. So in practice, select a team and a project before chatting: ```js theme={"system"} const { teams } = await rpc.request("team/list", {}); await rpc.request("team/select", { teamId: teams[0].id }); const { projects } = await rpc.request("project/list", {}); await rpc.request("project/select", { projectId: projects[0].id }); ``` Pick the project that matches your hardware to give the agent the right context (datasheets, SVD, peripherals); any project satisfies the proxy for a generic chat. Methods like `projectDirectory/*`, `peripheral/*`, and `schematics/*` also require a team and project, and return `-31009` / `-31012` otherwise. ## Send a message and stream the turn ### `chat/send` | Field | Type | Notes | | ---------------- | ---------- | ------------------------------------------------------------------------------- | | `content` | string | The user message (sent to the model). | | `displayContent` | string? | Optional user-visible text when `content` contains expanded context. | | `mentions` | string\[]? | File/directory paths to expand into context (resolved against `workspacePath`). | | `images` | object\[]? | Base64 image attachments for vision. | | `mode` | string? | Override the agent mode for this turn (`act` / `plan` / `debug`). | | `sessionId` | string? | Target a specific session (defaults to the active one). | Returns `{ messageId, queued, position? }`. If the agent is already busy, the message is **queued** (`queued: true`) rather than rejected. ### The turn streams as notifications After `chat/send`, the work happens asynchronously and is reported via notifications (all carry an optional `sessionId`): | Notification | Fires when | Key fields | | --------------------- | ------------------------------- | ------------------------------------------- | | `chat/userMessage` | Your message is committed | `messageId`, `content`, `checkpointHash?` | | `chat/textDelta` | Each chunk of assistant text | `messageId`, `delta`, `isFinal?`, `tokens?` | | `chat/toolCall` | A tool is invoked | `toolCallId`, `toolName`, `args`, `status` | | `chat/toolStream` | A tool emits incremental output | `toolCallId`, `output` | | `chat/toolResult` | A tool finishes | `toolCallId`, `result.{success,summary,…}` | | `chat/nestedToolCall` | A sub-agent spawns a tool | `parentToolCallId`, `toolCallId`, … | | `bash/output` | `bash/execute` output line | `executionId`, `output` | | `state/update` | Server state changes | full snapshot including `chatBusy` | | `usage/update` | Token usage / limits change | `tokensUsedToday`, `creditsAvailable` | ### Detect that a turn is complete Use either signal (the example client uses both): * **`state/update` → `chatBusy` goes `false`** after having been `true`. This is the authoritative "the turn (including any tool calls) is done" signal. * **`chat/textDelta` with `isFinal: true`** marks the final text chunk of the turn. <Tip> Add a small grace period (a few seconds) after `chatBusy` flips to `false` to avoid races with trailing notifications. </Tip> ## Approvals: tools, questions, and plans By default the agent **pauses for confirmation** before running tools that change files, run shell commands, or touch hardware. A headless client must either auto-confirm or respond to each request. ### Auto-confirm everything (unattended runs) ```js theme={"system"} await rpc.request("autoConfirm/set", { enabled: true }); ``` With auto-confirm on, tools run without prompting and no `tool/confirmRequest` notifications fire. This is the right choice for automation and CI. ### Respond to individual tool confirmations If auto-confirm is off, the server sends a `tool/confirmRequest` notification: ```json theme={"system"} { "toolCallId": "…", "toolName": "writeFile", "displayName": "Write File", "details": { "title": "…", "affectedPaths": ["…"], "isDestructive": false } } ``` Respond with `tool/respond`: | `response` | Effect | | ---------- | ----------------------------------------------------------------- | | `once` | Approve this call only. | | `always` | Approve and stop asking for this tool. | | `reject` | Deny this call. | | `redirect` | Deny with a `message` redirecting the agent (requires `message`). | ```js theme={"system"} await rpc.request("tool/respond", { toolCallId, response: "always" }); ``` ### Answer questions the agent asks The agent can ask a multiple-choice or free-text question via the `question/ask` notification: ```json theme={"system"} { "questionId": "…", "questions": [ { "question": "…", "header": "…", "options": [ { "label": "Yes", "description": "…" } ], "multiSelect": false } ] } ``` Respond with `question/respond`. `answers` is keyed by each question's `header` (or `question`): ```js theme={"system"} await rpc.request("question/respond", { questionId, answers: { "Board variant": { answers: ["nrf9160dk/nrf9160"], isCustom: false } }, }); ``` ### Approve a plan (plan mode) In plan mode the agent proposes a plan via the `plan/review` notification (`{ planPath, markdownContent }`). Approve it with `plan/approve { planPath }` to execute. ## Agent modes | Mode | Tools | Use | | ------- | ------------------------------------------ | ---------------------------- | | `act` | full (read, write, shell, build, flash, …) | execute changes (default) | | `plan` | read-only | produce a plan, no mutations | | `debug` | full + hardware/debug context | live debugging workflows | Set the mode for the session with `mode/set { mode }`, or per-message via the `mode` field on `chat/send`. <Warning> A mode switch is a **turn boundary** — switching mid-turn restarts the turn with the new tool set. </Warning> ## Sessions and the message queue * The server starts with one **default session** (its id is in the `initialize` result). You can run **multiple independent sessions** over one connection: `session/create`, `session/list`, `session/status`, `session/close`. Most methods accept an optional `sessionId` (defaulting to the active session). * If you `chat/send` while a turn is running, the message is **queued**. Manage the queue with `queue/clear`, `queue/edit`, `queue/delete`, and `queue/sendNow`. A `chat/queued` notification reports the queued message's position. * Stop an in-flight turn with `chat/stop { clearQueue? }`. ## Conversations, rewind, and snapshots * **History:** `conversation/list`, `conversation/read`, `conversation/load`, `conversation/delete`, `conversation/rename`, `conversation/threads`. * **Compression:** `conversation/compress` summarizes a long conversation to reduce tokens (`conversation/compressCancel` aborts it). The server also compresses automatically when needed. * **Rewind:** when `checkpointsEnabled` is true, each turn snapshots the workspace. `chat/rewindEntries` lists rewindable points; `chat/rewind` / `chat/undo` revert files and conversation; `chat/rewindConfirm` commits a rewind. `turnDiff/get` returns the file diff for a turn. ## Complete example client A self-contained client in plain JavaScript (runs under **Node 18+** or **Bun**), with no external dependencies — it hand-rolls the `Content-Length` framing. It spawns the server, waits for readiness, enables auto-confirm, sends one message, streams the turn, and exits when the turn completes. Save it as `embedder-client.mjs` and run: ```bash theme={"system"} node embedder-client.mjs /path/to/project "your prompt" ``` ```js embedder-client.mjs theme={"system"} #!/usr/bin/env node import { spawn } from "node:child_process"; const WORKSPACE = process.argv[2] ?? process.cwd(); const PROMPT = process.argv[3] ?? "List the files in this project and summarize it."; const PROTOCOL_VERSION = "0.7.0"; // the version your client targets // ---- spawn the headless server ---- const server = spawn("embedder", ["--server"], { cwd: WORKSPACE, env: process.env, // inherits stored credentials / EMBEDDER_AUTH_TOKEN stdio: ["pipe", "pipe", "pipe"], }); server.stderr.on("data", (b) => process.stderr.write(`[server] ${b}`)); // ---- JSON-RPC framing ---- let buf = Buffer.alloc(0); let nextId = 1; const pending = new Map(); function send(msg) { const body = JSON.stringify(msg); server.stdin.write(`Content-Length: ${Buffer.byteLength(body)}\r\n\r\n${body}`); } function request(method, params) { const id = nextId++; return new Promise((resolve, reject) => { pending.set(id, { resolve, reject }); send({ jsonrpc: "2.0", id, method, params }); }); } server.stdout.on("data", (chunk) => { buf = Buffer.concat([buf, chunk]); while (true) { const headerEnd = buf.indexOf("\r\n\r\n"); if (headerEnd === -1) break; const m = /content-length:\s*(\d+)/i.exec(buf.subarray(0, headerEnd).toString("ascii")); if (!m) { buf = buf.subarray(headerEnd + 4); continue; } const start = headerEnd + 4, end = start + Number(m[1]); if (buf.length < end) break; const msg = JSON.parse(buf.subarray(start, end).toString("utf8")); buf = buf.subarray(end); dispatch(msg); } }); function dispatch(msg) { if (msg.id !== undefined && (msg.result !== undefined || msg.error !== undefined)) { const p = pending.get(msg.id); pending.delete(msg.id); if (p) msg.error ? p.reject(new Error(JSON.stringify(msg.error))) : p.resolve(msg.result); } else if (msg.method) { onNotification(msg.method, msg.params ?? {}); } } // ---- turn state + completion detection ---- let sawBusy = false, busy = false, done; const turnComplete = new Promise((r) => (done = r)); let graceTimer = null; function maybeComplete() { clearTimeout(graceTimer); if (sawBusy && !busy) graceTimer = setTimeout(done, 3000); } function onNotification(method, p) { switch (method) { case "chat/textDelta": process.stdout.write(p.delta ?? ""); if (p.isFinal) maybeComplete(); break; case "chat/toolCall": if (p.status === "executing") console.error(`\n[tool] ${p.toolName} ${JSON.stringify(p.args)}`); break; case "chat/toolResult": console.error(`[tool ${p.result?.success === false ? "✗" : "✓"}] ${p.result?.summary ?? ""}`); break; case "tool/confirmRequest": // belt-and-suspenders if auto-confirm is off request("tool/respond", { toolCallId: p.toolCallId, response: "always" }); break; case "question/ask": { const answers = {}; for (const q of p.questions ?? []) answers[q.header ?? q.question] = { answers: [q.options?.[0]?.label ?? "yes"], isCustom: false }; request("question/respond", { questionId: p.questionId, answers }); break; } case "state/update": if (p.chatBusy && !busy) { busy = true; sawBusy = true; } else if (!p.chatBusy && busy) { busy = false; maybeComplete(); } break; } } // ---- run ---- const init = await request("initialize", { protocolVersion: PROTOCOL_VERSION, workspacePath: WORKSPACE, clientName: "example-client", clientVersion: "1.0.0", }); console.error(`initialized: ${init.serverName} v${init.serverVersion}, session ${init.sessionId}`); // wait for auth + model (see "Wait for readiness") const fatal = new Set(["unauthenticated", "error", "no_plan", "subscription_past_due", "evaluation_expired", "suspended"]); for (let i = 0; i < 90; i++) { const st = await request("session/status", {}); if (fatal.has(st.sessionStatus)) throw new Error(`not usable: ${st.sessionStatus}`); if (st.currentModel || st.sessionStatus === "ready") break; await new Promise((r) => setTimeout(r, 700)); } // select a team + project (required — the model proxy needs the project context) const { teams } = await request("team/list", {}); if (teams?.[0]) { await request("team/select", { teamId: teams[0].id }); console.error(`team: ${teams[0].name}`); } const { projects } = await request("project/list", {}); if (projects?.[0]) { await request("project/select", { projectId: projects[0].id }); console.error(`project: ${projects[0].name}`); } await request("autoConfirm/set", { enabled: true }); const res = await request("chat/send", { content: PROMPT, mode: "act" }); console.error(`\nchat/send accepted: ${res.messageId}\n`); await turnComplete; console.error("\n\n[turn complete]"); server.stdin.end(); // server exits on stdin EOF ``` ## Protocol reference The common methods and notifications are documented with their parameters in the sections above; the lists below are the complete surface. Most methods accept an optional `sessionId` (to target a specific session) and `correlation` (an opaque value the server echoes back on the notifications it triggers, so you can match events to the request that caused them). <AccordionGroup> <Accordion title="RPC methods"> | Category | Methods | | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Lifecycle & sessions | `initialize`, `session/create`, `session/list`, `session/status`, `session/load`, `session/rename`, `session/close`, `session/retry`, `stream/replay`, `turnDiff/get` | | Chat | `chat/send`, `chat/stop`, `chat/clear`, `chat/snapshot`, `chat/undo`, `chat/rewind`, `chat/rewindEntries`, `chat/rewindConfirm`, `chat/sideQuestion` | | Approvals | `tool/respond`, `question/respond`, `plan/approve` | | Configuration | `model/set`, `mode/set`, `autoConfirm/set`, `agentTeam/set` | | Teams & projects | `team/list`, `team/select`, `team/deselect`, `project/list`, `project/select`, `project/deselect`, `project/create`, `project/init`, `projectDirectory/get`, `projectDirectory/set` | | Catalog, peripherals & platforms | `catalog/list`, `peripheral/list`, `peripheral/add`, `peripheral/addCustom`, `platform/addCustom` | | Conversations | `conversation/list`, `conversation/read`, `conversation/load`, `conversation/delete`, `conversation/rename`, `conversation/threads`, `conversation/compress`, `conversation/compressCancel` | | Message queue | `queue/clear`, `queue/edit`, `queue/delete`, `queue/sendNow` | | Commands & skills | `command/list`, `command/execute`, `skill/list`, `skill/commandContent` | | Shell | `bash/execute` | | Auth & account | `auth/startDeviceCode`, `auth/logout`, `usage/status`, `billing/creditPackages`, `billing/checkoutSession`, `billing/purchaseCredits` | | Pull requests | `pr/list`, `pr/review` | | MCP servers | `mcp/serverList`, `mcp/catalogList`, `mcp/connect`, `mcp/disconnect`, `mcp/setEnabled`, `mcp/remove`, `mcp/addServer`, `mcp/installCatalog` | | Connected model providers | `connect/providerList`, `connect/providerStartAuth`, `connect/providerPollAuth`, `connect/providerDisconnect` | | Hardware arbitration | `hardwareWorkflow/status`, `hardwareWorkflow/request`, `hardwareWorkflow/release`, `hardwareWorkflow/cancel`, `hardwareWorkflow/clear`, `hardwareWorkflow/pause`, `hardwareWorkflow/resume`, `hardwareWorkflow/moveToFront`, `hardwareWorkflow/forceRelease` | | Serial / debug probes | `serial/listPorts`, `serial/connect`, `serial/disconnect`, `serial/send`, `serial/setConfig`, `serial/detectBaudRate`, `serial/listJlinkDevices`, `serial/setJlinkConfig`, `serial/listMapFiles`, `serial/setItmConfig`, `serial/listOpenocdTargets`, `serial/addTab`, `serial/removeTab`, `serial/selectTab`, `serial/clearOutput`, `serial/getState` | | Plotting | `plot/startOnPort`, `plot/stopOnPort`, `plot/getActivePorts`, `plot/listSessions`, `plot/loadSession`, `plot/renameSession`, `plot/exportCsv` | | Schematics | `schematic/parse`, `schematic/checkVersions`, `schematics/list`, `schematics/parsed`, `schematics/referenceCatalog` | | Tracing | `trace/sessionStart`, `trace/sessionStop`, `trace/listSessions`, `trace/loadSession`, `trace/queryEvents`, `trace/deleteSession`, `trace/attachElf`, `trace/detachElf`, `trace/resolveAddresses`, `trace/analyticsGet`, `trace/analyticsReport`, `trace/finderQuery` | | Hardware captures | `hardware/captureListSessions`, `hardware/captureLoadSession`, `hardware/captureDeleteSession` | | Misc | `bug/report` | </Accordion> <Accordion title="Notifications"> Notifications are server → client and have no `id`. Subscribe by `method` name; each carries an optional `sessionId`. | Category | Notifications | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Chat stream | `chat/userMessage`, `chat/textDelta`, `chat/queued`, `chat/toolCall`, `chat/toolStream`, `chat/toolResult`, `chat/nestedToolCall`, `chat/agentTeamSnapshot`, `chat/rewound`, `chat/sideQuestion/start`, `chat/sideQuestion/finish` | | Approvals | `tool/confirmRequest`, `question/ask`, `plan/review` | | State | `state/update`, `usage/update` | | Shell | `bash/output` | | Serial / hardware | `serial/data`, `serial/connectionState`, `serial/portsChanged`, `serial/baudDetected`, `serial/tabsChanged`, `serial/outputCleared`, `serial/jlinkChanged`, `serial/itmChanged`, `hardwareWorkflow/changed`, `hardware/capturePublished`, `peripheral/documentProcessing` | | Plotting | `plot/data`, `plot/config`, `plot/status`, `plot/textData`, `plot/xyConfig`, `plot/warning`, `plot/mathChannels`, `plot/mathChannelsChanged` | | Tracing | `trace/streamStatus`, `trace/eventBatch`, `trace/transportHealth`, `trace/sessionUpdate`, `trace/compatibilityWarning`, `trace/symbolizationChanged` | | Bluetooth | `bluetooth/scanAdvertisement`, `bluetooth/connectionState`, `bluetooth/serviceTreeUpdated`, `bluetooth/characteristicNotification`, `bluetooth/eventLog` | | Editor / providers / schematics | `editor/contextChanged`, `connect/providerStatusChanged`, `schematics/refresh` | <Note> **Transient vs replayable.** These high-frequency streaming notifications are **transient** — not buffered, so they cannot be replayed: `bash/output`, `serial/data`, `plot/data`, `plot/textData`, `trace/eventBatch`, `trace/streamStatus`, `trace/transportHealth`, `bluetooth/scanAdvertisement`, `bluetooth/characteristicNotification`. All **other** notifications are sequenced and can be replayed with `stream/replay` after a reconnect. </Note> </Accordion> <Accordion title="Error codes"> JSON-RPC errors arrive as `{ code, message, data }`, where `data` is `{ category, recoverable, details? }`. | Code | Name | Meaning | | -------- | ----------------------- | ---------------------------------------------------------------------------------------- | | `-32002` | NotInitialized | Call `initialize` first. | | `-31000` | ProtocolVersionMismatch | Client/server protocol versions incompatible. | | `-31002` | ChatAlreadyInProgress | A turn is already running. | | `-31003` | ChatNotInProgress | No active turn to act on. | | `-31004` | InvalidToolCallId | Unknown `toolCallId` in `tool/respond`. | | `-31005` | InvalidQuestionId | Unknown `questionId` in `question/respond`. | | `-31006` | ModelNotAvailable | No model available (still loading, or none configured). | | `-31007` | InvalidMode | Unsupported agent mode. | | `-31008` | NotAuthenticated | Not logged in, or init not yet complete — see [Wait for readiness](#wait-for-readiness). | | `-31009` | NoTeamSelected | Call `team/select` first. | | `-31010` | InvalidEntityId | Unknown id (team/project/etc.) or unusable workspace. | | `-31011` | ProjectLimitReached | Project limit reached. | | `-31012` | NoProjectSelected | Call `project/select` first. | | `-31013` | ConversationNotFound | Unknown conversation id. | | `-31014` | ConversationEmpty | Conversation has no messages. | | `-31015` | PendingConfirmation | A confirmation is already outstanding. | | `-31016` | InvalidRedirectMessage | `redirect` response needs a `message`. | | `-31017` | QueueOverflow | Message queue is full. | | `-31018` | OperationTimedOut | Operation timed out. | | `-31019` | OperationCancelled | Operation was cancelled/aborted. | | `-31020` | GhCliNotAvailable | The `gh` CLI is required but missing. | | `-31021` | FeatureNotAllowed | Feature not allowed for this account/mode. | | `-31050` | SerialError | Serial port / probe error. | | `-31099` | InternalServerError | Unexpected server error. | </Accordion> </AccordionGroup> <Note> The server is also self-describing at runtime: `initialize` returns its name, version, and protocol version, and every error carries a machine-readable `code` plus `data`. The protocol can change between `embedder` releases — pin to a version and handle `-31000` to detect drift. </Note> ## Daemon mode (autonomous) `embedder --daemon` is the other headless mode. Instead of a client driving it over stdio, the daemon **connects out** to the Embedder backend over a WebSocket and autonomously executes queued **GitHub** and **Slack** work items — claiming a task, running the agent to completion in an isolated git worktree (with all tool confirmations auto-approved), then committing and pushing a branch (the backend opens the PR and posts replies), or asking a follow-up question. | Aspect | `--server` | `--daemon` | | ------------- | ------------------------------- | -------------------------------- | | Driver | your client (stdio JSON-RPC) | the backend (outbound WebSocket) | | Auth | user token / stored credentials | `EMBEDDER_API_KEY` | | Human in loop | yes (or auto-confirm) | no | | Trigger | your `chat/send` | GitHub/Slack work items | To build your own client or editor integration, use `--server`. If you want autonomous PR work, use the headless integrations instead of driving the daemon yourself — see [Run the daemon](/headless/daemon) for the managed `embedder start daemon` / `embedder monitor` / `embedder stop daemon` flow. ## Troubleshooting <AccordionGroup> <Accordion title="-31008 NotAuthenticated right after initialize"> Auth loads **after** the handshake. Wait for readiness: poll `session/status` until `currentModel` is set, or watch `state/update` for `sessionStatus`. See [Wait for readiness](#wait-for-readiness). </Accordion> <Accordion title="-31008 that never clears"> No valid credentials. Run `embedder status`; log in via the interactive CLI or set `EMBEDDER_AUTH_TOKEN`. </Accordion> <Accordion title="-31006 ModelNotAvailable"> You sent `chat/send` before a model loaded. Apply the readiness wait. </Accordion> <Accordion title="Turn fails with 'Invalid headers: Invalid UUID … received undefined'"> No project selected — the backend model proxy needs the project-context header. Select a team and project before `chat/send` (see [the note on teams and projects](#select-a-team-and-project-before-chatting)). Note that `chat/send` itself does *not* error; only the turn fails. </Accordion> <Accordion title="Garbage or parse errors on stdout"> Your framing reader is misaligned, or something wrote non-protocol bytes to stdout. The server never logs to stdout (logs go to a file, and to stderr in dev builds); treat stdout as protocol-only. </Accordion> <Accordion title="-31000 ProtocolVersionMismatch"> Your `protocolVersion` doesn't match this `embedder` build. On mismatch, `error.data` reports the server's expected version — send that. The `initialize` result also returns the server's `protocolVersion`. </Accordion> <Accordion title="Tool calls hang waiting for approval"> Enable `autoConfirm/set { enabled: true }`, or respond to each `tool/confirmRequest` with `tool/respond`. </Accordion> <Accordion title="The turn never finishes in your client"> Track `state/update.chatBusy` (true → false) and/or `chat/textDelta.isFinal`; the turn can include many tool calls before completing. </Accordion> <Accordion title="Hardware or serial calls fail with -31009 / -31012"> Those need a selected team and project. Run `team/select` then `project/select`. </Accordion> </AccordionGroup> ## Next steps <CardGroup> <Card title="Headless overview" icon="robot" href="/headless/overview"> Run Embedder autonomously in the background to pick up GitHub and Slack work. </Card> <Card title="Use Embedder in VS Code" icon="code" href="/integrations/vscode-extension"> The official editor integration, built on this same headless protocol. </Card> </CardGroup> # Set up MCP servers Source: https://docs.embedder.com/integrations/mcp-servers Learn how to connect Embedder to your tools with the Model Context Protocol Embedder can connect to hundreds of external tools and data sources through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/), an open source standard for AI-tool integrations. MCP servers give Embedder access to your tools, databases, and APIs. ## What you can do with MCP With MCP servers connected, you can ask Embedder to: * **Implement features from issue trackers:** "Add the feature described in Linear issue ENG-4521 and create a PR on GitHub." * **Analyze monitoring data:** "Check Sentry for errors related to the serial monitor and summarize the most common crashes." * **Search the web:** "Use Firecrawl to find the latest ESP-IDF release notes and summarize what changed in the Wi-Fi driver." * **Track issues across tools:** "Find all open Jira issues tagged 'firmware' in Atlassian and cross-reference with related Sentry errors." ## Configure MCP servers Add and manage MCP servers with the `/mcp` command. <Steps> <Step title="Run /mcp"> Use the `/mcp` command to see the preconfigured MCPs Embedder has, or to add your own. </Step> <Step title="Add a server"> <Frame> <img alt="MCP servers panel showing installed servers and marketplace options" /> </Frame> Either select an MCP server from the marketplace, or press <kbd>a</kbd> to add your own. <Frame> <img alt="Add MCP Server form with fields for name, type, and command" /> </Frame> </Step> <Step title="Verify the connection"> Once configured, Embedder automatically connects to the MCP server. You can verify the connection by asking Embedder to list available tools. </Step> </Steps> <Warning> Only use MCP servers from trusted sources. </Warning> ## Supported transports Embedder supports the following MCP transport types: * **stdio** — Communicates with the server over standard input/output. This is the most common transport for local servers. * **SSE** — Connects to a remote server over Server-Sent Events. Use this for hosted or shared MCP servers. * **HTTP** — Connects to a remote server over HTTP. Use this for stateless or REST-based MCP servers. # Use Embedder in VS Code Source: https://docs.embedder.com/integrations/vscode-extension Integrate Embedder directly into your VS Code workflow <Frame> <img alt="Embedder VS Code extension showing the chat panel, code editor, and serial console side by side" /> </Frame> The Embedder VS Code extension provides a graphical interface for Embedder directly in your IDE. This is the recommended way to to use Embedder in VS Code and for Windows users. With the extension, you get all the core functionality of the CLI: a serial monitor, review and edit plans, @ mentioning files, sending serial commands, accessing conversation history, open multiple conversations in separate tabs, and more. ## Install the extension * **[Install for VS Code](vscode:extension/embedder.embedder-vscode)** Or in VS Code, press <kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>X</kbd> on Mac or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>X</kbd> on Windows and Linux to open the Extensions view, search for "Embedder", and click **Install**. <Tip> If the extension doesn't appear after installation, restart VS Code or run "Developer: Reload Window" from the Command Palette. </Tip> ## Get started Once installed, you can use Embedder natively in your IDE. <Steps> <Step title="Open the Embedder panel"> The quickest way to open Embedder is to click the <svg><path /><path /><path /></svg> icon from the Activity Bar on the left side of your screen. Another way to open Embedder is through the Command Palette. Use <kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (Mac) or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> (Windows/Linux), type "Embedder", and select "New Chat Tab". </Step> <Step title="Send a prompt"> Ask Embedder to help with your code or files, whether that's explaining how something works, debugging an issue, or making changes. See [common workflows](/core-concepts/common-workflows) for more examples of things you can do with Embedder. </Step> <Step title="Configure project"> 1. Choose an existing project or create a new one 2. Select the platform you're using from our catalog, or add your own 3. Select the peripherals you're using from our catalog, or add your own 4. Generate `EMBEDDER.md` with `/init` See [common workflows](/core-concepts/common-workflows) for more information on project configuration. </Step> <Step title="Review the changes"> When Embedder wants to edit a file, it shows a comparison of the proposed changes, then asks for permission. You can accept, reject, or tell Embedder what to do instead. <Frame> <img alt="Permission request dialog showing a comparison of proposed file changes" /> </Frame> </Step> </Steps> ## VS Code commands and shortcuts Open the Command Palette (<kbd>Cmd</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> on Mac or <kbd>Ctrl</kbd>+<kbd>Shift</kbd>+<kbd>P</kbd> on Windows/Linux) and type "Embedder" to see all available VS Code commands for the Embedder extension. <Note> These are VS Code commands for controlling the extension. Not all built-in Embedder commands are available in the extension. </Note> | Command | Description | | ------------------------------- | ------------------------------------ | | Open Embedder Chat | Opens the Embedder chat panel | | Switch Mode (Act/Plan) | Toggle between Act and Plan modes | | New Chat Tab | Creates a new chat tab | | Close Active Chat Tab | Closes the current chat tab | | Restart CLI Server | Restarts the CLI backend process | | Switch Model | Change the AI model | | Stop Generation | Stop the current AI response | | Install Embedder CLI | Install the CLI binary | | Clear Conversation | Clear chat history | | Compress Conversation | Compress conversation to save tokens | | View Usage & Billing | View usage statistics | | View Conversation History | Browse past conversations | | Switch Team | Change active team | | Switch Project | Change active project | | Undo Last Message | Undo the last message | | Open Web Console | Open web console | | Open CLI Logs | View CLI log files | | Show CLI Process Output | Show CLI process stdout | | Open Embedder Console (Panel) | Open serial console in panel | | Open Embedder Console in Editor | Open serial console as editor tab | | Toggle Embedder Console | Toggle serial console visibility | ## Rewind with checkpoints The VS Code extension supports checkpoints, which track Embedder's file edits and let you rewind to a previous state. Click the rewind button on any message, or use the `/undo` or `/rewind` commands, to choose from three options: * **Fork conversation from here** — Start a new conversation branch from this message while keeping all code changes intact. * **Rewind code to here** — Revert file changes back to this point in the conversation while keeping the full conversation history. * **Fork conversation and rewind code** — Start a new conversation branch and revert file changes to this point. ## Serial console Embedder can interact with your serial console directly. It can read serial output history to diagnose issues, send commands to your device, monitor for specific patterns, and help debug communication problems. The built-in serial console lets you communicate with embedded devices directly from VS Code. Open it from the bottom panel or as a full editor tab with the `embedder.openSerialInEditor` command. <Frame> <img alt="Serial console showing device output alongside the code editor" /> </Frame> ### Port selection Embedder auto-detects available serial ports and displays metadata like manufacturer, vendor/product ID, and serial number. Ports refresh automatically when devices are plugged in or unplugged. ### Baud rate Select from common baud rates manually, or let Embedder auto-detect the correct baud rate. Auto-detection shows a confidence level so you know how reliable the result is. ### Multi-tab interface Open multiple serial connections simultaneously. Each tab maintains its own port selection, baud rate, connection state, output history (up to 50,000 lines), filter pattern, and timestamp settings. ### Output features * **Log level coloring** — Errors appear in red, warnings in yellow, info in green, and debug in blue. * **Timestamps** — Optional timestamp prefix for each line. * **Regex filtering** — Filter output with regular expressions. * **Clear output** — Clear the console while preserving the connection. ### Connection types Embedder supports two connection types: | Type | Description | | ---- | ------------------------------------------------------------------- | | UART | Standard serial port connections (USB-to-UART adapters, dev boards) | | RTT | SEGGER Real-Time Transfer via J-Link probes | ### J-Link integration Embedder automatically detects connected J-Link probes and supports RTT for non-intrusive debugging output. Select your target device from a picker with search and filter, with suggested devices based on your project context. Supports both SWD and JTAG interfaces. ## Next steps <CardGroup> <Card title="Common workflows" icon="route" href="/core-concepts/common-workflows"> Explore practical examples of what you can do with Embedder. </Card> <Card title="Best practices" icon="star" href="/core-concepts/best-practices"> Learn tips and techniques to get the most out of Embedder. </Card> </CardGroup> # Quickstart Source: https://docs.embedder.com/quickstart ## Before you begin Make sure you have: * A terminal application (see recommendations below) * A firmware project to work with (or start a new one) ### Recommended terminals <Tabs> <Tab title="macOS / Linux"> [Ghostty](https://ghostty.org/) for the best experience on macOS and Linux. **macOS (Homebrew):** ```bash theme={"system"} brew install --cask ghostty ``` **Linux:** ```bash theme={"system"} # See https://ghostty.org/docs/install for distribution-specific instructions ``` </Tab> <Tab title="Windows"> The [VS Code extension](/integrations/vscode-extension) is recommended for the best experience on Windows. <Accordion title="I want to use a terminal"> We strongly recommend the [VS Code extension](/integrations/vscode-extension) for Windows users, but if you must use a terminal, [Alacritty](https://alacritty.org/) is recommended for the best experience on Windows. **Using winget:** ```powershell theme={"system"} winget install Alacritty.Alacritty ``` **Using Scoop:** ```powershell theme={"system"} scoop install alacritty ``` </Accordion> </Tab> </Tabs> ## Step 1: Install and Launch Embedder To install Embedder, use one of the following methods: <Tabs> <Tab title="macOS / Linux"> <Warning> Enterprise customers have a custom install script. Contact your organization's point of contact or reach out to [founders@embedder.com](mailto:founders@embedder.com) for your installation instructions. After you install, you can proceed from step 2. Do not use the installation script below! </Warning> ```bash theme={"system"} curl -fsSL https://embedder.com/install | bash ``` <Info> Native installations automatically update in the background to keep you on the latest version. </Info> </Tab> <Tab title="Windows"> <Warning> Enterprise customers have a custom installation link or script. Contact your organization's point of contact or reach out to [founders@embedder.com](mailto:founders@embedder.com) for your installation instructions. After you install, you can proceed from step 2. Do not use the installation link or script below! </Warning> * **[Install for VS Code](vscode:extension/embedder.embedder-vscode)** Follow the instructions in [Use Embedder in VS Code](/integrations/vscode-extension) for VS Code specific setup instructions. We strongly recommend the VS Code extension for Windows users, but if you must use a terminal use the following script and setup instructions: ```powershell theme={"system"} irm https://embedder.com/install | iex ``` <Info> Native installations automatically update in the background to keep you on the latest version. </Info> </Tab> </Tabs> To launch embedder, run `embedder` in your projects directory: ```bash theme={"system"} cd your-project embedder ``` ## Step 2: Log in to your account Embedder requires an account to use. When you start an interactive session with the `embedder` command, you'll need to log in: ```bash theme={"system"} embedder # You'll be prompted to log in on first use ``` Follow the prompts to log in with your account. If the web app doesn't automatically open, click the link on your screen. Once logged in, your credentials are stored and you won't need to log in again. To switch accounts later, use the `/logout` command. ## Step 3: Start your first session Open your terminal in any project directory and start Embedder: ```bash theme={"system"} cd /path/to/your/project embedder ``` You'll see the Embedder project selection screen. ### Creating a project <Frame> <img alt="SELECT PROJECT dialog with search box showing create new project button and previous projects list" /> </Frame> ### Select your platform After booting up, Embedder prompts you to select your hardware platform. Use the search box to filter platforms, then use the arrow keys to navigate and press Enter to select. <Frame> <img alt="SELECT PLATFORM dialog with search box showing nRF9xxx platforms from Nordic Semiconductor" /> </Frame> Select the platform that matches your hardware. Embedder uses the official documentation for your platform to ground its code generation and answers. To add custom platforms, see [common workflows](/core-concepts/common-workflows). <Tip> The `/console` command lets you upload additional documentation to existing platforms and peripherals in your project. </Tip> ### Select your peripherals After selecting your platform, Embedder prompts you to configure your peripherals — the external components your project uses. See [Add a Peripheral](/core-concepts/add-peripheral) for the full walkthrough, including how to upload a custom peripheral with its datasheet. <Tip> You can change your peripheral configuration later using the `/peripheral` command. </Tip> ## Step 4: Ask your first question Once your platform and peripherals are configured, you can ask hardware-specific questions in natural language. Embedder references the relevant datasheets, reference manuals, and errata automatically. If a device is connected, it also reads serial output in real time. Try asking about your hardware: ``` tell me about the nrf9151 gps capabilities ``` <Frame> <img alt="Embedder interface showing a prompt about nRF9151 GPS capabilities, document search results with hardware requirements table, and serial monitor displaying satellite information" /> </Frame> Embedder presents relevant documentation, including hardware requirements, pin configurations, and timing specifications. The serial monitor displays real-time output from your connected device. You can also ask about your codebase: ``` what does this project do? ``` ``` where is the main entry point? ``` ``` explain the folder structure ``` <Note> Embedder reads your files and datasheets as needed - you don't have to manually add context. </Note> ## Step 5: Make your first code change Now let's have Embedder generate some firmware. Try a simple task: ``` add a function to toggle the LED on GPIO pin 13 ``` Embedder will: 1. Find the appropriate file 2. Read the relevant register definitions from the datasheet 3. Show you the proposed changes with citations 4. Ask for your approval 5. Make the change <Note> Embedder always asks for permission before modifying files unless told otherwise. </Note> ## Step 6: Use Git with Embedder Embedder makes Git operations conversational: ``` what files have I changed? ``` ``` commit my changes with a descriptive message ``` You can also prompt for more complex Git operations: ``` create a new branch called feature/uart-driver ``` ``` show me the last 5 commits ``` ``` help me resolve merge conflicts ``` ## Step 7: Fix a bug or add a feature Describe the issue you're seeing and Embedder will help you debug it: ``` my SPI peripheral isn't responding - help me debug ``` Or paste an error: ``` I'm getting a hard fault when I call HAL_UART_Transmit - why? ``` Embedder will: * Check your code against the datasheet * Look for common configuration mistakes * Cross-reference known errata for your MCU * Suggest and test fixes ## Step 8: Test out other common workflows There are a number of ways to work with Embedder: **Write a driver** ``` write an I2C driver for the BME280 temperature sensor ``` **Configure a peripheral** ``` set up PWM on Timer 2 with a 1kHz frequency ``` **Generate initialization code** ``` initialize the ADC for 12-bit resolution on channel 5 ``` **Port code to a new platform** ``` help me port this STM32 driver to nRF52 ``` See [common workflows](/core-concepts/common-workflows) for more information. <Tip> **Remember**: Embedder understands your hardware. Ask it questions the way you'd ask an experienced firmware engineer who has memorized the datasheet. </Tip> ## Pro tips for beginners <AccordionGroup> <Accordion title="Be specific with your requests"> Instead of: "configure the timer" Try: "configure Timer 3 for a 10ms interrupt interval using the 16MHz HSI clock" </Accordion> <Accordion title="Use step-by-step instructions"> Break complex tasks into steps: ``` 1. initialize the SPI peripheral at 1MHz 2. write a function to read a register from the accelerometer 3. add a function to configure the accelerometer for ±2g range ``` </Accordion> <Accordion title="Let Embedder explore first"> Before making changes, let Embedder understand your code: ``` analyze my clock configuration ``` ``` what's the current interrupt priority setup? ``` </Accordion> </AccordionGroup> See [best practices](/core-concepts/best-practices) for more tips. ## What's next? Continue with [Best Practices](/core-concepts/best-practices) for tips on getting the best results, or [Common Workflows](/core-concepts/common-workflows) for practical examples. ## Getting help * **In Embedder**: Type `/help` or ask "how do I..." * **Documentation**: Browse our guides for detailed information * **Community**: Join our Discord community for tips and support