Skip to content

judell/bram

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1,114 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Bram

What is it?

A desktop app that helps you make best use of git and GitHub for AI-assisted software development.

Bram runs agents mindfully.

Who is it for?

Anyone who wants to use AI coding agents in a safe, orderly, and accountable way.

Bram has opinions. It thinks versioning and collaboration are well-handled by git and GitHub, so it guides agents to make best use of them on your behalf, in conversation with you. And it thinks GitHub is great for accountability, so it also guides agents to join you in orderly and well-documented collaboration that leaves an auditable trail.

v0 2 19

Blog

How to make best use of git and GitHub for AI-assisted software development

Vibe coding as a team sport

β€œDoctor, it hurts when agents create unreviewable PRs.” β€œDon’t do that.”

"What is the terminal?"

Don’t infer behavior from code, observe it in logs

Talking to Claude Code and Codex

Elsewhere

Technical or not, human or AI – could Bram be the missing link?

How does it work?

Terminal (optional)

A terminal where you run claude or codex. It can be hidden in favor of the agent pane's Worklist and Transcript views

Agent pane

  • Header: Switches between agents and adjusts font size.

  • Worklist: Guides you through the Bram workflow. Includes a live view of the last conversation turn.

  • Transcript: Shows your terminal session in a more readable form.

  • Issues: Tracks and searches GitHub issues for the repo Bram runs in.

  • Commits: Tracks and searches your commits.

  • Sessions: Lists your sessions; rename, delete, or switch between them.

  • History: Lists and searches your worklist items.

  • Settings: Shows and changes global (repo-independent) settings.

  • Footer: Shows agent status and the current session, and lets you message the agent.

Workflow

On the Worklist tab, create worklist items directly ("Hey agent, let's do x") or via the New Item button. Either way Bram proposes an item to be planned, built, iterated on, validated, committed, or maybe dropped along the way.

On the Issues tab, use + New issue to ask Bram to file a GitHub issue.

An item on the worklist lives in one of three phases: proposed β†’ applied β†’ committed. The arrows between the phases are approval gates where you can dwell and iterate with your agent to:

  • discuss and refine a proposal

  • discuss and refine an implementation

  • create, refine, and close issues

  • organize commits

By default every change request flows through the worklist. That's overkill for small things so, when messaging the agent from Bram's footer, you can use the skip worklist button instead of send. When messaging the agent from a worklist item, you can prefix your message with skip-worklist: or end it with "just do it".

Workflow conventions

The rules an agent follows when driving the Bram workflow β€” proposing worklist items, moving them through the approval gates, coordinating commits and issues β€” live in app/__shell/conventions.md. Each agent loads that file automatically: Claude through CLAUDE.md and the installed .claude hook/config path, Codex through a repo-local AGENTS.md block that setup installs. These aren't merely advisory: a PreToolUse hook on each agent enforces the core rule, rejecting file edits that aren't covered by an approved worklist item β€” so the convention enforces itself rather than relying on the agent's goodwill.

Prerequisites

  1. A local git repo in which you develop your app

  2. git β€” usually preinstalled on macOS and Linux; install via your package manager if missing.

  3. GitHub CLI (gh) - Powers the Issues tab in the agent pane and the agent's issue create / close / comment operations. Install from https://cli.github.com/ and run gh auth login once. Without it, the Issues tab shows an empty state.

  4. XMLUI CLI - optional. If you are developing an XMLUI app, or if you are developing Bram itself (the agent pane UI is an embedded XMLUI app) you will want the XMLUI MCP server. Follow the steps here to get it.

  5. whisper-server β€” optional. Powers the 🎀 voice button in the parent-shell toolbar and the agent pane. Tested on macOS and Windows/WSL, see Voice input below for install and per-platform status.

Install

macOS / Linux

curl -fsSL https://github.com/judell/bram/releases/latest/download/install.sh | bash

