A desktop app that helps you make best use of git and GitHub for AI-assisted software development.
Bram runs agents mindfully.
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.
How to make best use of git and GitHub for AI-assisted software development
βDoctor, it hurts when agents create unreviewable PRs.β βDonβt do that.β
Donβt infer behavior from code, observe it in logs
Talking to Claude Code and Codex
Technical or not, human or AI β could Bram be the missing link?
A terminal where you run claude or codex. It can be hidden in favor of the agent pane's Worklist and Transcript views
-
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.
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".
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.
-
A local git repo in which you develop your app
-
gitβ usually preinstalled on macOS and Linux; install via your package manager if missing. -
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 rungh auth loginonce. Without it, the Issues tab shows an empty state. -
XMLUI CLI - optional. If you are developing an XMLUI app, or if you are developing
Bramitself (the agent pane UI is an embedded XMLUI app) you will want the XMLUI MCP server. Follow the steps here to get it. -
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.
curl -fsSL https://github.com/judell/bram/releases/latest/download/install.sh | bashThe 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.
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.
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.
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.jsonregistrations. Claude reads that project config on launch; there is no separate Codex-style hook trust menu. The ClaudePreToolUsehook is the safety gate forWrite,Edit, andBash; 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 Bramdeveloper_instructions, and adds a repo-localAGENTS.mdblock. 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'sAGENTS.md. The CodexPreToolUsehook gatesapply_patch,Bash,Write,Edit, and mutation-shaped MCP tools; the Codex permission hook surfacesPermissionRequest/PostToolUsemenus in Bram's agent pane. - Do not approve unexpected Codex paths. If the Codex approval screen lists hook scripts somewhere other than
~/.bramor config changes somewhere other than the expected Codex/project files, stop and inspect them first.
Hook names mean the same thing in both providers:
PreToolUseruns before a tool executes and blocks mutations that are not covered by the worklist or by an explicit direct-edit authorization.PermissionRequestlets 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 inresources/.worklist-authorization.jsonand 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.jsonas aPreToolUsehook forWrite|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 toresources/worklist.jsonitself, and blocks mutation-shaped Bash commands without worklist coverage. Setup also installs.claude/hooks/permission-menu-hook.pyfor Claude permission and AskUserQuestion surfacing in the agent pane. - A Codex adapter:
~/.bram/codex-worklist-guard.py, registered in~/.codex/config.tomlas aPreToolUsehook with matcher^(apply_patch|Bash|Write|Edit|mcp__.*)$. Same coverage logic as the Claude hook, broadened to catch Codex'sapply_patchtool and MCP filesystem write/edit/create/move calls. Setup also writesdeveloper_instructionsinto the Codex config so the gate prose lands in the developer-role context part of every session, not just the user-roleAGENTS.md, and installs~/.bram/codex-permission-menu-hook.pyforPermissionRequest/PostToolUsemenu surfacing with the xterm grid retained as fallback. Existing~/.xmlui-desktop/codex-worklist-guard.pyinstalls 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.
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 toCLAUDE.md, and installs theworklist-guard.pyPreToolUse 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.mdpath 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-leveldeveloper_instructionsin~/.codex/config.toml, and registers the Codex worklist guard as a nativePreToolUsehook. Codex launches also receive the same concise worklist guidance as a startup seed. The app reinforces that with the shared local authorization record inresources/.worklist-authorization.jsonand 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.
.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.
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,claudeorcodex(defaults toclaude).argsβ optional extra arguments appended to the launch command.continueLastβ whentrue, 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.
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 avoice: ...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
/voicecommand. 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.
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.binsmall.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.
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.binThat'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-serverlistening on port18080(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-servershould print/usr/local/bin/whisper-server, andls ~/.local/share/whisper-models/should showggml-small.en.bin. If both look right, you're done.
The same setup is expected to work on non-WSL Linux, using the host process path and port 18080.
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
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: DENYorContent-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.
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
commandincwd, 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.
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").
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.
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
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.
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:
- 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 - After
signInWithOtp, render a code-input field and callverifyOtp({ email, token, type: 'email' }). Docs: https://supabase.com/docs/guides/auth/auth-email-passwordless - The existing
onAuthStateChangehandler fires onverifyOtpsuccess β no other plumbing needed.
community-calendar
implements this in xmlui/components/SignInDialog.xmlui and
xmlui/shell.js (window.signInWithEmail + window.verifyEmailOtp).
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.
The macOS/Linux Web Inspector behaves differently from Chromium's DevTools in a few ways that bite when you're testing auth flows:
const/letredeclaration throws. Pastingconst 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.
The frontend is static β no bundler, no package.json. The only build
step is the Tauri/Rust build.
From src-tauri/:
- Dev:
cargo run(orcargo tauri devwith the Tauri CLI) - Release:
cargo tauri build
Tauri docs: https://tauri.app/develop/, https://tauri.app/distribute/.
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.
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.jsthat the target app includes).src-tauri/β Rust backend (PTY for the terminal, customtauri://URI scheme handler that proxies the target app iframe to the project's HTTP server, filesystem watcher, IPC handlers).scripts/β auxiliary scripts.