The script detects your platform, verifies the archive's SHA256 against the published SHA256SUMS, extracts the binary, and copies it to /usr/local/bin (if writable) or ~/.local/bin. On macOS it also clears the com.apple.quarantine xattr. No sudo required.

Windows

powershell -NoProfile -ExecutionPolicy Bypass -Command "irm https://github.com/judell/bram/releases/latest/download/install.ps1 | iex"

Downloads bram-windows-amd64.zip, verifies its SHA256, extracts bram.exe to ~/bin, and adds ~/bin to your user PATH.

Smart App Control

On some Windows 11 setups, Smart App Control may block the unsigned binary β€” most users report no problem. If you do hit a block, you can disable SAC under Windows Security β†’ App & browser control β†’ Smart App Control settings. Before flipping the switch, read Microsoft's Smart App Control FAQ so you understand the consequences for your machine β€” the re-enable path has changed across Windows updates.

First-run setup

The first time you launch claude or codex in a repo, Bram checks what that provider needs and prompts once if anything is missing β€” no prompt on later launches. Accepting Bram's setup prompt installs the worklist conventions the agent reads each session plus provider-specific hooks that enforce the worklist and surface permission menus. A provider-neutral authorization record and watcher-revert fallback back up those hooks.

Claude and Codex differ in the trust step you see after Bram writes those files:

  • Claude setup is repo-local. Bram writes .claude/bram-conventions.md, .claude/hooks/worklist-guard.py, .claude/hooks/permission-menu-hook.py, and .claude/settings.json registrations. Claude reads that project config on launch; there is no separate Codex-style hook trust menu. The Claude PreToolUse hook is the safety gate for Write, Edit, and Bash; the permission hook surfaces Claude permission and AskUserQuestion prompts in Bram's agent pane.
  • Codex setup has an explicit trust/approval step. Bram writes user-global hook scripts under ~/.bram, updates ~/.codex/config.toml, writes Bram developer_instructions, and adds a repo-local AGENTS.md block. Codex then shows its hook approval screen; approve it only when it points at Bram-owned paths such as ~/.bram/codex-worklist-guard.py, ~/.bram/codex-permission-menu-hook.py, ~/.codex/config.toml, and this repo's AGENTS.md. The Codex PreToolUse hook gates apply_patch, Bash, Write, Edit, and mutation-shaped MCP tools; the Codex permission hook surfaces PermissionRequest / PostToolUse menus in Bram's agent pane.
  • Do not approve unexpected Codex paths. If the Codex approval screen lists hook scripts somewhere other than ~/.bram or config changes somewhere other than the expected Codex/project files, stop and inspect them first.

Hook names mean the same thing in both providers:

  • PreToolUse runs before a tool executes and blocks mutations that are not covered by the worklist or by an explicit direct-edit authorization.
  • PermissionRequest lets Bram render permission prompts from structured hook payloads instead of scraping the terminal grid.
  • PostToolUse, when present, is post-tool bookkeeping for the same hook-driven permission/menu path.

A healthy Codex hook screen should show Bram's installed lifecycle hooks as both Installed and Active. In the current setup that means at least PreToolUse, PermissionRequest, and PostToolUse each show 1 / 1; lifecycle events Bram does not use, such as SessionStart or Stop, can remain 0 / 0.

Setup internals: hook adapters, guard source-of-truth, and how conventions.md binds each provider

Once you launch an agent through Bram's terminal, the agent pane checks what that provider still needs for the current repo and prompts only when setup is missing.

Current behavior:

  • Claude in a fresh repo β€” prompt once. Setup installs the provider-neutral core plus the Claude-specific adapter.
  • Claude in a repo that is already set up β€” no prompt.
  • Codex in a fresh repo β€” prompt once. Setup installs the provider-neutral core, the codex hook adapter, and the codex developer_instructions, and it also refreshes the shared Claude-side artifacts that live in the repo.
  • Codex in a repo where setup has already run β€” no prompt. The repo and user-global Codex setup artifacts are already in place.

When the prompt runs, Bram installs two layers:

  • A provider-neutral core: Bram records the latest structured approved: / drop: payload in resources/.worklist-authorization.json and uses that local record when validating worklist removals. The desktop watcher can revert an invalid prune as a defense-in-depth fallback if a hook ever fails to fire.
  • A Claude adapter: .claude/hooks/worklist-guard.py, registered in .claude/settings.json as a PreToolUse hook for Write|Edit|Bash. The hook denies edits to project files not covered by a proposed/applied worklist item (with explicit opt-out phrases in the last user message as the escape hatch), validates worklist-prune authorization for changes to resources/worklist.json itself, and blocks mutation-shaped Bash commands without worklist coverage. Setup also installs .claude/hooks/permission-menu-hook.py for Claude permission and AskUserQuestion surfacing in the agent pane.
  • A Codex adapter: ~/.bram/codex-worklist-guard.py, registered in ~/.codex/config.toml as a PreToolUse hook with matcher ^(apply_patch|Bash|Write|Edit|mcp__.*)$. Same coverage logic as the Claude hook, broadened to catch Codex's apply_patch tool and MCP filesystem write/edit/create/move calls. Setup also writes developer_instructions into the Codex config so the gate prose lands in the developer-role context part of every session, not just the user-role AGENTS.md, and installs ~/.bram/codex-permission-menu-hook.py for PermissionRequest / PostToolUse menu surfacing with the xterm grid retained as fallback. Existing ~/.xmlui-desktop/codex-worklist-guard.py installs remain accepted during migration; rerunning Setup rewrites the config to the Bram path.

In the Bram source repo, the Claude guard's source of truth is app/__shell/worklist-guard.py. The .claude/hooks/worklist-guard.py file is the installed runtime copy, refreshed from the source bundle by Setup and by src-tauri/build.rs during Cargo builds. Functional edits belong in app/__shell/worklist-guard.py; editing the installed copy directly creates setup drift and may be overwritten. The Codex guard uses the same source/installed split: app/shell/worklist-guard-codex.py is canonical, ~/.bram/codex-worklist-guard.py is installed.

PreToolUse hooks are the generic extension point β€” both Claude Code and codex expose them β€” so the two adapters share the same shape: each runs before the agent invokes a tool, receives a JSON payload describing the pending call on stdin, and can exit 0 to allow, return a deny decision to block (stderr/permissionDecisionReason goes back to the agent as a tool error), or fail to launch.

That means first-run setup is provider-aware in when it prompts but provider-symmetric in what it installs: launching either claude or codex and accepting the prompt sets up the shared core, the Codex-side AGENTS.md guidance block, the Codex developer_instructions, the worklist guard hooks, and the permission-menu surfacing hooks.

How conventions.md governs both agents

app/__shell/conventions.md is the canonical project convention file. It governs Claude and Codex in different ways:

  • Claude: direct prompt binding plus enforcement. Setup copies that file to .claude/bram-conventions.md, adds an @-import block to CLAUDE.md, and installs the worklist-guard.py PreToolUse hook. A new Claude session therefore reads the conventions file directly and is also mechanically blocked from unsafe worklist edits. Existing projects with the legacy .claude/xmlui-desktop-conventions.md path are migrated to the new name on the next Setup run.
  • Codex: repo-local AGENTS.md plus native hook enforcement. Setup writes a marked Bram block into repo-root AGENTS.md, installs top-level developer_instructions in ~/.codex/config.toml, and registers the Codex worklist guard as a native PreToolUse hook. Codex launches also receive the same concise worklist guidance as a startup seed. The app reinforces that with the shared local authorization record in resources/.worklist-authorization.json and the watcher-revert fallback as defense in depth.

So the practical rule is: both agents are governed by the same worklist conventions, with Claude reading the imported conventions file directly and Codex receiving the equivalent guidance through AGENTS, top-level developer_instructions, and its native hook adapter.

Claude and Codex also differ in how they call Bram's worklist lifecycle routes. Claude uses the loopback HTTP endpoints directly. Codex's sandbox refuses loopback connections (curl: (7) even when Bram is listening, #130), so it drives the identical lifecycle over a filesystem channel instead β€” writing resources/.worklist-intent.json and reading resources/.worklist-result.json, which the host dispatches through the same handlers as the HTTP routes.

The provider hooks validate direct edits to resources/worklist.json. Proposal authoring and iterate-time prose refinement are allowed there; mechanical prune / status-advance operations are expected to go through POST /__worklist/mutate instead. Both providers now reject direct worklist edits that remove items or change their status, which keeps the shared backend endpoint as the canonical state machine for advance / prune. The watcher-based fallback (compare old/new worklist snapshots, consult resources/.worklist-authorization.json, restore prior contents if the prune wasn't authorized) remains as defense-in-depth β€” it fires later than a native hook, but it covers the case where a hook fails to launch (e.g., Python missing) or where a future provider integration lacks a comparable extension point.

The hook is a Python script and needs Python 3 to run. On macOS and Linux it's invoked directly via its shebang (#!/usr/bin/env python3), so python3 must be on PATH β€” almost always the case. On Windows it's invoked via py -3 <path>; the py launcher ships with the python.org installer and resolves Python via the Windows registry, independent of PATH. If Python isn't installed at all, Claude Code shows "Failed with non-blocking status code" for every Write/Edit and the validator is silently inert β€” writes still proceed, but the worklist guard isn't actually checking them. Install Python 3 to enable enforcement.

Configuration

.bram.json at project root is the primary config file. Legacy .xmlui-desktop.json is still accepted as a compatibility alias from Bram's prior name.

Startup

Bram autostarts an agent in the terminal at launch. Configure it under shell in .bram.json (the same keys Settings writes):

{
  "shell": {
    "agent": "claude",
    "continueLast": true
  }
}
  • agent β€” which provider to launch, claude or codex (defaults to claude).
  • args β€” optional extra arguments appended to the launch command.
  • continueLast β€” when true, resume the most recent session (claude --continue / codex resume --last) instead of starting fresh.
  • firstCommand β€” optional command typed into the agent once it's ready.

Voice input

Bram supports two ways to dictate instead of type:

  • 🎀 Whisper buttons (recommended). Local, low-latency dictation via whisper-server. Click the 🎀 button in the parent-shell toolbar (or the agent pane) to start recording, click again to send; the transcript arrives in the terminal as a voice: ... line so it's distinguishable from typed input. This is the better experience β€” lower latency, your choice of model, good transcription quality β€” but it needs local setup.
  • The agent's native /voice command. No local setup, but support varies by agent and platform. It's the zero-install fallback, and the working path where the Whisper button isn't proven yet.

Bram spawns the local whisper-server on the first record click and kills it on app exit β€” you don't manage the process; you just need the binary, ffmpeg, and a model file installed.

macOS

brew install whisper-cpp ffmpeg
mkdir -p ~/.local/share/whisper-models
curl -L -o ~/.local/share/whisper-models/ggml-small.en.bin \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin

small.en is ~466 MB, English-only, real-time on Apple Silicon. Swap in a different model from the same Hugging Face repo for other size/accuracy/language tradeoffs. The bundled Info.plist declares NSMicrophoneUsageDescription, so first use triggers the standard macOS mic-permission prompt. The model path the app loads is ~/.local/share/whisper-models/ggml-small.en.bin.

Windows / WSL

On Windows, Bram launches whisper-server inside WSL via wsl.exe bash -lc and talks to it through http://127.0.0.1:18080 from the WebView. WSL2 forwards that loopback port to Windows automatically, so the request path is the same as on macOS once the server is running.

Prerequisite: WSL2 with Ubuntu. If you don't already have it, open PowerShell and run wsl --install, which installs WSL and the default Ubuntu distro. Restart when prompted, then complete Ubuntu's first-run setup (pick a Linux username and password). Microsoft's full install doc: https://learn.microsoft.com/en-us/windows/wsl/install.

One-time setup inside Ubuntu. Open Ubuntu (Start menu β†’ Ubuntu, or run wsl from PowerShell), then paste this whole block. The cmake build takes a few minutes on modern hardware; the model download is ~466 MB.

sudo apt update
sudo apt install -y build-essential cmake ffmpeg git curl
git clone https://github.com/ggml-org/whisper.cpp.git
cd whisper.cpp
cmake -B build
cmake --build build -j --config Release
sudo cp build/bin/whisper-server /usr/local/bin/
mkdir -p ~/.local/share/whisper-models
curl -L -o ~/.local/share/whisper-models/ggml-small.en.bin \
  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.en.bin

That's the whole install. Bram handles starting/stopping whisper-server on its own β€” first 🎀 click after launching Bram spawns it inside WSL (the button shows ⏳ for ~2-3 s while the model loads), and every click after that is fast until Bram exits.

Notes:

  • Mic permission. WebView2 inherits the standard Windows microphone prompt. On first 🎀 click Windows asks to allow mic access; click Yes.
  • Multiple WSL distros. If Ubuntu isn't your default distro, set BRAM_WSL_DISTRO=Ubuntu (or whatever distro name) in your Windows environment before launching Bram. Single-distro setups need no env var.
  • Already running? If you happen to have whisper-server listening on port 18080 (e.g., started manually in another terminal), Bram detects it via a preflight probe and uses it instead of spawning a new one β€” no conflict.
  • Sanity check after install. From inside Ubuntu, which whisper-server should print /usr/local/bin/whisper-server, and ls ~/.local/share/whisper-models/ should show ggml-small.en.bin. If both look right, you're done.

Linux

The same setup is expected to work on non-WSL Linux, using the host process path and port 18080.

Target app

An optional pane above the agent pane, off by default β€” a project iframe for previewing your app inside Bram. Most people run their app in their own server and view it in their own browser, so it stays hidden unless you turn it on (Settings β†’ UI β†’ "Show target app"). It's handy for a very simple app or a quick check, not the common case. When enabled, Bram runs a basic static webserver and reloads the pane automatically as project files change.

Serving a real backend: fixed origin, project server, URL parameters

Working with a real backend

Bram binds the target app HTTP server to 127.0.0.1:<random-port> (it uses port 0 and lets the OS pick). That's fine for projects that talk only to public APIs or static files. It breaks when your project needs a fixed origin β€” OAuth callbacks, CORS allowlists, hardcoded API base URLs.

Compatibility note. The target app is an iframe. Backends that send X-Frame-Options: DENY or Content-Security-Policy: frame-ancestors 'none' (common for security-sensitive admin UIs) cannot be loaded into the target app regardless of port. Workarounds: configure the backend's dev mode to relax those headers, or serve the UI files via a permissive dev server (e.g. npx http-server) while keeping the real backend running for API calls. Otherwise, open the project in a standalone browser.

Declare a project server

Add .bram.json at the project root:

{
  "server": {
    "command": "python3 -m http.server 8080",
    "cwd": "xmlui",
    "port": 8080,
    "path": "/"
  }
}
field meaning
command shell command to bring up the project's server. Run via sh -c (Unix) or cmd /C (Windows).
cwd working directory for the command, relative to the project root. Optional; defaults to the project root.
port TCP port the iframe should target. Bram probes this port at startup.
path URL path appended to http://localhost:<port> for the iframe. Optional; defaults to /.

At startup, Bram:

  • probes 127.0.0.1:<port>. If it's already listening, it logs a notice and reuses the running server (useful when you start the server manually for log visibility);
  • otherwise spawns command in cwd, with stdout/stderr forwarded to Bram's own stderr (prefixed [server]);
  • waits up to 5s for the port to come up, then points the target app iframe at http://localhost:<port><path>. The iframe retries once on load error to absorb the case where the server takes a moment to bind;
  • on app exit, kills the spawned child.

The agent pane continues to load from Bram's internal loopback server regardless of this setting.

The app-under-test does not need to be an XMLUI app β€” .bram.json is Bram's own config file, separate from XMLUI's config.json. Legacy .xmlui-desktop.json remains supported.

URL parameters

Use query strings to parameterize the frontend without rebuilding β€” e.g. ?city=santarosa to switch tenant. Pass them on the command line to your server's command or bake them into path (e.g. "path": "/?city=santarosa").

Working example

community-calendar uses .bram.json for GitHub-OAuth-via-Supabase development. See docs/app-architecture.md for the Supabase URL-Configuration setup that requires the fixed localhost:8080/** origin.

Fallback: the redirect pattern

If you can't add a config file (e.g. you're working in a repo you don't own), you can still target a fixed origin by adding a self-redirect at the top of the project's index.html:

<script>
  if (location.hostname === '127.0.0.1' && location.port !== '8080') {
    var devQuery = location.search || '?defaultParam=value';
    location.replace('http://localhost:8080' + location.pathname + devQuery + location.hash);
  }
</script>

Run your frontend on a known port in a separate terminal (python3 -m http.server 8080) and launch Bram from the project root. Its iframe loads the random-port URL once, your script bounces it to localhost:8080. .bram.json is the preferred mechanism β€” it auto-spawns the server, surfaces logs, and doesn't pollute the project's HTML.

Service workers, auth callbacks, and DevTools

Service workers don't register on macOS/Linux

The target app iframe loads at tauri://localhost, and the WebKit engines on macOS (WKWebView) and Linux (WebKitGTK) don't treat custom-scheme origins as secure contexts. Service-worker registration silently fails there, so project features that depend on a service worker β€” Mock Service Worker (MSW), XMLUI's in-page apiInterceptor, custom offline caches β€” won't activate inside Bram on those platforms. Windows uses WebView2 (Chromium) with the http://tauri.localhost form, which is a secure context, so service workers register normally there.

Apps that hit a real HTTP backend are unaffected; the constraint only applies to in-page request interception. If you're developing against MSW or apiInterceptor, run your project in a regular browser tab at localhost:8080 while keeping Bram pointed at the same server for the agent loop.

Auth callbacks won't reach the target app

The target app webview has its own browser storage, isolated from your system browser's storage at the same origin. That breaks any auth flow that hands off to the system browser and expects a session to come back into the webview:

  • Magic links in email. Clicking the link opens your default browser, completes auth there, and stores the session in the browser's localStorage. The target app never sees it.
  • OAuth provider redirects that leave the webview have the same shape β€” the callback session lands in the wrong storage.

Even when the redirect script above lines the target app up on localhost:8080, that origin's storage in the Tauri webview is a different store from localhost:8080 storage in Safari or Chrome.

Workaround for email auth: send a one-time code, not a link. If your backend supports OTP codes (Supabase, Auth0, Clerk, Cognito all do), have the user paste the code from the email into a field in your dialog. No callback URL, no cross-context handoff. Works identically in the browser and inside Bram.

For Supabase specifically:

  1. Add {{ .Token }} to the Magic Link email template (Supabase dashboard β†’ Authentication β†’ Email Templates) so the email includes the 6/8-digit code. Docs: https://supabase.com/docs/guides/auth/auth-email-templates
  2. After signInWithOtp, render a code-input field and call verifyOtp({ email, token, type: 'email' }). Docs: https://supabase.com/docs/guides/auth/auth-email-passwordless
  3. The existing onAuthStateChange handler fires on verifyOtp success β€” no other plumbing needed.

community-calendar implements this in xmlui/components/SignInDialog.xmlui and xmlui/shell.js (window.signInWithEmail + window.verifyEmailOtp).

DevTools

Tauri uses the platform's native webview, so the DevTools you get inside the target app depend on the OS:

Platform Webview DevTools
macOS WKWebView Safari Web Inspector
Linux WebKitGTK Safari Web Inspector
Windows WebView2 (Chromium) Chromium DevTools

To open them, right-click inside the target app β†’ Inspect Element in dev/debug builds (cargo run or cargo tauri dev). Release builds disable DevTools by default. The execution context belongs to the target app document specifically. The shell window and the right pane both load at tauri://localhost (the parent shell directly, the target app via the scheme handler that proxies project content under /__project/*), so they share an origin and therefore a localStorage / IndexedDB partition β€” a console session in either reaches the same storage. A regular browser tab pointed at the project's own localhost:8080 server, by contrast, is a different origin with its own independent storage.

WebKit quirks worth knowing

The macOS/Linux Web Inspector behaves differently from Chromium's DevTools in a few ways that bite when you're testing auth flows:

  • const/let redeclaration throws. Pasting const sb = … a second time in the same console session yields "Unexpected identifier 'sb'. Expected ';' after variable declaration." Chromium silently redeclares; WebKit doesn't. Wrap repeated snippets in an async IIFE ((async () => { … })();) so the bindings are scoped to each call.
  • Frame/context switcher is sparser. The dropdown that picks the execution context (top-level vs iframes) often won't expose every frame the page contains. Right-clicking inside the frame you actually want and choosing Inspect Element is more reliable than picking it from the dropdown.
  • Service-worker and storage panels are less complete than Chromium's. If you need to inspect IndexedDB or service-worker scope details, run the same project in a regular Chrome/Edge tab pointed at localhost:8080.

If you'd rather use Chromium DevTools on macOS/Linux, you can run your project in a regular browser tab pointed at its localhost:8080 origin β€” but remember that the tab's localStorage is a separate store from the target app's (the target app is at tauri://localhost, a different origin), so a session created there won't carry into Bram.

Build

The frontend is static β€” no bundler, no package.json. The only build step is the Tauri/Rust build.

From src-tauri/:

  • Dev: cargo run (or cargo tauri dev with the Tauri CLI)
  • Release: cargo tauri build

Tauri docs: https://tauri.app/develop/, https://tauri.app/distribute/.

Calling Bram from project code

Because the target app is same-origin with the parent shell (tauri://localhost), project code can reach the Tauri command bridge directly through window.parent β€” no postMessage shim needed:

const { invoke } = window.parent.__TAURI__.core;
const url = await invoke("get_right_pane_url");

Use this when an XMLUI app embedded in the target app needs to read filesystem state, hit one of Bram's __-prefixed loopback endpoints, or invoke any of the Rust IPC commands. The helpers.js script loaded by the embedded XMLUI surfaces (toShell, toTurn, openExternal, logToHost) is built on top of this bridge β€” opt into the helpers for project XMLUI apps that need to talk back to the running agent.

Layout

  • Main.xmlui, components/, resources/, Globals.xs, config.json, index.html β€” the XMLUI app at the repo root.
  • app/ β€” parent shell (Tauri webview entry, terminal wiring, vendor scripts, and __shell/helpers.js that the target app includes).
  • src-tauri/ β€” Rust backend (PTY for the terminal, custom tauri:// URI scheme handler that proxies the target app iframe to the project's HTTP server, filesystem watcher, IPC handlers).
  • scripts/ β€” auxiliary scripts.

About

No description, website, or topics provided.

Resources

License

Security policy

Stars

32 stars

Watchers

1 watching

Forks

Packages

 
 
 

Contributors